TypeScript
Syntax
Primitives
let name: string = 'Tammy'
let age: number = 28
let active: boolean = true
let nothing: null = null
let missing: undefined = undefinedArrays & Tuples
let ids: number[] = [1, 2, 3]
let names: Array<string> = ['a', 'b']
// tuple — fixed length, typed positions
let pair: [string, number] = ['age', 28]Objects
// inline
let user: { name: string; age: number } = {
name: 'Tammy',
age: 28,
}
// optional property
let config: { debug?: boolean } = {}Interfaces
interface User {
id: number
name: string
email?: string // optional
}
const user: User = { id: 1, name: 'Tammy' }Type Aliases
type ID = string | number
type Status = 'loading' | 'error' | 'success'
type Point = { x: number; y: number }
let userId: ID = 42
let state: Status = 'loading'Functions
function add(a: number, b: number): number {
return a + b
}
const greet = (name: string): string => {
return `Hello ${name}`
}
function log(msg: string, level?: string) {}Enums
enum Status {
Idle, // 0
Loading, // 1
Success, // 2
Error, // 3
}
// string enum — more readable in logs
enum Dir {
Up = 'UP',
Down = 'DOWN',
}Unknown vs Any
// any — disables type checking (avoid)
let a: any = 'hello'
a.foo.bar // no error, but unsafe
// unknown — must narrow before using
let b: unknown = 'hello'
if (typeof b === 'string') {
b.toUpperCase() // safe after check
}Beginner Patterns
Union Types
let id: string | number = 'abc'
id = 42 // also valid
function show(val: string | number) {
if (typeof val === 'string') {
val.toUpperCase() // TS knows it's string
}
}Intersection Types
type HasName = { name: string }
type HasAge = { age: number }
// combine — must have ALL properties
type Person = HasName & HasAge
const p: Person = { name: 'Tammy', age: 28 }Type Narrowing
function handle(val: string | number) {
if (typeof val === 'string') {
val.toUpperCase() // string methods
} else {
val.toFixed(2) // number methods
}
}
function greet(name?: string) {
if (name) {
name.toUpperCase() // definitely string
}
}Type Assertion
// tell TS you know the type
const input = document.getElementById('name') as HTMLInputElement
input.value // no error
// non-null assertion (careful)
const el = document.querySelector('.btn')!Typeof / Keyof
const config = { debug: true, port: 3000 }
// typeof — extract type from a value
type Config = typeof config
// { debug: boolean; port: number }
// keyof — union of keys
type ConfigKey = keyof Config
// 'debug' | 'port'Generics
function first<T>(arr: T[]): T | undefined {
return arr[0]
}
first<number>([1, 2, 3]) // 1
first(['a', 'b']) // 'a' (inferred)Intermediate Patterns
Utility Types
interface User {
id: number
name: string
email: string
}
Partial<User> // all optional
Required<User> // all required
Pick<User, 'id' | 'name'> // subset
Omit<User, 'email'> // exclude
Readonly<User> // immutable
Record<string, number> // { [key]: num }Overloads
function format(val: string): string
function format(val: number): string
function format(val: string | number): string {
if (typeof val === 'string') return val.trim()
return val.toFixed(2)
}
format('hello') // string signature
format(3.14159) // number signatureDiscriminated Union
type Result =
| { status: 'ok'; data: string }
| { status: 'error'; message: string }
function handle(r: Result) {
switch (r.status) {
case 'ok':
r.data // TS knows data exists
break
case 'error':
r.message // TS knows message exists
break
}
}Generic Constraints
// T must have a length property
function longest<T extends { length: number }>(
a: T, b: T
): T {
return a.length >= b.length ? a : b
}
longest('abc', 'de') // 'abc'
longest([1, 2], [1]) // [1, 2]Advanced Patterns
as const
const routes = ['/', '/about', '/blog'] as const
// readonly ['/', '/about', '/blog']
type Route = (typeof routes)[number]
// '/' | '/about' | '/blog'
const config = { env: 'prod', port: 3000 } as const
// { readonly env: 'prod'; readonly port: 3000 }Type Guards
interface Dog { bark(): void }
interface Cat { meow(): void }
// custom guard — returns `is` type
function isDog(pet: Dog | Cat): pet is Dog {
return 'bark' in pet
}
const pet: Dog | Cat = getPet()
if (isDog(pet)) {
pet.bark() // TS knows it's Dog
}Satisfies
type Colors = Record<string, string | string[]>
// validates type without widening
const palette = {
red: '#ff0000',
blue: ['#0000ff', '#0033cc'],
} satisfies Colors
// TS still knows red is string, blue is string[]
palette.red.toUpperCase() // works
palette.blue.map(c => c) // worksMapped Types
type Optional<T> = {
[K in keyof T]?: T[K]
}
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
}
type UserGetters = Getters<{ name: string; age: number }>
// { getName: () => string; getAge: () => number }Last updated on