Open-source libraries used

1 libraries are bundled into this tool's code.

TypeScript Cheatsheet — Quick Reference

A quick reference for TypeScript 5 syntax, the type system, and the most common idioms — covers about 80% of day-to-day usage.

TS

TypeScript TypeScript 5.x

ECMAScript + types · Multi-paradigm, structured · Static (gradual) typing on top of JS

Recommended Learning Path

First, set up your compile environment (tsc/tsconfig) and understand that TS is a superset of JS → master basic type annotations, union types, and interfaces → go deeper into the type system (generics, type guards, type operations) → organize code with classes and modules → handle async and DOM/Node types → finally look up build config, testing, and debugging as needed. The FAQ section is good to revisit to avoid pitfalls.

1.Hello World and Build Environment

Compile and run TypeScript; understand tsc, tsconfig, and the type-checking pipeline.

Minimal Program

TS is a superset of JS: any valid JS is valid TS. After adding type annotations, tsc compiles it to JS.

1
2
3
4
5
6
// hello.ts
const message: string = 'Hello, world!';
console.log(message);
// Type annotation: `: string` after the variable name
// $ npx tsc hello.ts # Compile to hello.js
// $ node hello.js

tsc Compile

tsc compiles .ts to .js. --noEmit only type-checks without output, --watch watches for changes, --strict enables strict checks.

1
2
3
4
5
// $ npx tsc app.ts # Compile a single file
// $ npx tsc --noEmit # Type-check only, no output
// $ npx tsc --watch # Watch and auto-compile
// $ npx tsc --strict # Strict type checking
// $ npx tsc --outDir dist # Output directory

tsconfig.json

tsconfig.json configures compilation: target, module, strict, outDir. npx tsc reads it automatically.

1
2
3
4
5
6
7
8
9
10
11
12
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true, // Strict mode
"outDir": "dist",
"sourceMap": true // Source map for debugging
},
"include": ["src"]
}
// Use `tsc --init` to generate the default config

Run TS Directly

Use tsx/ts-node to run TS directly without compiling first. Node 22 supports --experimental-strip-types natively.

1
2
3
4
5
// $ npx tsx script.ts # Run TS directly
// $ npx ts-node script.ts # Older approach
// Node 22+:
// $ node --experimental-strip-types script.ts
// Dev scripts often use tsx; production uses tsc-compiled output

Strict Mode

strict: true turns on all strict checks: null checks, implicit any errors, unused-variable warnings. Required for new projects.

1
2
3
4
5
6
7
8
// `strict` includes:
// 1. strictNullChecks null can't be assigned to a plain type
// 2. noImplicitAny Error when a parameter has no type
// 3. noUnusedLocals Error on unused variables
function greet(name: string) { // Explicit type
return 'Hi ' + name;
}
// Without `strict`, `name` is implicitly `any` and no error is raised

TS vs JS Relationship

TS type-checks at compile time; the compiled JS has no type info. Types exist only at compile time and are erased at runtime.

1
2
3
4
5
6
7
8
interface User {
name: string;
age: number;
}
const u: User = { name: 'Nick', age: 30 };
// Compiled output (types erased):
// const u = { name: 'Nick', age: 30 };
// Type errors are caught at compile time and don't affect runtime

Dependencies and Type Packages

Libraries need type definitions. @types/* is the DefinitelyTyped community type package. typescript is the compiler tool dependency.

1
2
3
4
5
6
// $ npm i -D typescript @types/node
// $ npm i -D @types/express # Community-maintained types
// Libraries that ship their own types:
// axios exports types directly, no @types needed
// // @ts-ignore comment: skip checking the next line (use with care)
// import axios from 'axios'; has type definitions

Editor Integration

VS Code has built-in TS support: hover to see types, red squiggles for errors, autocomplete, refactoring. Error messages display inline.

1
2
3
4
5
6
7
// VS Code shortcuts:
// Hover: see the type
// Quick fix: Ctrl+. (Cmd+.)
// Rename symbol: F2
// Go to definition: F12
// Type errors appear in the Problems panel
// Config: the workspace tsconfig requires a matching `tsc` version

2.Variables and Type Annotations

Type annotations, type inference, union types, any/unknown, and type assertions.

Type Annotations

Annotate a type with `: Type` after the variable name. Once annotated, the type is fixed; assigning another type is a compile error.

1
2
3
4
5
6
const name: string = 'Nick';
let age: number = 30;
const isAdmin: boolean = true;
// Error example:
// age = 'thirty'; // Type error
// After initialization the type is inferred; explicit annotations are optional

Type Inference

TS infers types from initial values; most cases need no explicit annotation. Use an explicit interface for complex types.

1
2
3
4
5
6
7
8
const name = 'Nick'; // inferred as string
let count = 42; // number
const arr = [1, 2, 3]; // number[]
const obj = { a: 1 }; // { a: number }
// A literal union inferred at assignment:
let status = 'idle' as const;
// For complex cases, explicit annotations are clearer:
let data: Map<string, User> = new Map();

Union Types

`|` denotes a union type: `string | number` means either. Accessing members requires a type guard first.

1
2
3
4
5
6
7
8
9
10
11
12
function print(value: string | number) {
// Union types only expose common members
console.log(value.toString());
// Branch handling:
if (typeof value === 'string') {
console.log(value.toUpperCase()); // string
} else {
console.log(value.toFixed(2)); // number
}
}
// Literal union:
type Dir = 'up' | 'down' | 'left' | 'right';

any vs unknown

any disables type-checking (avoid); unknown means unknown (usable only after a guard). Use unknown for safe handling of external data.

1
2
3
4
5
6
7
8
9
let risky: any = 'text'; // any: unchecked, use with care
risky.method(); // Compiles but may crash at runtime
// unknown: safer
let data: unknown = getApi();
if (typeof data === 'string') {
console.log(data.length); // Usable after the guard
}
// Assert unknown: data as string
// Prefer unknown over any

Type Assertions

`as` asserts to the compiler that you know better. It doesn't change runtime; it's a compile-time declaration. Overuse hides errors.

1
2
3
4
5
6
7
const el = document.getElementById('btn') as HTMLButtonElement;
// Not recommended: double assertions (gotchas)
// const n = value as unknown as number;
// Safer: guard first, then assert
const json = JSON.parse(text) as User[];
// `as const`: literal narrowing
const modes = ['dev', 'prod'] as const;

Non-null Assertion

The `!` suffix asserts a value is non-null/undefined. Use only when sure, or it may crash at runtime.

1
2
3
4
5
6
7
8
9
let name: string | null = getMaybe();
const len = name!.length; // Assert non-null (risky)
// Safer alternatives:
if (name) {
const l2 = name.length;
}
// Or nullish coalescing:
const l3 = name?.length ?? 0;
// The non-null assertion is a compile-time promise; runtime null will still crash

Literal Types

Literal types narrow to specific values: 'up', 42, true. Pair with unions to enumerate options.

1
2
3
4
5
6
7
8
let direction: 'up' | 'down' = 'up';
// direction = 'sideways'; // Error: not in the union
const yes: true = true;
// Narrowing object properties:
const config = {
mode: 'production',
} as const; // mode: 'production' literal
// `as const` makes object members readonly literal types

Destructuring and Types

Destructuring preserves types. Destructured function parameters require annotating the whole parameter object type.

1
2
3
4
5
6
7
8
9
10
interface User { name: string; age: number; }
const { name, age }: User = getUser();
// Destructured function parameter:
function show({ name, age }: User) {
console.log(name, age);
}
// Array destructuring:
const [first, second] = [1, 2] as const;
// Destructuring optional properties:
const { name = 'guest' }: { name?: string } = data;

3.Type System

Primitive types, object/array/tuple types, interfaces, generics, type aliases, and type operations.

Primitive Types

The set of primitive types: string, number, boolean, null, undefined, void, symbol, bigint.

1
2
3
4
5
6
7
8
const s: string = 'text';
const n: number = 42;
const b: boolean = true;
const v: void = undefined; // Functions with no return value
const nl: null = null;
const u: undefined = undefined;
const sym: symbol = Symbol('id');
const big: bigint = 10n;

Arrays and Tuples

`number[]` for arrays, `[string, number]` for tuples (fixed length and order), `readonly` for read-only arrays.

1
2
3
4
5
6
7
8
9
const nums: number[] = [1, 2, 3];
const strs: Array<string> = ['a', 'b']; // Generic syntax
// Tuple:
let pair: [string, number] = ['age', 30];
// pair[0] = 42; // Error: type mismatch
// Read-only array:
const fixed: readonly number[] = [1, 2];
// fixed.push(3); // Error: read-only
// Optional tuple element: type T = [string, number?]

Object Types

Object types describe a shape: properties, optional `?`, read-only `readonly`, method signatures.

1
2
3
4
5
6
7
8
9
10
11
12
interface Point {
readonly x: number; // Read-only
y: number;
label?: string; // Optional
}
const p: Point = { x: 1, y: 2 };
// p.x = 10; // Error: readonly
// Methods:
interface Greeter {
greet(name: string): string;
// or greet: (name: string) => string;
}

interface vs type

`interface` defines an object shape (extendable); `type` aliases are more flexible (union/intersection/tuple). Prefer interface day-to-day.

1
2
3
4
5
6
7
8
9
10
11
interface User {
name: string;
}
// `interface` can merge/extend:
interface Admin extends User {
permissions: string[];
}
// `type` aliases:
type ID = string | number; // Union
type Pair = [string, number]; // Tuple
type Shape = { area: number } & { color: string }; // Intersection

Enums

`enum` is a named constant set: numeric, string, or const enum. String enums are more common.

1
2
3
4
5
6
7
8
9
10
11
12
13
enum Color {
Red, // 0
Green, // 1
Blue, // 2
}
enum Status {
Active = 'active',
Inactive = 'inactive',
}
const c: Color = Color.Green;
const s: string = Status.Active; // 'active'
// Reverse mapping: Color[0] === 'Red' (numeric enum)
// If you only want the type: type S = 'active' | 'inactive'

Generics

Generics parameterize types: T is a type parameter. Reuse across functions/classes/interfaces; resolved at compile time.

1
2
3
4
5
6
7
8
9
10
11
function identity<T>(value: T): T {
return value;
}
const s = identity('hello'); // string
const n = identity(42); // number
// Generic interface:
interface Box<T> {
value: T;
}
const box: Box<number> = { value: 42 };
// Multiple type parameters: function pair<A, B>(a: A, b: B)

keyof and Indexing

`keyof` gets a union of object keys, `T[K]` indexes, mapped types. The core tools of type operations.

1
2
3
4
5
6
7
8
9
10
interface User { name: string; age: number; }
type Keys = keyof User; // 'name' | 'age'
// Indexed access:
type NameType = User['name']; // string
// Mapped type:
type Readonly<T> = {
readonly [K in keyof T]: T[K];
};
// Built-in utility types: Partial<T>, Required<T>, Pick<T, K>
// Partially optional: type PartialUser = Partial<User>

Built-in Utility Types

Mapped types like Partial/Omit/Pick/Record/Exclude/ReturnType simplify common transformations.

1
2
3
4
5
6
7
type PartialUser = Partial<User>; // All properties optional
type PickName = Pick<User, 'name'>; // Pick only `name`
type NoAge = Omit<User, 'age'>; // Omit `age`
type Rec = Record<string, number>; // Key-value mapping
type WithoutZero = Exclude<0 | 1 | 2, 0>; // 1 | 2
type R = ReturnType<typeof fn>; // Function return type
// Parameters<typeof fn> for the parameter types

Template Literal Types

Template string syntax to construct string types. Combine with unions to generate permutations. Type-level string parsing.

1
2
3
4
5
6
7
8
type Event = `on${'Click' | 'Hover'}`;
// Event = 'onClick' | 'onHover'
type Size = `${'small' | 'large'}-${number}`;
// Size = 'small-1' | 'large-2' ...
// Extracting from a string:
// type Extracted = 'a:b'.split<'a:b', ':'>; // Type-level split
// Simple string parsing:
// type First = 'abc' extends `${infer F}bc` ? F : never; // 'a'

4.Type Guards and Nullability

Type guards, type narrowing, optional chaining, null handling, and reference semantics.

Type Guards

`typeof`, `instanceof`, `in` narrow union types. Types automatically narrow inside the branch.

1
2
3
4
5
6
7
8
9
10
11
function f(v: string | number | Date) {
if (typeof v === 'string') {
v.toUpperCase(); // string
} else if (v instanceof Date) {
v.getTime(); // Date
} else {
v.toFixed(2); // number
}
}
// `in` guard:
if ('permissions' in user) { /* Admin */ }

