TypeScript in real projects
Types are checked before a program runs and then erased, so they cannot see data that arrives while it runs: a server reply, a file, a form. Follow a small program that reads {"name":"Ada","age":"36"}. Cast with as User, tsc finds nothing and it prints 361. Typed unknown and checked by a type guard, the bad reply is stopped ("bad data") and the good one prints 37.
5 quick questions
Pick an answer to see why. Your score saves with your progress.
01Why does tsc report 0 errors for const user = JSON.parse(text) as User; when the text holds "age":"36"?
JSON.parse is declared as returning any, which turns checking off. as User then tells tsc to treat the value as a User without any check, so nothing stops the wrong data.
02The cast program prints 361. Why?
Types are erased before the program runs, so nothing checks the value. In JavaScript, + with a string on either side joins text, so "36" + 1 is "361".
03What does the type unknown make tsc do?
unknown can hold any value, but tsc will not let you use it until you narrow it. Using data.age directly gives error TS18046: 'data' is of type 'unknown'.
04In function isUser(x: unknown): x is User, what does x is User mean?
x is User is a type predicate. The function itself returns true or false; the predicate tells tsc that a true result proves x is a User, so tsc narrows it inside the if.
05In a real project, where should checks like isUser go?
Inside the program tsc can keep the types right. Data from outside was never checked by tsc, so it is checked once where it enters; past that edge the types can be trusted. Libraries such as Zod write these checks for you.
Glossary
- Run time
- While the program is running, as opposed to compile time, when tsc checks it before it runs.
- Type erasure
- tsc removes all types when it writes JavaScript, so no type exists while the program runs.
- JSON
- A text format for data, such as {"name":"Ada","age":36}. JSON.parse turns the text into a JavaScript value.
- any
- A type that switches checking off: any value fits, and any use of it is allowed.
- unknown
- A type that holds any value but cannot be used until you check what it is.
- Type assertion (as)
- value as User tells tsc to treat a value as a type. It changes nothing and checks nothing at run time.
- Type guard
- A run-time check that tsc understands and uses to narrow a type: a test such as typeof x === "number", or a function like isUser whose return type is a type predicate.
- Type predicate
- A return type such as x is User. It says that when the function returns true, x has that type.
Notes and sources
- In a real app the text on line 2 comes from a server (for example with fetch) or a file. Here it is written into line 2 so the whole program runs as it is.
- JSON.parse is declared in TypeScript's own library as returning any. A value typed any skips checking: JSON.parse(text).foo.bar would pass tsc too, and as User is accepted without a single check.
- A real guard checks every field the code relies on. Schema libraries such as Zod write the checks and the TypeScript type from one description, so they cannot drift apart.
- By default tsc still writes app.js even when it reports errors, and that file still prints 361. Set noEmitOnError in tsconfig.json to stop tsc writing output when there are errors.
- Every output shown is real: tsc 5.9 with --strict, then Node.js. The cast version prints 361; the guarded one prints bad data, and 37 once the data is right.
- TypeScript Handbook: Everyday Types (Type Assertions, any)
- TypeScript Handbook: Narrowing (typeof, in, type predicates)
- TypeScript Handbook: More on Functions (unknown)
- TSConfig reference: noEmitOnError
- TypeScript Handbook: The Basics (Erased Types)
- TypeScript source: lib.es5.d.ts (JSON.parse returns any)
- MDN: JSON.parse()
- MDN: Addition (+)
- MDN: typeof
- MDN: in operator
- Zod documentation
Quiz Unlocks after the last step.