# Typescript Skill

> This skill should be used when the user asks about TypeScript, type annotations, interfaces, generics, type guards, utility types, conditional types, mapped types, template literal types, enums, decorators, tsconfig.json, TypeScript compiler options, strict mode, type inference, declaration files (.d.ts), module augmentation, or migrating JavaScript to TypeScript. Trigger when the user mentions "typescript", "tsconfig", "interface", "type alias", "generic", "keyof", "typeof", "infer", "satisfies", "as const", "unknown vs any", "type narrowing", "discriminated union", "declaration merging", or any TypeScript-specific type system question.

- Skill: `sirhamza/typescript-skill` (Agent Skill)
- Install (CLI): `npx skillmds@latest add sirhamza/typescript-skill`
- Raw SKILL.md: https://api.skillmd.com/api/skills/sirhamza/typescript-skill/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: SirHamza (https://skillmd.com/u/sirhamza)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/sirhamza/typescript-skill

---


# TypeScript Expert

## Overview

Advanced expertise in TypeScript — from the core type system and generics to advanced type manipulation, compiler configuration, declaration files, and real-world patterns for React, Node.js, and library authoring.

---

## 1. Type System Fundamentals

```ts
// Primitive types
let name: string = 'Alice'
let age: number = 30
let active: boolean = true
let nothing: null = null
let missing: undefined = undefined
let unique: symbol = Symbol('id')
let big: bigint = 9007199254740991n

// Arrays & Tuples
let ids: number[] = [1, 2, 3]
let pair: [string, number] = ['Alice', 30]
let rgb: readonly [number, number, number] = [255, 0, 128]

// Object types
type Point = { x: number; y: number }
type User = { readonly id: string; name: string; email?: string }

// Union & Intersection
type ID = string | number
type Admin = User & { role: 'admin'; permissions: string[] }

// Literal types
type Direction = 'north' | 'south' | 'east' | 'west'
type StatusCode = 200 | 201 | 400 | 401 | 404 | 500

// unknown vs any
function parse(input: unknown) {
  if (typeof input === 'string') return input.toUpperCase() // narrowed
}
```

---

## 2. Interfaces vs Type Aliases

```ts
// Interface — extendable, declaration merging
interface Animal { name: string }
interface Dog extends Animal { breed: string }

// Declaration merging (interfaces only)
interface Window { myPlugin: Plugin }  // augments existing Window

// Type alias — more powerful for complex types
type StringOrNumber = string | number
type Callback<T> = (error: Error | null, result: T) => void
type DeepReadonly<T> = { readonly [K in keyof T]: DeepReadonly<T[K]> }

// When to use which:
// - Use interface for objects/classes (extendable, mergeable)
// - Use type for unions, intersections, mapped types, utility compositions
```

---

## 3. Generics

```ts
// Generic functions
function identity<T>(value: T): T { return value }
function first<T>(arr: T[]): T | undefined { return arr[0] }
function merge<A, B>(a: A, b: B): A & B { return { ...a, ...b } as A & B }

// Constraints
function getLength<T extends { length: number }>(val: T): number { return val.length }
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key] }

// Generic interfaces & classes
interface Repository<T extends { id: string }> {
  findById(id: string): Promise<T | null>
  findAll(): Promise<T[]>
  save(entity: T): Promise<T>
  delete(id: string): Promise<void>
}

// Generic with default
interface ApiResponse<T = unknown> {
  data: T
  status: number
  message: string
}

// Multiple constraints
function clone<T extends object>(obj: T): T {
  return JSON.parse(JSON.stringify(obj))
}
```

---

## 4. Utility Types