Type Narrowing

After a guard, the type narrows in the scope. Null checks and truthy checks both narrow.

1
2
3
4
5
6
7
8
9
10
11
let value: string | null = getMaybe();
if (value) {
value.length; // string (narrowed)
}
// Truthiness narrowing:
function f(s: string | undefined) {
s ?? console.log('missing'); // Nullish coalescing narrows
}
// After a null check:
value = null;
if (value === null) return; // After this, `value` is non-null

Discriminated Unions

Discriminated unions: share a discriminator (kind/type); after `switch` the type narrows precisely. Useful for state machines.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
type Shape =
| { kind: 'circle'; radius: number }
| { kind: 'square'; side: number };
function area(s: Shape): number {
switch (s.kind) {
case 'circle': return Math.PI * s.radius ** 2;
case 'square': return s.side * s.side;
// Exhaustiveness check:
default: {
const _exhaustive: never = s;
return _exhaustive;
}
}
}

Optional Chaining and Nullish Coalescing

`?.` safe access, `??` nullish fallback, `??=` default assign. Chained deep access without null crashes.

1
2
3
4
5
6
7
8
9
const name = user?.profile?.name ?? 'guest';
const count = data?.items?.length ?? 0;
// Nullish-coalescing assignment:
let settings = getConfig();
settings ??= { theme: 'dark' };
// Optional call:
callback?.();
// Difference between `??` and `||`:
// 0 ?? 'x' is 0; 0 || 'x' is 'x'

null and undefined

Under strictNullChecks, null/undefined cannot be assigned to plain types. You need an explicit union or handling.

1
2
3
4
5
6
7
8
9
let name: string | null = null; // Union includes null
let title: string | undefined;
// Function may return nullable:
function find(): User | null {
return Math.random() > 0.5 ? null : { name: 'x' };
}
const u = find();
if (u) { u.name; } // Access after narrowing
// Safe assertions: u!.name or u ?? { name: '?' }

Reference Semantics

TS does not change JS reference semantics: objects are shared by reference, arrays shallow-copy. Types just describe the shape.

1
2
3
4
5
6
7
8
9
const a = { x: 1 };
const b = a; // Shared reference
b.x = 99;
console.log(a.x); // 99
// Spreading copies shallowly:
const c = { ...a };
c.x = 1; // a.x is unchanged
// TS types don't guarantee immutability unless marked readonly
// For deep freezing, use `as const` plus a library

Custom Type Guards

The `is` keyword declares a function as a type guard: returns boolean and narrows the parameter. Common for filtering arrays.

1
2
3
4
5
6
7
8
9
function isString(v: unknown): v is string {
return typeof v === 'string';
}
const values: unknown[] = ['a', 1, 'b', null];
const strs = values.filter(isString);
// `strs` has type `string[]` (the guard narrows it)
// Arrow-function form:
const s2 = values.filter(
(v): v is string => typeof v === 'string');

Assertion Functions

`asserts` declares an assertion function: returns void but narrows the type after the call. Throws to interrupt.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function assertString(v: unknown): asserts v is string {
if (typeof v !== 'string') {
throw new Error('Expected string');
}
}
function process(v: unknown): void {
assertString(v);
v.toUpperCase(); // Narrowed to string
}
// Unconditional assertion:
function assert(cond: unknown): asserts cond {}
// Value assertion:
function assertNonNull<T>(v: T): asserts v is NonNullable<T> {}
// Useful for combining runtime invariants with type narrowing

5.Control Flow

Branches, loops, switch statements, and their interplay with type narrowing.

if / else

`if`/`else` branches with type guards. The condition expression narrows the variable type.

1
2
3
4
5
6
7
8
9
function describe(v: string | number) {
if (typeof v === 'string') {
return `string: ${v.toUpperCase()}`;
} else {
return `number: ${v.toFixed(2)}`;
}
}
// Multi-branch `else if` + guards narrow step by step
// Truthy check: `if (value)` narrows out null/undefined

switch and Exhaustiveness

`switch` handles discriminated unions. The `default` branch uses `never` to check all cases are covered.

1
2
3
4
5
6
7
8
9
10
11
12
13
type Action =
| { type: 'add'; n: number }
| { type: 'reset' };
function reducer(a: Action) {
switch (a.type) {
case 'add': return a.n;
case 'reset': return 0;
default: {
const exhaustive: never = a; // Compile error if a case is missing
return exhaustive;
}
}
}

Loops

`for`/`for...of`/`while` for iteration. Use `for...of` to iterate arrays; use `for` or `entries` when you need the index.

1
2
3
4
5
6
7
8
9
for (let i = 0; i < 10; i++) { }
for (const item of items) { }
for (const [i, v] of items.entries()) {
console.log(i, v);
}
let n = 0;
while (n < 5) { n++; }
// Iterating object keys:
for (const key of Object.keys(obj) as (keyof typeof obj)[]) { }

Ternary and Types

The two sides of a ternary form a union. When branches return different types, the result is a union type.

1
2
3
4
5
6
const result = cond ? 'yes' : 0;
// `result` has type `'yes' | 0` (literal union)
// Annotate explicitly when you need a unified type:
const msg: string = cond ? 'yes' : String(0);
// Nested ternaries hurt readability; prefer guards
// For nullish values: v ?? fallback

break and continue

`continue` skips the current iteration, `break` exits, labeled `break`/`continue` control nested loops.

1
2
3
4
5
6
7
8
9
10
11
for (let i = 0; i < 10; i++) {
if (i % 2 === 0) continue;
if (i > 7) break;
}
outer:
for (const a of list) {
for (const b of a.items) {
if (b.done) continue outer;
}
}
// Types are unchanged; this is pure control flow

Early Return

Guard clauses return early to reduce nesting. After a null check the type narrows.

1
2
3
4
5
6
7
8
function process(u: User | null) {
if (u === null) return; // Early return
if (u.age < 18) return;
console.log(u.name); // Already narrowed to non-null
}
// Multiple guards keep the main logic flat
// Default up front with nullish coalescing:
const name = u?.name ?? 'guest';

do...while

`do...while` runs once before testing. Use for loops that must run at least once. Types don't participate.

1
2
3
4
5
6
7
8
9
10
11
let attempts = 0;
do {
attempts++;
const ok = tryOnce();
if (ok) break;
} while (attempts < 3);
// Runs at least once, then checks the condition
// `while`: checks first, may run zero times
// Common use cases:
// retries, menu selection, input validation
// Beware infinite loops: the condition must eventually be false

Object Iteration

`Object.keys` iterates object keys. Asserting `keyof` keeps it type-safe. Use `Object.values` to iterate values.

1
2
3
4
5
6
7
8
9
10
11
const config = { host: 'x', port: 3000 };
// Iterate keys (assertion needed):
for (const key of Object.keys(config) as (keyof typeof config)[]) {
console.log(key, config[key]);
}
// Iterate values:
for (const value of Object.values(config)) { }
// Iterate entries:
for (const [k, v] of Object.entries(config)) { }
// Note:
// Object.keys returns string[]; assert before reading values safely

6.Functions

Function types, optional and default parameters, overloads, rest parameters, and this.

Function Types

Function type annotation: parameter types and return type. Arrow function types are equivalent to function declarations.

1
2
3
4
5
6
7
8
9
10
11
// Function declaration:
function add(a: number, b: number): number {
return a + b;
}
// Arrow function:
const add = (a: number, b: number): number => a + b;
// Function-type variable:
type Fn = (a: number, b: number) => number;
const f: Fn = add;
// `void` return:
function log(msg: string): void { console.log(msg); }

Optional and Default Parameters

`?` for optional parameters, `=` for default. Defaults imply optional. Optional parameters come after required ones.

1
2
3
4
5
6
7
8
9
10
11
function greet(name: string, title?: string): string {
return title ? `${title} ${name}` : name;
}
// Default parameter:
function mul(a: number, b = 2): number {
return a * b;
}
mul(3); // 6
// Default parameters can be omitted when calling
// Optional parameters go last:
// greet('Nick', undefined) also works

Rest Parameters

`...rest` collects a variable number of arguments into an array. Rest parameters need an array type annotation.

1
2
3
4
5
6
7
8
9
function sum(...nums: number[]): number {
return nums.reduce((a, b) => a + b, 0);
}
sum(1, 2, 3); // 6
// Generic `rest` preserves the tuple:
function tuple<T extends unknown[]>(...args: T): T {
return args;
}
const t = tuple(1, 'a', true); // [number, string, boolean]

Overload Signatures

