Quick Overview
A union permits only what every member allows, so shared properties are all you can touch. `typeof`, `instanceof` and `in` narrow the union inside a branch, unlocking the rest of that member's shape.
A Union Type (|) allows a variable or parameter to accept one of several possible data types.
type ID = string | number;
function printID(id: ID) {
console.log(`ID is: ${id}`);
}
printID(101); // Valid
printID("usr_99"); // Valid
When working with a union type, TypeScript only allows operations that are valid on ALL union members. To perform type-specific logic, you must narrow the wide union down to a specific concrete type using runtime type guards.
typeof Guard: Narrows primitive types (string, number, boolean).instanceof Guard: Narrows class instances and built-in objects (Date, Error).in Operator Guard: Checks if a property key exists on an object.function formatPadding(padding: string | number): string {
// typeof guard narrowing
if (typeof padding === "number") {
return `${padding}px`; // Inside this block, padding is type 'number'!
}
return padding.trim(); // Inside this block, padding is type 'string'!
}
function processDate(input: Date | string): number {
// instanceof guard narrowing
if (input instanceof Date) {
return input.getTime();
}
return new Date(input).getTime();
}
id.toUpperCase() on a string | number union without a typeof check throws a compile error because .toUpperCase() does not exist on number.Use type guards to narrow unions safely. Next, let me explain Optional Properties, Strict Null Checks, and non-null handling!
TypeScript erases entirely at build time, so a `string` annotation produces no runtime check whatsoever. A value arriving from an API is whatever the server actually sent, regardless of what the annotation claims.
`let x = "GET"` infers `string` because the binding can change; `const x = "GET"` infers the literal `"GET"`. That widening rule is why object properties need `as const` to stay narrow.
`string[]` is any number of strings; `[string, number]` is exactly two, in that order. `readonly` removes the mutating methods at compile time, so `push` becomes a type error rather than a runtime surprise.