```ts
// Built-in utility types
type Partial<T>           // all props optional
type Required<T>          // all props required
type Readonly<T>          // all props readonly
type Record<K, V>         // { [key in K]: V }
type Pick<T, K>           // keep only K keys
type Omit<T, K>           // remove K keys
type Exclude<T, U>        // remove U from union T
type Extract<T, U>        // keep only U from union T
type NonNullable<T>       // remove null | undefined
type ReturnType<F>        // return type of function F
type Parameters<F>        // parameter types of function F
type InstanceType<C>      // instance type of class C
type Awaited<T>           // unwrap Promise<T> → T

// Examples
type UserPreview = Pick<User, 'id' | 'name'>
type UserUpdate = Partial<Omit<User, 'id'>>
type StringKeys<T> = Extract<keyof T, string>
type UnwrapPromise<T> = T extends Promise<infer R> ? R : T

// Custom utilities
type Nullable<T> = T | null
type Maybe<T> = T | null | undefined
type DeepPartial<T> = { [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K] }
type Mutable<T> = { -readonly [K in keyof T]: T[K] }
type RequiredKeys<T> = { [K in keyof T]-?: {} extends Pick<T, K> ? never : K }[keyof T]
```

---

## 5. Mapped Types

```ts
// Basic mapped type
type Flags<T> = { [K in keyof T]: boolean }

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

// Filtering keys
type OnlyStrings<T> = {
  [K in keyof T as T[K] extends string ? K : never]: T[K]
}

// Example
type UserGetters = Getters<{ name: string; age: number }>
// → { getName: () => string; getAge: () => number }

// Optional/required modifiers
type OptionalProps<T> = { [K in keyof T]+?: T[K] }    // add optional
type RequiredProps<T> = { [K in keyof T]-?: T[K] }    // remove optional
type MutableProps<T> = { -readonly [K in keyof T]: T[K] } // remove readonly
```

---

## 6. Conditional Types

```ts
// Basic conditional
type IsString<T> = T extends string ? true : false

// Distributive conditional (applies to each union member)
type ToArray<T> = T extends unknown ? T[] : never
// ToArray<string | number> → string[] | number[]

// infer — extract types
type UnpackArray<T> = T extends (infer Item)[] ? Item : T
type UnpackPromise<T> = T extends Promise<infer R> ? R : T
type FirstArg<T> = T extends (first: infer F, ...rest: any[]) => any ? F : never

// Recursive types
type DeepReadonly<T> = T extends (infer U)[]
  ? ReadonlyArray<DeepReadonly<U>>
  : T extends object
  ? { readonly [K in keyof T]: DeepReadonly<T[K]> }
  : T

// NonNullable implementation
type MyNonNullable<T> = T extends null | undefined ? never : T
```

---

## 7. Template Literal Types

```ts
type EventName = 'click' | 'focus' | 'blur'
type Handler = `on${Capitalize<EventName>}`
// → 'onClick' | 'onFocus' | 'onBlur'

type CSSProperty = 'margin' | 'padding'
type CSSDirection = 'Top' | 'Bottom' | 'Left' | 'Right'
type CSSProp = `${CSSProperty}${CSSDirection}`
// → 'marginTop' | 'marginBottom' | ... | 'paddingRight'

// Parsing string patterns
type ExtractRoute<T extends string> =
  T extends `${infer _Start}:${infer Param}/${infer Rest}`
    ? Param | ExtractRoute<`/${Rest}`>
    : T extends `${infer _Start}:${infer Param}`
    ? Param
    : never

type Params = ExtractRoute<'/users/:id/posts/:postId'>
// → 'id' | 'postId'
```

---

## 8. Type Narrowing & Type Guards

```ts
// typeof narrowing
function process(val: string | number) {
  if (typeof val === 'string') return val.toUpperCase()
  return val.toFixed(2)
}

// instanceof narrowing
function handleError(err: unknown) {
  if (err instanceof Error) console.error(err.message)
}

// in narrowing
type Cat = { meow(): void }
type Dog = { bark(): void }
function speak(animal: Cat | Dog) {
  if ('meow' in animal) animal.meow()
  else animal.bark()
}

// Discriminated union
type Shape =
  | { kind: 'circle'; radius: number }
  | { kind: 'rect'; width: number; height: number }

function area(shape: Shape): number {
  switch (shape.kind) {
    case 'circle': return Math.PI * shape.radius ** 2
    case 'rect':   return shape.width * shape.height
  }
}

// User-defined type guard
function isUser(val: unknown): val is User {
  return typeof val === 'object' && val !== null && 'id' in val && 'name' in val
}

// Assertion function
function assertDefined<T>(val: T | null | undefined): asserts val is T {
  if (val == null) throw new Error('Expected defined value')
}
```

