Checkpoint: The Type System
This checkpoint consolidates everything from the type-system unit — unions and intersections, literals, enums, narrowing, generics, object types, overloads, and tuples — into one recap, a hands-on build challenge, and a short quiz.
Learn Checkpoint: The Type System in our free TypeScript course — an interactive lesson with runnable examples, a practice exercise and a quick reference.
Part of the free TypeScript course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
📚 Unit Recap
Here's the whole type-system unit on one page. Each row links back to its full lesson if you want a refresher.
Everything in this unit answers one question: "what shape is this value, and what can I safely do with it?" Unions and literals describe the possibilities , narrowing and guards confirm which one you have, generics keep your helpers reusable without losing that knowledge , and object/array/tuple types pin down structure . The build challenge below uses several of these together — exactly how real code does.
🎯 Build Challenge: A Shape Domain
Model a small shape domain as a discriminated union, write an area function that narrows on the kind field, and add a generic sumBy helper to total the areas. Fill in the two blanks, run it, and match the expected output — then compare against the full TypeScript solution.
🔁 Rapid Self-Check
Predict every line of output before you run it — it touches narrowing, discriminated unions, generics, and tuple destructuring in one go.
📝 Checkpoint Quiz
Answer each in your head, then expand to check. Six questions covering the whole unit.
A | B (union) means the value is either an A or a B — you must narrow before using members unique to one. A & B (intersection) means the value is both at once — it has every member of A and B, so all are safe to use immediately.
Because let is reassignable, TypeScript widens the inferred type to string . Use const , an explicit literal annotation, or as const to keep the narrow literal type "dark" .
It's a type predicate . At runtime the function returns a boolean, but the predicate tells the compiler: "if this returned true , treat the argument as Fish from here on." Without it, TypeScript sees a plain boolean and won't narrow.
The generic preserves the input type in the output, so callers keep full type safety downstream. any throws that information away, silencing the very checks TypeScript exists to provide. Generics give reuse and safety.
When the return type depends on which argument type you passed — e.g. make("point") returns an object but make("name") returns a string. A single union signature can't express that input-to-output link; overloads can.
[string, number] is a tuple : exactly two elements, a string then a number, order fixed. string[] is an array : any number of elements, all strings. Tuples pin down shape; arrays describe "a list of one type".
Practice quiz
What does the union type 'string | number' allow?
- A value that is both a string and a number at once
- Only strings, with numbers coerced automatically
- A value that is either a string or a number
- An array containing strings and numbers
Answer: A value that is either a string or a number. A union 'A | B' means the value is EITHER an A OR a B. You must narrow before using members unique to one side.
What does the intersection type 'A & B' describe?
- A value that is both A and B at once, having every member of each
- A value that is only A or only B
- The properties A and B have in common
- A tuple of an A followed by a B
Answer: A value that is both A and B at once, having every member of each. An intersection 'A & B' combines all types into one — the value has every member of A AND B, so all are safe to use immediately.
Which is a literal type?
- string
A literal type is one exact value used as a type. "'dark' | 'light'" is a union of two string literal types.
Why might 'let mode = "dark"' be inferred as 'string' rather than '"dark"'?
- TypeScript never infers literal types
- Because 'let' is reassignable, so TypeScript widens the type to 'string'
- Because string literals are not valid types
- Because 'dark' is a reserved word
Answer: Because 'let' is reassignable, so TypeScript widens the type to 'string'. Since 'let' allows reassignment, TS widens to 'string'. Use 'const', an explicit literal annotation, or 'as const' to keep the literal type.
In a type guard, what does the return type 'pet is Fish' do?
- Tells the compiler to narrow pet to Fish when the function returns true
- Converts pet into a Fish at runtime
- Throws an error if pet is not a Fish
- Declares pet as a global Fish variable
Answer: Tells the compiler to narrow pet to Fish when the function returns true. 'pet is Fish' is a type predicate. At runtime it returns a boolean, but it tells the compiler to treat the argument as Fish when true.
Why is a generic 'identity<T>(x: T): T' better than 'identity(x: any): any'?
- It runs faster at runtime
- It allows passing more arguments
- It preserves the input type in the output, keeping type safety
- There is no real difference
Answer: It preserves the input type in the output, keeping type safety. The generic preserves the relationship between input and output types. 'any' throws that information away, silencing TypeScript's checks.
How do you mark an object property as optional?
- nickname!: string
- nickname?: string
- readonly nickname: string
- optional nickname: string
Answer: nickname?: string. The '?' after the property name makes it optional, so the property may be absent on values of that type.
What does the index signature '[k: string]: number' mean?
- The object has exactly one key named k
- The object is read-only
- Keys must be numbers
- Any string key maps to a number value
Answer: Any string key maps to a number value. An index signature describes open-ended keys: any string key is allowed, and every value must be a number.
When do function overloads beat a single union signature?
- When you want fewer lines of code
- When the return type depends on which argument type was passed
- When the function takes no arguments
- Overloads are never useful in TypeScript
Answer: When the return type depends on which argument type was passed. Overloads express that the return type depends on the input type (e.g. make('point') vs make('name')) — something a single union signature cannot capture.
What is the difference between '[string, number]' and 'string[]'?
- They are identical
A tuple '[string, number]' pins down length and per-position types; 'string[]' is a list of any number of strings.