TypeScript

Essential TypeScript types, syntax, and patterns for type-safe JavaScript development.

languages
typescriptjavascripttypesstatic-typing

Basic Types

// Primitive types
let name: string = 'Alice';
let age: number = 30;
let isActive: boolean = true;
let nothing: null = null;
let notDefined: undefined = undefined;

// Arrays
let numbers: number[] = [1, 2, 3];
let names: Array<string> = ['Alice', 'Bob'];

// Tuple - fixed length array with specific types
let tuple: [string, number] = ['Alice', 30];

// Any and Unknown
let flexible: any = 'anything';       // Avoid when possible
let safe: unknown = 'must check';     // Safer than any

// Void and Never
function log(msg: string): void { console.log(msg); }
function error(msg: string): never { throw new Error(msg); }

Type Inference

// TypeScript infers types automatically
let message = 'Hello';          // inferred as string
let count = 42;                 // inferred as number
let items = [1, 2, 3];          // inferred as number[]

// Contextual typing
const names = ['Alice', 'Bob'];
names.forEach(name => {         // name is inferred as string
  console.log(name.toUpperCase());
});

Interfaces

// Define object shape
interface User {
  id: number;
  name: string;
  email?: string;              // Optional property
  readonly createdAt: Date;    // Cannot be modified
}

// Extend interfaces
interface Admin extends User {
  role: 'admin';
  permissions: string[];
}

// Function interface
interface SearchFunc {
  (query: string, limit?: number): Promise<User[]>;
}

// Index signatures
interface Dictionary {
  [key: string]: string;
}

Type Aliases

// Type alias for primitives and unions
type ID = string | number;
type Status = 'pending' | 'active' | 'inactive';

// Type alias for objects
type Point = {
  x: number;
  y: number;
};

// Intersection types
type Employee = User & {
  department: string;
  salary: number;
};

// Template literal types
type EventName = `on${Capitalize<string>}`;

Union and Intersection

// Union types - one of several types
type Result = string | number | boolean;
type Status = 'success' | 'error' | 'loading';

function printId(id: string | number) {
  if (typeof id === 'string') {
    console.log(id.toUpperCase());
  } else {
    console.log(id);
  }
}

// Intersection types - combine multiple types
type Name = { firstName: string; lastName: string };
type Age = { age: number };
type Person = Name & Age;

Generics

// Generic function
function identity<T>(arg: T): T {
  return arg;
}
const result = identity<string>('hello');

// Generic interface
interface Container<T> {
  value: T;
  getValue(): T;
}

// Generic constraints
function getLength<T extends { length: number }>(item: T): number {
  return item.length;
}

// Multiple type parameters
function pair<K, V>(key: K, value: V): [K, V] {
  return [key, value];
}

// Generic defaults
interface Response<T = any> {
  data: T;
  status: number;
}

Utility Types

interface User {
  id: number;
  name: string;
  email: string;
  age: number;
}

// Partial - all properties optional
type PartialUser = Partial<User>;

// Required - all properties required
type RequiredUser = Required<User>;

// Readonly - all properties readonly
type ReadonlyUser = Readonly<User>;

// Pick - select specific properties
type UserPreview = Pick<User, 'id' | 'name'>;

// Omit - exclude specific properties
type UserWithoutEmail = Omit<User, 'email'>;

// Record - construct object type
type UserRoles = Record<string, 'admin' | 'user' | 'guest'>;

// Extract and Exclude
type T1 = Extract<'a' | 'b' | 'c', 'a' | 'f'>;  // 'a'
type T2 = Exclude<'a' | 'b' | 'c', 'a'>;         // 'b' | 'c'

// ReturnType and Parameters
type Fn = (a: string, b: number) => boolean;
type FnReturn = ReturnType<Fn>;      // boolean
type FnParams = Parameters<Fn>;      // [string, number]

Classes

class Person {
  // Properties with access modifiers
  public name: string;
  private age: number;
  protected id: number;
  readonly createdAt: Date;
  