Overloads: multiple signature declarations plus one implementation. Calls match by signature. Use to constrain return types per parameter shape.

1
2
3
4
5
6
7
8
function pick(obj: Record<string, unknown>, key: string): unknown;
function pick(obj: number[], index: number): number;
function pick(obj: any, key: string | number): unknown {
return obj[key];
}
// The implementation signature isn't visible to callers
// Overloads match in order; put the broadest one last
// Common when return types differ by input shape (e.g. DOM APIs)

Generic Functions

Generic parameter constraints: `T extends ...`. Constraints limit the type and expose its members.

1
2
3
4
5
6
7
8
9
10
function first<T extends string | number[]>(arr: T): T[number] {
return arr[0];
}
const s = first('hello'); // string
const n = first([1, 2, 3]); // number
// Constrained calls:
function getLen<T extends { length: number }>(v: T): number {
return v.length;
}
// Multiple type parameters: <K, V extends keyof K>

this Type

The `this` parameter annotates the `this` type. Method chains return `this` to enable chaining. Arrow functions don't bind `this`.

1
2
3
4
5
6
7
8
9
10
11
12
13
class Builder {
private items: string[] = [];
add(item: string): this {
this.items.push(item);
return this; // For chaining
}
}
const b = new Builder().add('a').add('b');
// Explicit `this` parameter (must come first):
function log(this: { name: string }) {
console.log(this.name);
}
// Arrow functions inherit `this` from the enclosing scope

Callbacks and Function Parameters

Callbacks as parameters: annotate with a function type. Type inference for array higher-order functions like map/filter/reduce.

1
2
3
4
5
6
7
8
9
function withLog(fn: (n: number) => number) {
return fn(42);
}
withLog(n => n * 2); // Parameter type inferred automatically
// Array higher-order functions:
const doubled = [1, 2, 3].map(n => n * 2);
const evens = [1, 2, 3, 4].filter(n => n % 2 === 0);
const total = [1, 2, 3].reduce((acc, n) => acc + n, 0);
// Mind the `this` type in callbacks so it doesn't get lost

Function Constraint Techniques

Parameter-union narrowing, optional callbacks, return inference. Prefer interfaces over concrete classes for function parameters.

1
2
3
4
5
6
7
8
9
10
function handle(v: string | number, cb?: (r: string) => void) {
const r = typeof v === 'string' ? v.toUpperCase() : String(v);
cb?.(r); // Optional callback
}
// Use an interface for parameters (structural typing):
interface HasId { id: number }
function findById<T extends HasId>(arr: T[], id: number): T | undefined {
return arr.find(x => x.id === id);
}
// Structural typing: matching shape is enough; instances need not share a class

7.Strings

Template strings, common methods, regex, and character handling.

Template Strings

Backtick template strings: `${}` for interpolation, multi-line preserved. The type is still `string`.

1
2
3
4
5
6
7
8
9
10
11
12
const name = 'Nick';
const greeting = `Hello, ${name}!`;
// Multi-line:
const lines = `
line 1
line 2
`;
// Expressions:
const total = `Sum: ${1 + 2}`;
// Template literal types:
// type T = `id-${string}`
// The type is still `string`; templates are just syntax sugar

Common Methods

`slice`/`substring` for substrings, `toUpperCase` for case, `split` to split, `includes`/`startsWith` to query, `replace` to replace.

1
2
3
4
5
6
7
8
9
const s = 'TypeScript';
const sub = s.slice(0, 4); // 'Type'
const upper = s.toUpperCase(); // 'TYPESCRIPT'
const parts = s.split(''); // char array
s.includes('Script'); // true
s.startsWith('Type'); // true
s.endsWith('t'); // true
const r = 'a-b-c'.replace(/-/g, '_'); // 'a_b_c'
const padded = '7'.padStart(3, '0'); // '007'

Template Literal Types

Type-level template strings: `${}` concatenates types. Use `infer` to extract string structure.

1
2
3
4
5
6
7
8
type Route = `/users/${string}/profile`;
const good: Route = '/users/123/profile';
// Type-level extraction:
type ExtractId<S extends string> =
S extends `/users/${infer Id}/profile` ? Id : never;
type Id = ExtractId<'/users/42/profile'>; // '42'
// Uppercase conversion: Uppercase<T>, Lowercase<T>
// Composition: type All = `${'a'|'b'}${'1'|'2'}` // 'a1'|'a2'|'b1'|'b2'

Characters and Code Points

`length` counts UTF-16 code units (emojis count as two). Iterate code points with `for...of` / `Array.from`.

1
2
3
4
5
6
7
8
9
const emoji = '👋';
console.log(emoji.length); // 2 (surrogate pair)
console.log([...emoji].length); // 1
const s = 'abc';
for (const ch of s) { } // by code point
// Code-point access:
const first = Array.from(s)[0];
// charCodeAt / fromCodePoint handle code points:
String.fromCodePoint(128075); // '👋'

Regex

`RegExp` for matching and replacing. `match` returns a capture-group array; `matchAll` iterates globally. The type-level `RegExp` is fixed.

1
2
3
4
5
6
7
8
9
10
11
const re = /(\w+)@(\w+)/g;
const all = s.matchAll(re);
for (const m of all) {
console.log(m[1], m[2]); // capture group
}
// Replace with capture:
const r = '2026-01-01'.replace(/(\d{4})-(\d{2})-(\d{2})/, '$3/$2/$1');
// Assert non-null:
const match = s.match(/(\w+)@/);
if (match) { console.log(match[1]); }

Locale and Comparison

`localeCompare` for locale comparison, `toLocaleLowerCase` for locale case, `Intl` for formatting numbers and dates.

1
2
3
4
5
6
const arr = ['ä', 'a', 'z'].sort((a, b) => a.localeCompare(b, 'zh'));
const num = (1234.5).toLocaleString('zh-CN'); // '1,234.5'
const date = new Date().toLocaleDateString('zh-CN');
// Intl.NumberFormat:
const nf = new Intl.NumberFormat('zh-CN', { style: 'currency', currency: 'CNY' });
// Locale list: Intl.supportedValuesOf('language')

Escape Characters

String escapes: `\n` newline, `\t` tab, `\\` backslash, `\u` code point. Escape inside single or double quotes.

1
2
3
4
5
6
7
8
9
10
const nl = '第一行\n第二行';
const tab = 'a\tb'; // a [tab] b
const backslash = 'C:\\path'; // C:\path
const quote = 'He said \'hi\'';
const uni = '\u4e2d'; // '中'
const code = '\u{1F600}'; // emoji code point
// Template strings don't require escaping single or double quotes
const tmpl = `She said "hi" and 'bye'`;
// Common: \n newline, \t indent, \\ path
// JSON output must escape quotes

Reverse and Compare

Reverse a string with split/array, trim whitespace, dedupe. Compare with `localeCompare` or canonical comparison.

1
2
3
4
5
6
7
8
9
10
11
12
const s = 'hello';
// Reverse:
const rev = [...s].reverse().join(''); // 'olleh'
// Trim leading/trailing whitespace:
const t = ' text '.trim();
// Strip all whitespace:
const compact = s.replace(/\s+/g, '');
// Repeat: 'ab'.repeat(3) // 'ababab'
// Palindrome check:
const isPalindrome = s === [...s].reverse().join('');
// Canonical comparison (case-insensitive):
s.toLowerCase() === 'HELLO'.toLowerCase();

8.Collections and Objects

Arrays, objects, Map/Set, and immutable update patterns.

Array Operations

`push`/`pop` at the tail, `unshift`/`shift` at the head, `splice` insert/remove, `slice` copy, `includes`/`indexOf` to query.

1
2
3
4
5
6
7
8
9
const arr = [1, 2, 3];
arr.push(4); // [1,2,3,4]
const last = arr.pop(); // 4
arr.unshift(0); // [0,1,2,3]
const first = arr.shift(); // 0
const removed = arr.splice(1, 1); // remove
const copy = arr.slice(); // shallow copy
arr.includes(2);
arr.indexOf(2); // first index, or -1

map / filter / reduce

Functional iteration: `map` to transform, `filter` to filter, `reduce` to aggregate, `find` to find, `every`/`some` to test. Returns a new array, never mutates the original.

1
2
3
4
5
6
7
8
9
const nums = [1, 2, 3, 4];
const doubled = nums.map(n => n * 2);
const evens = nums.filter(n => n % 2 === 0);
const sum = nums.reduce((acc, n) => acc + n, 0);
const found = nums.find(n => n > 2); // 3 | undefined
const ok = nums.every(n => n > 0); // true
const has = nums.some(n => n === 4); // true
// Chaining:
nums.filter(n => n % 2).map(n => n * 10).reduce((a, b) => a + b, 0);

Object Operations

Spread and merge `{...a, ...b}`; `Object.keys`/`values`/`entries` to iterate; `keyof` for typed keys.

1
2
3
4
5
6
7
8
9
10
const base = { id: 1, name: 'Nick' };
const extended = { ...base, age: 30 }; // merge
const override = { ...base, name: 'New' }; // override
const keys = Object.keys(base);
// Type-safe iteration:
for (const key of Object.keys(base) as (keyof typeof base)[]) {
console.log(base[key]);
}
// entries:
for (const [k, v] of Object.entries(base)) { }

Map

`Map` maps any key: set/get/has/delete/size. Preserves insertion order; O(1) lookup.

1
2
3
4
5
6
7
8
9
10
11
const scores = new Map<string, number>();
scores.set('alice', 90);
const v = scores.get('alice'); // number | undefined
scores.has('bob'); // false
scores.delete('alice');
scores.size;
// Iterate:
for (const [k, val] of scores) { }
for (const key of scores.keys()) { }
// Initialize:
new Map([['a', 1], ['b', 2]]);

Set

`Set` deduplicates: add/delete/has/size. Use to dedupe arrays, compute unions/intersections.

1
2
3
4
5
6
7
8
9
10
const set = new Set<number>();
set.add(1).add(2).add(1); // {1, 2}
set.has(1); // true
set.delete(2);
// Deduplicate an array:
const unique = [...new Set([1, 2, 2, 3])]; // [1, 2, 3]
// Union:
const union = new Set([...a, ...b]);
// Intersection:
const inter = new Set([...a].filter(x => bSet.has(x)));

Immutable Updates

Update with spread/copy rather than in place. Replace objects, add/remove items, and return a new reference. Common in React state.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
interface State {
items: string[];
count: number;
}
const next: State = {
...state,
items: [...state.items, 'new'], // append
count: state.count + 1,
};
// Remove one item:
const filtered = state.items.filter(x => x !== 'old');
// Update one item:
const updated = state.items.map((x, i) => i === 0 ? 'new' : x);
// readonly helps the type checker prevent in-place mutation

