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.