type Result = T extends Condition ? TrueType : FalseType;
example
type IsString<T> = T extendsstring ? "yes" : "no";
type A = IsString<string>; // "yes"type B = IsString<number>; // "no"type C = IsString<"hello">; // "yes"// Practical: flatten one level of arraytype Flatten<T> = T extends Array<infer Item> ? Item : T;
type Str = Flatten<string[]>; // stringtype Num = Flatten<number>; // number (not an array, returned as-is)
output
// Conditional types branch on whether T matches a shape
Note Conditional types distribute over naked union type parameters: IsString<string | number> becomes IsString<string> | IsString<number> = "yes" | "no". Wrap in tuple to prevent distribution: [T] extends [string] ? ... : ...
conditional typetype branchingtype-level ifextends conditionaldistribute union
Mapped Types
syntax
type Result = {
[K in keyof T]: NewType;
};
example
interface UserProfile {
name: string;
email: string;
age: number;
}
// Make every property a getter functiontype Getters<T> = {
[K in keyof T as`get${Capitalize<string & K>}`]: () => T[K];
};
type UserGetters = Getters<UserProfile>;
// {// getName: () => string;// getEmail: () => string;// getAge: () => number;// }// Make all properties mutable (remove readonly)type Mutable<T> = {
-readonly [K in keyof T]: T[K];
};
output
// Mapped types transform every property of an existing type
Note Key remapping with 'as' (TS 4.1+) enables renaming keys. Filter out keys by mapping to never: [K in keyof T as T[K] extends Function ? never : K]. The +/- modifiers add or remove readonly and optional (?) markers.
mapped typetransform propertieskey remappingmodify all propertiesiterate keys
infer Keyword
syntax
type Result = T extends Pattern<infer U> ? U : Fallback;
example
// Extract the resolved type from a Promisetype UnwrapPromise<T> = T extends Promise<infer R> ? R : T;
type A = UnwrapPromise<Promise<string>>; // stringtype B = UnwrapPromise<number>; // number// Extract function first argumenttype FirstArg<T> = T extends (first: infer F, ...rest: any[]) => any ? F : never;
type FA = FirstArg<(name: string, age: number) => void>; // string// Extract array element typetype ArrayItem<T> = T extends readonly (infer E)[] ? E : never;
type Item = ArrayItem<readonly ["a", "b", "c"]>; // "a" | "b" | "c"
output
// infer declares a type variable that TypeScript figures out from context
Note infer can only be used inside the extends clause of a conditional type. It captures whatever type fits in that position. Multiple infer clauses in the same conditional are allowed. In union position, infer produces a union; in intersection position (like function params), it produces an intersection.
infer keywordextract typepattern matching typecapture genericunwrap type
Recursive Types
syntax
type TreeNode<T> = {
value: T;
children: TreeNode<T>[];
};
example
// JSON-compatible typetype JsonValue =
| string
| number
| boolean
| null
| JsonValue[]
| { [key: string]: JsonValue };
// Deep Readonlytype DeepReadonly<T> = T extends object
? { readonly [K in keyof T]: DeepReadonly<T[K]> }
: T;
interface NestedConfig {
db: { host: string; port: number; ssl: { enabled: boolean } };
}
type FrozenConfig = DeepReadonly<NestedConfig>;
// All levels are readonly — db.ssl.enabled cannot be reassigned
output
// Recursive types reference themselves in their definition
Note TypeScript handles direct recursive type aliases since TS 3.7. Earlier versions needed interface workarounds. Be careful with deeply recursive conditional types — the compiler has a recursion depth limit (around 50 levels by default) and will error if exceeded.
recursive typenested typedeep typetree typeJSON typeself-referencing type
Branded / Nominal Types
syntax
type Brand<T, B> = T & { readonly __brand: B };
example
type USD = number & { readonly __brand: "USD" };
type EUR = number & { readonly __brand: "EUR" };
function usd(amount: number): USD {
return amount as USD;
}
function eur(amount: number): EUR {
return amount as EUR;
}
function chargeUSD(amount: USD) {
console.log(`Charging $${amount}`);
}
const price = usd(29.99);
chargeUSD(price); // OK// chargeUSD(eur(25.00)); // Error: EUR is not assignable to USD// chargeUSD(29.99); // Error: number is not assignable to USD
Note TypeScript uses structural typing — two types with the same shape are interchangeable. Branded types add a phantom property to create nominal (name-based) distinction. The __brand property never exists at runtime; it is purely a compile-time discriminator. Use this for user IDs, currency, coordinates, etc.
// Template literals combined with infer enable string parsing at the type level
Note Template literal types combined with conditional types and infer enable powerful string parsing and transformation at compile time. This is the foundation for type-safe routing, ORM query builders, and CSS-in-JS libraries.
type ColorMap = Record<string, string | number[]>;
// Without satisfies: loses specific types// const colors: ColorMap = { ... } → all values are string | number[]// With satisfies: validates shape AND keeps specific typesconst colors = {
red: "#ff0000",
green: [0, 255, 0],
blue: "#0000ff",
} satisfies ColorMap;
colors.red.toUpperCase(); // OK — TypeScript knows red is string
colors.green.map(c => c); // OK — TypeScript knows green is number[]// colors.red.map(c => c); // Error — string has no .map
output
// satisfies validates compatibility without widening the inferred type
Note Added in TS 4.9. satisfies checks that a value matches a type without changing the inferred type. This gives you the best of both worlds: validation that the shape is correct, plus precise inference of each property. Use it instead of explicit type annotations when you want both safety and specificity.
satisfiesvalidate typecheck without wideningsatisfies operatortype validation
keyof & typeof Operators
syntax
type Keys = keyof Type;
type ValueType = typeof runtimeValue;
// keyof extracts property names; typeof extracts the type of a value
Note keyof works on types; typeof works on values. They are often combined: keyof typeof myObject gives you the union of property names of a runtime object. typeof only works with variables and properties, not with arbitrary expressions like function calls.
keyoftypeofproperty names typeextract keysvalue to typeobject keys type