Best forFormatting, validating, shrinking, or inspecting code-adjacent text.
169 snippets
Basics (20)
string — text values
let name: string = "Lei";
let greet: string = `Hello, ${name}`; // 模板字符串
let multi: string = `line 1
line 2`; // 多行
The `string` primitive accepts single quotes, double quotes, and backtick template literals. Use template literals for interpolation and multi-line strings.
number — int + float in one type
let count: number = 42;
let pi: number = 3.14;
let hex: number = 0xff; // 255
let bin: number = 0b1010; // 10
let big: bigint = 9007199254740993n; // 超过 Number.MAX_SAFE_INTEGER 用 bigint
TypeScript has one `number` for ints and floats (IEEE 754). Beyond Number.MAX_SAFE_INTEGER (2^53 − 1), use `bigint` with the `n` suffix.
boolean — true / false
let done: boolean = false;
let ok: boolean = !!getValue(); // 双感叹号转 boolean
Just `true` and `false`. Convert anything to boolean with the double-bang `!!value` idiom.
null vs undefined — two empties
let a: null = null;
let b: undefined = undefined;
let c: string | null = getValue(); // 可能为 null
let d: string | undefined; // 默认 undefined
// strictNullChecks 开了之后必须显式处理
if (c !== null) {
c.toUpperCase(); // ✅ 安全
}
null is an intentional empty, undefined is "not assigned yet". With strictNullChecks on (you should), neither is assignable to other types without an explicit union.
any — the escape hatch
let x: any = "hello";
x = 42; // 允许
x = { foo: 1 };// 允许
x.bar.baz(); // 不报错,运行时炸
// ❌ 几乎从不该用,优先 unknown
`any` disables type checking entirely. Almost never the right choice — use `unknown` instead. Acceptable only at JS migration boundaries.
unknown — safe any
let x: unknown = JSON.parse(input);
// ❌ 直接用会报错
// x.toUpperCase();
// ✅ 必须先收窄
if (typeof x === "string") {
x.toUpperCase(); // 这里 x 已经是 string
}
unknown accepts anything but lets you do NOTHING with it until you narrow. The right type for parsed JSON, deserialized data, third-party callbacks.
never — the impossible type
function fail(msg: string): never {
throw new Error(msg); // 永远不返回
}
function loop(): never {
while (true) {} // 死循环也是 never
}
// 用在穷举检查
const _exhaustive: never = value;
never represents values that never occur — thrown functions, infinite loops, the empty set in a union. Use `let _: never = value` for exhaustiveness checks on discriminated unions.
void means "no return value the caller should use". Quirk: a callback typed `() => void` accepts a function returning anything — the return is ignored.
Literal types — exact value as type
let dir: "up" | "down" | "left" | "right";
dir = "up"; // ✅
// dir = "north"; // ❌ 不在联合里
let status: 200 | 404 | 500;
let flag: true; // 唯一一个 true
String, number, and boolean literals can be types — a value must equal one of the listed literals. The foundation of discriminated unions and lookup tables.
Array<T> and T[] — two syntaxes
let xs: number[] = [1, 2, 3];
let ys: Array<number> = [1, 2, 3]; // 同义
// 嵌套时 T[] 更清爽
let grid: number[][] = [[1, 2], [3, 4]];
let grid2: Array<Array<number>> = [[1, 2], [3, 4]];
`T[]` and `Array<T>` are interchangeable. Use `T[]` for short, `Array<T>` when T is complex (Array<string | number>).
readonly arrays — immutable
let xs: readonly number[] = [1, 2, 3];
// xs.push(4); ❌ Property 'push' does not exist
// xs[0] = 9; ❌ Index signature is readonly
// 等价语法
let ys: ReadonlyArray<number> = [1, 2, 3];
`readonly T[]` removes mutating methods (push, pop, splice) and disallows index assignment. Use for function params to signal "I will not mutate this".
Tuple types — fixed-length, mixed types
let pair: [string, number] = ["age", 30];
let trio: [string, number, boolean] = ["x", 1, true];
// 命名元组(4.0+),仅文档用途
let coord: [x: number, y: number] = [10, 20];
// 可选元组元素
let opt: [string, number?] = ["x"];
// rest 元组元素
let rest: [string, ...number[]] = ["x", 1, 2, 3];
Tuples are fixed-length arrays with per-position types. Named labels (4.0+) document intent. `[string, number?]` allows trailing optional; `[T, ...U[]]` allows rest.
object — non-primitive type
let o: object = { x: 1 };
o = [1, 2, 3]; // ✅ 数组也是 object
o = () => {}; // ✅ 函数也是 object
// o = "hello"; // ❌ 字符串是 primitive
// ⚠️ object 上几乎什么也拿不到,你要的多半是 Record<string, unknown>
let m: Record<string, unknown> = { a: 1, b: "x" };
`object` means "anything not a primitive" — array, function, plain object all qualify. You can't access any properties on it; for typed dict use `Record<string, unknown>`.
enum — runtime + type
enum Color { Red, Green, Blue } // 0, 1, 2
let c: Color = Color.Red; // 0
Color[0]; // "Red" (反查)
enum Status { Ok = "OK", Err = "ERR" } // 字符串 enum
enum generates a runtime object with two-way mapping (number → name) for numeric enums. String enums are one-way. Usually prefer string literal unions for new code.
symbol — unique property keys
const id: symbol = Symbol("id");
const a = Symbol("x");
const b = Symbol("x");
a === b; // false,每个 Symbol 都唯一
// unique symbol 才能当类型层面的常量键
const KEY: unique symbol = Symbol();
interface Box { [KEY]: number }
Every `Symbol()` call returns a unique value, so two symbols with the same description are not equal. Only a `const x: unique symbol` can be used as a computed property key in a type.
const enum — inlined, zero runtime
const enum Dir { Up, Down }
const d = Dir.Up;
// 编译输出直接内联为: const d = 0;
// 不生成 Dir 这个运行时对象
// ⚠️ 与 isolatedModules / 单文件转译(Babel、esbuild)冲突
`const enum` is erased at compile time and its members are inlined as literals, so no runtime object is emitted. It clashes with `isolatedModules` and single-file transpilers like Babel.
Function type annotation
let add: (a: number, b: number) => number;
add = (x, y) => x + y; // 参数类型自动推断
// 类型别名形式
type BinOp = (a: number, b: number) => number;
const mul: BinOp = (a, b) => a * b;
A function type `(a: T, b: U) => R` describes parameters and return type. Assigning a matching arrow lets parameter types be inferred contextually, so you skip re-annotating them.
`p?: T` makes a parameter `T | undefined`, so you must handle the missing case. A default `p = value` removes `undefined` from the type and fills it in when the arg is omitted.
Function overloads
function len(x: string): number;
function len(x: any[]): number;
function len(x: string | any[]): number {
return x.length;
}
len("abc"); // ✅ number
len([1, 2, 3]); // ✅ number
Overload signatures declare multiple call shapes above one implementation. Callers see only the overloads; the implementation signature is hidden and must be compatible with all of them.
this parameter typing
interface Counter { count: number }
function inc(this: Counter, by: number) {
this.count += by; // this 已知是 Counter
}
const c: Counter = { count: 0 };
inc.call(c, 5); // ✅ this 类型被检查
A fake first parameter named `this` types the receiver without appearing in the actual argument list. The compiler then checks `call` / `apply` / `bind` for a compatible `this`.
Interface vs type (12)
interface — object shape
interface User {
id: number;
name: string;
email?: string; // 可选属性
readonly createdAt: Date; // 只读
}
const u: User = { id: 1, name: "Lei", createdAt: new Date() };
Use interface for object shapes. `?` marks optional, `readonly` blocks reassignment. The everyday way to model entities and props.
type alias — anything, including unions
type ID = string | number;
type Point = { x: number; y: number };
type Callback = (err: Error | null, data?: string) => void;
type Tuple = [string, number];
type alias gives any type a name — unions, tuples, function types, primitives. interface cannot do unions; type alias can.
interface can extend one or more interfaces. The child gets all parent props plus its own. Type errors fire at the declaration if props conflict.
type intersection — & to merge
type Named = { name: string };
type Aged = { age: number };
type Person = Named & Aged; // { name: string; age: number }
const p: Person = { name: "Lei", age: 30 };
Use intersection `&` on type aliases to combine shapes — equivalent to interface extends but works with any type, not just interfaces.
Interface can declare a call signature — describes a function that ALSO has properties. Useful for typing function-objects (React FCs with displayName, etc.).
interface vs type — which to pick
// ✅ 对象形状要被 extend / implement → interface
interface User { id: number; name: string }
// ✅ 联合 / 元组 / 映射 / 条件 → type
type ID = string | number;
type Tuple = [string, number];
type Keys<T> = keyof T;
Rule of thumb: interface for object shapes that other code extends or implements; type alias for unions, tuples, and any computed shape. Library public APIs lean interface; internal computed types lean type.
Construct signature — newable interface
interface UserCtor {
new (id: number, name: string): { id: number; name: string };
}
function create(Ctor: UserCtor, id: number, name: string) {
return new Ctor(id, name);
}
A `new (...) : T` signature describes a constructor type, so a value can be used with `new`. Useful when a factory takes a class reference and instantiates it.
One interface can combine a call signature, properties, and methods to model a function object. jQuery-style APIs (callable plus namespaced helpers) are the classic example.
`class C implements I` makes the compiler verify the class has every member of `I`. It is a structural check only; it does not inherit implementations the way `extends` does.
A type alias may reference itself, which is how you model recursive shapes like JSON, trees, and nested menus. Each branch of the union can recurse independently.
Both `m?(): void` and `m?: () => void` make a method optional, so call them through `obj.m?.()`. The property form is contravariant under `strictFunctionTypes`; the shorthand is bivariant.
Union & intersection (9)
Union | — A or B
type StringOrNumber = string | number;
function format(x: string | number): string {
if (typeof x === "string") return x.toUpperCase();
return x.toFixed(2);
}
Union `A | B` accepts either type. You can only call methods/properties that exist on BOTH unless you narrow first.
Intersection & — A and B
type WithId = { id: string };
type WithName = { name: string };
type Entity = WithId & WithName; // 同时有 id 和 name
const e: Entity = { id: "a1", name: "Lei" };
Intersection `A & B` requires all properties of A AND all properties of B. Useful for composing capabilities.
Discriminated union — tagged variants
type Shape =
| { kind: "circle"; radius: number }
| { kind: "rect"; w: number; h: number }
| { kind: "triangle"; base: number; h: number };
function area(s: Shape): number {
switch (s.kind) {
case "circle": return Math.PI * s.radius ** 2;
case "rect": return s.w * s.h;
case "triangle": return 0.5 * s.base * s.h;
}
}
A common discriminator field (`kind` here) makes the union narrow-able via switch. The most reliable pattern for modeling variant data.
Exhaustive check with never
function area(s: Shape): number {
switch (s.kind) {
case "circle": return Math.PI * s.radius ** 2;
case "rect": return s.w * s.h;
default:
const _exhaustive: never = s; // ✅ 漏掉一个分支这里直接报错
throw new Error(`Unknown shape: ${_exhaustive}`);
}
}
Assigning the unhandled value to `never` causes compile error if any union member is missing — catches "added a new variant, forgot to handle it" bugs.
Union of literals — enum-lite
type Status = "idle" | "loading" | "success" | "error";
let s: Status = "idle";
s = "loading"; // ✅
// s = "fetching"; // ❌ not in union
String literal unions are the modern alternative to enum — zero runtime cost, perfect autocomplete, can be JSON-serialized.
Optional chaining `?.` short-circuits to `undefined` as soon as the left side is `null` or `undefined`. The result type always includes `undefined`, which keeps it honest.
`??` falls back only when the left side is `null` or `undefined`, while `||` also replaces `0`, `""`, and `false`. Use `??` for numeric and boolean defaults to avoid swallowing valid falsy values.
Union of function types
type Handler = ((e: string) => void) | ((e: number) => void);
// ⚠️ 调用联合函数时,参数类型取交集(never)
declare const h: Handler;
// h("x"); ❌ 参数被推为 string & number = never
// 多数时候你想要的是单个带联合参数的函数:
type Better = (e: string | number) => void;
Calling a union of function types requires arguments assignable to all members at once, so the parameter collapses to an intersection (often `never`). Usually you want one function taking a union parameter instead.
Narrowing a discriminated union by payload
type Result<T> =
| { status: "ok"; data: T }
| { status: "err"; message: string };
function unwrap<T>(r: Result<T>): T {
if (r.status === "ok") return r.data; // r.data 可用
throw new Error(r.message); // r.message 可用
}
Checking the discriminant (`status` here) narrows the union so the branch-specific payload becomes accessible. This is the backbone of typed Result and remote-data patterns.
Generics (14)
Generic function — single type parameter
function identity<T>(x: T): T {
return x;
}
const s = identity("hello"); // T 推断为 string
const n = identity(42); // T 推断为 number
const arr = identity<number[]>([1, 2, 3]); // 显式传 T
Type parameter `<T>` makes the function work with any type while preserving the relationship between input and output. T is usually inferred — explicit only when inference fails.
Multiple type parameters
function pair<A, B>(a: A, b: B): [A, B] {
return [a, b];
}
const p = pair("lei", 30); // [string, number]
function map<T, U>(xs: T[], fn: (x: T) => U): U[] {
return xs.map(fn);
}
Multiple type parameters keep relationships between args. `map<T, U>` ties the input array element type to the function param, and the function return to the output array element.
Generic constraint with extends
function longest<T extends { length: number }>(a: T, b: T): T {
return a.length >= b.length ? a : b;
}
longest("abc", "ab"); // ✅ string 有 length
longest([1, 2, 3], [1]); // ✅ array 有 length
// longest(42, 99); // ❌ number 没有 length
`T extends Shape` limits T to types matching Shape — you can use the constrained properties inside the body. Far better than `any` for "needs property X".
Generic component preserves the item type across props — `render` gets the actual T, not `any`. The standard pattern for typed list components, tables, and selects.
Generic class
class Stack<T> {
private items: T[] = [];
push(x: T): void { this.items.push(x); }
pop(): T | undefined { return this.items.pop(); }
peek(): T | undefined { return this.items[this.items.length - 1]; }
}
const s = new Stack<number>();
s.push(1);
s.push(2);
s.pop(); // number | undefined
Classes take type parameters too. The parameter is bound when you instantiate (`new Stack<number>()`) and applies to all methods.
const type parameter (5.0+)
function asTuple<const T extends readonly unknown[]>(t: T): T {
return t;
}
const a = asTuple([1, 2, 3]); // readonly [1, 2, 3],不是 number[]
// const T 让推断像加了 as const 一样窄
A `const` type parameter (`<const T>`) infers the narrowest literal types from the argument, as if the caller wrote `as const`. It avoids widening tuples to arrays and literals to their base type.
Generic with multiple constraints
function merge<T extends object, U extends object>(a: T, u: U): T & U {
return { ...a, ...u };
}
const r = merge({ id: 1 }, { name: "Lei" });
// r: { id: number } & { name: string }
Each type parameter can carry its own `extends` constraint, and the return type can combine them with `&`. This is how a typed `Object.assign`-style merge preserves both input shapes.
Generic default referencing earlier param
interface Reducer<S, A = { type: string }> {
(state: S, action: A): S;
}
// A 默认 { type: string },也可显式传更精确的 action 联合
type CountReducer = Reducer<number>;
A later type parameter's default may reference an earlier one, e.g. `<S, A = S[]>`. Framework signatures use this so callers only specify the parameters they actually care about.
Constraining to `readonly [unknown, ...unknown[]]` requires at least one element, so indexing `t[0]` is provably safe. The compiler rejects an empty array at the call site.
Returning a generic arrow from an outer function carries one type parameter into the inner scope. The inner function still infers `T` freshly at each call, keeping the helper reusable.
Record<K, V> = { [P in K]: V } — an object with keys K and values V. With a literal union K, the compiler verifies every key is present.
Exclude<T, U> — filter union
type Status = "idle" | "loading" | "success" | "error";
type Resolved = Exclude<Status, "idle" | "loading">;
// "success" | "error"
Exclude<T, U> removes from T those members assignable to U — set difference on unions.
Extract<T, U> — intersect union
type All = string | number | boolean | null;
type Numerics = Extract<All, number | bigint>;
// number
Extract<T, U> keeps only the members of T assignable to U — set intersection on unions.
NonNullable<T> — strip null/undefined
type Maybe = string | number | null | undefined;
type Real = NonNullable<Maybe>;
// string | number
NonNullable<T> removes both null and undefined from a union. Common after narrowing with `x != null`.
ReturnType<F> — extract return type
function getUser() {
return { id: 1, name: "Lei", email: "x@y.z" };
}
type User = ReturnType<typeof getUser>;
// { id: number; name: string; email: string }
ReturnType<F> reads the return type from a function type. Combined with `typeof fn`, lets you derive entity types from factories without restating the shape.
Parameters<F> returns a tuple of the function's param types. Useful for wrappers that need to forward the same argument list.
Awaited<T> — unwrap Promise
type T1 = Awaited<Promise<string>>; // string
type T2 = Awaited<Promise<Promise<number>>>; // number (递归解)
type T3 = Awaited<number>; // number (不是 Promise 就原样)
Awaited<T> recursively unwraps nested Promises. The official `await` semantics in the type system since 4.5.
ConstructorParameters<C>
class User {
constructor(public id: number, public name: string) {}
}
type Args = ConstructorParameters<typeof User>;
// [id: number, name: string]
ConstructorParameters<C> extracts the constructor's parameter tuple. Use for factory functions that mirror a class's constructor.
InstanceType<C> — what `new C()` returns
class User { /* ... */ }
type U = InstanceType<typeof User>;
// User
InstanceType<C> gives the type of `new C()`. Mostly used with mixins and `typeof Class` to refer to the instance type.
Uppercase / Lowercase / Capitalize
type H = Uppercase<"hello">; // "HELLO"
type W = Lowercase<"WORLD">; // "world"
type T = Capitalize<"foo">; // "Foo"
type U = Uncapitalize<"Bar">; // "bar"
Built-in intrinsic types that transform string literals at the type level. Used to build typed event names, CSS-in-JS keys, etc.
OmitThisParameter<F> — drop this
function fn(this: { x: number }, y: number) {
return this.x + y;
}
type Plain = OmitThisParameter<typeof fn>;
// (y: number) => number
OmitThisParameter<F> removes the `this` parameter from a function type, leaving the plain callable signature. Useful after `bind` removes the receiver requirement.
ThisParameterType<F> — extract this
function fn(this: { x: number }, y: number) {
return this.x + y;
}
type Self = ThisParameterType<typeof fn>;
// { x: number }
ThisParameterType<F> pulls out the type of the `this` parameter declared on a function. Combine with `OmitThisParameter` when you wrap or rebind methods.
NoInfer<T> (5.4+) — block inference
function paint<C extends string>(
palette: C[],
fallback: NoInfer<C>,
) { /* ... */ }
paint(["red", "blue"], "red"); // ✅
// paint(["red", "blue"], "x"); ❌ "x" 不在 palette 推断出的 C 里
NoInfer<T> tells the compiler not to use that position when inferring the type parameter. So `C` is fixed by the first argument and the second is merely checked against it.
Record + Partial — sparse dictionary
type Lang = "en" | "zh" | "ja";
type Translations = Partial<Record<Lang, string>>;
const t: Translations = { en: "Hello" }; // zh / ja 可缺省
Wrapping `Record<K, V>` in `Partial` builds a dictionary where every key is optional, so you can fill a subset of a known key set. Common for partial i18n bundles and feature flags.
Pick + Required — selectively force keys
interface User { id?: number; name?: string; email?: string }
type WithId = User & Required<Pick<User, "id">>;
// id 必填,name / email 仍可选
const u: WithId = { id: 1 }; // ✅
Intersecting a type with `Required<Pick<T, K>>` forces just the chosen keys to be present while leaving the rest optional. Handy for "this field is now guaranteed" view models.
TypeScript ships no built-in `Mutable`, so the `-readonly` mapped modifier is the canonical way to strip immutability. Pair it with `as const` data you later need to mutate.
Tuple to union via T[number]
const ROLES = ["admin", "user", "guest"] as const;
type Role = typeof ROLES[number];
// "admin" | "user" | "guest"
Indexing a readonly tuple type with `[number]` yields the union of its element types. This is the standard trick to derive a string-literal union from a single source-of-truth array.
Type narrowing (13)
typeof narrowing
function format(x: string | number): string {
if (typeof x === "string") {
return x.toUpperCase(); // x: string
}
return x.toFixed(2); // x: number
}
`typeof x === "string" | "number" | "boolean" | "object" | "function" | "undefined" | "symbol" | "bigint"`. The compiler narrows the union inside the branch.
instanceof narrowing
class HttpError extends Error { status: number = 500 }
class NetError extends Error { code: string = "ECONN" }
function handle(e: HttpError | NetError) {
if (e instanceof HttpError) {
console.log(e.status); // e: HttpError
} else {
console.log(e.code); // e: NetError
}
}
`instanceof Class` narrows to the class type. Works for any constructor — both built-in (Error, Date) and user-defined classes.
in operator narrowing
type Fish = { swim: () => void };
type Bird = { fly: () => void };
function move(a: Fish | Bird) {
if ("swim" in a) {
a.swim(); // a: Fish
} else {
a.fly(); // a: Bird
}
}
`"key" in obj` narrows to the union members that have the key. Lighter than discriminated unions when you don't want to add a tag field.
Equality narrowing
function example(x: string | null) {
if (x !== null) {
x.toUpperCase(); // x: string
}
}
function check(a: "a" | "b", b: "b" | "c") {
if (a === b) {
// 这里 a 和 b 都是 "b",唯一公共值
}
}
`===` and `!==` narrow union members. Comparing two unions narrows BOTH sides to the common members.
Truthy check `if (x)` narrows away null, undefined, 0, "", NaN, false. Watch out: empty string is also falsy, which is rarely what you want.
User-defined type guard with `is`
function isString(x: unknown): x is string {
return typeof x === "string";
}
function example(x: unknown) {
if (isString(x)) {
x.toUpperCase(); // x: string
}
}
Return type `x is T` tells the compiler "this function returns true iff x is T". The way to teach the type system about custom checks.
Assertion function with `asserts`
function assertString(x: unknown): asserts x is string {
if (typeof x !== "string") throw new Error("not string");
}
function example(x: unknown) {
assertString(x);
x.toUpperCase(); // x: string (assert 之后整段都是 string)
}
`asserts x is T` narrows x AFTER the call (not inside a branch). Useful when you want a guard that throws instead of returning a boolean.
Discriminated union switch — full pattern
type Action =
| { type: "ADD"; payload: number }
| { type: "RESET" }
| { type: "SET"; payload: number };
function reduce(state: number, a: Action): number {
switch (a.type) {
case "ADD": return state + a.payload;
case "RESET": return 0;
case "SET": return a.payload;
default:
const _: never = a; // 漏一个分支编译报错
return state;
}
}
The full Redux-style discriminated-union reducer pattern with exhaustiveness check. Adding a new action type without handling it fails compilation.
`Array.isArray(x)` is a built-in type guard that narrows `T | T[]` down to the array branch. It is the reliable way to accept "one or many" arguments.
Discriminant on a literal property
type Resp =
| { ok: true; body: string }
| { ok: false; code: number };
function read(r: Resp) {
if (r.ok) return r.body; // ok: true 分支
return `error ${r.code}`; // ok: false 分支
}
A boolean (or any literal) field works as a discriminant just like a string `kind`. Checking `r.ok` narrows the union without needing a dedicated tag string.
Narrowing with const assertion
function handle(method: string) {
const m = method.toUpperCase();
if (m === "GET" || m === "POST") {
const verb = m as "GET" | "POST"; // 明确收窄
return verb;
}
}
After comparing a widened `string` against literals, an `as` assertion locks it to the literal union for downstream use. Prefer typing the source narrowly, but this rescues already-widened values.
Control-flow narrowing after assignment
let x: string | number;
x = "hello";
x.toUpperCase(); // x: string,赋值后立即收窄
x = 42;
x.toFixed(2); // x: number
Assigning a value narrows a variable to that value's type for the following statements, even when the declared type is a union. This is the everyday flow analysis you rely on without noticing.
Narrowing object property via local const
function run(cfg: { value?: number }) {
// cfg.value 每次访问可能被别处改,TS 不替你收窄
const v = cfg.value;
if (v !== undefined) {
v.toFixed(2); // ✅ 局部常量收窄成立
}
}
Narrowing on `obj.prop` can be invalidated by intervening calls, so copy it into a `const` first and narrow that. The local binding is provably stable, so the guard sticks.
Mapped types (11)
Basic mapped type
type Stringify<T> = {
[K in keyof T]: string;
};
interface User { id: number; age: number }
type UserStrings = Stringify<User>;
// { id: string; age: string }
`{ [K in keyof T]: ... }` iterates over T's keys and produces a new type. The foundation of Partial, Required, Readonly — they're all mapped types.
Mapped with value transform
type Nullable<T> = {
[K in keyof T]: T[K] | null;
};
interface User { id: number; name: string }
type N = Nullable<User>;
// { id: number | null; name: string | null }
Reference the original value type via `T[K]` and transform it (here, union with null). Common for "all fields optional/nullable" wrappers.
Key remapping with `as` (4.1+)
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
interface User { id: number; name: string }
type UserGetters = Getters<User>;
// { getId: () => number; getName: () => string }
The `as` clause renames keys during mapping. Combined with template literal types, generates getter / setter / event-name shapes from a base type.
Filter keys with as never
type PickStrings<T> = {
[K in keyof T as T[K] extends string ? K : never]: T[K];
};
interface Mixed { name: string; age: number; role: string }
type Strings = PickStrings<Mixed>;
// { name: string; role: string }
Remap a key to `never` to drop it from the result. Combined with conditional types, lets you filter object properties by value type.
Modifier + and - on optional / readonly
type Mutable<T> = { -readonly [K in keyof T]: T[K] };
type Optional<T> = { [K in keyof T]+?: T[K] };
type Required<T> = { [K in keyof T]-?: T[K] };
interface Frozen { readonly id: number; readonly name: string }
type Editable = Mutable<Frozen>;
// { id: number; name: string }
Use `-` to remove an `optional` or `readonly` modifier, `+` to add (default). The mechanism behind built-in Required and Mutable utilities.
DeepReadonly with mapped + recursion
type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends object
? T[K] extends Function
? T[K]
: DeepReadonly<T[K]>
: T[K];
};
interface Tree { left: Tree | null; value: number }
type FrozenTree = DeepReadonly<Tree>;
// 整棵树都是 readonly
Recursive mapped type freezes nested object properties too. Exclude Function (typeof function === "object") to avoid mangling methods.
Iterate over a finite string union to require every key. Compiler verifies completeness — useful for form schemas, permission tables, route maps.
Homomorphic mapped type preserves modifiers
type Clone<T> = { [K in keyof T]: T[K] };
interface User { readonly id: number; name?: string }
type Copy = Clone<User>;
// { readonly id: number; name?: string },readonly / ? 都保留
A mapped type written as `[K in keyof T]` is homomorphic, so it copies each property's `readonly` and `?` modifiers automatically. That is why `Partial`/`Readonly` keep the original structure.
Record via mapped over a union
type MyRecord<K extends keyof any, V> = {
[P in K]: V;
};
type Flags = MyRecord<"a" | "b", boolean>;
// { a: boolean; b: boolean }
Mapping `[P in K]` over a union of keys is exactly how the built-in `Record<K, V>` is defined. `keyof any` (`string | number | symbol`) is the broadest valid key constraint.
Mapped type value lookup with conditional
type Boxed<T> = {
[K in keyof T]: T[K] extends string ? { text: T[K] } : { value: T[K] };
};
interface M { name: string; age: number }
type R = Boxed<M>;
// { name: { text: string }; age: { value: number } }
Inside a mapped type you can branch on `T[K]` with a conditional to transform values per their type. Useful for wrapping fields into tagged containers based on their original type.
Mapped type over array / tuple
type Boxed<T> = { [K in keyof T]: { v: T[K] } };
type R = Boxed<[number, string]>;
// [{ v: number }, { v: string }],元组结构保留
Mapping over a tuple keeps it a tuple of the same length, transforming each positional element. Array/tuple-aware mapped types power utilities like a per-element wrapper.
Conditional types (10)
Basic conditional type — extends ? :
type IsString<T> = T extends string ? "yes" : "no";
type A = IsString<"hello">; // "yes"
type B = IsString<42>; // "no"
type C = IsString<string>; // "yes"
`T extends U ? X : Y` chooses a type based on assignability. The base mechanism for all the smart utility types.
Distributive conditional type
type ToArray<T> = T extends any ? T[] : never;
type A = ToArray<string | number>;
// string[] | number[] (而不是 (string | number)[])
//
// 联合传入裸类型参数时,条件类型会分配到每个成员
When the type parameter is "naked" (just `T`, not wrapped), the conditional type distributes over each union member. To prevent distribution, wrap: `[T] extends [U]`.
Wrap to prevent distribution
type IsUnion<T, _T = T> = T extends any
? [Exclude<_T, T>] extends [never]
? false
: true
: never;
type A = IsUnion<string | number>; // true
type B = IsUnion<string>; // false
Wrap a type parameter in a tuple `[T]` to prevent distributive behavior. Pattern used to test "is this exactly a union?".
Conditional type chain
type TypeName<T> =
T extends string ? "string" :
T extends number ? "number" :
T extends boolean ? "boolean" :
T extends undefined ? "undefined" :
T extends Function ? "function" :
"object";
type A = TypeName<"hello">; // "string"
type B = TypeName<() => void>;// "function"
Chain conditionals to mimic switch / pattern matching. Reads top to bottom — first match wins.
Conditional with constraint
type NonEmpty<T extends string> = T extends "" ? never : T;
type A = NonEmpty<"hello">; // "hello"
type B = NonEmpty<"">; // never
function tag<T extends string>(s: NonEmpty<T>): T {
return s;
}
// tag(""); ❌ "" 推断为 never,无法调用
Combine generic constraint with a conditional to validate input shape at call site. `NonEmpty<"">` resolves to `never`, blocking the call.
NonNullable — built-in conditional
// 内置实现:
type NonNullable<T> = T extends null | undefined ? never : T;
type A = NonNullable<string | null>; // string
type B = NonNullable<string | undefined>; // string
type C = NonNullable<null>; // never
NonNullable is implemented as a distributive conditional that maps null/undefined to never. Reading built-in utility implementations is the best way to grok conditional types.
Flatten one array level
type Flatten<T> = T extends (infer E)[] ? E : T;
type A = Flatten<string[]>; // string
type B = Flatten<number>; // number(不是数组就原样)
A conditional with `infer E` peels one array level off when `T` is an array, and returns `T` unchanged otherwise. The same shape underlies the standard library's `FlatArray`.
Recursive deep-flatten conditional
type DeepFlatten<T> = T extends (infer E)[] ? DeepFlatten<E> : T;
type A = DeepFlatten<number[][][]>; // number
type B = DeepFlatten<string>; // string
Recursing into the `infer E` branch flattens nested arrays of any depth down to the leaf type. Recursive conditionals are the type-level equivalent of a `while` loop.
Conditional inside a generic default
type Unbox<T, F = T extends Promise<infer U> ? U : T> = F;
type A = Unbox<Promise<number>>; // number
type B = Unbox<string>; // string
A default type parameter can itself be a conditional that computes from earlier parameters. It lets you expose a derived type while keeping the public signature short.
Function vs value with conditional
type Unwrap<T> = T extends () => infer R ? R : T;
type A = Unwrap<() => number>; // number
type B = Unwrap<string>; // string
Inferring the return type of a no-arg function and falling back to the value itself models a "lazy or eager" input. Common for config options that accept either a value or a factory.
infer (10)
infer — extract function return type
type MyReturnType<T> = T extends (...args: any) => infer R ? R : never;
type R1 = MyReturnType<() => string>; // string
type R2 = MyReturnType<(x: number) => boolean>; // boolean
infer R binds a name to a type position you want to extract. The built-in ReturnType uses exactly this pattern.
infer — unwrap Promise
type MyAwaited<T> = T extends Promise<infer U> ? U : T;
type A = MyAwaited<Promise<string>>; // string
type B = MyAwaited<number>; // number
Use infer inside Promise<...> to extract the resolved type. The built-in Awaited recurses to handle nested Promises.
infer — array element type
type Element<T> = T extends (infer E)[] ? E : never;
type A = Element<number[]>; // number
type B = Element<Array<{ x: number }>>;// { x: number }
Pattern `(infer E)[]` extracts the element type from an array. The same as the lookup `T[number]` for arrays.
infer — tuple head / tail
type Head<T extends any[]> = T extends [infer H, ...any[]] ? H : never;
type Tail<T extends any[]> = T extends [any, ...infer R] ? R : [];
type H = Head<[string, number, boolean]>; // string
type T = Tail<[string, number, boolean]>; // [number, boolean]
Combine variadic tuple types with infer to deconstruct tuples — head, tail, last, init. Foundation of compile-time list manipulation.
infer — first function parameter
type FirstParam<F> = F extends (first: infer P, ...rest: any[]) => any ? P : never;
type A = FirstParam<(name: string, age: number) => void>; // string
Use infer in the first position of the parameter list, ignore the rest. Mirrors the built-in Parameters, narrowed to just the head.
infer — last tuple element
type Last<T extends any[]> = T extends [...any[], infer L] ? L : never;
type A = Last<[1, 2, 3]>; // 3
type B = Last<[]>; // never
A rest pattern `[...any[], infer L]` captures the final element of a tuple. Variadic tuple positions plus `infer` let you read head, tail, and last without recursion.
infer — Promise resolved type (recursive)
type Resolved<T> = T extends Promise<infer U> ? Resolved<U> : T;
type A = Resolved<Promise<Promise<number>>>; // number
type B = Resolved<string>; // string
Recursing on the inferred `U` unwraps arbitrarily nested promises down to the eventual value, mirroring the built-in `Awaited`. The non-promise case stops the recursion.
infer with constraint (extends)
type FirstString<T> =
T extends [infer S extends string, ...any[]] ? S : never;
type A = FirstString<["hi", 1, 2]>; // "hi"
type B = FirstString<[1, 2]>; // never
Since 4.7 you can constrain an inferred type with `infer S extends C`, so the branch only matches when the inferred type fits. It avoids a separate nested conditional to validate the inference.
infer — object property type
type PropType<T, K extends string> =
T extends { [P in K]: infer V } ? V : never;
type A = PropType<{ id: number; name: string }, "id">; // number
Placing `infer V` in a property position extracts that field's type from a matching object shape. This generalizes indexed access `T[K]` to patterns the compiler can match structurally.
infer — rest parameters tuple
type RestArgs<F> =
F extends (first: any, ...rest: infer R) => any ? R : never;
type A = RestArgs<(id: number, a: string, b: boolean) => void>;
// [a: string, b: boolean]
Inferring `...rest: infer R` captures every parameter after the first as a tuple. Useful for wrappers that fix the first argument and forward the remainder.
Template literal types (4.1+) let you constrain strings by pattern. Embedding `string` makes that part variable; embedding a literal union makes it a finite set.
Cross product with union
type Lang = "en" | "zh";
type Kind = "button" | "link";
type Key = `${Kind}.${Lang}`;
// "button.en" | "button.zh" | "link.en" | "link.zh"
Embedding a union in a template produces the cross product. Used for i18n keys, event names, CSS class names.
Template literal in mapped type
type EventNames<T> = {
[K in keyof T as `on${Capitalize<string & K>}`]?: (value: T[K]) => void;
};
interface Form { name: string; age: number }
type FormHandlers = EventNames<Form>;
// { onName?: (v: string) => void; onAge?: (v: number) => void }
Combine mapped types + key remapping + template literals to generate handler types. The pattern behind type-safe event APIs.
String parsing with infer
type Split<S extends string, D extends string> =
S extends `${infer A}${D}${infer B}`
? [A, ...Split<B, D>]
: [S];
type A = Split<"a,b,c,d", ",">; // ["a", "b", "c", "d"]
Recursive template literal + infer can split strings, parse routes, validate string shapes — all at compile time.
Template literal coerces unions to string
type Id = `user_${number}`;
const a: Id = "user_42"; // ✅
// const b: Id = "user_x"; ❌ 必须是数字
type Hex = `#${string}`;
const c: Hex = "#ff0000"; // ✅
Embedding `number` or `string` in a template type constrains the shape while leaving that segment free. Great for branded id formats, hex colors, and prefixed keys.
Uppercase inside template literal
type Method = "get" | "post";
type Route = `${Uppercase<Method>} /api`;
// "GET /api" | "POST /api"
Intrinsic string types like `Uppercase` compose directly inside template literals, transforming each union member as the cross product is built. Handy for HTTP method tables and event channels.
Extract prefix with template + infer
type RemovePrefix<S extends string, P extends string> =
S extends `${P}${infer Rest}` ? Rest : S;
type A = RemovePrefix<"on:click", "on:">; // "click"
type B = RemovePrefix<"click", "on:">; // "click"
Matching `\`${P}${infer Rest}\`` strips a known prefix and captures whatever follows. Use it to parse event names, namespaced keys, or CSS variable prefixes at the type level.
Join tuple of strings with separator
type Join<T extends string[], D extends string> =
T extends [infer F extends string, ...infer R extends string[]]
? R extends []
? F
: `${F}${D}${Join<R, D>}`
: "";
type A = Join<["a", "b", "c"], "/">; // "a/b/c"
Recursive template literals concatenate a tuple of strings with a separator, the type-level inverse of `Split`. The empty-rest case avoids a trailing separator.
Assertions (10)
as — type assertion (cast)
const el = document.getElementById("input") as HTMLInputElement;
el.value = "hello"; // ✅ 编译器信你说的是 input
// ❌ 不检查,错了运行时炸
const wrong = "hello" as unknown as number;
`as Foo` is an UNCHECKED cast — the compiler trusts you. Use only when you genuinely know more than the compiler. Never as a "shut up the type error" hammer.
`as const` freezes a value to its narrowest type — string stays as the literal, array becomes readonly tuple, object becomes deeply readonly. Use on lookup tables and configs.
`satisfies` verifies a value matches a type WITHOUT widening the inferred type. Best for configs where you want both compile-time validation and narrow literal types for lookups.
Non-null assertion !
const el = document.getElementById("input")!; // 断言不是 null
el.click();
// ❌ 滥用会埋雷
// 更安全:
const el2 = document.getElementById("input");
if (el2) el2.click();
`!` postfix tells the compiler "I promise this is not null/undefined". DOM lookups are the main legit use case — and even there, prefer an explicit null check.
Angle-bracket cast — legacy syntax
const a = <string>someValue; // 旧写法
const b = someValue as string; // ✅ 推荐(JSX 不冲突)
Old `<Type>value` cast conflicts with JSX. Always use `as Type` in TSX files (and prefer it everywhere for consistency).
Double assertion via unknown
// ❌ 直接断不让你过
// const n = "hello" as number;
// ✅ 经过 unknown 二段断言(编译器允许,但你自己要负责)
const n = "hello" as unknown as number;
// 真要这么干说明设计有问题,重新想想类型
When source and target types overlap insufficiently, TypeScript blocks a direct cast. `as unknown as Foo` is the escape hatch — almost always a red flag that the types are wrong.
Chaining `as const satisfies T` freezes the value to its narrowest literal type and validates it against `T` in one step. You get both deep-readonly literals and shape checking.
const assertion on object literal
function on<E extends string>(event: E) { return event; }
on("click"); // E 推断为 string
on("click" as const); // E 推断为 "click"
const ev = { type: "click" } as const;
ev.type; // "click",不是 string
Applying `as const` to a literal or object freezes properties to their exact values and prevents widening at the call site. It is the lightweight way to pass precise literals into generics.
Definite assignment assertion !
let value!: number; // 告诉编译器:我保证用前会赋值
function init() { value = 42; }
init();
value.toFixed(2); // ✅ 不再报 used before assigned
class C {
ref!: HTMLElement; // 类字段同理(由 DI / 生命周期赋值)
}
A `!` after a variable or class field name is the definite assignment assertion, telling the compiler a value will be assigned before use. Common for fields set by DI, lifecycle hooks, or test setup.
const assertion for readonly tuple args
function dispatch(...args: readonly [string, number]) {}
const payload = ["save", 1] as const;
dispatch(...payload); // ✅ as const 让它是 readonly ["save", 1]
// 不加 as const 会推成 (string | number)[],spread 不匹配
Spreading an array into a tuple parameter needs the array typed as a fixed-length tuple, which `as const` provides. Without it the array widens to `T[]` and the spread fails to match positions.
Declaration merging (7)
Interface declaration merging
interface User { id: number }
interface User { name: string }
// 自动合并:
// interface User { id: number; name: string }
const u: User = { id: 1, name: "Lei" };
Two interfaces with the same name in the same scope merge automatically. Used by libraries to let users extend their types.
`declare global { ... }` inside a module adds to the global scope. The trailing `export {}` is required to make the file a module. Used for build-time defines and Window augmentation.
namespace + function merging
function area(r: number) { return Math.PI * r * r; }
namespace area {
export const unit = "cm²";
}
area(2); // 函数调用
area.unit; // "cm²",挂在同名 namespace 上
A function and a same-named `namespace` merge so you can attach static helpers and constants to a function. This is how libraries expose `fn` plus `fn.helper` with full typing.
enum + namespace merging
enum Color { Red, Green, Blue }
namespace Color {
export function hex(c: Color): string {
return ["#f00", "#0f0", "#00f"][c];
}
}
Color.hex(Color.Red); // "#f00"
A `namespace` with the same name as an `enum` merges, letting you attach helper functions to the enum object. The enum stays a value and a type while gaining methods.
`declare` introduces a type-only declaration for something that exists at runtime but has no TypeScript source, like a script-injected global. It emits no JavaScript.
Reopening a global interface inside `declare global` adds fields to existing ambient types, like Vite's `ImportMetaEnv`. The trailing `export {}` keeps the file a module so augmentation is scoped correctly.
Decorators (6)
Legacy class decorator (experimental)
// tsconfig: { "experimentalDecorators": true }
function logged<T extends { new(...args: any[]): {} }>(Cls: T) {
return class extends Cls {
constructor(...args: any[]) {
console.log("creating", Cls.name);
super(...args);
}
};
}
@logged
class User {
constructor(public name: string) {}
}
new User("Lei"); // logs "creating User"
Old decorator syntax requires `experimentalDecorators: true` in tsconfig. Still widely used in Angular, NestJS, TypeORM.
Stage 3 standard decorator (5.0+)
// 不需要 experimentalDecorators
function logged<C extends new (...args: any[]) => any>(
Cls: C,
ctx: ClassDecoratorContext,
) {
return class extends Cls {
constructor(...args: any[]) {
console.log("creating", ctx.name);
super(...args);
}
};
}
@logged
class User {
constructor(public name: string) {}
}
TypeScript 5.0 implements the Stage 3 ECMAScript decorator proposal — different shape than legacy. Receives a `context` object. New code should target this.
Method decorator
// Stage 3
function timed(
fn: Function,
ctx: ClassMethodDecoratorContext,
) {
return function (this: unknown, ...args: any[]) {
const t0 = performance.now();
const result = fn.apply(this, args);
console.log(`${String(ctx.name)} took ${performance.now() - t0}ms`);
return result;
};
}
class Api {
@timed
fetch() { /* ... */ }
}
Method decorator wraps or replaces a method. The context tells you the method name, whether it's static, and access metadata.
Accessor decorator (Stage 3)
function logged<T>(
target: { get: () => T },
ctx: ClassGetterDecoratorContext,
) {
return function (this: unknown) {
console.log("read", String(ctx.name));
return target.get.call(this);
};
}
class Box {
#v = 1;
@logged get value() { return this.#v; }
}
A getter decorator receives `{ get }` plus a `ClassGetterDecoratorContext` and may return a replacement accessor. Stage 3 decorators have distinct context types per target kind.
Field decorator with initializer
function double(
_target: undefined,
ctx: ClassFieldDecoratorContext,
) {
return function (initial: number) {
return initial * 2; // 改写初始值
};
}
class C {
@double count = 5; // 实例化后 count === 10
}
A Stage 3 field decorator returns an initializer function that receives the original value and returns the stored one. It runs per instance, so it can transform or validate the initial field value.
The Stage 3 context exposes `addInitializer`, a callback that runs during construction. Binding the method to `this` there gives the classic auto-bind behavior without a constructor.
Common pitfalls (17)
any vs unknown — the most common trap
// ❌ any 把检查全关了,错误悄悄溜进运行时
function badParse(input: string): any {
return JSON.parse(input);
}
const data = badParse("{}");
data.foo.bar.baz(); // 不报错,运行时 TypeError
// ✅ unknown 强制你先收窄
function goodParse(input: string): unknown {
return JSON.parse(input);
}
const safe = goodParse("{}");
// safe.foo; ❌ 不让你动
if (typeof safe === "object" && safe !== null && "foo" in safe) {
// 这里才安全
}
any disables type checking; unknown forces narrowing. For "value of unknown shape" (parsed JSON, third-party callbacks), use unknown — any is the wrong default 99% of the time.
For library public APIs that consumers may augment, use interface (declaration merging works). For unions, tuples, mapped, conditional types, you NEED type alias.
Method shorthand (`f(): T`) is bivariant; function property (`f: () => T`) is contravariant under strictFunctionTypes. Use function property form for safer event handler types.
enum runtime cost vs const enum vs string union
// ❌ enum 会生成运行时代码(双向映射对象)
enum Color { Red, Green, Blue }
// ⚠️ const enum 内联,没运行时代码,但与 isolatedModules / babel 冲突
const enum Status { Ok, Err }
// ✅ 字符串字面量联合:零运行时、JSON 友好、自动补全完美
type ColorLit = "red" | "green" | "blue";
Regular enums emit a runtime object (extra bytes). const enum inlines but breaks isolatedModules. String literal unions are the modern recommendation — zero cost, JSON-friendly, perfect autocomplete.
as Foo unsafe cast hides errors
// ❌ as 是不检查的强转
const x = "hello" as unknown as number;
const y = x.toFixed(2); // 编译过,运行时 TypeError
// ✅ 用 satisfies 校验
const config = {
port: 8080,
host: "localhost",
} satisfies Record<string, string | number>;
// ✅ 真不得已用 as,加注释说明原因
// DOM 保证返回 input,querySelector 不知道
const input = document.querySelector(".x") as HTMLInputElement;
`as` bypasses the compiler. Use satisfies for validation; use `as` only when you genuinely know more than the compiler (DOM lookups), and document the reason.
Spreading two Partial<T> gives Partial<T> back — the compiler can't prove required fields are present. Provide explicit fallbacks for required keys before spreading.
Function parameter with `void` return type accepts ANY return. Designed so callbacks like forEach (which expects `void`) can accept any expression-bodied arrow. Quirk to know, not necessarily a bug.
Excess property checks fire only when you pass an object literal directly. Assigning to a variable first relaxes to structural compatibility, so the extra property slips through silently.
Array index access is not undefined-checked
const xs = [1, 2, 3];
const x = xs[10]; // 类型是 number,但运行时是 undefined!
// ✅ 开 noUncheckedIndexedAccess 后:
// x 的类型变成 number | undefined
By default `arr[i]` is typed as the element type even for out-of-range indices, hiding real `undefined`s. Enable `noUncheckedIndexedAccess` to make index access return `T | undefined`.
Empty object type {} is too permissive
// {} 不是"空对象",而是"除 null/undefined 外的任何值"
let a: {} = 42; // ✅
let b: {} = "x"; // ✅
let c: {} = [1, 2]; // ✅
// let d: {} = null; ❌
// 想表达"任意非空对象"用 Record<string, unknown> 或 object
The type `{}` means "any value except null and undefined", so it accepts numbers and strings, not just objects. For an actual object use `object` or `Record<string, unknown>`.
Forgotten await on a Promise condition
async function check() {
// ❌ Promise 永远是 truthy,分支必走
if (isReady()) { /* 总是进来 */ }
// ✅ 别忘了 await
if (await isReady()) { /* 正确 */ }
}
declare function isReady(): Promise<boolean>;
A `Promise` object is always truthy, so an un-awaited promise in an `if` always takes the branch. Enable `no-misused-promises` (typescript-eslint) to catch this class of bug.
Loose `==` performs implicit coercion, producing surprising equalities like `0 == ""`. Always use strict `===`; the `eqeqeq` lint rule enforces it across a codebase.
Return type widening on object literals
// 返回值被推宽成 string,丢了字面量
function makeBad() {
return { status: "ok" }; // { status: string }
}
// ✅ as const 或显式返回类型保留字面量
function makeGood() {
return { status: "ok" } as const; // { readonly status: "ok" }
}
A returned object literal widens its property types (`"ok"` becomes `string`), which breaks downstream discriminated-union narrowing. Use `as const` or an explicit return type to keep the literals.
Since 4.4 a `catch` variable is typed `unknown`, because anything can be thrown, not just `Error`. Narrow with `instanceof Error` before touching `.message` or `.stack`.
Chaining `?.` through fields that should always exist hides structural bugs behind a silent fallback. Apply optional chaining only at genuinely nullable links, not as a blanket guard.
A string index signature makes every property access type-check, so typos like `d.conut` pass silently as the value type. Reserve index signatures for truly dynamic keys and list known keys explicitly.
What this tool does
Searchable TypeScript cheat sheet — 100+ snippets
working TypeScript developers actually type, not toy
"let x: number = 1" examples. Fourteen categories:
basics (string / number / boolean / null / undefined /
never / unknown / any / void / literal types / readonly
arrays / tuples), interface vs type alias (extends,
declaration merging, index + call signatures), union
and intersection (A | B vs A & B, distributive over
union, discriminated unions), generics (single + multi
type parameter, extends constraint, default parameter,
keyof constraint, generic React components, generic
classes), utility types (Partial, Required, Readonly,
Pick, Omit, Record, Exclude, Extract, NonNullable,
ReturnType, Parameters, Awaited, ConstructorParameters,
InstanceType, Uppercase / Lowercase / Capitalize), type
narrowing (typeof, instanceof, in, equality, truthy,
user-defined guard with `is`, assertion function with
`asserts`, discriminated switch with never
exhaustiveness check), mapped types (homomorphic, key
remapping with `as`, + / - modifiers), conditional
types (T extends U ? X : Y, distributive, wrap to
prevent distribution, chain), infer (ReturnType,
Promise unwrap, tuple head/tail, array element,
template parsing), template literal types (4.1+, union
cross product, recursive string parsing), assertions
(as Foo, as const, satisfies 4.9+, non-null !),
declaration merging (interface auto-merge, module
augmentation, global), decorators (legacy experimental
vs Stage 3 5.0+), and seven pitfalls (any vs unknown,
non-null ! abuse, type vs interface choice, parameter
variance, enum runtime cost, unsafe as cast, void
return widening). Every entry: bilingual title,
runnable code, bilingual description. Search across
title / code / description together. 100% client-side,
zero requests. Pair with Python / SQL / Vim / Regex
cheatsheets and JSON Formatter for the full stack.
Tool details
Input
Text
The page exposes text boxes, numeric controls, file pickers, or structured inputs depending on the tool.
Output
Live result + Copy
The result area focuses on usable output, with copy, download, or preview actions when supported.
Privacy
May use a live lookup
A network call is detected in the component, so redact sensitive data when appropriate.
Save / share
No account required
Open the page and use it; whether results survive refresh depends on the tool.
Performance budget
Initial JS <= 32 KB
No WASM budget is declared, keeping the tool quick to open on mobile.
Best fit
Developer & DevOps · Developer
Category and role tags drive related tools, internal links, and quick fit checks.
How to use
1
1. Input
Paste or drop your content into the tool panel.
2
2. Process
Click the button. All processing is local in your browser.
3
3. Copy / Download
Copy the result or download to disk in one click.
How TypeScript Cheatsheet fits into your work
Use it in the small gaps between coding, reviewing, debugging, and shipping.
Developer jobs
Formatting, validating, shrinking, or inspecting code-adjacent text.
Preparing snippets for documentation, tickets, commits, or handoff.
Checking a small payload quickly without switching tools.
Developer checks
Run irreversible transforms like minify or obfuscate on a copy.
Keep secrets out of pasted snippets unless the tool explicitly stays local.
Use your normal tests or linter before shipping transformed code.
Good next steps
These links move the current task into a more complete workflow.
Pin down the exact utility type during a code review
A reviewer flags a 400-line PR where someone hand-wrote
a Partial-style mapped type instead of using the built-in.
You filter to "Utility types", copy the Pick + Omit and
Partial snippets, and leave three line comments swapping
25 lines of bespoke types for stdlib one-liners. The PR
shrinks and the diff stays reviewable.
Write a discriminated-union state machine that compiles
You are modeling a fetch hook with idle / loading / success /
error states and keep accessing .data on the error branch.
Filter to "Union and intersection", copy the discriminated
union plus the never exhaustiveness switch, and the compiler
now rejects any branch you forgot. One 12-line pattern kills
a whole class of runtime undefined errors.
Extract a Promise's value type with infer at 2am
A teammate's API returns Promise<User[]> and you need just
User without importing it. You search "infer", copy the
Awaited-with-infer conditional type, and write
ElementOf<Awaited<ReturnType<typeof fetchUsers>>>. No new
import, no any, and the type stays in sync if the endpoint
shape changes later.
Validate a config object with satisfies, not as
Your theme config has 40 color keys and you want the
compiler to catch a typo while keeping each key's literal
type for autocomplete. You copy the satisfies snippet,
change `as const` to `satisfies Theme`, and now a misspelled
"primry" fails the build while `theme.primary` still infers
as the exact hex string downstream.
Common pitfalls
Reaching for `any` to silence an error when you mean `unknown`. `any` disables every check downstream; `unknown` forces a `typeof`/guard before use. Copy the unknown snippet and narrow instead.
Using `as Foo` to "fix" a type error. A cast is unchecked and just hides the mismatch until runtime. Prefer `satisfies` for validation, and only cast when you truly know more than the compiler (e.g. a known DOM element).
Picking `interface` for a union or computed shape and then fighting it. Interfaces cannot express unions, tuples, or conditional types. Use a `type` alias the moment you go past a flat object shape.
Privacy
Everything runs in your browser. The cheat sheet is a single
static page and search filters an in-memory snippet array, so
your search terms never leave the tab and nothing is written to
the URL. No code execution, no upload, no network requests. Safe
to use behind a corporate proxy or on an air-gapped machine.
FAQ
Related tools
Hand-picked utilities that pair well with this one.