Optional type annotations for variables, parameters, and return types layered on top of JavaScript.
function greet(name: string): string { return 'Hello, ' + name;}Named structural contracts — types are compatible if they share the right shape, not by name.
interface Animal { name: string; sound(): string;}Type-safe parameterized abstractions that preserve type information through transformations.
function identity<T>(arg: T): T { return arg; }const n = identity(42); // T = numberconst s = identity('hi'); // T = stringEnum types for named constants and class syntax with access modifiers (public, private, protected).
enum Direction { Up, Down, Left, Right }class Animal { private name: string; constructor(name: string) { this.name = name; }}null and undefined are no longer silently assignable to every type — eliminates an entire class of null-dereference bugs.
// With strictNullChecks: truelet name: string = null; // Error — null not assignable to stringlet name: string | null = null; // OK — explicitfunction greet(name: string | null) { if (name !== null) { name.toUpperCase(); // narrowed to string }}Tell the compiler a value is definitely non-null when it cannot verify it — a targeted escape hatch.
const input = document.getElementById('name')!; // asserts non-nullinput.value = 'Alice'; // no "possibly null" errorconst first = arr[0]!; // assert first element existsTypeScript follows every branch and assignment to narrow types at each point — the foundation of modern type narrowing.
function f(x: string | null) { if (x === null) return; // eliminated null x.toUpperCase(); // x: string — null was ruled out}A type-safe alternative to any — values typed as unknown must be narrowed before use.
function process(value: unknown) { // value.toUpperCase(); // Error — must narrow first if (typeof value === 'string') { value.toUpperCase(); // OK — narrowed to string }}Split a TypeScript codebase into sub-projects that can reference each other, enabling incremental builds and better monorepo support.
// tsconfig.json{ "references": [ { "path": "./packages/core" }, { "path": "./packages/ui" } ]}// tsc --build — builds only what changedRest elements in tuple types enable precise typing of heterogeneous rest parameters and variadic generics.
type Strings = [string, ...string[]];type Head<T extends unknown[]> = T extends [infer H, ...unknown[]] ? H : never;type Tail<T extends unknown[]> = T extends [unknown, ...infer R] ? R : never;TypeScript shipped ?. and ?? before they landed in the ECMAScript standard.
const city = user?.address?.city; // undefined if any part is null/undefinedconst port = config.port ?? 3000; // default only for null/undefined (not 0 or '')Functions annotated with asserts narrow the type for the caller after the call returns.
function assertIsString(val: unknown): asserts val is string { if (typeof val !== 'string') throw new Error('Expected string');}let x: unknown = getValue();assertIsString(x);x.toUpperCase(); // x is string here — no errorType aliases can now directly reference themselves, enabling types like JSONValue without interface indirection.
type JSONValue = | null | boolean | number | string | JSONValue[] | { [key: string]: JSONValue };Construct string literal types using template syntax — distributes over union members to produce every combination.
type Direction = 'top' | 'right' | 'bottom' | 'left';type CSSMargin = `margin-${Direction}`;// 'margin-top' | 'margin-right' | 'margin-bottom' | 'margin-left'type EventName<T extends string> = `on${Capitalize<T>}`;type ClickEvent = EventName<'click'>; // 'onClick'Remap keys in a mapped type using the as clause — enables renaming, filtering, and template-based transformations.
type Getters<T> = { [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];};type PersonGetters = Getters<{ name: string; age: number }>;// { getName: () => string; getAge: () => number }Conditional types can now reference themselves, enabling deep unwrapping and recursive type transformations.
type DeepReadonly<T> = { readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];};Validates that a value matches a type while preserving the inferred literal type — get both validation and precision.
type Palette = Record<'red' | 'green' | 'blue', string | number[]>;const palette = { red: [255, 0, 0], green: '#00ff00', blue: [0, 0, 255],} satisfies Palette;palette.red.map(c => c / 255); // OK — red is number[], not string | number[]The accessor keyword generates a private backing field with a getter/setter pair — designed for use with decorators.
class Person { accessor name: string; // generates #name field + get/set name constructor(name: string) { this.name = name; }}Decorators now follow the TC39 Stage 3 proposal with a new API — class, method, field, accessor, getter/setter decorators.
function sealed(target: typeof MyClass, _ctx: ClassDecoratorContext) { Object.seal(target); Object.seal(target.prototype);}@sealedclass MyClass { name = 'TypeScript'; }Generic functions with const modifier infer literal types instead of widened types, eliminating the need for as const at every call site.
function identity<const T>(value: T): T { return value; }const a = identity(['a', 'b', 'c']);// readonly ['a', 'b', 'c'] — not string[]function defineRoutes<const T extends { path: string }[]>(routes: T): T { return routes;}Enums are now treated as unions of their member types, enabling narrowing per member.
enum Status { Active = 'active', Inactive = 'inactive' }function handle(s: Status) { if (s === Status.Active) { /* narrowed to Status.Active */ }}Explicit resource management via the Disposable interface — resources call [Symbol.dispose] automatically when they leave scope.
class DatabaseConnection implements Disposable { [Symbol.dispose]() { this.close(); } query(sql: string) { /* ... */ }}function runQuery() { using db = new DatabaseConnection(); // auto-closed at block exit return db.query('SELECT * FROM users');} // db[Symbol.dispose]() called hereDecorators can access context.metadata to attach and read metadata about the decorated class member.
function logged(_target: unknown, ctx: ClassMethodDecoratorContext) { console.log(`Registering method: ${String(ctx.name)}`);}class Greeter { @logged greet(name: string) { return `Hello, ${name}`; }}TypeScript now infers return type predicates automatically from function bodies — filter(isString) finally returns string[].
function isString(x: unknown) { return typeof x === 'string'; // inferred: x is string}const mixed: (string | number)[] = [1, 'hello', 2, 'world'];const strings = mixed.filter(isString); // string[] — previously (string | number)[]New --isolatedDeclarations flag enforces explicit types on exports so .d.ts files can be generated without full type-checking — enables parallel DTS emit in monorepos.
// With isolatedDeclarations, public exports need explicit typesexport function add(a: number, b: number): number { // explicit return type required return a + b;}// export function add(a: number, b: number) { ... } // Error — inferred returnTypeScript validates regex syntax and flags at compile time, catching typos in regex literals.
/(?<year>\d{4})-(?<month>\d{2})/; // OK — valid named groups// /[a-z]/Z; // Error: Unknown flag 'Z'// /(?!abc/; // Error: Unterminated group