TypeScript Type System
102 flashcards by MentisLibrary
How TypeScript's types actually work: structural typing and inference, unions and narrowing, generics, conditional and mapped types, the utility types, declaration files, the strictness flags, and what survives compilation.
- What does it mean that TypeScript's type system is STRUCTURAL?
- Assignability is decided by shape, not by declared name or inheritance. Anything with the required members is accepted, even if it never mentions the type.
- How does inference differ between let and const?
- const with a literal initialiser infers the LITERAL type, because the binding can never change. let widens to the general primitive type.
- What does an as const assertion do?
- Infers the narrowest literal types and marks every property and array element readonly, all the way down.
- How do any and unknown differ?
- any switches type checking off for that value. unknown accepts any value but permits no operation until you narrow it.
- What is the never type?
- The type with no values. It is assignable to every type, and nothing except never is assignable to it. Functions that never return normally have return type never.
- What is contextual typing?
- Inference flowing INTO an expression from the position it appears in, so callback parameters need no annotations.
- How does a type assertion with as differ from a real conversion?
- It only tells the compiler to treat the expression as that type. Nothing is checked and nothing happens at runtime.
- What does the satisfies operator do that an annotation does not?
- It checks the expression against a type WITHOUT widening the inferred type, so you keep the precise literal types and still get the constraint checked.
- What does an excess property check do, and when does it apply?
- Assigning a fresh OBJECT LITERAL to a typed target errors on properties the target does not declare. It applies only to literals, not to variables.
- When should you use interface and when a type alias?
- interface describes object shapes and supports declaration merging. A type alias can name ANY type, including unions, tuples, primitives, conditional and mapped types.
- What does declaration merging do to interfaces?
- Two interfaces with the same name in the same scope combine into one with the union of their members.
- How does an optional property differ from one typed | undefined?
- name?: string may be absent OR present and undefined. name: string | undefined must be present, though its value may be undefined.
- What does readonly on a property prevent?
- Reassignment of that property through that type. It is compile-time only and does not freeze the object.
- What is an index signature, and what does it force?
- [key: string]: T says any string key maps to T. Every explicitly named property must then be assignable to T.
- How do you get a union of an object type's keys?
- keyof T. For an object literal's type, combine it with typeof.
- What is an indexed access type?
- T[K] extracts the type of a property. K may be a union or keyof T, and T[number] gets an array's element type.
- How do you express nominal typing in a structural system?
- Brand the type with a property that nothing else can produce, then create values through a validating function.
- What does a union type A | B let you access?
- Only the members present on EVERY member of the union. Anything else requires narrowing first.
- What does an intersection type A & B produce?
- A type with all members of both. For object types it is a merge; for conflicting primitive members the result can be never.
- What is a discriminated union, and what makes it work?
- A union of object types sharing a property whose type is a distinct LITERAL in each member. Narrowing on that property selects one member.
- How do you narrow a union with typeof?
- The compiler understands JavaScript's typeof and narrows the variable inside the branch.
- When does instanceof narrowing apply?
- When the right-hand side is a class or constructor function. It cannot narrow to an interface, which has no runtime representation.
- How does the in operator narrow a union?
- 'prop' in obj narrows to the union members that declare that property.
- What is a user-defined type guard?
- A function whose return type is arg is T. When it returns true, the compiler narrows the argument at the call site.
- What does an assertion function look like?
- function assert(x: unknown): asserts x is T. After the call, the compiler narrows for the rest of the scope rather than inside a branch.
- How do you make the compiler enforce that a union is handled exhaustively?
- Assign the value to a never variable in the default branch. Adding a new union member makes that assignment an error.
- Why does narrowing get lost inside a callback?
- The compiler cannot know WHEN the callback runs, so narrowing of a mutable let is discarded inside a function body.
- How do you type a function value?
- With a function type (a: string) => number, or a call signature inside an object type.
- How are optional, default and rest parameters typed?
- a?: T may be omitted (and is T | undefined inside). A parameter with a default is optional at the call site but non-optional inside. A rest parameter is typed as an array or a tuple.
- How do function overloads work?
- Write several signature declarations followed by one implementation signature. Callers see only the overload list; the implementation signature is not callable.