ExoSnip Code Snippets

TypeScript Snippets

← Back to Home

About TypeScript

TypeScript adds a static type system to JavaScript, helping you catch mistakes earlier and design better APIs. This track covers core types and a small project pattern.

Initialize a TypeScript Project

Create a project and install TypeScript compiler tooling.

mkdir ts-lab
cd ts-lab
npm init -y
npm install -D typescript tsx @types/node
npx tsc --init

Recommended tsconfig for Learning

Enable strict mode early so type feedback is useful from day one.

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "outDir": "dist"
  },
  "include": ["src"]
}

Model Domain Data with Types and Interfaces

Use aliases for unions and interfaces for object shapes.

type ExpenseCategory = 'food' | 'travel' | 'tools'

interface Expense {
  id: string
  description: string
  amount: number
  category: ExpenseCategory
}

const sample: Expense = {
  id: 'e1',
  description: 'Keyboard',
  amount: 89,
  category: 'tools'
}

Generic Helper for API Results

Generics let one utility function stay type-safe for many response shapes.

type Result<T> =
  | { ok: true; data: T }
  | { ok: false; error: string }

async function toResult<T>(promise: Promise<T>): Promise<Result<T>> {
  try {
    return { ok: true, data: await promise }
  } catch (error) {
    return { ok: false, error: (error as Error).message }
  }
}

Discriminated Union for Command Parsing

Use command tags to safely branch business logic.

type Command = { type: 'add'; text: string } | { type: 'list' } | { type: 'remove'; id: string }

function run(command: Command) {
  switch (command.type) {
    case 'add':
      return `adding ${command.text}`
    case 'remove':
      return `removing ${command.id}`
    case 'list':
      return 'listing'
  }
}

Type Guard for Unknown JSON

Validate unknown values from files or APIs before trusting their shape.

interface User {
  id: number
  name: string
}

function isUser(value: unknown): value is User {
  return typeof value === 'object' && value !== null &&
    typeof (value as User).id === 'number' &&
    typeof (value as User).name === 'string'
}

Mini Project: Expense Store with Typed Reducer

Small project idea: build a typed expense tracker core you can reuse in React, Node, or tests.

interface Expense { id: string; description: string; amount: number }
type Action = { type: 'expense/added'; expense: Expense } | { type: 'expense/removed'; id: string }

function expenseReducer(state: Expense[], action: Action): Expense[] {
  switch (action.type) {
    case 'expense/added':
      return [...state, action.expense]
    case 'expense/removed':
      return state.filter((expense) => expense.id !== action.id)
  }
}

Typed Fetch Function

Create one reusable fetch wrapper with a typed return value.

async function getJson<T>(url: string): Promise<T> {
  const response = await fetch(url)
  if (!response.ok) throw new Error(`Request failed: ${response.status}`)
  return (await response.json()) as T
}

type Post = { id: number; title: string }
getJson<Post[]>('https://jsonplaceholder.typicode.com/posts').then(console.log)

Run Type Checks and Execute Files

Use tsc for static checks and tsx for fast local execution.

npx tsc --noEmit
npx tsx src/index.ts
npx tsc

Next Learning Upgrades

Move from language fundamentals to architecture-level usage.

1. Add zod schemas and infer TS types from them.
2. Add utility types for API response normalization.
3. Add strict lint rules for unsafe any usage.
4. Add contract tests for typed clients.
5. Add monorepo packages with shared TypeScript types.