TypeScript syntax

33 cards covering key concepts and patterns. Pin anything to keep it visible in the side panel.

Type Annotations

Annotate variables, parameters, and return types with a colon suffix.

typescript
// Variable annotations
let name: string = 'Alice';
let age: number = 30;
let active: boolean = true;
let data: unknown = fetch('/api'); // safe — must narrow before use
// Function annotations
function greet(name: string): string {
return `Hello, ${name}!`;
}
// Arrow function
const add = (a: number, b: number): number => a + b;
// void — function that returns nothing
function log(msg: string): void {
console.log(msg);
}
// Inferred types — annotation optional when value is obvious
const x = 42; // inferred: number
const y = [1, 2, 3]; // inferred: number[]
coretypesannotationsbasics
not reviewed

Interfaces

Named structural contracts describing the shape of objects and classes.

typescript
interface User {
id: number;
name: string;
email?: string; // optional property
readonly createdAt: Date; // cannot be reassigned after init
}
// Implementing an interface
interface Serializable {
serialize(): string;
}
class Config implements Serializable {
private data: Record<string, unknown> = {};
serialize() { return JSON.stringify(this.data); }
}
// Extending interfaces
interface AdminUser extends User {
permissions: string[];
}
// Index signature — dynamic keys
interface Dictionary {
[key: string]: string;
}
// Call signature
interface Comparator<T> {
(a: T, b: T): number;
}
coreinterfacesobjectstypes
not reviewed

Type Aliases

Name any type — primitives, unions, intersections, objects, functions, and complex generics.

typescript
// Object type
type Point = { x: number; y: number };
// Union type
type ID = string | number;
// Intersection (combine shapes)
type Named = { name: string };
type Aged = { age: number };
type Person = Named & Aged; // { name: string; age: number }
// Literal union — string "enum" without enum overhead
type Direction = 'north' | 'south' | 'east' | 'west';
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
// Generic type alias
type Maybe<T> = T | null | undefined;
type Result<T, E = Error> =
| { ok: true; value: T }
| { ok: false; error: E };
// Function type
type Predicate<T> = (value: T) => boolean;
const isEven: Predicate<number> = (n) => n % 2 === 0;
coretype-aliasunionstypes
not reviewed

Union Types

A value that can be one of several types — use type guards to work with each member.

typescript
type StringOrNumber = string | number;
function format(value: string | number): string {
if (typeof value === 'string') {
return value.toUpperCase(); // value: string here
}
return value.toFixed(2); // value: number here
}
// Literal unions — prefer over enums for simple cases
type Status = 'pending' | 'active' | 'inactive';
type Side = 'top' | 'right' | 'bottom' | 'left';
// Nullable union
type MaybeString = string | null;
function upper(s: MaybeString): string {
return s?.toUpperCase() ?? '';
}
// Width of a union — distributive utility types
type Strings = Extract<string | number | boolean, string>; // string
type NonNulls = NonNullable<string | null | undefined>; // string
coreunion-typestype-narrowing
not reviewed

Intersection Types

Combine multiple types — the resulting type has all properties of every member.

typescript
type HasName = { name: string };
type HasAge = { age: number };
type HasRole = { role: 'admin' | 'user' };
type AdminUser = HasName & HasAge & HasRole;
const admin: AdminUser = { name: 'Alice', age: 30, role: 'admin' };
// Mixin pattern
type Loggable = { log(): void };
type Serializable = { serialize(): string };
type Service = Loggable & Serializable;
// Common in React — extend HTML element props
type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
variant: 'primary' | 'secondary';
loading?: boolean;
};
coreintersection-typescomposition
not reviewed

Enums

Named sets of numeric or string constants — const enum inlines values at compile time.

