TypeScript: JavaScript's First Line of Defence

I never really understood the hype around TypeScript.
JavaScript worked. I could build with it, things ran, and whenever I saw TypeScript code, a lot of it just looked like JavaScript with extra stuff added everywhere.
Take this:
let username = "Bashir";
Perfectly normal JavaScript.
Then you see the TypeScript version:
let username: string = "Bashir";
What is : string doing there? "Bashir" is obviously text. JavaScript already knows that.
So why do we need TypeScript?
Turns out, that little : string is a good place to start.
First, what is a type?
Every value you work with in JavaScript has a type.
"Bashir" // string
25 // number
true // boolean
A string is text. A number is, well, a number. A boolean is either true or false.
JavaScript already has these types. TypeScript did not invent them.
The difference is what TypeScript lets us do with that information.
When I write:
let username: string = "Bashir";
I'm telling TypeScript:
username should contain a string.
The colon basically reads as "has the type."
So:
let age: number = 22;
can be read as:
age has the type number.
And:
let loggedIn: boolean = true;
means:
loggedIn has the type boolean.
Now if I do this:
let age: number = 22;
age = "twenty two";
TypeScript complains.
I said age should be a number, then tried to give it a string.
JavaScript would let me do that. TypeScript asks me to explain myself before the code even runs.
That is our first bit of defence.
But surely we don't need to write types on everything?
No, and this was something I misunderstood about TypeScript too.
This is completely valid TypeScript:
let username = "Bashir";
TypeScript can look at "Bashir" and figure out that username is a string.
That's called type inference. In simple terms, TypeScript is saying:
You didn't tell me the type, but I can work it out.
So TypeScript isn't really about adding : string to every variable you create. Things get more interesting when the answer isn't so obvious.
Functions are where I started seeing the point
Consider some regular JavaScript:
function calculatePrice(price, quantity) {
return price * quantity;
}
What is price supposed to be? What about quantity?
We can probably guess they're numbers, but JavaScript doesn't enforce that. Someone could call:
calculatePrice("hello", 4);
The function will still try to run.
With TypeScript, we can describe what the function accepts:
function calculatePrice(
price: number,
quantity: number
) {
return price * quantity;
}
Remember what the colon means: has the type. So:
price: number
reads as: price has the type number. Same thing we did with variables.
Now this:
calculatePrice("hello", 4);
gets flagged before the program runs.
We can go one step further:
function calculatePrice(
price: number,
quantity: number
): number {
return price * quantity;
}
There's another : number after the brackets. That one describes what the function gives back.
So we can almost read the entire thing as English:
calculatePrice takes a number called price, another number called quantity, and returns a number.
Suddenly the extra syntax is actually telling us something.
We aren't limited to string and number
Imagine we're building something with users.
A user isn't just a string or a number. Maybe our users look like this:
{
name: "Bashir",
email: "bashir@example.com",
age: 22
}
Writing that shape out by hand every time we need a user gets old fast, and it's easy to accidentally leave a field out somewhere. TypeScript lets us give the shape a name once, and reuse it everywhere:
type User = {
name: string;
email: string;
age: number;
};
Don't let type User make this more complicated than it is. We're basically saying:
Whenever I say something is a User, this is what I mean.
Now:
const user: User = {
name: "Bashir",
email: "bashir@example.com",
age: 22
};
Remember our colon again: user has the type User. And therefore TypeScript knows what should be inside it.
If I accidentally write:
const user: User = {
name: "Bashir",
email: "bashir@example.com",
age: "twenty two"
};
we have a problem. We previously said age: number, but provided a string. TypeScript catches it.
And this is where type started making more sense to me. We aren't just labelling individual variables anymore. We're describing what our application's data is supposed to look like.
And none of this is happening while your app runs
This is probably the most important part.
TypeScript checks your code before it runs. The types themselves don't stick around.
This:
const username: string = "Bashir";
eventually becomes JavaScript without that type information:
const username = "Bashir";
That means TypeScript is not some security guard watching every value while your application is running. It is more like the person checking everything before the doors open.
And that also means it has limits.
If your server expects:
type User = {
name: string;
age: number;
};
someone on the internet can still send:
{
"name": 500,
"age": "banana"
}
TypeScript cannot stop them from sending it. That is where things like validation come in, but that's a conversation for another day.
TypeScript's job happened earlier. It helped us catch mistakes in our code before that code reached the person using it.
So, is JavaScript not enough?
I still wouldn't put it that way.
JavaScript gets the job done. It did before TypeScript existed, and it still does now.
What I'm starting to understand is why so many developers choose to put TypeScript on top of it.
The little : string that initially looked like unnecessary syntax is part of something much bigger. We're describing what our program expects. What should go into this variable? What should this function accept? What should it return? What should a user look like?
And when our code contradicts one of those expectations, TypeScript gets the first chance to catch it.
That's why I'm starting to think of TypeScript as JavaScript's first line of defence.
Not its only one. Just the first.