---

## 9. `satisfies` & `as const`

```ts
// as const — infer literal types
const config = {
  endpoint: '/api/v1',
  timeout: 3000,
  methods: ['GET', 'POST'] as const,
} as const
// config.endpoint: '/api/v1' (literal, not string)
// config.methods: readonly ['GET', 'POST']

// satisfies — validate type without widening
const palette = {
  red: [255, 0, 0],
  green: '#00ff00',
} satisfies Record<string, string | number[]>
// palette.red is number[] (not string | number[])
// palette.green is string (not string | number[])

// Combine both
const routes = {
  home: '/',
  users: '/users',
  userDetail: '/users/:id',
} as const satisfies Record<string, `/${string}`>
```

---

## 10. `tsconfig.json` — Key Options

```json
{
  "compilerOptions": {
    // Strictness
    "strict": true,                     // enables all strict checks
    "noUncheckedIndexedAccess": true,   // arr[0] → T | undefined
    "exactOptionalPropertyTypes": true, // { x?: string } ≠ { x: string | undefined }
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,

    // Module
    "target": "ES2022",
    "module": "NodeNext",               // or "ESNext" for bundlers
    "moduleResolution": "NodeNext",     // or "Bundler"
    "lib": ["ES2022", "DOM", "DOM.Iterable"],

    // Output
    "outDir": "./dist",
    "rootDir": "./src",
    "declaration": true,                // generate .d.ts files
    "declarationMap": true,             // source maps for .d.ts
    "sourceMap": true,

    // Paths
    "baseUrl": ".",
    "paths": { "@/*": ["./src/*"] },

    // Next.js / Bundler
    "moduleResolution": "Bundler",
    "allowImportingTsExtensions": true,
    "jsx": "preserve",
    "incremental": true,
    "plugins": [{ "name": "next" }]
  }
}
```

---

## 11. Declaration Files & Module Augmentation

```ts
// Augment existing module
declare module 'express' {
  interface Request {
    user?: User
    requestId: string
  }
}

// Augment global
declare global {
  interface Window {
    analytics: Analytics
  }
  var __APP_VERSION__: string
}

// .d.ts for untyped JS modules
declare module 'untyped-lib' {
  export function doSomething(val: string): number
  export const VERSION: string
}

// Ambient declarations
declare const __DEV__: boolean
declare function require(module: string): unknown
```

---

## 12. Advanced Patterns

```ts
// Builder pattern with method chaining
class QueryBuilder<T> {
  private filters: Partial<T>[] = []
  where(filter: Partial<T>): this { this.filters.push(filter); return this }
  build(): Partial<T> { return Object.assign({}, ...this.filters) }
}

// Branded types (nominal typing)
type UserId = string & { readonly brand: unique symbol }
type PostId = string & { readonly brand: unique symbol }
const toUserId = (id: string): UserId => id as UserId
// UserId and PostId are incompatible even though both are strings

// Const enum (inlined at compile time)
const enum Direction { Up = 'UP', Down = 'DOWN' }

// Variadic tuple types
type Concat<T extends unknown[], U extends unknown[]> = [...T, ...U]
type Prepend<T, U extends unknown[]> = [T, ...U]

// Function overloads
function format(value: string): string
function format(value: number, decimals: number): string
function format(value: string | number, decimals = 2): string {
  if (typeof value === 'string') return value.trim()
  return value.toFixed(decimals)
}
```

---

## Core Competency Summary

- Write fully type-safe TypeScript with `strict` mode enabled
- Master generics, constraints, and generic utility composition
- Use mapped types, conditional types, and template literals for advanced type manipulation
- Narrow types with type guards, discriminated unions, and assertion functions
- Configure `tsconfig.json` for Node.js, Next.js, and library projects
- Author `.d.ts` declaration files and module augmentations
- Apply branded types, builder patterns, and overloads in real-world code