typescript
// Numeric enum (auto-incremented from 0)
enum Direction { Up, Down, Left, Right }
Direction.Up; // 0
// String enum — explicit values, preferred for readability
enum Status {
Pending = 'PENDING',
Active = 'ACTIVE',
Inactive = 'INACTIVE',
}
// const enum — inlined at compile time, no runtime object
const enum Color { Red = 'red', Green = 'green', Blue = 'blue' }
function move(dir: Direction) { /* ... */ }
move(Direction.Up); // Direction.Up, not 0
// Alternative: literal union (no runtime overhead, no reverse lookup)
type Role = 'admin' | 'user' | 'guest';
coreenumsconstants
not reviewed

Tuple Types

Fixed-length arrays with a known type at each position.

typescript
// Basic tuple
type Pair = [number, number];
const p: Pair = [10, 20];
// Named elements (TypeScript 4.0+)
type Range = [start: number, end: number];
// Optional tail element
type WithOptional = [string, number?];
// Rest elements
type OneOrMore = [string, ...string[]];
// Destructuring
const [x, y]: Pair = [10, 20];
const [first, ...rest]: OneOrMore = ['a', 'b', 'c'];
// Common: useState-like return values
function useToggle(): [boolean, () => void] {
let on = false;
return [on, () => { on = !on; }];
}
const [isOpen, toggle] = useToggle();
coretuplesarrays
not reviewed

Type Assertions & Non-null Assertion

Override inferred types when you know more than the compiler — use as sparingly as possible.

typescript
// as assertion — tell TS what type you know it to be
const el = document.getElementById('root') as HTMLDivElement;
const user = response.json() as User;
// Non-null assertion (!) — assert value is not null/undefined
const el2 = document.getElementById('root')!;
el2.style.color = 'red'; // no "possibly null" error
// as const — infer literal types instead of widened types
const config = { port: 3000, env: 'production' } as const;
// config.port: 3000 (literal, not number)
// config is readonly
// Double assertion — forced override, use rarely
const value = (getValue() as unknown) as SpecificType;
// Prefer type predicates over raw assertions
function isUser(val: unknown): val is User {
return typeof val === 'object' && val !== null && 'name' in val;
}
coretype-assertionsasnon-null
not reviewed

Generics

Parameterize functions, interfaces, and classes over types they operate on.

