TypeScript Advanced Patterns
Conditional Types
// Basic conditional type
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
// Practical example
type Flatten<T> = T extends Array<infer U> ? U : T;
type Str = Flatten<string>; // string
type Num = Flatten<number[]>; // number
// Distributive conditional types
type ToArray<T> = T extends any ? T[] : never;
type StrOrNum = string | number;
type Arrays = ToArray<StrOrNum>; // string[] | number[]
// infer keyword
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
function getUser() {
return { id: 1, name: 'John' };
}
type UserType = GetReturnType<typeof getUser>;
// { id: number; name: string }
// Nested conditional types
type Unpromisify<T> = T extends Promise<infer U>
? U extends Promise<infer V>
? Unpromisify<V>
: U
: T;
type A = Unpromisify<Promise<string>>; // string
type B = Unpromisify<Promise<Promise<number>>>; // numberMapped Types Advanced
// Make properties nullable
type Nullable<T> = {
[P in keyof T]: T[P] | null;
};
interface User {
id: number;
name: string;
}
type NullableUser = Nullable<User>;
// { id: number | null; name: string | null }
// Get function properties
type FunctionPropertyNames<T> = {
[K in keyof T]: T[K] extends Function ? K : never;
}[keyof T];
type FunctionProperties<T> = Pick<T, FunctionPropertyNames<T>>;
class MyClass {
name: string = '';
count: number = 0;
method1() {}
method2() {}
}
type Methods = FunctionProperties<MyClass>;
// { method1: () => void; method2: () => void }
// Deep readonly
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object
? DeepReadonly<T[P]>
: T[P];
};
interface Config {
database: {
host: string;
port: number;
};
cache: {
ttl: number;
};
}
type ReadonlyConfig = DeepReadonly<Config>;
// All nested properties are readonlyType Challenges
// Deep Partial
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
// Awaited (built-in TypeScript utility)
type Awaited<T> = T extends Promise<infer U> ? Awaited<U> : T;
type A = Awaited<Promise<string>>; // string
type B = Awaited<Promise<Promise<number>>>; // number
// Required keys
type RequiredKeys<T> = {
[K in keyof T]-?: {} extends Pick<T, K> ? never : K;
}[keyof T];
interface Example {
a: string;
b?: number;
c: boolean;
}
type ReqKeys = RequiredKeys<Example>; // 'a' | 'c'
// Readonly keys
type ReadonlyKeys<T> = {
[K in keyof T]-?: (<F>() => F extends { [Q in K]: T[K] } ? 1 : 2) extends
(<F>() => F extends { -readonly [Q in K]: T[K] } ? 1 : 2)
? never
: K;
}[keyof T];Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free