Tuples and Record

Tuples are fixed-length; `Record` is a key-value map. Use `as const` to make object literals constants.

1
2
3
4
5
6
7
8
9
10
11
const point: [number, number] = [10, 20];
const [x, y] = point; // destructure
// Record:
type Config = Record<string, boolean>;
const flags: Config = { debug: true };
// `as const` constant object:
const statusMap = {
active: '运行中',
stopped: '已停止',
} as const;
// statusMap.active's type is the literal '运行中'

WeakMap / WeakSet

`WeakMap`/`WeakSet` require object keys and hold them weakly. Don't prevent GC. Use for metadata caches or side effects.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const meta = new WeakMap<object, { visited: boolean }>();
const node = document.getElementById('a');
if (node) meta.set(node, { visited: true });
// Keys must be objects:
// meta.set(1, {}) // Error
// Not enumerable (no keys / size)
// Use cases:
// 1. Attach private metadata to an object
// 2. Cache computed results without leak risk
// 3. Tag a listener
// WeakSet for tagging:
const processed = new WeakSet<object>();
processed.add(obj);
processed.has(obj); // true

9.Performance and Memory

Memory under garbage collection, large data, string optimization, and monitoring.

Garbage Collection

TS/JS has GC; no manual free. Objects are collected when no longer referenced. Beware closures keeping long-lived objects alive.

1
2
3
4
5
6
7
8
9
10
// Objects are reclaimed by the GC once they leave scope and have no references
function create() {
const big = new Array(1e6);
return () => big.length; // closure keeps big alive
}
// Closures keep `big` alive:
const f = create(); // `big` cannot be reclaimed
// Release:
f = null; // drop the reference
// Avoid unbounded growth in global caches

Large Arrays

For large arrays consider TypedArray or streaming. `filter`/`map` allocate new arrays; a single loop is cheaper.

1
2
3
4
5
6
7
8
9
10
// TypedArray handles binary data:
const buf = new Float64Array(1e6);
// Avoid repeated map/filter chains over large arrays:
// BAD: multiple passes
// GOOD: a single pass
const src = new Array(1e6).fill(0);
let sum = 0;
for (let i = 0; i < src.length; i++) sum += src[i];
// Binary: DataView + ArrayBuffer
// Numeric precision: BigInt for large integers, BigInt.asIntN to truncate

String Memory

Strings are immutable; concatenation creates new strings. For heavy concatenation, use array `join` or templates. Strings are interned.

1
2
3
4
5
6
7
8
9
// String concat in a loop is slow:
let s = '';
for (let i = 0; i < 1e5; i++) s += i; // BAD
// Array join:
const parts: string[] = [];
for (let i = 0; i < 1e5; i++) parts.push(String(i));
const s2 = parts.join(''); // GOOD
// Or chunked template strings
// Use slice to take substrings of long strings (O(n))

WeakRef and Caching

`WeakRef` holds a weak reference that doesn't prevent GC; `WeakMap`/`WeakSet` keys are weak. Use weak containers for caches or side effects.

1
2
3
4
5
6
7
8
9
const cache = new WeakMap<object, number>();
const obj = { id: 1 };
cache.set(obj, compute(obj));
// The entry disappears automatically once `obj` is collected
// WeakRef:
const ref = new WeakRef(obj);
const alive = ref.deref(); // object | undefined
// FinalizationRegistry listens for collection:
const reg = new FinalizationRegistry(held => console.log('collected', held));

Performance Tips

Avoid `any` slowing optimization, reduce reallocations, cache results. V8 optimizations depend on stable object shapes.

1
2
3
4
5
6
7
8
9
10
11
12
13
// Avoid changing the shape of an object dynamically:
// BAD:
const obj: Record<string, number> = {};
obj.a = 1; obj.b = 2; // shape change
// GOOD: declare the full shape
const obj = { a: 0, b: 0 };
// Cache long chains:
const len = arr.length; // avoid repeated lookup in a loop
// Avoid implicit coercion:
const s = String(n) + x;
// Avoid allocating closures on hot paths:
for (let i = 0; i < n; i++) { }
// rather than allocating a new arrow function per map step

Memory Monitoring

Node memory: `process.memoryUsage()`, `--max-old-space-size`. In the browser, the Performance API.

1
2
3
4
5
6
7
8
9
// Node:
console.log(process.memoryUsage());
// heapUsed: used heap, heapTotal: total heap
// Increase the heap: node --max-old-space-size=4096 app.js
// Browser:
performance.measureMemory?.()
.then(m => console.log(m.bytes));
// Heap snapshot: Chrome DevTools Memory panel
// Leak signature: heapUsed keeps rising and never drops

Closures and Memory

Closures hold outer variables, prolonging their lifetime. Watch for closure traps in loops. Release references.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function makeCounter() {
let count = 0; // captured by the closure
return () => ++count; // stays alive
}
const c = makeCounter();
c(); // 1
// Closures captured inside a loop (the `var` pitfall):
// BAD: `var` is shared
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i)); // 3 3 3
}
// GOOD: `let` has block-scoped capture
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i)); // 0 1 2
}
// Release long-lived closures:
// fn = null

TypedArray and Binary

TypedArray for binary numerics: `Uint8Array`/`Float64Array`. Views share the underlying buffer.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const bytes = new Uint8Array(16); // 16 bytes
bytes[0] = 255;
// Create from your own data:
const arr = new Uint8Array([1, 2, 3]);
// Floating point:
const floats = new Float64Array(8);
// Shared backing buffer:
const buffer = new ArrayBuffer(16);
const view = new DataView(buffer);
view.setInt32(0, 42);
view.getInt32(0); // 42
// To a plain array:
const plain = Array.from(bytes);
// Encoding:
new TextEncoder().encode('中文');
// Common for large files / network protocols / Canvas pixels

10.Classes and OOP

class, access modifiers, inheritance, abstract classes, and generic classes.

class Basics

class syntax: fields, constructor, methods. Fields can be annotated with types and visibility.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
greet(): string {
return `Hi, I'm ${this.name}`;
}
}
const p = new Person('Nick', 30);
// Parameter-property shorthand:
class P2 {
constructor(public name: string, private age: number) {}
}

Access Modifiers

`public`, `private`, `protected`, `readonly`. All are compile-time checks.

1
2
3
4
5
6
7
8
9
10
11
12
class Account {
public owner: string; // public by default
private balance = 0; // private
protected type = 'basic'; // accessible to subclasses
readonly id: string; // read-only
constructor(owner: string) {
this.owner = owner;
this.id = crypto.randomUUID();
}
}
// `#` private fields (runtime-private, ES2022):
class C { #secret = 1; get() { return this.#secret; } }

Inheritance and override

`extends` to inherit, `super()` to call the parent constructor, `override` to override a method. A subclass is-a parent.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Animal {
constructor(public name: string) {}
speak(): string { return `${this.name} makes a sound`; }
}
class Dog extends Animal {
constructor(name: string, public breed: string) {
super(name); // call the parent constructor first
}
override speak(): string { // `override` is an explicit marker
return `${this.name} barks`;
}
}
const d = new Dog('Rex', 'Husky');
// `d` is both a Dog and an Animal

Abstract Classes and Interfaces

Abstract classes can't be instantiated; abstract methods must be implemented by subclasses. Interfaces constrain shape. Abstract classes can have implementations.

1
2
3
4
5
6
7
8
9
10
11
12
13
abstract class Shape {
abstract area(): number; // subclass must implement
describe(): string {
return `Area: ${this.area()}`;
}
}
class Circle extends Shape {
constructor(private r: number) { super(); }
override area(): number { return Math.PI * this.r ** 2; }
}
// Interface constraint:
interface HasArea { area(): number }
function printArea(s: HasArea) { console.log(s.area()); }

implements

`class implements Interface`: the class must satisfy the interface's shape. A class can implement multiple interfaces.

1
2
3
4
5
6
7
8
9
10
11
12
interface Runnable {
run(): void;
}
interface Jumpable {
jump(): void;
}
class Player implements Runnable, Jumpable {
run(): void { console.log('running'); }
jump(): void { console.log('jump'); }
}
// A missing method triggers a compile error
// `implements` only constrains the shape; it does not imply inheritance

Generic Classes

Generic classes: type parameters on fields and methods. Constraints narrow the generic range.

1
2
3
4
5
6
7
8
9
10
11
class Box<T> {
private value: T;
constructor(value: T) { this.value = value; }
get(): T { return this.value; }
set(v: T): void { this.value = v; }
}
const numBox = new Box<number>(42);
const strBox = new Box('hi'); // inferred as string
// Static members cannot reference type parameters:
// static arr: T[] // Error
// Generic constraint: class Box<T extends { id: number }>

getter / setter

`get`/`set` accessors wrap field reads/writes. Add validation and computed logic.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Temperature {
private _celsius = 0;
get celsius(): number { return this._celsius; }
set celsius(value: number) {
if (value < -273.15) throw new Error('below absolute zero');
this._celsius = value;
}
get fahrenheit(): number {
return this._celsius * 9 / 5 + 32;
}
}
const t = new Temperature();
t.celsius = 25;
console.log(t.fahrenheit); // 77

Static Members

`static` defines class-level fields and methods. Static members don't depend on instances. Use `static` for factories and constants.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class MathHelper {
static readonly PI = 3.14159; // class constant
static max(arr: number[]): number {
return Math.max(...arr);
}
// Factory method:
static create(name: string): MathHelper {
return new MathHelper(name);
}
constructor(private name: string) {}
}
MathHelper.PI;
MathHelper.max([1, 5, 3]);
// Static members are accessed via the class name:
// new MathHelper().PI // Error
// Static property initialization runs at class-definition time

11.Exception Handling

throw/try-catch, error types, custom errors, and async errors.

throw / try-catch

`throw` raises an error, `try-catch` catches it, `finally` runs cleanup. The `catch` variable defaults to `unknown` and needs narrowing.

1
2
3
4
5
6
7
8
9
10
11
try {
const n = JSON.parse(text);
if (typeof n !== 'number') throw new Error('number required');
} catch (err) {
if (err instanceof Error) {
console.log(err.message); // access after narrowing
}
} finally {
cleanup(); // runs whether it succeeds or fails
}
// Uncaught throws bubble up; unhandled ones crash

Custom Errors

