type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
variant: 'primary' | 'secondary';
loading?: boolean;
};
coreintersection-typescomposition
not reviewed
Enums
A set of named constants (fixed values). String enums are preferred for readability. Use const enum to have TypeScript replace the name with its value during compilation, leaving no runtime object.
typescript
// Numeric enum (auto-incremented from 0)
enum Direction { Up, Down, Left, Right }
Direction.Up; // 0
// String enum — explicit values, preferred for readability
enum Status {
Pending = 'PENDING',
Active = 'ACTIVE',
Inactive = 'INACTIVE',
}
// const enum — inlined at compile time, no runtime object
constenum Color { Red = 'red', Green = 'green', Blue = 'blue' }
function move(dir: Direction) { /* ... */ }
move(Direction.Up); // Direction.Up, not 0
// Alternative: literal union (no runtime overhead, no reverse lookup)
type Role = 'admin' | 'user' | 'guest';
coreenumsconstants
not reviewed
Tuple Types
An array with a fixed number of elements where each position has its own specific type.
Tell TypeScript to treat a value as a specific type when you know something the compiler does not. Use as rarely as possible — it bypasses type checking.
typescript
// as assertion — tell TS what type you know it to be
const el = document.getElementById('root') as HTMLDivElement;
const user = response.json() as User;
// Non-null assertion (!) — assert value is not null/undefined
const el2 = document.getElementById('root')!;
el2.style.color = 'red'; // no "possibly null" error
// as const — infer literal types instead of widened types
const value = (getValue() as unknown) as SpecificType;
// Prefer type predicates over raw assertions
function isUser(val: unknown): val is User {
returntypeof val === 'object' && val !== null && 'name'in val;
}
coretype-assertionsasnon-null
not reviewed
Generics
Write functions, interfaces, and classes that work with any type by using a type parameter (like T) as a placeholder. The actual type is filled in when the code is called or used.
typescript
// Generic function
function identity<T>(value: T): T { return value; }
const n = identity(42); // T inferred as number
const s = identity('hello'); // T inferred as string
// Generic interface
interface Box<T> {
value: T;
map<U>(fn: (v: T) => U): Box<U>;
}
// Generic class
class Stack<T> {
#items: T[] = [];
push(item: T): void { this.#items.push(item); }
pop(): T | undefined { returnthis.#items.pop(); }
peek(): T | undefined { returnthis.#items.at(-1); }
get size(): number { returnthis.#items.length; }
}
// Multiple type parameters
function zip<T, U>(a: T[], b: U[]): [T, U][] {
return a.map((item, i) => [item, b[i]]);
}
zip([1, 2], ['a', 'b']); // [[1,'a'], [2,'b']]
coregenericstype-parameters
not reviewed
Generic Constraints
Restrict what types are allowed for a type parameter using extends. This lets you safely access properties that are guaranteed to exist on the narrowed type.
typescript
// Constrain T to have a length property
function longest<T extends { length: number }>(a: T, b: T): T {
return a.length >= b.length ? a : b;
}
longest('hello', 'hi'); // string
longest([1, 2, 3], [1, 2]); // number[]
// longest(1, 2); // Error — number has no length
// K must be a key of T
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
type Data = Awaited<ReturnType<typeof fetchData>>; // { id: number }
advancedutility-typesexcludeextract
not reviewed
Type Guards
Checks that tell TypeScript which specific type a value is at a given point in the code. You can use built-in checks like typeof and instanceof, or write your own.
typescript
// typeof guard
function process(val: string | number) {
if (typeof val === 'string') val.toUpperCase(); // string
else val.toFixed(2); // number
}
// instanceof guard
function handle(err: unknown) {
if (err instanceof TypeError) console.error('Type error:', err.message);
A union of object types that each share a common property (like kind or type) with a unique string value. TypeScript uses that property to figure out which specific variant you are working with.
typescript
type Result<T> =
| { status: 'success'; data: T }
| { status: 'error'; message: string; code: number }
Choose between two types based on a condition using T extends U ? A : B. When T is a union, the condition is applied to each member separately.
typescript
// Basic conditional
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
// Distributive over unions
type C = IsString<string | number>; // true | false
// Non-distributive (bracketed)
type IsStringExact<T> = [T] extends [string] ? true : false;
type D = IsStringExact<string | number>; // false
// infer — extract a type from a position
type Unwrap<T> = T extends Promise<infer R> ? R : T;
type E = Unwrap<Promise<string>>; // string
type F = Unwrap<number>; // number
// Flatten arrays one level
type Flatten<T> = T extends (infer E)[] ? E : T;
type G = Flatten<string[]>; // string
advancedconditional-typesinfertype-manipulation
not reviewed
The infer Keyword
Pull out and name a piece of a type inside a conditional type. Used to extract things like the return type of a function or the value type inside a Promise.
typescript
// Extract return type (like ReturnType<T>)
type MyReturnType<T> =
T extends (...args: any[]) => infer R ? R : never;
type R = MyReturnType<(n: number) => boolean>; // boolean
// Extract parameter types
type Params<T> =
T extends (...args: infer P) => unknown ? P : never;
type P = Params<(a: string, b: number) => void>; // [string, number]
// Unwrap a Promise
type Await<T> = T extends Promise<infer V> ? V : T;
// Extract both key and value from Map
type UnpackMap<T> =
T extends Map<infer K, infer V> ? { key: K; value: V } : never;
type KV = UnpackMap<Map<string, number>>; // { key: string; value: number }
// Get constructor instance type
type NewableReturn<T> =
T extendsnew (...args: any[]) => infer I ? I : never;
advancedinferconditional-typestype-manipulation
not reviewed
Template Literal Types
TS 4.1
Build new string literal types by combining strings and other types using backtick syntax, the same way you write template strings in JavaScript.
typescript
type Direction = 'top' | 'right' | 'bottom' | 'left';
[K in T as`on${Capitalize<K>}`]: (e: Event) => void;
};
type ClickFocusBlur = EventHandlers<'click' | 'focus' | 'blur'>;
// { onClick: ..., onFocus: ..., onBlur: ... }
// Strongly typed column ordering
type Column = 'name' | 'age' | 'email';
type OrderBy = `${Column} ASC` | `${Column} DESC`;
// Extract from template with infer
type ExtractId<T extends string> =
T extends`${string}_${infer ID}` ? ID : never;
type Id = ExtractId<'user_abc123'>; // 'abc123'
advancedtemplate-literal-typesstring-manipulation
not reviewed
Class Modifiers
TypeScript adds keywords to classes that control visibility (public, protected, private), prevent reassignment (readonly), require subclasses to implement a method (abstract), and confirm a method overrides a parent method (override).
typescript
class Animal {
readonly name: string; // cannot be reassigned after init
public species: string = ''; // visible everywhere (default)
protected age: number = 0; // accessible in subclasses
override speak(): void { // 'override' asserts this overrides a base method
console.log(`${this.name} barks!`);
}
birthday() { this.age++; } // OK — protected accessible here
}
// Parameter properties — shorthand: declare + assign in one step
class Point {
constructor(
public readonly x: number,
public readonly y: number,
) {}
}
// equivalent to: class Point { readonly x; readonly y; constructor(x,y) {...} }
coreclassesmodifiersoop
not reviewed
Abstract Classes
A class that acts as a template for other classes. It can define methods that every subclass must provide, but you cannot create an instance of the abstract class itself.
typescript
abstract class Shape {
abstract area(): number;
abstract perimeter(): number;
describe(): string { // concrete method on abstract class
Special annotations (prefixed with @) that wrap or modify classes, methods, and fields — letting you add behaviour like logging or validation without changing the original code.
typescript
// Method decorator — wrap with timing
function timed(target: unknown, ctx: ClassMethodDecoratorContext) {