typescript
// Generic function
function identity<T>(value: T): T { return value; }
const n = identity(42); // T inferred as number
const s = identity('hello'); // T inferred as string
// Generic interface
interface Box<T> {
value: T;
map<U>(fn: (v: T) => U): Box<U>;
}
// Generic class
class Stack<T> {
#items: T[] = [];
push(item: T): void { this.#items.push(item); }
pop(): T | undefined { return this.#items.pop(); }
peek(): T | undefined { return this.#items.at(-1); }
get size(): number { return this.#items.length; }
}
// Multiple type parameters
function zip<T, U>(a: T[], b: U[]): [T, U][] {
return a.map((item, i) => [item, b[i]]);
}
zip([1, 2], ['a', 'b']); // [[1,'a'], [2,'b']]
coregenericstype-parameters
not reviewed

Generic Constraints

Limit what types a type parameter can be with extends — access only guaranteed properties.

typescript
// Constrain T to have a length property
function longest<T extends { length: number }>(a: T, b: T): T {
return a.length >= b.length ? a : b;
}
longest('hello', 'hi'); // string
longest([1, 2, 3], [1, 2]); // number[]
// longest(1, 2); // Error — number has no length
// K must be a key of T
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
getProperty({ name: 'Alice', age: 30 }, 'name'); // string
// Default type parameter
interface Container<T = string> {
value: T;
}
const c1: Container = { value: 'hello' }; // T = string (default)
const c2: Container<number> = { value: 42 };
coregenericsconstraintsextends
not reviewed

keyof & typeof Operators

keyof extracts a union of property keys; typeof captures the type of a runtime value.

typescript
// keyof — union of object property names
type User = { id: number; name: string; email: string };
type UserKey = keyof User; // 'id' | 'name' | 'email'
function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
return keys.reduce((acc, k) => ({ ...acc, [k]: obj[k] }), {} as Pick<T, K>);
}
// typeof — get the type of a variable
const config = { port: 3000, host: 'localhost', debug: false };
type Config = typeof config; // { port: number; host: string; debug: boolean }
// typeof on a function
function fetchUser(id: string) { return { id, name: '' }; }
type FetchReturn = ReturnType<typeof fetchUser>; // { id: string; name: string }
// Combine keyof + typeof to extract value union
const ROUTES = { home: '/', about: '/about', contact: '/contact' } as const;
type Route = (typeof ROUTES)[keyof typeof ROUTES]; // '/' | '/about' | '/contact'
corekeyoftypeoftype-operators
not reviewed

Indexed Access Types

Look up the type of a property using T[K] — extract types from nested structures.

typescript
type User = { id: number; address: { city: string; zip: string } };
type UserID = User['id']; // number
type Address = User['address']; // { city: string; zip: string }
type City = User['address']['city']; // string
// Union of keys
type IdOrName = User['id' | 'name']; // would Error — use keyof for dynamic
// Array element type
type FruitItem = string[][number]; // string (any array index)
// Tuple element types
type Tuple = [string, number, boolean];
type First = Tuple[0]; // string
type AnyElement = Tuple[number]; // string | number | boolean
// Extract element type from a const array
const routes = [
{ path: '/', component: 'Home' },
] as const;
type RouteItem = (typeof routes)[number];
// { readonly path: '/'; readonly component: 'Home' }
advancedindexed-accesstype-operators
not reviewed

Utility Types: Object Transformation

Partial, Required, Readonly, Record, Pick, and Omit — reshape object types without repetition.

typescript
type User = { id: number; name: string; email: string; role: 'admin' | 'user' };
// Partial — all properties optional (useful for PATCH payloads)
type UpdateUser = Partial<User>;
// Required — remove all ? modifiers
type RequiredUser = Required<Partial<User>>;
// Readonly — prevent reassignment
const user: Readonly<User> = { id: 1, name: 'Alice', email: '', role: 'user' };
// user.name = 'Bob'; // Error
// Record — object type from key union + value type
type RolePerms = Record<'admin' | 'user', string[]>;
const perms: RolePerms = { admin: ['read', 'write'], user: ['read'] };
// Pick — select a subset of properties
type UserSummary = Pick<User, 'id' | 'name'>;
// Omit — exclude properties
type PublicUser = Omit<User, 'email' | 'role'>;
coreutility-typespartialpick
not reviewed

Utility Types: Unions & Functions

Exclude, Extract, NonNullable, ReturnType, Parameters, and Awaited.

typescript
// Exclude — remove members from a union
type A = Exclude<'a' | 'b' | 'c', 'a' | 'c'>; // 'b'
// Extract — keep matching members
type B = Extract<string | number | boolean, number | boolean>; // number | boolean
// NonNullable — remove null and undefined
type C = NonNullable<string | null | undefined>; // string
// ReturnType / Parameters — introspect functions
type Fn = (a: string, b: number) => boolean;
type P = Parameters<Fn>; // [a: string, b: number]
type R = ReturnType<Fn>; // boolean
// Awaited — unwrap Promise recursively
type D = Awaited<Promise<string>>; // string
type E = Awaited<Promise<Promise<number>>>; // number
async function fetchData(): Promise<{ id: number }> { /* ... */ }
type Data = Awaited<ReturnType<typeof fetchData>>; // { id: number }
advancedutility-typesexcludeextract
not reviewed

Type Guards

Narrow a union to a specific member using built-in or user-defined guards.

typescript
// typeof guard
function process(val: string | number) {
if (typeof val === 'string') val.toUpperCase(); // string
else val.toFixed(2); // number
}
// instanceof guard
function handle(err: unknown) {
if (err instanceof TypeError) console.error('Type error:', err.message);
else if (err instanceof Error) console.error('Error:', err.message);
else console.error('Unknown:', err);
}
// in guard
type Cat = { meow(): void };
type Dog = { bark(): void };
function sound(pet: Cat | Dog) {
if ('meow' in pet) pet.meow(); // Cat
else pet.bark(); // Dog
}
// User-defined type predicate
function isUser(val: unknown): val is User {
return (
typeof val === 'object' &&
val !== null &&
typeof (val as User).name === 'string'
);
}
const values: unknown[] = getValues();
const users = values.filter(isUser); // User[]
coretype-guardsnarrowingtypeof
not reviewed

Discriminated Unions

A union with a shared literal tag property enabling exhaustive, type-safe branching.

typescript
type Result<T> =
| { status: 'success'; data: T }
| { status: 'error'; message: string; code: number }
| { status: 'loading' };
function render<T>(result: Result<T>): string {
switch (result.status) {
case 'success': return `Data: ${JSON.stringify(result.data)}`;
case 'error': return `Error ${result.code}: ${result.message}`;
case 'loading': return 'Loading...';
default: {
const _exhaustive: never = result; // compile error if new status added
return _exhaustive;
}
}
}
advanceddiscriminated-unionspatternexhaustiveness
not reviewed

Mapped Types

Iterate over the keys of a type and transform each property — the basis of all built-in utility types.

typescript
// Add optional modifier (+?)
type Partial<T> = { [K in keyof T]?: T[K] };
// Remove optional modifier (-?)
type Required<T> = { [K in keyof T]-?: T[K] };
// Add readonly
type Readonly<T> = { readonly [K in keyof T]: T[K] };
// Remove readonly
type Mutable<T> = { -readonly [K in keyof T]: T[K] };
// Key remapping with as
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
type UserGetters = Getters<{ name: string; age: number }>;
// { getName: () => string; getAge: () => number }
// Filter keys by value type (as never removes a key)
type PickByValue<T, V> = {
[K in keyof T as T[K] extends V ? K : never]: T[K];
};
type StringProps = PickByValue<{ a: string; b: number; c: string }, string>;
// { a: string; c: string }
advancedmapped-typestype-manipulation
not reviewed

Conditional Types

Branch type logic with T extends U ? A : B — distributes over union members by default.

typescript
// Basic conditional
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
// Distributive over unions
type C = IsString<string | number>; // true | false
// Non-distributive (bracketed)
type IsStringExact<T> = [T] extends [string] ? true : false;
type D = IsStringExact<string | number>; // false
// infer — extract a type from a position
type Unwrap<T> = T extends Promise<infer R> ? R : T;
type E = Unwrap<Promise<string>>; // string
type F = Unwrap<number>; // number
// Flatten arrays one level
type Flatten<T> = T extends (infer E)[] ? E : T;
type G = Flatten<string[]>; // string
advancedconditional-typesinfertype-manipulation
not reviewed

The infer Keyword

Capture and name a matched sub-type inside a conditional type.

typescript
// Extract return type (like ReturnType<T>)
type MyReturnType<T> =
T extends (...args: any[]) => infer R ? R : never;
type R = MyReturnType<(n: number) => boolean>; // boolean
// Extract parameter types
type Params<T> =
T extends (...args: infer P) => unknown ? P : never;
type P = Params<(a: string, b: number) => void>; // [string, number]
// Unwrap a Promise
type Await<T> = T extends Promise<infer V> ? V : T;
// Extract both key and value from Map
type UnpackMap<T> =
T extends Map<infer K, infer V> ? { key: K; value: V } : never;
type KV = UnpackMap<Map<string, number>>; // { key: string; value: number }
// Get constructor instance type
type NewableReturn<T> =
T extends new (...args: any[]) => infer I ? I : never;
advancedinferconditional-typestype-manipulation
not reviewed

Template Literal Types

TS 4.1

Construct new string literal types using template syntax — distributes over union members.

typescript
type Direction = 'top' | 'right' | 'bottom' | 'left';
type CSSMargin = `margin-${Direction}`;
// 'margin-top' | 'margin-right' | 'margin-bottom' | 'margin-left'
// Built-in string manipulation types
type Upper = Uppercase<'hello'>; // 'HELLO'
type Cap = Capitalize<'world'>; // 'World'
// Event handler map from shape
type EventHandlers<T extends string> = {
[K in T as `on${Capitalize<K>}`]: (e: Event) => void;
};
type ClickFocusBlur = EventHandlers<'click' | 'focus' | 'blur'>;
// { onClick: ..., onFocus: ..., onBlur: ... }
// Strongly typed column ordering
type Column = 'name' | 'age' | 'email';
type OrderBy = `${Column} ASC` | `${Column} DESC`;
// Extract from template with infer
type ExtractId<T extends string> =
T extends `${string}_${infer ID}` ? ID : never;
type Id = ExtractId<'user_abc123'>; // 'abc123'
advancedtemplate-literal-typesstring-manipulation
not reviewed

Class Modifiers

TypeScript adds access modifiers, readonly, abstract, and override to JavaScript classes.

typescript
class Animal {
readonly name: string; // cannot be reassigned after init
public species: string = ''; // visible everywhere (default)
protected age: number = 0; // accessible in subclasses
#secret: string = ''; // truly private (JS private field)
constructor(name: string) { this.name = name; }
}
class Dog extends Animal {
override speak(): void { // 'override' asserts this overrides a base method
console.log(`${this.name} barks!`);
}
birthday() { this.age++; } // OK — protected accessible here
}
// Parameter properties — shorthand: declare + assign in one step
class Point {
constructor(
public readonly x: number,
public readonly y: number,
) {}
}
// equivalent to: class Point { readonly x; readonly y; constructor(x,y) {...} }
coreclassesmodifiersoop
not reviewed

Abstract Classes

Define a base class with methods subclasses must implement — cannot be instantiated directly.

typescript
abstract class Shape {
abstract area(): number;
abstract perimeter(): number;
describe(): string { // concrete method on abstract class
return `Area: ${this.area().toFixed(2)}, P: ${this.perimeter().toFixed(2)}`;
}
}
class Circle extends Shape {
constructor(private r: number) { super(); }
area() { return Math.PI * this.r ** 2; }
perimeter() { return 2 * Math.PI * this.r; }
}
class Rectangle extends Shape {
constructor(private w: number, private h: number) { super(); }
area() { return this.w * this.h; }
perimeter() { return 2 * (this.w + this.h); }
}
// new Shape(); // Error — cannot instantiate abstract class
const shapes: Shape[] = [new Circle(5), new Rectangle(3, 4)];
shapes.forEach(s => console.log(s.describe()));
advancedclassesabstractoop
not reviewed

Function Overloads

Declare multiple call signatures to express type-level polymorphism for a single function.

typescript
// Overload signatures — these are what callers see
function format(value: string): string;
function format(value: number, decimals?: number): string;
function format(value: Date): string;
// Implementation signature — not visible to callers
function format(value: string | number | Date, decimals = 2): string {
if (typeof value === 'string') return value.trim();
if (typeof value === 'number') return value.toFixed(decimals);
return value.toISOString();
}
format(' hello '); // string overload
format(3.14, 2); // number overload
format(new Date()); // Date overload
// Method overloads on classes
class EventBus {
on(event: 'data', fn: (data: Buffer) => void): this;
on(event: 'end', fn: () => void): this;
on(event: 'error', fn: (e: Error) => void): this;
on(event: string, fn: (...args: any[]) => void): this {
return this;
}
}
advancedoverloadsfunctionspolymorphism
not reviewed

as const

Infer the narrowest possible literal type and make the value readonly throughout.

typescript
// Without as const — types are widened
const config1 = { port: 3000, env: 'production' };
// config1.port: number (not 3000)
// config1.env: string (not 'production')
// With as const — literal types preserved, object readonly
const config2 = { port: 3000, env: 'production' } as const;
// config2.port: 3000 (literal)
// config2.env: 'production' (literal)
// config2 is Readonly<...>
// Arrays — inferred as readonly tuple
const tuple = [1, 'hello', true] as const;
// readonly [1, 'hello', true]
// Extract a value union from a const object
const STATUS = {
Pending: 'pending',
Active: 'active',
Inactive: 'inactive',
} as const;
type Status = (typeof STATUS)[keyof typeof STATUS];
// 'pending' | 'active' | 'inactive'
coreas-constliteral-typesreadonly
not reviewed

satisfies Operator

TS 4.9

Validate a value against a type without widening its inferred literal types.

typescript
type Config = { host: string; port: number; debug?: boolean };
// as Config — validates but widens to Config
const c1: Config = { host: 'localhost', port: 3000 };
// c1.port: number — literal 3000 is lost
// satisfies — validates shape, preserves inferred type
const c2 = { host: 'localhost', port: 3000 } satisfies Config;
// c2.port: 3000 (literal — not widened)
// c2.host: 'localhost' (literal)
// Mixed-value objects
type Palette = Record<string, string | number[]>;
const palette = {
red: [255, 0, 0],
green: '#00ff00',
} satisfies Palette;
// palette.red is number[], not string | number[]
palette.red.map(c => c / 255); // OK
advancedsatisfiestype-validation
not reviewed

Index Signatures

Describe objects with dynamic string or number keys.

typescript
// Basic index signature
interface StringMap {
[key: string]: string;
}
const map: StringMap = {};
map.anyKey = 'value'; // OK
// Mix of specific and indexed properties
// (specific properties must satisfy the index signature type)
interface Config {
version: string; // must be string to match index type
[key: string]: string;
}
// Number index
interface ArrayLike {
[index: number]: string;
length: number;
}
// Template literal index signature (TS 4.4)
interface EventHandlers {
[event: `on${string}`]: (...args: unknown[]) => void;
}
// Prefer Record<K, V> for simple homogeneous maps
type Cache = Record<string, { value: unknown; expiresAt: number }>;
coreindex-signaturesdynamic-keys
not reviewed

using & await using

TS 5.2

Explicit resource management — automatically dispose resources when they leave scope.

typescript
// Implement Disposable
class DatabaseConnection implements Disposable {
constructor() { console.log('Connected'); }
query(sql: string) { /* ... */ }
[Symbol.dispose]() { console.log('Closed'); }
}
// using — auto-disposed at block exit
function runQuery() {
using db = new DatabaseConnection();
return db.query('SELECT * FROM users');
} // db[Symbol.dispose]() called here
// AsyncDisposable
class FileHandle implements AsyncDisposable {
async [Symbol.asyncDispose]() {
await this.flush();
await this.close();
}
}
async function processFile() {
await using file = openFile('./data.csv');
return file.read();
} // file[Symbol.asyncDispose]() awaited here
advancedusingdisposableresource-management
not reviewed

Module Augmentation & Declaration Merging

Extend third-party module types or add to the global scope without touching their source.

typescript
// Augment Express Request
import 'express';
declare module 'express-serve-static-core' {
interface Request {
user?: { id: string; role: 'admin' | 'user' };
requestId: string;
}
}
// req.user is now typed in all route handlers
// Interface merging — extend an existing interface
interface Window {
analytics: { track(event: string, data?: object): void };
}
window.analytics.track('pageview'); // typed
// Global augmentation inside a module file
declare global {
interface Array<T> {
groupBy<K extends PropertyKey>(fn: (item: T) => K): Record<K, T[]>;
}
}
// Ambient declarations (.d.ts or declare blocks)
declare const __DEV__: boolean; // injected by bundler
declare module '*.svg' { const url: string; export default url; }
advanceddeclaration-mergingmodule-augmentationambient
not reviewed

Decorators (TC39 Stage 3)

TS 5.0

Declarative annotations that augment classes, methods, fields, and accessors.

typescript
// Method decorator — wrap with timing
function timed(target: unknown, ctx: ClassMethodDecoratorContext) {
return function (this: unknown, ...args: unknown[]) {
const t = performance.now();
const result = (target as Function).apply(this, args);
console.log(`${String(ctx.name)}: ${(performance.now() - t).toFixed(1)}ms`);
return result;
};
}
// Field decorator — validate on assignment
function positive(_: unknown, ctx: ClassFieldDecoratorContext) {
return (value: number) => {
if (value < 0) throw new RangeError(`${String(ctx.name)} must be positive`);
return value;
};
}
class Counter {
@positive count = 0;
@timed
increment() { this.count++; }
}
advanceddecoratorsmetadataclasses
not reviewed

const Type Parameters

TS 5.0

Infer literal types in generic functions without requiring as const at every call site.

typescript
// Without const — infers widened type
function identity<T>(value: T): T { return value; }
const a = identity(['a', 'b']); // string[] — not ideal
// With const modifier — infers literal/readonly type
function identity<const T>(value: T): T { return value; }
const b = identity(['a', 'b']); // readonly ['a', 'b']
// Practical: route definitions with literal path types
function defineRoutes<const T extends readonly { path: string }[]>(
routes: T,
): T { return routes; }
const routes = defineRoutes([
{ path: '/', component: 'Home' },
{ path: '/about', component: 'About' },
]);
// routes[0].path: '/' — literal, not just string
advancedgenericsconstliteral-types
not reviewed

Recursive Types

Types that reference themselves — useful for trees, JSON, deeply nested structures.

typescript
// JSON value type
type JSONValue =
| null | boolean | number | string
| JSONValue[]
| { [key: string]: JSONValue };
// Tree node
type TreeNode<T> = {
value: T;
children?: TreeNode<T>[];
};
// Deep Partial — optional at every nesting level
type DeepPartial<T> = T extends object
? { [K in keyof T]?: DeepPartial<T[K]> }
: T;
// Deep Readonly — readonly at every nesting level
type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K];
};
type Config = { db: { host: string; port: number }; debug: boolean };
type ImmutableConfig = DeepReadonly<Config>;
// config.db.host is readonly — nested too
advancedrecursivetypesjson
not reviewed

NoInfer Utility Type

TS 5.4

Prevent TypeScript from using a specific argument to infer a type parameter.

typescript
// Without NoInfer — both arguments influence T inference
function createStore<T>(initial: T, defaultState: T): T { return initial; }
// If initial: { count: 0 } and default: { count: 0, extra: '' }
// T widens to { count: number; extra: string } — may be unintended
// With NoInfer — only 'initial' drives T; defaultState must match
function createStore<T>(initial: T, defaultState: NoInfer<T>): T {
return initial ?? defaultState;
}
createStore({ count: 0 }, { count: 0 }); // OK
// createStore({ count: 0 }, { count: 0, extra: '' }); // Error — extra not in T
// Default fallback that shouldn't widen the inferred type
function withDefault<T>(value: T | undefined, fallback: NoInfer<T>): T {
return value ?? fallback;
}
const n = withDefault<number>(undefined, 0); // fallback must be number
advancedutility-typesgenericsinference
not reviewed

Type Predicates & Assertion Functions

Functions that communicate type information back to the caller for narrowing.

typescript
// Type predicate — val is Type in the true branch
function isString(val: unknown): val is string {
return typeof val === 'string';
}
function isUser(val: unknown): val is User {
return typeof val === 'object' && val !== null && 'id' in val;
}
// filter retains the narrowed element type
const values: unknown[] = getValues();
const users = values.filter(isUser); // User[]
// Assertion function — throws if condition fails, narrows for caller
function assertDefined<T>(
val: T | null | undefined,
msg = 'Expected defined value',
): asserts val is T {
if (val == null) throw new Error(msg);
}
let maybeUser: User | null = getUser();
assertDefined(maybeUser, 'User not found');
maybeUser.name; // User — not User | null
advancedtype-predicatesassertion-functionsnarrowing
not reviewed