Basics

About

TypeScript adds static types to JavaScript; types are erased at build time.

tsc 7 is the native compiler; defaults are strict, module: esnext and target: es2025.

See: TypeScript Handbook

Basic types

Primitives

TypeExample
string'Ada'
number42
booleantrue
bigint42n
symbolSymbol('id')
null / undefinednull
object{ id: 1 }

Special types

TypeMeaning
anyopts out of checking
unknowntop type; safe any, narrow first
voidno return value
neverbottom type; unreachable
let tags: string[] = ['a', 'b']
let pair: [string, number] = ['a', 1]

let id: string | number = 1
let input: unknown = JSON.parse(json)

function fail(msg: string): never {
  throw new Error(msg)
}

See: Everyday types

Enums

enum Color {
  Red,
  Green = 4,
}

let c: Color = Color.Green

Numeric enums are union enums; enums are not erasable syntax.

See: Enums

Declarations

const pi = 3.14          // type 3.14, not number
let total: number = 0

function add(a: number, b: number): number {
  return a + b
}

const double = (n: number) => n * 2

See: Variable declarations

Type assertions

declare const value: unknown

const input = value as string
const len = (input as string).length
const upper = (<string>value).toUpperCase()  // not in .tsx

as is preferred; angle brackets do not work in .tsx files.

See: Type assertions

Interfaces

Inline

function printLabel(options: { label: string }) {
  console.log(options.label)
}

See: Object types

Explicit

interface LabelOptions {
  label: string
}

function printLabel(options: LabelOptions) {
  console.log(options.label)
}

See: Object types

Optional properties

interface User {
  name: string
  age?: number
}

See: Optional properties

Readonly properties

interface User {
  readonly id: number
}

See: Readonly properties

Index signatures

interface Dict {
  [key: string]: unknown[]
}

Same shape as Record<string, unknown[]>.

See: Index signatures

Type aliases

Aliases

type Name = string | string[]
type Point = { x: number; y: number }

See: Type aliases

Intersections

interface Colorful { color: string }
interface Circle { radius: number }

type ColorfulCircle = Colorful & Circle

See: Intersection types

Literal types

type Direction = 'north' | 'south'
type DiceRoll = 1 | 2 | 3 | 4 | 5 | 6

const point = { x: 4, y: 2 } as const
// point: { readonly x: 4; readonly y: 2 }

See: Literal types

Type operators

Indexed access

interface Building {
  room: {
    door: string
    walls: string[]
  }
}

type Walls = Building['room']['walls']  // string[]

See: Indexed access types

keyof

type Point = { x: number; y: number }

type P = keyof Point  // 'x' | 'y'

See: keyof type operator

typeof

const user = { name: 'Ada', age: 36 }

type User = typeof user
// { name: string; age: number }

See: typeof type operator

Conditional types

type ToArray<T> = T extends unknown ? T[] : never

type StrOrNum = ToArray<string | number>
// string[] | number[]

See: Conditional types

infer

type GetReturn<T> = T extends (...args: unknown[]) => infer R
  ? R
  : never

type Num = GetReturn<() => number>  // number
type First<T extends unknown[]> =
  T extends [infer F, ...unknown[]] ? F : never

type Str = First<['hello', 1]>  // 'hello'

See: Inferring within conditional types

Template literal types

type Space = ' ' | '\n' | '\t'

type TrimLeft<S extends string> =
  S extends `${Space}${infer Rest}` ? TrimLeft<Rest> : S

type Trimmed = TrimLeft<'  hello'>  // 'hello'

See: Template literal types

Functions

Function types

interface User { name: string }

function getUser(cb: (user: User) => void) {
  cb({ name: 'Ada' })
}

getUser(user => console.log(user.name))

See: Function types

this parameters

function object(
  this: { a: number; b: number },
  a: number,
  b: number
) {
  this.a = a
  this.b = b
  return this
}

const result = object.call({ a: 0, b: 0 }, 1, 2)
// result: { a: number; b: number }

See: Declaring this

Type predicates

