Open-source libraries used

1 libraries are bundled into this tool's code.

TypeScript Cheat Sheet

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.

Version 2.1.1