Advanced TypeScript Patterns Every Senior Dev Should Know
1. Branded Types (Nominal Typing)
TypeScript uses structural typing — two types with the same shape are interchangeable. Branded types add nominal safety:
type UserId = string & { readonly __brand: 'UserId' }
type ProductId = string & { readonly __brand: 'ProductId' }
function createUserId(id: string): UserId { return id as UserId } function createProductId(id: string): ProductId { return id as ProductId }
function getUser(id: UserId): User { / ... / }
const userId = createUserId('user-123') const productId = createProductId('prod-456')
getUser(userId) // ✓ getUser(productId) // ❌ Error: ProductId is not assignable to UserId getUser('user-123') // ❌ Error: string is not branded UserId
Useful for IDs, validated strings, monetary values, and CSS pixel values.
2. Template Literal Types
type Alignment = 'left' | 'center' | 'right'
type Direction = 'horizontal' | 'vertical'
type ClassName = align-${Alignment} // 'align-left' | 'align-center' | 'align-right'
// Build event handler types type EventName<T extends string> = on${Capitalize<T>} type ClickHandler = EventName<'click'> // 'onClick'
// CSS property builder type CSSProperty = 'margin' | 'padding' type CSSEdge = 'Top' | 'Bottom' | 'Left' | 'Right' type CSSEdgeProperty = ${CSSProperty}${CSSEdge} // 'marginTop' | 'marginBottom' | ... | 'paddingRight' (8 combinations)
3. Recursive Types
// JSON value — any valid JSON
type JSONValue =
| string | number | boolean | null
| JSONValue[]
| { [key: string]: JSONValue }
// Deep partial — every nested object's properties are optional type DeepPartial<T> = { [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K] }
// Deep readonly type DeepReadonly<T> = { readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K] }
// File system tree type FileTree = { name: string children?: FileTree[] // recursive reference }
4. Distributive Conditional Types
// When T is a union, conditional types are distributed over each member
type IsString<T> = T extends string ? true : false
type A = IsString<string | number> // boolean (true | false) — distributed!
// Prevent distribution with brackets type IsStringExact<T> = [T] extends [string] ? true : false type B = IsStringExact<string | number> // false — not distributed
// Use distribution to filter unions: type FilterStrings<T> = T extends string ? T : never type OnlyStrings = FilterStrings<string | number | boolean> // string
5. Variadic Tuple Types
// Spread in tuple positions
type Concat<A extends unknown[], B extends unknown[]> = [...A, ...B]
type C = Concat<[1,2], [3,4]> // [1, 2, 3, 4]
// Type-safe curry type Curry<F extends (...args: any) => any> = F extends (first: infer A, ...rest: infer R) => infer T ? (first: A) => R extends [] ? T : Curry<(...args: R) => T> : never
6. Assertion Functions
// Narrows type for rest of scope when the function doesn't throw
function assert(condition: unknown, message: string): asserts condition {
if (!condition) throw new Error(message)
}
function assertIsUser(val: unknown): asserts val is User { if (!val || typeof val !== 'object' || !('id' in val)) { throw new Error('Not a user') } }
const data: unknown = parseJSON(response) assertIsUser(data) console.log(data.id) // data: User ✓ — TypeScript narrows after the assertion
7. Const Type Parameters (TypeScript 5.0+)
// Without const — TypeScript widens the type
function makeArray<T>(items: T[]) { return items }
const arr = makeArray(['a', 'b', 'c']) // string[]
// With const — infers literal types function makeArray<const T>(items: T[]) { return items } const arr2 = makeArray(['a', 'b', 'c']) // readonly ['a', 'b', 'c']
8. Satisfies Operator (TypeScript 4.9+)
const palette = {
red: [255, 0, 0],
green: '#00ff00',
blue: [0, 0, 255],
} satisfies Record<string, string | number[]>
// Without satisfies — TypeScript widens to Record<string, string | number[]> // palette.red would be string | number[], losing the specific type
// With satisfies — validates shape AND preserves narrower inferred types palette.red.map(c => c * 2) // number[] ✓ — preserved as number[] palette.green.toUpperCase() // string ✓ — preserved as string
Practice advanced TypeScript at [JSPrep Pro](/auth).