function isString(x: unknown): x is string {
  return typeof x === 'string'
}

if (isString(value)) {
  value.toUpperCase()
}
const mixed = [1, 'a', 2]
const nums = mixed.filter(x => typeof x === 'number')
// number[] (inferred predicate, 5.5)

See: Narrowing

Classes

Fields

class Point {
  x: number
  y: number
  static instances = 0

  constructor(x: number, y: number) {
    this.x = x
    this.y = y
    Point.instances++
  }
}

See: Classes

Inheritance

class Point3D extends Point {
  z: number

  constructor(x: number, y: number, z: number) {
    super(x, y)
    this.z = z
  }
}

interface Colored { color: string }

class Pixel extends Point3D implements Colored {
  color = '#000'
}

See: Classes

Parameter properties

class Point {
  constructor(
    public x: number,
    public y: number,
  ) {}
}

Not erasable syntax; see erasable syntax.

See: Parameter properties

Definite assignment

class Point {
  // assigned outside the constructor, e.g. in init()
  value!: number
}

See: Strict property initialization

Decorators

function logged(
  value: unknown,
  context: ClassMethodDecoratorContext
) {
  console.log(`decorating ${String(context.name)}`)
}

class Greeter {
  @logged greet() {
    return 'hi'
  }
}

See: Decorators

Generics

Generic classes

class Greeter<T> {
  greeting: T

  constructor(message: T) {
    this.greeting = message
  }
}

const greeter = new Greeter<string>('Hello')

See: Generics

Constraints

function pluck<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key]
}

const name = pluck({ name: 'Ada' }, 'name')  // string

See: Generic constraints

Const type parameters

declare function tuple<const T extends readonly unknown[]>(
  xs: T,
): T

const t = tuple([1, 'a'])
// readonly [1, 'a']

See: const type parameters

Modules

Exports

export const pi = 3.14
export default class Circle {}
export type { User } from './user'
export type * from './types'

See: Modules

Type-only imports

import type { User } from './user'
import { createUser, type NewUser } from './user'

verbatimModuleSyntax keeps these imports as written.

See: Type-only imports

Compiler options

OptionUse
module: "nodenext"Node ESM and CJS resolution
module: "bundler"Bundler-style resolution
module: "node20"Node 20+ import attributes
verbatimModuleSyntaxKeep imports as written
erasableSyntaxOnlyBan enums, namespaces, parameter properties
isolatedDeclarationsRequire explicit types on exports
rewriteRelativeImportExtensionsRewrite .ts paths to .js

target: es2025 is the 7.0 default; ES5 support was removed.

See: TSConfig reference

Recent additions

satisfies

type Palette = Record<string, string | number[]>

const palette = {
  red: [255, 0, 0],
  green: '#00ff00',
} satisfies Palette

palette.red    // number[]
palette.green  // string

See: satisfies

Resource management

class Resource {
  [Symbol.dispose]() {
    console.log('closed')
  }
}

{
  using file = new Resource()
}  // logs "closed" at block exit

using and await using dispose resources at scope exit; add esnext.disposable to lib.

See: using declarations

Erasable syntax

SyntaxErasable?
interface, type, asyes
enum, namespaceno
Parameter propertiesno
import x = require('x')no

--erasableSyntaxOnly rejects non-erasable syntax so Node can strip types without a transpiler.

See: erasableSyntaxOnly

Utility types

Object types

UtilityMeaning
Partial<T>All properties optional
Required<T>All properties required
Readonly<T>All properties readonly
Pick<T, K>Only keys K
Omit<T, K>All keys except K
Record<K, V>Object with keys K

See: Utility types

Union and function types

UtilityMeaning
Exclude<T, U>Union members not in U
Extract<T, U>Union members in U
NonNullable<T>Remove null and undefined
ReturnType<F>Function return type
Parameters<F>Function parameter tuple
Awaited<T>Unwrap a promise
Uppercase<S>Uppercase string literal

See: Utility types

Also see

0 Comments for this cheatsheet. Write yours!