  // Parameter properties shorthand
  constructor(
    public firstName: string,
    private lastName: string
  ) {
    this.createdAt = new Date();
  }
  
  // Method
  greet(): string {
    return `Hello, ${this.firstName}`;
  }
  
  // Getter and Setter
  get fullName(): string {
    return `${this.firstName} ${this.lastName}`;
  }
  
  set fullName(value: string) {
    [this.firstName, this.lastName] = value.split(' ');
  }
  
  // Static members
  static species = 'Homo sapiens';
  static create(name: string): Person {
    return new Person(name, '');
  }
}

// Abstract class
abstract class Animal {
  abstract makeSound(): void;
  
  move(): void {
    console.log('Moving...');
  }
}

Type Guards

// typeof guard
function process(value: string | number) {
  if (typeof value === 'string') {
    return value.toUpperCase();
  }
  return value * 2;
}

// instanceof guard
class Dog { bark() {} }
class Cat { meow() {} }

function speak(animal: Dog | Cat) {
  if (animal instanceof Dog) {
    animal.bark();
  } else {
    animal.meow();
  }
}

// in operator guard
interface Fish { swim(): void }
interface Bird { fly(): void }

function move(animal: Fish | Bird) {
  if ('swim' in animal) {
    animal.swim();
  } else {
    animal.fly();
  }
}

// Custom type guard
function isString(value: unknown): value is string {
  return typeof value === 'string';
}

Type Assertions

// as syntax (preferred)
const input = document.getElementById('input') as HTMLInputElement;

// Angle bracket syntax
const input2 = <HTMLInputElement>document.getElementById('input');

// Non-null assertion
function getValue(map: Map<string, string>, key: string) {
  return map.get(key)!;  // Assert value is not undefined
}

// const assertion
const config = {
  endpoint: '/api',
  timeout: 3000
} as const;  // Makes all properties readonly and literal types

Enums

// Numeric enum
enum Direction {
  Up,      // 0
  Down,    // 1
  Left,    // 2
  Right    // 3
}

// String enum
enum Status {
  Pending = 'PENDING',
  Active = 'ACTIVE',
  Inactive = 'INACTIVE'
}

// Const enum (inlined at compile time)
const enum Color {
  Red = '#ff0000',
  Green = '#00ff00',
  Blue = '#0000ff'
}

// Usage
let dir: Direction = Direction.Up;
let status: Status = Status.Active;

Mapped Types

// Create new type by transforming properties
type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};

type Optional<T> = {
  [P in keyof T]?: T[P];
};

type Nullable<T> = {
  [P in keyof T]: T[P] | null;
};

// Conditional types
type NonNullable<T> = T extends null | undefined ? never : T;

type Flatten<T> = T extends Array<infer U> ? U : T;

// Key remapping
type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};

Module Declarations

// Type-only imports
import type { User } from './types';
import { type User, createUser } from './user';

// Declare module augmentation
declare module 'express' {
  interface Request {
    user?: User;
  }
}

// Ambient declarations for JS libraries
declare const $: (selector: string) => any;

// Declaration files (.d.ts)
declare module '*.css' {
  const styles: { [className: string]: string };
  export default styles;
}

Useful Patterns

// Discriminated unions
type Result<T> = 
  | { success: true; data: T }
  | { success: false; error: string };

function handleResult<T>(result: Result<T>) {
  if (result.success) {
    console.log(result.data);
  } else {
    console.error(result.error);
  }
}

// Builder pattern with method chaining
class QueryBuilder {
  select(fields: string[]): this { return this; }
  where(condition: string): this { return this; }
  orderBy(field: string): this { return this; }
}

// Exhaustive checking
type Shape = 'circle' | 'square' | 'triangle';

function getArea(shape: Shape): number {
  switch (shape) {
    case 'circle': return Math.PI;
    case 'square': return 1;
    case 'triangle': return 0.5;
    default:
      const _exhaustive: never = shape;
      return _exhaustive;
  }
}