Skill v1.0.0
currentAutomated scan100/100name: mastering-typescript description: | Master enterprise-grade TypeScript development with type-safe patterns, modern tooling, and framework integration. This skill provides comprehensive guidance for TypeScript 5.9+, covering type system fundamentals (generics, mapped types, conditional types, satisfies operator), enterprise patterns (error handling, validation with Zod), React integration for type-safe frontends, NestJS for scalable APIs, and LangChain.js for AI applications. Use when building type-safe applications, migrating JavaScript codebases, configuring modern toolchains (Vite 7, pnpm, ESLint, Vitest), implementing advanced type patterns, or comparing TypeScript with Java/Python approaches. version: 1.0.0 category: programming-languages triggers:
- typescript
- ts
- type-safe
- generics
- nestjs typescript
- react typescript
- typescript migration
- tsconfig
- type guards
- mapped types
- conditional types
- satisfies operator
- zod validation
author: Richard Hightower license: MIT tags:
- typescript
- type-safety
- enterprise
- react
- nestjs
- langchain
- vite
Mastering Modern TypeScript
Build enterprise-grade, type-safe applications with TypeScript 5.9+.
Compatibility: TypeScript 5.9+, Node.js 22 LTS, Vite 7, NestJS 11, React 19
Quick Start
# Initialize TypeScript project with ESMpnpm create vite@latest my-app --template vanilla-tscd my-app && pnpm install# Configure strict TypeScriptcat > tsconfig.json << 'EOF'{"compilerOptions": {"target": "ES2024","module": "ESNext","moduleResolution": "bundler","strict": true,"noUncheckedIndexedAccess": true,"exactOptionalPropertyTypes": true,"esModuleInterop": true,"skipLibCheck": true}}EOF
When to Use This Skill
Use when:
- Building type-safe React, NestJS, or Node.js applications
- Migrating JavaScript codebases to TypeScript
- Implementing advanced type patterns (generics, mapped types, conditional types)
- Configuring modern TypeScript toolchains (Vite, pnpm, ESLint)
- Designing type-safe API contracts with Zod validation
- Comparing TypeScript approaches with Java or Python
Project Setup Checklist
Before starting any TypeScript project:
- [ ] Use pnpm for package management (faster, disk-efficient)- [ ] Configure ESM-first (type: "module" in package.json)- [ ] Enable strict mode in tsconfig.json- [ ] Set up ESLint with @typescript-eslint- [ ] Add Prettier for consistent formatting- [ ] Configure Vitest for testing
Type System Quick Reference
Primitive Types
const name: string = "Alice";const age: number = 30;const active: boolean = true;const id: bigint = 9007199254740991n;const key: symbol = Symbol("unique");
Union and Intersection Types
// Union: value can be one of several typestype Status = "pending" | "approved" | "rejected";// Intersection: value must satisfy all typestype Employee = Person & { employeeId: string };// Discriminated union for type-safe handlingtype Result<T> =| { success: true; data: T }| { success: false; error: string };function handleResult<T>(result: Result<T>): T | null {if (result.success) {return result.data; // TypeScript knows data exists here}console.error(result.error);return null;}
Type Guards
// typeof guardfunction process(value: string | number): string {if (typeof value === "string") {return value.toUpperCase();}return value.toFixed(2);}// Custom type guardinterface User { type: "user"; name: string }interface Admin { type: "admin"; permissions: string[] }function isAdmin(person: User | Admin): person is Admin {return person.type === "admin";}
The satisfies Operator (TS 5.0+)
Validate type conformance while preserving inference:
// Problem: Type assertion loses specific type infoconst colors1 = {red: "#ff0000",green: "#00ff00"} as Record<string, string>;colors1.red.toUpperCase(); // OK, but red could be undefined// Solution: satisfies preserves literal typesconst colors2 = {red: "#ff0000",green: "#00ff00"} satisfies Record<string, string>;colors2.red.toUpperCase(); // OK, and TypeScript knows red exists
Generics Patterns
Basic Generic Function
function first<T>(items: T[]): T | undefined {return items[0];}const num = first([1, 2, 3]); // number | undefinedconst str = first(["a", "b"]); // string | undefined
Constrained Generics
interface HasLength {length: number;}function logLength<T extends HasLength>(item: T): T {console.log(item.length);return item;}logLength("hello"); // OK: string has lengthlogLength([1, 2, 3]); // OK: array has lengthlogLength(42); // Error: number has no length
Generic API Response Wrapper
interface ApiResponse<T> {data: T;status: number;timestamp: Date;}async function fetchUser(id: string): Promise<ApiResponse<User>> {const response = await fetch(`/api/users/${id}`);const data = await response.json();return {data,status: response.status,timestamp: new Date()};}
Utility Types Reference
| Type | Purpose | Example | ||
|---|---|---|---|---|
Partial<T> | All properties optional | Partial<User> | ||
Required<T> | All properties required | Required<Config> | ||
Pick<T, K> | Select specific properties | `Pick<User, "id" \ | "name">` | |
Omit<T, K> | Exclude specific properties | Omit<User, "password"> | ||
Record<K, V> | Object with typed keys/values | Record<string, number> | ||
ReturnType<F> | Extract function return type | ReturnType<typeof fn> | ||
Parameters<F> | Extract function parameters | Parameters<typeof fn> | ||
Awaited<T> | Unwrap Promise type | Awaited<Promise<User>> |
Conditional Types
// Basic conditional typetype IsString<T> = T extends string ? true : false;// Extract array element typetype ArrayElement<T> = T extends (infer E)[] ? E : never;type Numbers = ArrayElement<number[]>; // numbertype Strings = ArrayElement<string[]>; // string// Practical: Extract Promise result typetype UnwrapPromise<T> = T extends Promise<infer R> ? R : T;
Mapped Types
// Make all properties readonlytype Immutable<T> = {readonly [K in keyof T]: T[K];};// Make all properties nullabletype Nullable<T> = {[K in keyof T]: T[K] | null;};// Create getter functions for each propertytype Getters<T> = {[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];};interface Person { name: string; age: number }type PersonGetters = Getters<Person>;// { getName: () => string; getAge: () => number }
Framework Integration
React with TypeScript
// Typed functional componentinterface ButtonProps {label: string;onClick: () => void;variant?: "primary" | "secondary";}const Button: React.FC<ButtonProps> = ({ label, onClick, variant = "primary" }) => (<button className={variant} onClick={onClick}>{label}</button>);// Typed hooksconst [count, setCount] = useState<number>(0);const userRef = useRef<HTMLInputElement>(null);
NestJS with TypeScript
// Type-safe DTO with class-validatorimport { IsString, IsEmail, MinLength } from 'class-validator';class CreateUserDto {@IsString()@MinLength(2)name: string;@IsEmail()email: string;}// Or with Zod (modern approach)import { z } from 'zod';const CreateUserSchema = z.object({name: z.string().min(2),email: z.string().email()});type CreateUserDto = z.infer<typeof CreateUserSchema>;
See react-integration.md and nestjs-integration.md for detailed patterns.
Validation with Zod
import { z } from 'zod';// Define schemaconst UserSchema = z.object({id: z.string().uuid(),name: z.string().min(1).max(100),email: z.string().email(),role: z.enum(["user", "admin", "moderator"]),createdAt: z.coerce.date()});// Infer TypeScript type from schematype User = z.infer<typeof UserSchema>;// Validate at runtimefunction parseUser(data: unknown): User {return UserSchema.parse(data); // Throws ZodError if invalid}// Safe parsing (returns result object)const result = UserSchema.safeParse(data);if (result.success) {console.log(result.data); // Typed as User} else {console.error(result.error.issues);}
Modern Toolchain (2025)
| Tool | Version | Purpose | |
|---|---|---|---|
| TypeScript | 5.9+ | Type checking and compilation | |
| Node.js | 22 LTS | Runtime environment | |
| Vite | 7.x | Build tool and dev server | |
| pnpm | 9.x | Package manager | |
| ESLint | 9.x | Linting with flat config | |
| Vitest | 3.x | Testing framework | |
| Prettier | 3.x | Code formatting |
ESLint Flat Config (ESLint 9+)
// eslint.config.jsimport eslint from '@eslint/js';import tseslint from 'typescript-eslint';export default tseslint.config(eslint.configs.recommended,...tseslint.configs.strictTypeChecked,{languageOptions: {parserOptions: {projectService: true,tsconfigRootDir: import.meta.dirname,},},});
Migration Strategies
Incremental Migration
- Add
allowJs: trueandcheckJs: falseto tsconfig.json - Rename files from
.jsto.tsone at a time - Add type annotations gradually
- Enable stricter options incrementally
JSDoc for Gradual Typing
// Before full migration, use JSDoc/*** @param {string} name* @param {number} age* @returns {User}*/function createUser(name, age) {return { name, age };}
See enterprise-patterns.md for comprehensive migration guides.
Common Mistakes
| Mistake | Problem | Fix | |
|---|---|---|---|
Using any liberally | Defeats type safety | Use unknown and narrow | |
| Ignoring strict mode | Misses null/undefined bugs | Enable all strict options | |
Type assertions (as) | Can hide type errors | Use satisfies or guards | |
| Enum for simple unions | Generates runtime code | Use literal unions instead | |
| Not validating API data | Runtime type mismatches | Use Zod at boundaries |
Cross-Language Comparison
| Feature | TypeScript | Java | Python | ||
|---|---|---|---|---|---|
| Type System | Structural | Nominal | Gradual (duck typing) | ||
| Nullability | Explicit (`T \ | null`) | @Nullable annotations | Optional via typing | |
| Generics | Type-level, erased | Type-level, erased | Runtime via typing | ||
| Interfaces | Structural matching | Must implement | Protocol (3.8+) | ||
| Enums | Avoid (use unions) | First-class | Enum class |
Reference Files
- type-system.md — Complete type system guide
- generics.md — Advanced generics patterns
- enterprise-patterns.md — Error handling, validation, architecture
- react-integration.md — React + TypeScript patterns
- nestjs-integration.md — NestJS API development
- toolchain.md — Modern build tools configuration
Assets
- tsconfig-template.json — Strict enterprise config
- eslint-template.js — ESLint 9 flat config
Scripts
- validate-setup.sh — Verify TypeScript environment