Extend `Error` to define a custom error class. Carry extra info. The error name distinguishes the type.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class ValidationError extends Error {
constructor(public field: string, message: string) {
super(message);
this.name = 'ValidationError';
}
}
try {
throw new ValidationError('email', 'invalid email format');
} catch (err) {
if (err instanceof ValidationError) {
console.log(err.field, err.message);
}
}
// Set the prototype chain so `instanceof` works:
// Object.setPrototypeOf(this, ValidationError.prototype)

Common Error Types

Error base class: `TypeError`, `RangeError`, `ReferenceError`. Test with `instanceof`.

1
2
3
4
5
6
7
8
9
10
11
12
try {
// TypeError: calling a method that does not exist
// RangeError: out-of-bounds / recursion too deep
// ReferenceError: referencing an undeclared variable
const arr = [1, 2];
arr[5].toFixed(); // TypeError
} catch (err) {
if (err instanceof TypeError) { }
else if (err instanceof RangeError) { }
// `instanceof Error` as a fallback
}
// Use the error's `name` property for cross-realm cases

Async Errors

An `async` function's `throw` becomes a rejected Promise. Catch with `try-catch` on `await`, or with the `.catch` chain.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
async function load(): Promise<void> {
try {
const res = await fetch('/api');
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
} catch (err) {
console.error('load failed', err);
}
}
// Promise chain:
fetch('/api').then(r => r.json()).catch(e => {
console.error(e);
});
// An uncaught rejected Promise fires `unhandledrejection`

Error Boundaries

Module boundaries catch and convert error types. Wrap third-party errors uniformly. Don't let errors escape and crash.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function safeParse<T>(json: string): T {
try {
return JSON.parse(json) as T;
} catch {
throw new Error('JSON parse failed'); // consistent wrapping
}
}
// Top-level capture:
process.on('uncaughtException', (err) => {
console.error('uncaught exception', err);
process.exit(1);
});
// In Node, sync exceptions are caught at the top level
// Browser: window.onerror / unhandledrejection

Error Handling Patterns

Return a result for predictable errors, throw for unexpected ones. Result style (`ok`/`err`) and `throw` each have their place.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Expected failures: return null / Result object
type Result<T> = { ok: true; value: T } | { ok: false; error: string };
function parseNum(s: string): Result<number> {
const n = Number(s);
return Number.isNaN(n)
? { ok: false, error: 'not a number' }
: { ok: true, value: n };
}
const r = parseNum('abc');
if (r.ok) console.log(r.value);
else console.log(r.error);
// Unexpected errors: throw + catch at a higher layer
// Rule: if the caller can handle it, return; otherwise throw
// Don't swallow: log inside catch at minimum

Error Message Quality

Error messages should include context: what, where, how to fix. Custom errors carry fields.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class HttpError extends Error {
constructor(
public status: number,
public url: string,
message: string,
) {
super(message);
this.name = 'HttpError';
}
}
// GOOD: include context
throw new HttpError(404, url, `resource not found: ${url}`);
// BAD: no information
// throw new Error('failed');
// Logging should preserve the stack:
console.error(err); // keeps the stack
// Error cause chaining:
new Error('outer failure', { cause: innerErr })

Unhandled Rejections

An uncaught rejected Promise fires `unhandledrejection`. Add a top-level fallback to avoid silent failures.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Node:
process.on('unhandledRejection', (reason) => {
console.error('unhandled Promise rejection', reason);
});
// Browser:
window.addEventListener('unhandledrejection', (e) => {
e.preventDefault();
console.error('unhandled rejection', e.reason);
});
// Sync exceptions:
process.on('uncaughtException', (err) => {
console.error('uncaught exception', err);
process.exit(1);
});
// Audit: rejection listeners catch missed rejections during tests
// A safety net should log and locate issues, not silently swallow them

12.Input and Output

console output, Node file/stream, fetch networking, and typed JSON.

console Output

`console.log`/`info`/`warn`/`error`, template output, `%o` formatting, groups and counts.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
console.log('Hello');
console.info('info message');
console.warn('warning');
console.error('error');
// Formatting:
console.log('%o', { a: 1 }); // expand the object
console.table([{ a: 1 }, { a: 2 }]);
// Group:
console.group('group');
console.log('content');
console.groupEnd();
// Timing:
console.time('t');
console.timeEnd('t');

fetch Networking

`fetch` for async requests. `await` the response, parse JSON, handle errors, assert the result type.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
async function getUsers(): Promise<User[]> {
const res = await fetch('/api/users');
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json() as Promise<User[]>; // assertion
}
// POST:
await fetch('/api', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Nick' }),
});
// AbortController timeout:
const ac = new AbortController();
setTimeout(() => ac.abort(), 3000);
await fetch(url, { signal: ac.signal });

Node Files

`fs/promises` for async read/write. `readFile`/`writeFile`/`mkdir`/`readdir` return Promises.

1
2
3
4
5
6
7
8
9
10
import { readFile, writeFile, mkdir, readdir } from 'node:fs/promises';
const text = await readFile('data.txt', 'utf8');
await writeFile('out.txt', text.toUpperCase());
await mkdir('dir', { recursive: true });
const files = await readdir('.');
// Stream large files:
import { createReadStream } from 'node:fs';
const stream = createReadStream('big.log');
// Requires @types/node:
// npm i -D @types/node

JSON and Types

`JSON.parse`/`stringify` for serialization. `parse` results need a guard; `stringify` ignores functions.

1
2
3
4
5
6
7
8
9
10
11
12
13
interface User { name: string; age: number }
const u: User = { name: 'Nick', age: 30 };
const json = JSON.stringify(u);
// Parse with validation:
function isUser(v: unknown): v is User {
return typeof v === 'object' && v !== null
&& typeof (v as any).name === 'string'
&& typeof (v as any).age === 'number';
}
const data = JSON.parse(json);
if (isUser(data)) console.log(data.name);
// JSON.parse returns `any`; narrow before asserting
// Serialization options: JSON.stringify(u, null, 2) pretty-prints

Stream Processing

`ReadableStream`/Web Streams for large responses. Read progress, process chunk by chunk.

1
2
3
4
5
6
7
8
9
10
11
12
13
const res = await fetch('/big');
const reader = res.body!.getReader();
const decoder = new TextDecoder();
let total = 0;
while (true) {
const { done, value } = await reader.read();
if (done) break;
total += value.length;
const chunk = decoder.decode(value, { stream: true });
process(chunk);
}
// Stream large file reads:
// for await (const chunk of createReadStream('big.log')) { }

Environment Variables and Arguments

Node reads `process.argv` / `process.env`. Argument parsing; narrow environment variable types.

1
2
3
4
5
6
7
8
9
10
11
// process.argv[0]=node, [1]=script, [2..]=args
const args = process.argv.slice(2);
// Environment variables:
const port = Number(process.env.PORT ?? 3000);
const mode = process.env.NODE_ENV ?? 'development';
// Compare:
if (mode === 'production') { }
// Load .env:
// npm i dotenv
// import 'dotenv/config'
// Type-safe env: process.env is Record<string, string | undefined>

Browser Web APIs

Types for `localStorage`/`sessionStorage`, `navigator`, `WebSocket`. Stored values need serialization.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Local storage:
localStorage.setItem('token', 'abc');
const t = localStorage.getItem('token'); // string | null
// Storing objects requires serialization:
localStorage.setItem('user', JSON.stringify(user));
// Reading back requires parsing + validation:
const raw = localStorage.getItem('user');
if (raw) { const u = JSON.parse(raw) as User; }
// Geolocation:
if ('geolocation' in navigator) {
navigator.geolocation.getCurrentPosition((pos) => {
console.log(pos.coords.latitude);
});
}
// WebSocket event types:
ws.addEventListener('message', (e: MessageEvent) => {
console.log(e.data);
});

File Reading

`input[type=file]` to get a `File`, `FileReader` to read text/DataURL, object URL for preview.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const input = document.querySelector('input[type=file]') as HTMLInputElement;
input.addEventListener('change', async () => {
const file = input.files?.[0];
if (!file) return;
// Read as text:
const text = await file.text();
// Read as a Data URL:
const reader = new FileReader();
reader.onload = () => console.log(reader.result);
reader.readAsDataURL(file);
// Preview image:
const url = URL.createObjectURL(file);
img.src = url;
// File has name/size/type
console.log(file.name, file.size, file.type);
});

13.Common Pitfalls

The most common day-to-day pitfalls and the correct way to write things.

Overuse of any

`any` disables type checks and hides real errors. Prefer `unknown` plus a guard.

1
2
3
4
5
6
7
8
9
10
// BAD: `any` swallows errors
function getLen(v: any): number {
return v.length; // compiles, but may crash at runtime
}
// GOOD: `unknown` + guard
function getLen(v: unknown): number {
if (typeof v !== 'string') return 0;
return v.length;
}
// `any` propagates implicitly: a returned `any` infects the caller

Nullish Checks

`!!` for truthy, `== null` for both null and undefined, check array length. Don't treat `0` or `''` as empty.

1
2
3
4
5
6
7
8
9
10
// BAD: the value may be 0/''
if (value) { } // 0 is falsy
// GOOD: check for null explicitly
if (value !== null && value !== undefined) { }
// Shorthand `== null` covers both at once:
if (value == null) { } // null or undefined
// Empty array:
if (arr.length === 0) { }
// Empty object:
if (Object.keys(obj).length === 0) { }

Async Misuse

Async functions always return a Promise. Forgetting `await` leaks a Promise. `forEach` does not await async work.

1
2
3
4
5
6
7
8
9
10
11
12
13
// BAD: forEach does not await
async function main() {
[1, 2, 3].forEach(async n => { await work(n); });
console.log('done'); // printed first!
}
// GOOD: for...of
for (const n of [1, 2, 3]) {
await work(n);
}
// BAD: forgot to await
const res = fetch('/api'); // is a Promise
// Parallel:
await Promise.all([a(), b()]);

Equality Comparison

`===` for strict equality. `NaN !== NaN`. Objects compare by reference. Deep compare manually or with a library.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// BAD: `==` coerces across types
'1' == 1; // true, a footgun
// GOOD: use `===` for strict equality
'1' === 1; // false
// NaN is special:
NaN === NaN; // false
Number.isNaN(NaN); // true
// Objects compare by reference:
{} === {}; // false
// Compare arrays by content:
[1, 2].join() === [1, 2].join(); // simple approach
// For deep equality, use a library:
// import { isEqual } from 'lodash-es';
// Performance: avoid deep equality checks on large, deeply-nested objects

null vs undefined Confusion

`null` is intentional emptiness, `undefined` is unassigned. Under strictNullChecks handle them separately. Optional chaining vs assertion.

