TypeScript Quick Reference
Type basics, interfaces, generics, enums, utility types, type guards, decorators and tsconfig.json.
Type Basics
// Primitive type annotations
let name: string = 'Alex';
let age: number = 30;
let active: boolean = true;
let id: bigint = 9007199254740991n;
let sym: symbol = Symbol('unique');
// Arrays
let numbers: number[] = [1, 2, 3];
let names: Array<string> = ['Alice', 'Bob'];
// Tuples — fixed-length arrays with specific types per position
let point: [number, number] = [10, 20];
let entry: [string, number] = ['count', 42];
let labeled: [id: number, name: string] = [1, 'Alex']; // labeled tuple
// Special types
let anything: any = 'no type checking'; // escapes type system
let unknown: unknown = getData(); // safer than any — must narrow before use
let nothing: void = undefined; // functions that don't return
let impossible: never = throwError(); // functions that never return
// Type assertion — tell the compiler you know better
const input = document.getElementById('name') as HTMLInputElement;
const canvas = <HTMLCanvasElement>document.getElementById('canvas');
// Literal types
let direction: 'up' | 'down' | 'left' | 'right' = 'up';
let httpStatus: 200 | 404 | 500 = 200;
// Nullability
let optional: string | null = null;
let maybeUndefined: string | undefined = undefined;
// Non-null assertion (use sparingly)
const el = document.getElementById('app')!; // asserts non-null
| Type | Description | Example |
|---|---|---|
string | Text data | 'hello' |
number | All numbers (int + float) | 42, 3.14 |
boolean | True/false | true |
any | Opts out of type checking | Avoid when possible |
unknown | Type-safe any — must narrow | Prefer over any |
void | No return value | (): void |
never | Unreachable code | Exhaustive checks |
null / undefined | Absence of value | Enable strictNullChecks |
Interfaces
// Basic interface
interface User {
id: number;
name: string;
email: string;
role?: string; // optional property
readonly createdAt: Date; // cannot be reassigned
}
const user: User = {
id: 1,
name: 'Alex',
email: '[email protected]',
createdAt: new Date()
};
// Method signatures
interface Logger {
log(message: string): void;
warn(message: string): void;
error(message: string, code?: number): void;
}
// Index signatures — dynamic keys
interface StringMap {
[key: string]: string;
}
interface NumberDict {
[key: string]: number;
length: number; // named properties must match the index type
}
// Extending interfaces
interface Animal {
name: string;
sound(): string;
}
interface Dog extends Animal {
breed: string;
fetch(): void;
}
// Extending multiple interfaces
interface ServiceWorker extends Employee, Contractor {
serviceId: string;
}
// Declaration merging — interfaces with the same name are merged
interface Config {
apiUrl: string;
}
interface Config {
timeout: number;
}
// Config now has both apiUrl and timeout
// Callable interface
interface Formatter {
(input: string): string;
locale: string;
}
// Class implementing an interface
class AdminUser implements User {
id: number;
name: string;
email: string;
readonly createdAt: Date;
permissions: string[];
constructor(id: number, name: string, email: string) {
this.id = id;
this.name = name;
this.email = email;
this.createdAt = new Date();
this.permissions = [];
}
}
Type Aliases & Unions
// Type alias — name for any type
type ID = string | number;
type Coordinate = [number, number];
type Callback = (data: unknown) => void;
// Union types — a value that can be one of several types
type Status = 'idle' | 'loading' | 'success' | 'error';
type Result = string | number | boolean;
function formatId(id: string | number): string {
return typeof id === 'string' ? id : id.toString();
}
// Intersection types — combine multiple types
type Timestamped = {
createdAt: Date;
updatedAt: Date;
};
type UserRecord = User & Timestamped;
// Discriminated unions — the recommended pattern for variant types
type Shape =
| { kind: 'circle'; radius: number }
| { kind: 'rectangle'; width: number; height: number }
| { kind: 'triangle'; base: number; height: number };
function area(shape: Shape): number {
switch (shape.kind) {
case 'circle':
return Math.PI * shape.radius ** 2;
case 'rectangle':
return shape.width * shape.height;
case 'triangle':
return (shape.base * shape.height) / 2;
}
}
// Template literal types
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
type ApiRoute = `/api/${string}`;
type EventName = `on${Capitalize<string>}`;
// Conditional types
type IsString<T> = T extends string ? true : false;
type A = IsString<'hello'>; // true
type B = IsString<42>; // false
// Mapped types
type ReadonlyUser = {
readonly [K in keyof User]: User[K];
};
type OptionalUser = {
[K in keyof User]?: User[K];
};
Generics
// Generic function
function identity<T>(value: T): T {
return value;
}
const str = identity('hello'); // type: string
const num = identity(42); // type: number
// Generic with constraint
function getLength<T extends { length: number }>(item: T): number {
return item.length;
}
getLength('hello'); // 5
getLength([1, 2, 3]); // 3
// Generic interface
interface ApiResponse<T> {
data: T;
status: number;
message: string;
}
const userRes: ApiResponse<User> = {
data: { id: 1, name: 'Alex', email: '[email protected]', createdAt: new Date() },
status: 200,
message: 'OK'
};
// Generic class
class Stack<T> {
private items: T[] = [];
push(item: T): void {
this.items.push(item);
}
pop(): T | undefined {
return this.items.pop();
}
peek(): T | undefined {
return this.items[this.items.length - 1];
}
get size(): number {
return this.items.length;
}
}
const numStack = new Stack<number>();
numStack.push(10);
// Multiple type parameters
function merge<A, B>(a: A, b: B): A & B {
return { ...a, ...b } as A & B;
}
// Default type parameter
interface PaginatedResult<T = unknown> {
items: T[];
page: number;
totalPages: number;
}
// keyof and indexed access
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const userName = getProperty(user, 'name'); // type: string
Enums
// Numeric enum (auto-increments from 0)
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right // 3
}
// Numeric enum with custom start
enum HttpStatus {
OK = 200,
Created = 201,
BadRequest = 400,
NotFound = 404,
ServerError = 500
}
const status: HttpStatus = HttpStatus.OK;
// String enum — prefer these for readability and debugging
enum Role {
Admin = 'ADMIN',
Editor = 'EDITOR',
Viewer = 'VIEWER'
}
function checkAccess(role: Role): boolean {
return role === Role.Admin || role === Role.Editor;
}
// Const enum — inlined at compile time (no runtime object)
const enum LogLevel {
Debug = 'DEBUG',
Info = 'INFO',
Warn = 'WARN',
Error = 'ERROR'
}
// Reverse mapping (numeric enums only)
enum Color {
Red,
Green,
Blue
}
Color[0]; // 'Red'
Color['Red']; // 0
Prefer string enums or union literal types (
type Role = 'admin' | 'editor') over numeric enums for better debugging and type safety. Const enums produce smaller output but can't be iterated at runtime.Utility Types
| Utility | Description | Example |
|---|---|---|
Partial<T> | All properties optional | Partial<User> |
Required<T> | All properties required | Required<Config> |
Readonly<T> | All properties readonly | Readonly<State> |
Pick<T, K> | Select specific keys | Pick<User, 'id' | 'name'> |
Omit<T, K> | Remove specific keys | Omit<User, 'password'> |
Record<K, V> | Object type with key/value | Record<string, number> |
Exclude<T, U> | Remove types from union | Exclude<Status, 'error'> |
Extract<T, U> | Extract types from union | Extract<Status, 'idle' | 'loading'> |
NonNullable<T> | Remove null/undefined | NonNullable<string | null> |
ReturnType<T> | Return type of function | ReturnType<typeof fn> |
Parameters<T> | Param types as tuple | Parameters<typeof fn> |
Awaited<T> | Unwrap Promise type | Awaited<Promise<string>> |
// Practical usage
interface User {
id: number;
name: string;
email: string;
password: string;
}
// Update payload — all fields optional
type UpdateUser = Partial<User>;
// Public-facing user — no password
type PublicUser = Omit<User, 'password'>;
// Summary — only specific fields
type UserSummary = Pick<User, 'id' | 'name'>;
// Lookup table
type UserMap = Record<number, User>;
// Combine utilities
type CreateUser = Omit<User, 'id'> & { role?: string };
// Deep readonly (recursive)
type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K];
};
Type Guards
// typeof guard
function format(value: string | number): string {
if (typeof value === 'string') {
return value.toUpperCase(); // narrowed to string
}
return value.toFixed(2); // narrowed to number
}
// instanceof guard
function handleError(err: Error | string): string {
if (err instanceof Error) {
return err.message; // narrowed to Error
}
return err; // narrowed to string
}
// in operator guard
interface Fish { swim(): void; }
interface Bird { fly(): void; }
function move(animal: Fish | Bird) {
if ('swim' in animal) {
animal.swim(); // narrowed to Fish
} else {
animal.fly(); // narrowed to Bird
}
}
// Custom type guard with type predicate
function isString(value: unknown): value is string {
return typeof value === 'string';
}
function isUser(obj: unknown): obj is User {
return (
typeof obj === 'object' &&
obj !== null &&
'id' in obj &&
'name' in obj &&
'email' in obj
);
}
// Discriminated union guard
type ApiResult =
| { status: 'success'; data: User[] }
| { status: 'error'; message: string };
function handle(result: ApiResult) {
if (result.status === 'success') {
console.log(result.data); // narrowed — data is available
} else {
console.error(result.message); // narrowed — message is available
}
}
// Assertion function (throws if condition is false)
function assertDefined<T>(value: T | undefined, msg: string): asserts value is T {
if (value === undefined) throw new Error(msg);
}
const maybeUser: User | undefined = getUser();
assertDefined(maybeUser, 'User not found');
maybeUser.name; // safe — type is now User
Decorators
Decorators require
"experimentalDecorators": true in tsconfig.json (legacy) or the TC39 Stage 3 decorator syntax (TS 5.0+). The examples below use the legacy syntax commonly seen in Angular, NestJS and similar frameworks.// Class decorator
function Sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@Sealed
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
}
// Decorator factory — returns a decorator
function Logger(prefix: string) {
return function (target: Function) {
console.log(`${prefix}: ${target.name} registered`);
};
}
@Logger('API')
class UserController { }
// Method decorator
function Log(target: any, key: string, descriptor: PropertyDescriptor) {
const original = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Calling ${key} with`, args);
const result = original.apply(this, args);
console.log(`${key} returned`, result);
return result;
};
}
class MathService {
@Log
add(a: number, b: number): number {
return a + b;
}
}
// Property decorator
function MinLength(min: number) {
return function (target: any, key: string) {
let value: string;
Object.defineProperty(target, key, {
get: () => value,
set: (newVal: string) => {
if (newVal.length < min) {
throw new Error(`${key} must be at least ${min} characters`);
}
value = newVal;
}
});
};
}
class Account {
@MinLength(3)
username!: string;
}
Module System
// Named exports
export const API_URL = 'https://api.example.com';
export function fetchData(url: string): Promise<Response> {
return fetch(url);
}
export interface Config {
apiUrl: string;
timeout: number;
}
// Default export
export default class UserService {
async getAll(): Promise<User[]> { /* ... */ }
}
// Re-export
export { fetchData } from './api';
export { default as UserService } from './UserService';
export * from './types';
export * as utils from './utils';
// Import
import UserService from './UserService';
import { API_URL, fetchData } from './api';
import type { Config } from './config'; // type-only import (erased at runtime)
import { type User, createUser } from './user'; // inline type import
// Dynamic import
const module = await import('./heavy-module');
// Namespace (avoid in modern code — prefer modules)
namespace Validation {
export function isEmail(s: string): boolean {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(s);
}
}
// Ambient declarations — for untyped JS libraries
declare module 'legacy-lib' {
export function doThing(input: string): void;
}
// Global augmentation
declare global {
interface Window {
analytics: AnalyticsAPI;
}
}
tsconfig.json Reference
{
"compilerOptions": {
// Language and environment
"target": "ES2022", // JS output version
"lib": ["ES2022", "DOM"], // available type declarations
"module": "ESNext", // module system for output
"moduleResolution": "bundler", // how imports are resolved
// Strictness — enable all for new projects
"strict": true, // enables all strict flags below
"strictNullChecks": true, // null/undefined not assignable to other types
"strictFunctionTypes": true, // stricter function type checking
"noImplicitAny": true, // error on implicit any
"noImplicitReturns": true, // error if not all paths return
"noUncheckedIndexedAccess": true, // adds undefined to index signatures
"exactOptionalPropertyTypes": true,
// Output
"outDir": "./dist",
"rootDir": "./src",
"declaration": true, // generate .d.ts files
"declarationMap": true, // source maps for .d.ts
"sourceMap": true, // generate .js.map files
"removeComments": true,
// Interop
"esModuleInterop": true, // allows default imports from CJS
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true, // import .json files
"isolatedModules": true, // required for Babel/SWC/esbuild
// Path aliases
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@components/*": ["src/components/*"],
"@utils/*": ["src/utils/*"]
},
// Decorators
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
// Performance
"skipLibCheck": true,
"incremental": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "**/*.test.ts"]
}
| Flag | Purpose |
|---|---|
"strict": true | Enables all strict type-checking options at once |
"noEmit": true | Type-check only, don't produce JS output (useful with bundlers) |
"isolatedModules": true | Required when using Babel, SWC, or esbuild for compilation |
"skipLibCheck": true | Skip type checking of declaration files for faster builds |
"moduleResolution": "bundler" | Modern resolution for Vite, webpack, esbuild projects |