Why TypeScript?
See which JavaScript mistakes TypeScript catches as you type, how it helps your editor, and what it can't check, with a cart you can edit and break.
- 01Say what TypeScript addsDescribe TypeScript as JavaScript plus types, which are checked before the code runs and removed after.
- 02Read what it catchesSpot the mistakes TypeScript reports as you type, read its error messages in plain words, and use its editor help.
- 03Know its limitsExplain what TypeScript can't check, like data that arrives while the page runs, and what it costs to use.
A bug caught before anyone sees it
A shop page adds up a cart. Tea costs 3 and cake costs 4, so the total should be 7. But the cake's price came from a form, and a form gives you text, not a number. Here is what plain JavaScript does with that:
const teaPrice = 3;
const cakePrice = "4"; // typed into a form, so it's text
console.log(teaPrice + cakePrice);Line 1 stores the number 3. Line 2 stores "4" in quotes, which is text. Line 3 adds them and prints 34. When one side of + is text, JavaScript joins the two instead of adding them (the Type conversion lesson explains why). Nothing warns you. The first person to notice might be a shopper looking at a total of $34.
TypeScript lets you say what kind of value each name should hold. That kind is called a type. Here is the same code with two types added:
const teaPrice: number = 3;const cakePrice: number = "4";console.log(teaPrice + cakePrice);Line 1 says teaPrice is a number, and it is. Line 2 says cakePrice is a number too, but gives it text. TypeScript marks line 2 with an error, on purpose here, because it's the bug:
Type 'string' is not assignable to type 'number'means "you're putting text where a number has to go". Astringis text, and "assignable" means "allowed to be stored there".ts(2322)is the error's number. Every TypeScript error has one, so you can look it up.
The list under the code comes from the real TypeScript compiler, running in this page. Point at a name to see its type. Now try it yourself: type a different value for the cake's price, and watch the list under the code as you type.
const teaPrice: number = 3;const cakePrice: number = "4";console.log(teaPrice + cakePrice);"4"1 error: line 2prints 34Line 2 puts the text "4" where a number has to go, so TypeScript marks it: Type 'string' is not assignable to type 'number'. When it runs, 3 + "4" joins the two and prints 34.
With 4 or Number("4"), line 2 holds a number, so the error disappears. Put the quotes back and it returns. true and four get errors of their own, and an empty box gets Expression expected, because line 2 needs a value. Every TypeScript example in this lesson works the same way: press Edit & run, change the code, and the errors update as you type.
$34.When you type a letter on a computer, a spell checker underlines "recieve" the moment you write it. You fix it right there, long before anyone reads the letter. The underlines help you while you write, and they never appear on the paper.
- In real life: You write a letter
- In TypeScript: You write your code
- In real life: The spell checker underlines a wrong word as you type
- In TypeScript: TypeScript marks a wrong type as you type
- In real life: You fix the word before you post the letter
- In TypeScript: You fix the code before a shopper sees it
- In real life: The underlines aren't printed on the letter
- In TypeScript: The types are removed before the browser runs the code
TypeScript is JavaScript plus types. A type says what kind of value a name must hold, like cakePrice: number. TypeScript checks your code against its types while you type and points at mistakes. Then it removes the types, so the browser runs plain JavaScript.
JavaScript with types
TypeScript isn't a new language you have to learn from the start. Almost any JavaScript is already TypeScript. Here is plain JavaScript, with no types written at all, checked by TypeScript:
const user = { name: "Asha", cartItems: 2 };const greeting = `${user.name} has ${user.cartItems} items`;// ^?console.log(greeting);Line 1 makes a user object with a name and a number of cart items. Line 2 builds a message from them with a template literal. Line 4 prints Asha has 2 items. There are no errors.
Line 3 is a comment, so it changes nothing when the code runs. In this lesson a comment like // ^? asks a question: "what type is the name my ^ points at?" The page writes TypeScript's answer after it: const greeting: string. You never wrote string. TypeScript worked it out from the value, which is called inference. In your own editor, you get the same answer by pointing at the name.
When you want to say a type yourself, you write a colon and the type after a name. This is called a type annotation. Functions are where you'll write them most:
function lineTotal(price: number, quantity: number) { return price * quantity;} const total = lineTotal(3, 2);// ^?console.log(total);Line 1 says lineTotal takes a price that is a number and a quantity that is a number. Line 2 multiplies them. Line 5 calls it with 3 and 2. The query on line 6 shows that TypeScript knows total is a number, without an annotation on line 5. Line 7 prints 6.
Browsers can't run price: number; it isn't JavaScript. So TypeScript does two jobs. First it checks the types. Then it removes them, and what's left is plain JavaScript. The program that does this is the TypeScript compiler, called tsc. Here is what it gives the browser for the example above:
cart.ts with its types. TypeScript checks the types, then removes them. The browser runs cart.js, the same code without the types.function lineTotal(price, quantity) {
return price * quantity;
}
const total = lineTotal(3, 2);
console.log(total);Line 1 has lost its two : number annotations. The rest is the same code, and it still prints 6. The query comment is left out here: it only asked TypeScript a question, and a comment does nothing when the code runs. The next lesson, How TypeScript works, follows this step closely.
The table below compares the two. Read each row as a question, then each language's answer.
| Question | JavaScript | TypeScript |
|---|---|---|
| What does the file name end in? | .js, like cart.js. | .ts, like cart.ts. |
Can you write a type, like price: number? | No. That line is a syntax error in JavaScript. | Yes. Types are the part TypeScript adds. |
| When do you find a wrong type? | When the code runs and something goes wrong, maybe for a user. | As you type, before the code runs. |
| What does the browser run? | Your code, as you wrote it. | Plain JavaScript: your code with the types removed. |
Bugs caught while you type
The price bug from the start of the lesson is one of many mistakes TypeScript points at before your code runs. Try it on a whole cart. The code has a type alias on line 1: type CartItem = … gives a name to the shape every cart item must have, a name that is text and a price that is a number. Line 3 says cart is a list of those items.
type CartItem = { name: string; price: number }; const cart: CartItem[] = [ { name: "Tea", price: 3 }, { name: "Cake", price: 4 },]; let total = 0;for (const item of cart) { total += item.price;}console.log(`Total: $${total}`);Total: $74No errorsLine 5 gives Cake the number 4. Every price is a number, so TypeScript has nothing to report, and the cart prints Total: $7.
Press each button and watch the list under the code. With A number, there are no errors and the total is $7. Text from a form puts "4" on line 5: the error appears, and the shopper would see $34. Text, converted wraps it in Number(…), which turns text into a number, and the error disappears. Price left out gets Property 'price' is missing, because every CartItem needs a price; the shopper would see NaN, which means "not a number". You can also press Edit & run and type any price you like on line 5.
Here are three more mistakes. Each one gets its error on purpose. First, a misspelled property:
const cart = { items: 2, total: 7 };console.log(cart.totl);Line 1 makes a cart with items and total. Line 2 asks for cart.totl. TypeScript knows the cart has no totl, and its message even suggests the fix: Did you mean 'total'? Plain JavaScript would print undefined.
function applyDiscount(total: number, percent: number) { return total - (total * percent) / 100;}console.log(applyDiscount(20));Line 1 says applyDiscount takes a total and a percent. Line 2 takes the percent off the total. Line 4 calls it with only 20. TypeScript says Expected 2 arguments, but got 1. Plain JavaScript would use undefined for the missing percent and print NaN.
const cart = [{ name: "Tea", price: 3 }];const cake = cart.find((item) => item.name === "Cake");console.log(cake.price);Line 1 makes a cart with only tea. Line 2 uses find, which gives back the first item that matches, or undefined when none does (Searching arrays covers it). There's no cake, so cake is undefined. Line 3 reads its price. When this runs, JavaScript throws a TypeError and the code stops. TypeScript saw it coming: 'cake' is possibly 'undefined'.
The table below puts the four mistakes side by side. Read each row as: this mistake, then what each language does about it.
| The mistake | Plain JavaScript | TypeScript |
|---|---|---|
A price arrives as text: price: "4" | It prints Total: $34. Nothing warns you. | Line 5 gets an error: Type 'string' is not assignable to type 'number'. |
A misspelled property: cart.totl | It prints undefined. | An error that also suggests the fix: Did you mean 'total'? |
A missing argument: applyDiscount(20) | It prints NaN, which means "not a number". | An error: Expected 2 arguments, but got 1. |
A product that isn't in the cart: cake.price | It throws a TypeError, and the code stops. | An error: 'cake' is possibly 'undefined'. |
Editor help: autocomplete and rename
Because TypeScript knows the type of every value, your editor can use that knowledge while you write. Editors like VS Code ask TypeScript about your code all the time, even in plain .js files. Here is what TypeScript knows about one object:
const settings = { theme: "dark", fontSize: 16, showTips: true };// ^?settings.fontSize = 18;Line 1 makes a settings object for a page: a theme, a font size, and whether to show tips. The query on line 2 shows the type TypeScript worked out: theme is a string, fontSize is a number, and showTips is a boolean (true or false). Line 3 changes the font size to 18, which is fine because 18 is a number.
That type is how autocomplete works. When you type settings., the editor lists the three properties, so you pick a name instead of remembering how to spell it:
settings. and the editor lists the three properties settings has. It knows them from the type TypeScript worked out on line 1.The same knowledge makes renaming safe. Say you decide price should be called unitPrice. In plain JavaScript, you search for every price and hope you found them all. With TypeScript, change the name in the type, and every place still using the old name gets an error. Step through a rename:
type CartItem = { name: string; price: number }; const tea: CartItem = { name: "Tea", price: 3 };const cake: CartItem = { name: "Cake", price: 4 }; function lineTotal(item: CartItem, quantity: number) { return item.price * quantity;} console.log(lineTotal(tea, 2) + lineTotal(cake, 1));10No errorsEvery line says price. TypeScript has no errors, and the order costs 10: two teas at 3 and one cake at 4.
Step 2 changes only line 1, and TypeScript lists lines 3, 4, and 7: a to-do list of places to update. Step 3 updates the two objects, and only line 7 is left. That is the dangerous moment in plain JavaScript: the objects say unitPrice, line 7 reads price, and the order quietly costs NaN. Step 4 fixes line 7, and the errors are gone. In VS Code, the Rename Symbol command (the F2 key) does all of this in one go, because it uses the same types.
What TypeScript doesn't do
TypeScript checks your code before it runs. Once it has removed the types, its job is done. So it can't check anything that only exists while the page runs, like a reply from a server:
type Product = { name: string; price: number }; const reply = '{ "name": "Cake", "price": "4" }';const cake: Product = JSON.parse(reply);// ^?console.log(cake.price + 3);Line 1 describes a Product: a name and a number price. Line 3 is the reply as the server sent it, and its price is the text "4". Line 4 turns the reply into an object with JSON.parse and says it's a Product. The query on line 5 shows TypeScript believes you: const cake: Product. There are no errors, but line 6 prints 43, because the price is really text.
JSON.parse can give back anything, so TypeScript can't know what's inside. It trusts the type you wrote. (The JSON lesson covers JSON.parse itself.)
"4" reaches cake.price + 3 and makes "43".A friend reads your recipe card before you cook and spots that you wrote "2 cups of salt". That's useful. But your friend never sees the eggs that are delivered on the day. If they're bad, you only find out when you open the box.
- In real life: A friend checks your recipe card
- In TypeScript: TypeScript checks your code
- In real life: "Two eggs" is written correctly
- In TypeScript:
price: numberis written correctly - In real life: The eggs delivered on the day
- In TypeScript: The reply that arrives while the page runs
- In real life: You still look at the eggs when they come
- In TypeScript: You still check the reply with code, like
Number(data.price)
TypeScript also can't tell that a calculation is wrong when every type is right:
function lineTotal(price: number, quantity: number) { return price + quantity;}console.log(lineTotal(3, 2));Line 2 should multiply the price by the quantity, but it adds. Both are numbers, so every type is right, and line 4 prints 5 instead of 6 with no error. Tests, and trying your page, find this kind of mistake.
The table below lists jobs you might expect from TypeScript. Read each row as: this job, does TypeScript do it, and why.
| The job | Does TypeScript do it? | Why |
|---|---|---|
| Point at a wrong type while you type | Yes. | It reads your code and compares each value with the type it should have. |
| Check data from a server, a form, or storage while the page runs | No. | The types are removed before the code runs, so nothing is left to check with. |
Find a wrong calculation, like + where you meant * | No. | Both sides are numbers, so every type is right. Tests find this kind of mistake. |
| Make your code run faster | No. | The browser runs the same JavaScript either way. |
| Change what your code does | No. | Your code does exactly what it would do as plain JavaScript. |
TypeScript also has costs. They're worth knowing before you start:
- A build step: something has to remove the types before the browser runs your code.
- New things to learn: the type syntax, and how to read the error messages.
- Time spent writing types, mostly for data that comes from outside, like server replies.
- Now and then, code you know is right needs a small change before TypeScript accepts it.
For most frontend projects, the bugs it catches early are worth that cost, which is why so many teams use it.
A quantity box on a checkout page
On a real checkout page, the shopper types how many cakes they want into a box. The page reads the box with .value, and adds one when they press a plus button. TypeScript already knows the browser's own types, like what an <input> element holds:
const quantityInput = document.createElement("input");quantityInput.value = "2"; const quantity = quantityInput.value;// ^?const nextQuantity: number = quantity + 1;console.log(nextQuantity);Line 1 makes an <input> text box, and line 2 puts "2" in it, as if the shopper typed 2 (Form elements covers inputs and .value). Line 4 reads the value. The query on line 5 shows it's a string: a text box always gives text. Line 6 adds 1 and says the result must be a number. TypeScript marks line 6, because text plus 1 is text. Run it anyway and it prints 21.
const quantityInput = document.createElement("input");quantityInput.value = "2"; const quantity = Number(quantityInput.value);// ^?const nextQuantity: number = quantity + 1;console.log(nextQuantity);The only change is line 4: Number(quantityInput.value) turns the text into a number as it comes in. The query on line 5 now says number, the error is gone, and line 7 prints 3. Converting outside data once, at the point it arrives, is the habit TypeScript pushes you toward.
const price: number = "4";const price: number = Number("4");const cart = { total: 7 }; console.log(cart.totl);const cart = { total: 7 }; console.log(cart.total);Math.round("4.5");const quantity = 2; quantity.toUpperCase();const reply: { price: number } = JSON.parse('{"price":"4"}');const label = "Total: " + 7;
Sort each line by what TypeScript says about it. Each line is checked on its own.
Common mix-ups
TypeScript only catches a wrong type when it knows the right one. Here is the price bug again, with no types written:
const teaPrice = 3;const cakePrice = "4";console.log(teaPrice + cakePrice);Line 1 makes teaPrice a number, and line 2 makes cakePrice a string. Line 3 joins them and prints 34, and TypeScript reports nothing. Joining text with + is allowed, and often exactly what you want. The overview caught the bug only because line 2 said cakePrice must be a number.
- "TypeScript is a different language from JavaScript." No. It's JavaScript with types added. The browser runs the JavaScript that's left when the types are removed.
- "TypeScript checks my data while the page runs." No. The types are gone by then. A server reply with a price as text gets through, as the
43example showed. - "No errors means no bugs." No. A sum with
+where you meant*has the right types and the wrong answer. - "I have to write a type on every variable." No. TypeScript works out most types from the values, like
const total: number. You write types mostly for function parameters and outside data. - "A type error stops my code from running." No. The overview's typed example still prints
34when you run it. The error is a warning for you. The next lesson shows what the compiler does with code that has errors. - "TypeScript makes my code faster." No. The browser runs the same JavaScript either way.
Practice exercises
Each exercise is TypeScript. When you fix code, press Edit & run on the starter code: the errors update as you type, and the console shows what it prints. Try a guess first, then use the hints.
Two cart items, one with a mistake. Which line will TypeScript mark?
type CartItem = { name: string; price: number };
const tea: CartItem = { name: "Tea", price: 3 };
const cake: CartItem = { name: "Cake", price: "4" };type CartItem = { name: string; price: number }; const tea: CartItem = { name: "Tea", price: 3 };const cake: CartItem = { name: "Cake", price: "4" }; // line 4 has the errorLine 4 gives the cake the text "4" where CartItem says a number goes, so TypeScript reports Type 'string' is not assignable to type 'number' there.
No types are written here. What type does TypeScript give paid? Guess first. Then point at paid to check, or press Edit & run and add a // ^? line under it.
const order = { id: 7, paid: false };const paid = order.paid;const order = { id: 7, paid: false };const paid = order.paid;// ^?order.paid is false, so TypeScript works out that paid is a boolean: a value that is true or false. The query on line 3 shows const paid: boolean.
TypeScript marks line 5. Fix it so the cart holds two numbers. What does the fixed code print?
type CartItem = { name: string; price: number }; const cart: CartItem[] = [ { name: "Tea", price: 3 }, { name: "Cake", price: "4" },]; console.log(cart[0].price + cart[1].price);type CartItem = { name: string; price: number }; const cart: CartItem[] = [ { name: "Tea", price: 3 }, { name: "Cake", price: Number("4") },]; console.log(cart[0].price + cart[1].price);Number("4") turns the text into the number 4, so the cake matches CartItem and the error is gone. Then 3 + 4 prints 7, not 34.
A settings object and a typo. Fix line 3. Which name should it use?
const settings = { theme: "dark", fontSize: 16 }; console.log(settings.fontsize);const settings = { theme: "dark", fontSize: 16 }; console.log(settings.fontSize);The object has fontSize with a capital S. TypeScript's message asks Did you mean 'fontSize'?. With the right name the error is gone and it prints 16.
A server sends a product. TypeScript is happy with the code. What does it print? Then change it so the price really is a number.
type Product = { name: string; price: number }; const reply = '{ "name": "Tea", "price": "3" }';const tea: Product = JSON.parse(reply);console.log(tea.price * 2 + tea.price);type Product = { name: string; price: number }; const reply = '{ "name": "Tea", "price": "3" }';const data = JSON.parse(reply);const tea: Product = { name: String(data.name), price: Number(data.price) };console.log(tea.price * 2 + tea.price);The starter prints 63, because the price is really text. The fix checks the reply where it arrives: Number(data.price) makes a real number, so the sum is 3 * 2 + 3, which is 9.
This code throws a TypeError when it runs, and TypeScript warned about it. What does find give back when nothing matches? Then fix the code so it prints a message instead.
const cart = [{ name: "Tea", price: 3 }];const cake = cart.find((item) => item.name === "Cake");console.log(cake.price);const cart = [{ name: "Tea", price: 3 }];const cake = cart.find((item) => item.name === "Cake");if (cake) { console.log(cake.price);} else { console.log("No cake in the cart");}find gives back undefined when nothing matches. Inside if (cake), cake can't be undefined, so TypeScript allows cake.price. With no cake in the cart, the code prints No cake in the cart instead of throwing.
Check your understanding
For each question, keep two moments apart: what TypeScript says before the code runs, and what the code does when it runs. The quiz shows its code without the checker, so the error list doesn't give the answers away.
Question 1 of 7Which sentence describes TypeScript best?
Choose an answer to see the explanation.
Question 2 of 7Which line does TypeScript flag in this order?
Read the code, then predicttype Order = { item: string; quantity: number }; const order: Order = { item: "Tea", quantity: 2 }; order.quantity = "3";Choose an answer to see the explanation.
Question 3 of 7This code has a type error. What does it print when it runs anyway?
Read the code, then predictconst items: number = 2; const extra: number = "1"; console.log(items + extra);Choose an answer to see the explanation.
Question 4 of 7Does TypeScript report an error for this server reply, and what prints?
Read the code, then predicttype User = { name: string; age: number }; const user: User = JSON.parse('{ "name": "Asha", "age": "30" }'); console.log(user.age + 1);Choose an answer to see the explanation.
Question 5 of 7What type does TypeScript show when you hover
totalon line 1?Read the code, then predictlet total = 0; total = total + 3;Choose an answer to see the explanation.
Question 6 of 7Which job does TypeScript NOT do for you?
Choose an answer to see the explanation.
Question 7 of 7What does this order total print, and does TypeScript complain?
Read the code, then predictfunction orderTotal(price: number, quantity: number) { return price + quantity; } console.log(orderTotal(4, 3));Choose an answer to see the explanation.
Key takeaways
- TypeScript is JavaScript plus types. A type says what kind of value a name must hold, like
price: number. - It points at wrong types while you type: text where a number goes, a misspelled property, a missing argument, a value that may be
undefined. - You don't write every type. TypeScript works most of them out, and you can see them by pointing at a name.
- Your editor uses the types for autocomplete and for renaming safely.
- The types are removed before the browser runs the code, so data that arrives while the page runs isn't checked.
- It can't find a wrong calculation with the right types, and it doesn't make code faster.
Remember the one-liner.
TypeScript checks your types while you write, then steps aside, and the browser runs plain JavaScript.
Coming next: How TypeScript works. It follows your code through the compiler: checking, removing the types, and why code with errors still runs.