1
2
3
4
5
6
7
8
9
10
11
12
// BAD: non-null assertion hides a possible null
const len = name!.length; // `name` may really be null
// GOOD: check first
if (name) {
const len = name.length;
}
// Nullish coalescing:
const n = name ?? 'default';
// Optional chaining for safe access:
user?.profile?.email;
// Use `??` rather than `||`: 0 and '' are valid values
const port = port ?? 3000; // keeps 0 when port=0

Lost this

`this` becomes `undefined` in callbacks. Bind with arrow functions or explicit `.bind`. Class-field arrow functions are common.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Counter {
count = 0;
increment = () => { this.count++; }; // arrow binds `this`
}
// BAD: `this` is lost in a callback
class C {
value = 1;
method() { return this.value; }
}
const fn = new C().method;
fn(); // undefined, TypeError
// GOOD:
const fn2 = new C().method.bind(new C());
// or always call via `.method()`

Narrowing Failure

Property accesses can lose narrowing. Destructure to keep the narrowed value. Reassigning a parameter widens the type.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// BAD: property narrowing does not survive reassignment
function f(p: { a?: string }) {
if (p.a) {
p.a.toUpperCase(); // may already have changed
}
}
// GOOD: capture into a local first
function f2(p: { a?: string }) {
const a = p.a;
if (a) {
a.toUpperCase();
}
}
// Optional-property accesses twice create a race
// Re-assigning a parameter drops the narrowing:
// assign to a local and check it instead

Exhaustiveness Check

Use `never` in the `default` of a discriminated union. Adding a new case without handling it becomes a compile error.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// GOOD: use `never` in the default branch for exhaustiveness
type Event =
| { kind: 'open' }
| { kind: 'close' };
function handle(e: Event) {
switch (e.kind) {
case 'open': break;
case 'close': break;
default: {
const _exhaustive: never = e;
// adding a new `kind` triggers a compile error here
return _exhaustive;
}
}
}
// BAD: without a default branch, new variants are silently unhandled
// The compiler then reminds you of every switch after updating the type

Copy and Modify

Arrays/objects share references by default. Copy before updating. `sort` mutates in place.

1
2
3
4
5
6
7
8
9
10
11
12
13
const a = [1, 2, 3];
// BAD: `sort` mutates in place
const sorted = a.sort(); // `a` is also changed
// GOOD: copy first
const sorted = [...a].sort();
// BAD: shared reference causes unexpected mutation
const b = a;
b.push(4); // `a` is changed too
// Shallow copy:
const copy = [...a];
// Shallow copy of an object via spread:
const o2 = { ...o1 };
// Nested structures need a layer-by-layer deep copy

14.Concurrency and Async

Promise, async/await, Worker threads, and the event loop.

Promise

Promise represents an async result. `then` chain, `catch` for errors, `finally` for cleanup. Annotate as `Promise<T>`.

1
2
3
4
5
6
7
8
9
10
const p: Promise<number> = new Promise((resolve, reject) => {
setTimeout(() => resolve(42), 1000);
});
p.then(n => console.log(n))
.catch(err => console.error(err))
.finally(() => console.log('done'));
// Create immediately resolved/rejected:
Promise.resolve(1);
Promise.reject(new Error('x'));
// Generic error: Promise<T> holds the success value type T

async / await

Async functions return a Promise. `await` unwraps it. Catch errors with `try-catch`. Top-level `await` needs ESM.

1
2
3
4
5
6
7
8
9
10
11
12
async function load(): Promise<User> {
const res = await fetch('/api/user');
if (!res.ok) throw new Error('load failed');
return res.json() as Promise<User>;
}
// Invocation:
const user = await load();
// Error handling:
try { await load(); } catch (err) { }
// Run in parallel:
const [a, b] = await Promise.all([load(), load()]);
// Top-level await (in ESM `.mjs`)

Parallel and Race

`Promise.all` waits for all, `allSettled` waits for all (including failures), `race` takes the first to settle, `any` takes the first to succeed.

1
2
3
4
5
6
7
8
9
10
11
const tasks = [fetch1(), fetch2(), fetch3()];
// All succeed (any rejection fails the whole thing):
const all = await Promise.all(tasks);
// Collect every result without short-circuiting:
const settled = await Promise.allSettled(tasks);
// First to settle (including failure):
const first = await Promise.race(tasks);
// First to succeed (AggregateError if all reject):
const any = await Promise.any(tasks);
// Concurrency cap:
// chunk via a for-loop, or use the `p-limit` library

Worker Threads

Web Worker / Node `worker_threads` for parallel compute. `postMessage` to communicate; `transferable` to transfer ownership.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Main thread:
const worker = new Worker('/worker.js');
worker.postMessage({ n: 42 });
worker.onmessage = (e) => console.log(e.data);
worker.onerror = (e) => console.error(e);
// worker.js:
self.onmessage = (e) => {
const result = heavyCompute(e.data.n);
self.postMessage(result);
};
// Node:
// import { Worker } from 'node:worker_threads';
// Transfer large buffers without copying:
// postMessage(buf, [buf.buffer])

Event Loop

Synchronous code runs first; microtasks (Promise) before macrotasks (setTimeout). Blocking stalls everything.

1
2
3
4
5
6
7
8
9
console.log('1'); // synchronous
Promise.resolve().then(() =>
console.log('2')); // microtask
setTimeout(() => console.log('3'), 0); // macrotask
console.log('4');
// Output order: 1 4 2 3
// Long tasks block the event loop:
// for (let i=0;i<1e9;i++){} // freezes the loop
// Break it up or yield: await new Promise(r => setTimeout(r, 0))

Generators

`function*` is a lazy generator. `yield` pauses and resumes. Annotate as `Generator`.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function* count(max: number): Generator<number> {
let i = 0;
while (i < max) {
yield i++; // pause and emit
}
}
for (const n of count(3)) console.log(n); // 0 1 2
// Manual driving:
const g = count(2);
g.next(); // { value: 0, done: false }
g.next();
// Infinite sequence + laziness:
function* naturals(): Generator<number> {
let n = 0;
while (true) yield n++;
}
// Isomorphic with the iterator protocol

Async Iterators

`for await...of` iterates async data sources. `async function*` for async generators. Stream-friendly consumption.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
async function* generate(): AsyncGenerator<number> {
let i = 0;
while (i < 3) {
await new Promise(r => setTimeout(r, 100));
yield i++;
}
}
// Consume:
for await (const n of generate()) {
console.log(n); // 0 1 2
}
// Async iteration over a fetch body:
const res = await fetch('/big');
const reader = res.body!.getReader();
// Node streams:
// for await (const chunk of createReadStream('f'))
// Lazy + backpressure-friendly, memory-efficient

Concurrency Limit

Limit the number of concurrent async tasks. Batch, semaphore, or `p-limit`. Don't overwhelm resources.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
async function mapLimit<T, R>(
items: T[], limit: number, fn: (x: T) => Promise<R>,
): Promise<R[]> {
const results: R[] = [];
let i = 0;
const workers = Array.from({ length: Math.min(limit, items.length) },
async () => {
while (i < items.length) {
const idx = i++;
results[idx] = await fn(items[idx]);
}
});
await Promise.all(workers);
return results;
}
// Use case: batched downloads/requests where `limit` caps concurrency
// Avoid firing everything at once in bulk-download scenarios

15.Networking and Modules

Module system, import/export, fetch, and typed API wrappers.

ES Modules

`import`/`export` for static import/export. Use `export type` for TS types. Modules isolate scope.

1
2
3
4
5
6
7
8
9
10
11
// utils.ts:
export const version = '1.0';
export function add(a: number, b: number): number { return a + b; }
export type ID = string; // type export
export default class App { } // default export
// main.ts:
import App, { add, version, type ID } from './utils';
// Rename on import:
import { add as plus } from './utils';
// Namespace import:
import * as utils from './utils';

Type-only Imports

`import type` imports only types; erased at compile time. Avoids runtime dependencies and circular references.

1
2
3
4
5
6
7
8
9
10
11
// BAD: type import may still exist at runtime (esbuild can leave it behind)
// GOOD:
import type { User } from './types';
// Mixed with a value import:
import { fetchUsers, type User } from './api';
// Type-only:
import type { Options } from './config';
// Re-exporting types:
export type { ID } from './utils';
// The compiler elides type-only imports automatically; explicit is clearer
// Use `import type` to break circular references

Typed API Wrappers

Wrap `fetch` to return strong types. Validate responses; handle errors uniformly. Generic request function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
async function api<T>(
url: string, opts?: RequestInit
): Promise<T> {
const res = await fetch(url, opts);
if (!res.ok) {
throw new Error(`HTTP ${res.status}`);
}
return res.json() as Promise<T>;
}
// Usage:
interface User { name: string }
const user = await api<User>('/api/user');
// Generics keep call-sites type-safe
// At the edge: validate with a schema (zod) to avoid runtime/type drift
// import { z } from 'zod';

Node HTTP Servers

`node:http` or frameworks (Express/Fastify). Typed request/response. Route handling.

1
2
3
4
5
6
7
8
9
10
11
import { createServer } from 'node:http';
const server = createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ ok: true }));
});
server.listen(3000);
// Express:
// import express from 'express';
// @types/express provides the types
// Parameter typing:
// app.get('/user/:id', (req: Request<{id: string}>, res: Response) => {})

DOM Types

`document.getElementById` return type, event types, HTML element type mapping.

1
2
3
4
5
6
7
8
9
10
const btn = document.getElementById('btn');
// HTMLElement | null; needs a non-null check:
if (btn) { btn.addEventListener('click', handler); }
// Assert to a specific element type:
const input = document.querySelector('input') as HTMLInputElement;
// Event types:
function handler(e: MouseEvent) { console.log(e.clientX); }
// Generic events:
const f = (e: KeyboardEvent) => { e.key };
// Reading form values: input.value is `string`

URL and Parameters

`URLSearchParams` builds a query string; `URL` parses. Type-safe parameter reads.

1
2
3
4
5
6
7
8
9
const params = new URLSearchParams({ q: 'ts', page: '2' });
params.toString(); // 'q=ts&page=2'
const url = new URL('https://example.com/search?q=ts');
const q = url.searchParams.get('q'); // 'ts'
// Mutate:
url.searchParams.set('page', '3');
// Request headers:
headers.append('Authorization', `Bearer ${token}`);
// Type note: `get` returns `string | null`; narrow before using

WebSocket

