Advanced TypeScript

Autonomous Agents Advanced autonomous-agent-skills openai-codex, chatgpt, claude-code
0 Upvotes
7 Views
0 Downloads
950 Words

Description

Designs and maintains advanced TypeScript types (generics, conditional/mapped types, guards, declarations) to catch errors at compile time and guide correct usage.

When to Use

I need advanced TypeScript patterns | Show me generic and conditional types examples | Help me design a typed API with discriminated unions | I want to write strict declaration files | Refactor with mapped types and type guards

Use Cases

Design generic APIs with related input/output types | Model discriminated unions for safe API responses | Infer return types from functions with conditional types | Ensure strict module boundaries via declarations

SKILL.md Content

---
name: typescript-advanced
description: "Designs and maintains advanced TypeScript types (generics, conditional/mapped types, guards, declarations) to catch errors at compile time and guide correct usage."
metadata:
  tags: "autonomous-agents, typescript, type-systems, generics, conditional-types, mapped-types, type-guards, api-design"
  source: "https://skilldb.dev/skills/autonomous-agent-skills/typescript-advanced"
  pack: "autonomous-agent-skills"
  category: "Autonomous Agents"
---

# Advanced TypeScript

## When to use this skill
Use when the user says things like:
- "I need advanced TypeScript patterns"
- "Show me generic and conditional types examples"
- "Help me design a typed API with discriminated unions"
- "I want to write strict declaration files"
- "Refactor with mapped types and type guards"


You are an autonomous agent that writes and maintains TypeScript code. Your role is to leverage the type system to catch bugs at compile time, produce self-documenting code, and build APIs that guide consumers toward correct usage through types alone.

## Philosophy

TypeScript's type system is a tool for expressing intent. The goal is not to make the compiler happy — it is to encode domain rules, prevent invalid states, and make incorrect code impossible to write. Invest in precise types upfront; they pay dividends in fewer runtime errors and better developer experience.

## Techniques

### Generic Types
- Use generics when a function or type works with multiple types while preserving relationships: `function first<T>(arr: T[]): T | undefined`.
- Constrain generics with `extends` to narrow the accepted types: `function getLength<T extends { length: number }>(item: T): number`.
- Use default type parameters for common cases: `type Response<T = unknown> = { data: T; status: number }`.
- Name generic parameters meaningfully for complex generics: `TInput`, `TOutput`, `TKey` instead of single letters when clarity demands it.

### Conditional Types
- Use conditional types for type-level branching: `type IsString<T> = T extends string ? true : false`.
- Use `infer` to extract types within conditional expressions: `type ReturnOf<T> = T extends (...args: any[]) => infer R ? R : never`.
- Conditional types distribute over unions: `IsString<string | number>` becomes `true | false`.
- Use conditional types in library and utility code. Avoid them in application-level types where simpler constructs work.

### Mapped Types
- Transform existing types with mapped types: `type Readonly<T> = { readonly [K in keyof T]: T[K] }`.
- Use key remapping with `as`: `type Getters<T> = { [K in keyof T as `get${Capitalize<K & string>}`]: () => T[K] }`.
- Combine with conditional types for selective transformation: make only string properties optional, for example.
- Understand built-in mapped types before building custom ones.

### Template Literal Types
- Use template literal types for string pattern enforcement: `type EventName = `on${Capitalize<string>}``.
- Combine with mapped types for type-safe event systems or API route definitions.
- Use for CSS unit types: `type CSSLength = `${number}${'px' | 'rem' | 'em'}``.
- Keep template literal types focused. Overly complex string type patterns become unreadable.

### Discriminated Unions
- Model states with discriminated unions using a shared literal type field:
  ```
  type Result<T> = { status: 'success'; data: T } | { status: 'error'; error: Error }
  ```
- Use `switch` or `if` on the discriminant field for exhaustive handling.
- Add a `never` default case to catch unhandled variants at compile time.
- Prefer discriminated unions over boolean flags. `{ loading: boolean; error: boolean; data: T | null }` has impossible states; a union does not.

### Type Guards
- Write custom type guards with `is` return type: `function isUser(val: unknown): val is User`.
- Use `typeof` for primitive narrowing and `in` operator for property checks.
- Use `satisfies` operator to validate a value matches a type without widening: `const config = { ... } satisfies Config`.
- Validate external data (API responses, user input) at runtime boundaries with type guards or validation libraries like Zod.

### Utility Types
- `Pick<T, K>`: Select specific properties from a type.
- `Omit<T, K>`: Remove specific properties from a type.
- `Partial<T>`: Make all properties optional. Useful for update operations.
- `Required<T>`: Make all properties required. Useful for ensuring complete configuration.
- `Record<K, V>`: Create an object type with specified keys and value types.
- `Extract<T, U>` and `Exclude<T, U>`: Filter union types.
- `NonNullable<T>`: Remove `null` and `undefined` from a type.
- Compose utility types: `Partial<Pick<User, 'name' | 'email'>>` for targeted partial updates.

### Declaration Files
- Write `.d.ts` files to type untyped JavaScript libraries or global declarations.
- Use `declare module` for augmenting existing module types.
- Use `declare global` to extend global types (Window, NodeJS.ProcessEnv).
- Check DefinitelyTyped (`@types/*`) before writing custom declarations.

### Strict Mode
- Enable all strict flags in `tsconfig.json`: `"strict": true`.
- Key strict options: `strictNullChecks` (no implicit null), `noImplicitAny` (no untyped variables), `strictFunctionTypes` (correct function variance).
- Enable `noUncheckedIndexedAccess` to treat index access as potentially undefined.
- Strict mode catches real bugs. Never disable it to "make things easier."

### Module Resolution
- Use `"moduleResolution": "bundler"` for modern bundler-based projects or `"nodenext"` for Node.js packages.
- Configure path aliases in `tsconfig.json` with `paths` and ensure the bundler resolves them too.
- Use `import type` for type-only imports to ensure they are erased at compile time.
- Understand the difference between `require` (CommonJS) and `import` (ESM). Configure `"module"` appropriately.

## Best Practices
- Start with strict mode on every new project. Retrofitting strict mode onto a large codebase is painful.
- Use `unknown` instead of `any` for values of uncertain type. `unknown` forces safe narrowing.
- Use `as const` for literal type inference on objects and arrays.
- Export types alongside their implementations. Consumers should not need to reconstruct your types.
- Use Zod, Valibot, or ArkType for runtime validation that generates TypeScript types.

## Anti-Patterns
- Using `any` to silence type errors instead of fixing the underlying type issue.
- Casting with `as` when a type guard or proper narrowing would be safer.
- Disabling strict mode or individual strict checks to avoid fixing type errors.
- Creating overly complex conditional types that no one on the team can read or maintain.
- Using `@ts-ignore` or `@ts-expect-error` without a comment explaining why.
- Defining all types as `interface` when `type` would be more appropriate (unions, intersections, primitives).
- Not using discriminated unions for state management, leading to impossible state combinations.
- Ignoring the `satisfies` operator and using `as` for type validation instead.