WebSocket is bidirectional. `onmessage` event, generic data, `readyState`.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const ws = new WebSocket('wss://example.com/socket');
// Once the socket is open:
ws.onopen = () => ws.send(JSON.stringify({ type: 'join' }));
// Receive:
ws.onmessage = (e: MessageEvent) => {
// e.data may be string | Blob | ArrayBuffer
const data = JSON.parse(e.data as string);
console.log(data);
};
ws.onerror = (e) => console.error('connection error', e);
ws.onclose = (e) => console.log('closed', e.code);
// Close from this side:
ws.close(1000, 'normal closure');
// Auto-reconnect: use setTimeout in `onclose`

Headers and Auth

Type-safe `Headers` setup. `Authorization: Bearer ...`, `Content-Type`. Inject via an interceptor.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const headers = new Headers();
headers.set('Content-Type', 'application/json');
headers.set('Authorization', `Bearer ${token}`);
const res = await fetch('/api', { headers });
// Read response headers:
const type = res.headers.get('content-type');
// Centralize:
function authFetch(url: string, init?: RequestInit) {
return fetch(url, {
...init,
headers: {
...init?.headers,
Authorization: `Bearer ${getToken()}`,
},
});
}
// Caution: never log tokens or put them in URLs
// Refresh on 401 and retry

16.Date and Time

The Date object, timestamps, formatting, and time zones.

Date Basics

`Date` constructor, `getFullYear`/`getMonth`/`getDate` reads, `set*` writes. Months start at 0.

1
2
3
4
5
6
7
8
9
10
11
const now = new Date();
const d = new Date('2026-08-02T12:00:00Z');
const y = d.getFullYear(); // 2026
const m = d.getMonth(); // 7 (zero-based!)
const day = d.getDate(); // 2
// Mutate:
d.setFullYear(2030);
d.setMonth(0); // January
// Local vs UTC accessors:
// getUTCFullYear() / getUTCHours()
// getTimezoneOffset() returns the offset in minutes

Timestamps

`getTime()` ms timestamp, `Date.now()`, `Date.parse`. Compare dates via timestamps.

1
2
3
4
5
6
7
8
9
const ms = Date.now(); // current ms
const d = new Date(ms);
const older = new Date('2020-01-01');
if (d.getTime() > older.getTime()) { } // compare
// Seconds: Math.floor(Date.now() / 1000)
// Date.parse('2026-08-02') returns milliseconds
// Add/subtract time:
d.setDate(d.getDate() + 7); // add 7 days
// Note: `setMonth` / `setDate` carry across month boundaries

Formatting

`toISOString` for UTC, `toLocaleDateString` for local, `Intl.DateTimeFormat` for custom.

1
2
3
4
5
6
7
8
9
10
const d = new Date();
const iso = d.toISOString(); // '2026-08-02T04:00:00.000Z'
const local = d.toLocaleDateString('zh-CN');
// Custom with Intl:
new Intl.DateTimeFormat('zh-CN', {
year: 'numeric', month: 'long', day: 'numeric',
hour: '2-digit', minute: '2-digit',
}).format(d);
// Just the time:
// d.toLocaleTimeString('zh-CN')

Time Zones

Timestamps are UTC milliseconds; formatting applies the local time zone. Use `Intl` with the `timeZone` option to specify a zone.

1
2
3
4
5
6
7
8
9
10
11
const d = new Date();
// Specify a time zone:
new Intl.DateTimeFormat('zh-CN', {
timeZone: 'Asia/Shanghai',
hour12: false,
}).format(d);
// `getTimezoneOffset` returns the local-vs-UTC offset in minutes
// Store in ISO / timestamps, render locally
// Converting across zones:
// store UTC, render with toLocaleString('zh-CN', { timeZone })
// For simple math prefer dayjs / date-fns

Timers

`setTimeout` for delay, `setInterval` for periodic, `clearTimeout` to cancel. Return type is `number`.

1
2
3
4
5
6
7
8
9
10
11
const timer = setTimeout(() => {
console.log('after 1s');
}, 1000);
clearTimeout(timer); // cancel
const interval = setInterval(() => {
console.log('every second');
}, 1000);
clearInterval(interval); // stop
// Async wait:
await new Promise(r => setTimeout(r, 500));
// Note: timer callbacks run in the macrotask phase of the event loop

Durations and Intervals

Subtract two timestamps to get milliseconds. Use timestamps for range checks. Avoid `setInterval` drift.

1
2
3
4
5
6
7
8
9
10
11
const start = Date.now();
// Elapsed time:
const elapsed = Date.now() - start; // ms
console.log(`${elapsed}ms`);
// Range checks:
const inWindow = t >= start && t <= end;
// Poll every N ms:
const poll = setInterval(() => { }, 1000);
// Mitigate `setInterval` drift:
// use `setTimeout` recursion and compute the correction
// Higher precision: `performance.now()`

Date Libraries

dayjs/date-fns provide clear APIs and time-zone handling. Small and immutable. Reach for a library for complex time zones.

1
2
3
4
5
6
7
8
9
10
11
12
13
// dayjs:
// import dayjs from 'dayjs';
// dayjs().format('YYYY-MM-DD');
// dayjs().add(7, 'day').toDate();
// dayjs('2026-08-02').isBefore('2026-09-01');
// date-fns:
// import { format, addDays, isBefore } from 'date-fns';
// format(new Date(), 'yyyy-MM-dd');
// addDays(new Date(), 7);
// Time zones:
// import { formatInTimeZone } from 'date-fns-tz';
// formatInTimeZone(d, 'Asia/Shanghai', 'yyyy-MM-dd HH:mm')
// Both are immutable: they return a new value and do not mutate the original Date

High-Resolution Timing

`performance.now()` is millisecond-resolution, monotonic, unaffected by system time changes. Use for performance measurement.

1
2
3
4
5
6
7
8
9
10
11
12
13
const t0 = performance.now();
// measure code...
const elapsed = performance.now() - t0;
console.log(`${elapsed.toFixed(2)}ms`);
// Wall-clock time: Date.now() can be skewed by NTP adjustments
// performance.now() is monotonic
// Available in both browser and Node
// Mark / measure:
performance.mark('start');
// ...
performance.mark('end');
performance.measure('task', 'start', 'end');
// Read the result: performance.getEntriesByName('task')

17.Process and System

Node processes, CLI tools, standard streams, and running built artifacts.

Node Process

The `process` global: `argv` args, `env` environment, `exit` code, `stdout`/`stderr` streams.

1
2
3
4
5
6
7
8
9
10
import { argv, env, exit, stdout, stderr } from 'node:process';
const args = argv.slice(2);
const mode = env.NODE_ENV;
exit(0); // successful exit
stdout.write('output');
stderr.write('error');
// Exit codes: 0 success, 1 generic error
process.exitCode = 1; // set gracefully
// Signal handling:
process.on('SIGINT', () => { console.log('Ctrl+C'); process.exit(0); });

CLI Tools

Writing a CLI: parse args, help output, exit code. Shebang makes the script executable.

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/env node
const [cmd, ...rest] = process.argv.slice(2);
if (cmd === '--help' || cmd === '-h') {
console.log('Usage: ts-tool <command> [options]');
process.exit(0);
}
if (!cmd) {
console.error('missing command');
process.exit(1);
}
// Argument-parsing libraries: commander / yargs
// Example:
// ts-tool build --out dist --watch

Standard Streams

`stdin` reads input, `stdout` writes output, `stderr` for errors. `readline` for interaction. Piped data.

1
2
3
4
5
6
7
8
9
10
11
import { stdin, stdout } from 'node:process';
import * as readline from 'node:readline/promises';
const rl = readline.createInterface({ input: stdin, output: stdout });
const name = await rl.question('Name? ');
console.log(`Hello, ${name}`);
rl.close();
// Read all of stdin:
import { readFileSync } from 'node:fs';
// Pipe: echo hi | ts-tool
// Iterate line by line:
for await (const line of rl) { process(line); }

Running Compiled Output

After `tsc`, run `node dist/main.js`. Register a command via `package.json` `bin`. Type declaration `.d.ts`.

1
2
3
4
5
6
7
8
9
10
11
// package.json:
// {
// "bin": { "ts-tool": "./dist/cli.js" },
// "types": "./dist/index.d.ts",
// "main": "./dist/index.js"
// }
// Compile: npx tsc
// Run: node dist/cli.js
// Publish to npm: npm publish
// Declaration files (.d.ts) let other TS projects consume the lib
// tsconfig declaration: true

Execute System Commands

`execFile` to run an external command, `spawn` for streaming. `child_process` types. Escape carefully to avoid injection.

1
2
3
4
5
6
7
8
9
10
11
import { execFile } from 'node:child_process';
execFile('ls', ['-l'], (err, stdout) => {
if (err) { console.error(err); return; }
console.log(stdout);
});
// Streaming spawn:
import { spawn } from 'node:child_process';
const child = spawn('node', ['worker.js']);
child.stdout.on('data', (d) => console.log(d.toString()));
// Safety: avoid `exec` + string concatenation
// Pass arguments via arrays, never build shell commands by concat

Exit Codes

`0` success, non-zero failure. Convention: `1` general error, `2` usage error. CI scripts rely on exit codes.

1
2
3
4
5
6
7
8
9
process.exit(0); // success
process.exit(1); // generic error
process.exit(2); // usage / argument error
// An uncaught exception exits with code 1
// Manual assignment:
process.exitCode = 2;
// In CI: a non-zero exit code is a failure:
// ts-tool check && echo OK || echo FAIL
// Signal termination: SIGTERM defaults to exit code 143

npm scripts

`package.json` `scripts` orchestrate commands. `pre`/`post` hooks, chain with `&&`, parallel with `&`. Common in build chains.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// package.json:
// {
// "scripts": {
// "dev": "tsx src/dev.ts",
// "typecheck": "tsc --noEmit",
// "lint": "eslint src",
// "test": "vitest run",
// "build": "npm run typecheck && tsup src/index.ts",
// "prepublishOnly": "npm run build"
// }
// }
// Chain: `&&` (fail-fast)
// Parallel: `&` or the `concurrently` package
// pre/post hooks: `prebuild` runs automatically before `build`
// One-off runner: npx vitest

Config and Environments

`dotenv` loads `.env`. Typed config object, runtime validation. Config separated from code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Install: npm i dotenv
// import 'dotenv/config';
const port = Number(process.env.PORT ?? 3000);
const dbUrl = process.env.DATABASE_URL;
if (!dbUrl) {
throw new Error('DATABASE_URL is required');
}
// Typed configuration:
interface Config {
port: number;
debug: boolean;
apiKey: string;
}
// Have the validation function return `Config` so the type is guaranteed
// Keep .env out of version control (gitignore)
// Per-environment files: .env.development / .env.production

18.Regex and Text Processing

Regex syntax, flags, typed matching, and text-processing idioms.

Regex Syntax

Literal `/.../` or `RegExp` constructor. Character classes, quantifiers, groups, anchors.

1
2
3
4
5
6
7
8
9
const re = /\b\w+@\w+\.com\b/;
// \d digit \w word character \s whitespace \b word boundary
// [abc] character class [^abc] negation
// * zero-or-more + one-or-more ? zero-or-one {2,4} range
// ^ start $ end
// () capturing group (?:) non-capturing group
// Alternation: /cat|dog/
// Test:
re.test('hi [email protected]'); // true

Flags

`g` global, `i` case-insensitive, `m` multiline, `s` dot matches newline, `u` Unicode, `y` sticky.

1
2
3
4
5
6
7
8
9
10
const g = /a/g; // global (needed by `matchAll`)
const i = /HELLO/i; // case-insensitive
const m = /^line/m; // multiline: ^/$ anchor per line
const s = /a.b/s; // `.` matches newline (dotAll)
const u = /\p{Emoji}/u; // Unicode property
const y = /a/y; // sticky (match starting at lastIndex)
// Combine: /foo/gim
// Construct dynamically:
new RegExp(`\\d{${len}}`, 'gi');
// In regex literals, escape `\` itself

Match and Extract

`match` returns an array (full match + capture groups), `matchAll` iterates globally, `match` returns `null` on failure (check it).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const s = 'id=123&id=456';
const re = /id=(\d+)/g;
// Match all:
for (const m of s.matchAll(re)) {
console.log(m[1]); // '123' '456'
}
// Single match:
const first = s.match(re);
if (first) { console.log(first[0]); }
// Named captures:
const named = /(?<year>\d{4})-(?<month>\d{2})/;
const r = s.match(named);
if (r) { r.groups!.year; }
// Lazy quantifier `?`: /(\d+?)(x)/ // minimal match

Replace

`replace` with string/function. `$1` capture references; global `g` replaces all. Function replacement handles logic.

1
2
3
4
5
6
7
8
9
10
11
const s = '2026-08-02';
// Backreference:
const a = s.replace(/(\d{4})-(\d{2})-(\d{2})/, '$3/$2/$1');
// Functional replacement:
const b = s.replace(/(\d+)/g, (m) => String(Number(m) + 1));
// Replace all (g flag is required):
'aaa'.replace(/a/g, 'b'); // 'bbb'
// Strip whitespace:
s.replace(/\s+/g, ' ').trim();
// Simple substitutions can use split + join:
s.split('-').join('/');

Validation Idioms

Anchor whole matches with `^...$`. Common patterns for numbers, emails, URLs. `test` returns boolean.

1
2
3
4
5
6
7
8
9
10
11
12
13
function isEmail(v: string): boolean {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v);
}
function isInt(v: string): boolean {
return /^[-+]?\d+$/.test(v);
}
function isUrl(v: string): boolean {
try { new URL(v); return true; }
catch { return false; }
}
// Strict matches must anchor with `^` and `$`
// Length check: /^.{8,20}$/
// Escape greediness: /^\.$/ matches a literal dot

Regex Performance

Avoid catastrophic backtracking: nested quantifiers. Pre-compile regexes. Mind `lastIndex` with the `g` flag.

1
2
3
4
5
6
7
8
9
10
11
// BAD: catastrophic backtracking
// /^(a+)+$/ is extremely slow on 'aaaaaaaaaaaaaaaaaaaa!'
// GOOD: avoid nested quantifiers
/^(a+)$/;
// Pre-compile to avoid recreating:
const re = /\d+/g; // reuse at module level
// The `g` flag is stateful:
re.lastIndex = 0; // reset
// Process large inputs in chunks
// Prefer indexOf / split for simple parsing
// Use regex only for structural matching; business logic stays in code

Common Regex Pitfalls

Literals need escapes, the `g` flag has `lastIndex` state, greedy matching, and `\` in strings is double-escaped.

1
2
3
4
5
6
7
8
9
10
11
12
13
// In a string-literal regex, write `\` twice:
new RegExp('\\d+'); // equivalent to /\d+/
// The `g` flag carries `lastIndex` state:
const re = /a/g;
re.lastIndex = 0; // reset before reuse
// Greedy matching:
'<a><b>'.match(/<.*>/); // matches through the last `>`
'<a><b>'.match(/<.*?>/); // non-greedy, shortest match
// In a regex literal, escape `.`:
/1\.0/; // matches '1.0' rather than '1X0'
// Inside a character class `[^...]` negates:
/[^a]/; // any character that isn't `a`
// Empty match: /(?:)/ // matches any position

Common Patterns

Common regex snippets: ID numbers, phones, colors, dates, etc. For business format validation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Mainland China mobile number:
const mobile = /^1[3-9]\d{9}$/;
// Email:
const email = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
// Hex color:
const color = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
// Date yyyy-mm-dd:
const date = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/;
// URL:
const url = /^https?:\/\/[^\s]+$/;
// CJK character:
const zh = /[\u4e00-\u9fff]/;
// Blank line:
const blank = /^\s*$/;
// Reminders: anchor with ^/$, set explicit quantifiers, use a clear character class

19.Build and Tooling

tsconfig, bundlers, linting/formatting, testing, and CI.

tsconfig in Depth

Common compiler options: `moduleResolution`, `declaration`, `noUnusedLocals`, `esModuleInterop`, `paths` alias.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"lib": ["ES2022", "DOM"],
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"declaration": true, // emit .d.ts files
"outDir": "dist",
"noUnusedLocals": true,
"paths": { "@/*": ["./src/*"] },
"resolveJsonModule": true
},
"include": ["src"]
}
// npm-run: tsc --noEmit for type-checking

Bundlers

Vite/Webpack/Rollup for bundling. Vite is the default for TS/frontends. For libraries, use tsup/Rollup to emit ESM+CJS.

1
2
3
4
5
6
7
8
9
10
11
12
// Vite: dev server + bundling
// vite.config.ts:
export default {
build: { target: 'esnext' },
// plugins: [react(), vue()]
};
// Scripts:
// npm run dev development
// npm run build production build
// Library build: tsup
// tsup src/index.ts --format esm,cjs --dts
// Env vars: import.meta.env.VITE_XXX

Lint and Formatting

ESLint for rules, Prettier for formatting. `ts-eslint` provides type-aware rules.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ESLint config:
// {
// "parser": "@typescript-eslint/parser",
// "plugins": ["@typescript-eslint"],
// "rules": {
// "@typescript-eslint/no-explicit-any": "warn"
// }
// }
// Lint:
// npx eslint src --fix
// Prettier:
// npx prettier --write "src/**/*.ts"
// Bootstrap a rule set:
// npx eslint --init
// Pre-commit: husky + lint-staged

Testing

Vitest/Jest for unit tests. `describe`/`it`/`expect`. Types and runtime are separate. Test TS directly.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { describe, it, expect } from 'vitest';
import { add } from './add';
describe('add', () => {
it('adds two numbers', () => {
expect(add(1, 2)).toBe(3);
});
it('type error fails at compile time', () => {
// add('a', 2) // caught at compile time
});
});
// Type-level tests:
// import { expectType } from 'tsd';
// Coverage: vitest run --coverage
// Mocks: vi.mock() / vi.fn()

CI and Deployment

CI stages: type check, lint, test, build. `tsc --noEmit` blocks type errors.

1
2
3
4
5
6
7
8
9
10
11
// GitHub Actions:
// steps:
// - run: npm ci
// - run: npx tsc --noEmit # type-checking
// - run: npx eslint src
// - run: npm test
// - run: npm run build
// - run: npm publish --dry-run
// Put type-checking first so failures abort early
// Speed up installs with actions/cache
// Test against a matrix of Node versions: 18/20/22

Debugging

sourceMap + Node `--inspect` breakpoint debugging. `console` debugging, type assertions to help.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// tsconfig sourceMap: true
// VS Code launch.json:
// {
// "type": "node",
// "request": "launch",
// "program": "${file}",
// "runtimeArgs": ["--loader", "tsx"]
// }
// Debug from the CLI:
// node --inspect dist/main.js
// Browser: DevTools Sources + source maps
// Type debugging:
// type Debug<T> = T; hover to inspect
// Use console output to triangulate

Publishing to npm

Publishing a library: `files` controls content, semantic `version`, `main`/`types`/`exports` entry points. Build before publishing.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// package.json:
// {
// "name": "@org/my-lib",
// "version": "1.2.0",
// "main": "./dist/index.js",
// "types": "./dist/index.d.ts",
// "exports": {
// ".": { "types": "./dist/index.d.ts", "import": "./dist/index.mjs" }
// },
// "files": ["dist"],
// "sideEffects": false
// }
// Publish:
// npm run build && npm publish
// Dry run: npm publish --dry-run
// Version: npm version patch|minor|major
// Access: npm publish --access public

Monorepo Config

npm/pnpm workspaces for multi-package repos. Shared dependencies, inter-package references, unified scripts.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// pnpm-workspace.yaml:
// packages:
// - 'packages/*'
// - 'apps/*'
// npm workspaces:
// {
// "workspaces": ["packages/*"]
// }
// Cross-package reference:
// npm i @org/shared -w packages/web
// Run a script across all packages:
// pnpm -r run build
// Shared TS config:
// tsconfig.base.json extended by each package
// Hoisting: pnpm isolates by default; declare what needs to be shared

Official Links

Direct links to the official docs and resources.

About this Cheatsheet

This page is a self-contained cheatsheet for TypeScript 5.x, covering the type system and core language features that make up roughly 80% of real-world use. The content leans toward modern idioms: interfaces and type aliases, generics, union and intersection types, type narrowing, literal types, keyof/typeof, mapped and conditional types, and their interplay with async programming. TypeScript was released by Microsoft in 2012 as a superset of JavaScript, adding static type-checking at compile time and dramatically improving maintainability for large codebases — today it's the mainstream choice for front-end engineering. The 19 sections each focus on one theme: basic syntax, variables and type inference, the type system, references and value semantics, control flow, functions and overloads, strings and templates, collections, memory and type erasure, classes and interfaces, error handling, I/O, common pitfalls, concurrency (async types), networking, time, processes, regex, and build tools (tsc/tsconfig). Each sub-section pairs a concept with a directly copy-pastable snippet. All code and text render locally in your browser; no data leaves your device. For the authoritative reference, see the official TypeScript handbook.

Version 2.1.0