Claude-skill-registry-data mastering-typescript

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry-data
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry-data "$T" && mkdir -p ~/.claude/skills && cp -r "$T/data/mastering-typescript" ~/.claude/skills/majiayu000-claude-skill-registry-data-mastering-typescript && rm -rf "$T"
manifest: data/mastering-typescript/SKILL.md
source content

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 ESM
pnpm create vite@latest my-app --template vanilla-ts
cd my-app && pnpm install

# Configure strict TypeScript
cat > 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 types
type Status = "pending" | "approved" | "rejected";

// Intersection: value must satisfy all types
type Employee = Person & { employeeId: string };

// Discriminated union for type-safe handling
type 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 guard
function process(value: string | number): string {
  if (typeof value === "string") {
    return value.toUpperCase();
  }
  return value.toFixed(2);
}

// Custom type guard
interface 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 info
const colors1 = {
  red: "#ff0000",
  green: "#00ff00"
} as Record<string, string>;

colors1.red.toUpperCase(); // OK, but red could be undefined

// Solution: satisfies preserves literal types
const 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 | undefined
const 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 length
logLength([1, 2, 3]);   // OK: array has length
logLength(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

TypePurposeExample
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 type
type IsString<T> = T extends string ? true : false;

// Extract array element type
type ArrayElement<T> = T extends (infer E)[] ? E : never;

type Numbers = ArrayElement<number[]>; // number
type Strings = ArrayElement<string[]>; // string

// Practical: Extract Promise result type
type UnwrapPromise<T> = T extends Promise<infer R> ? R : T;

Mapped Types

// Make all properties readonly
type Immutable<T> = {
  readonly [K in keyof T]: T[K];
};

// Make all properties nullable
type Nullable<T> = {
  [K in keyof T]: T[K] | null;
};

// Create getter functions for each property
type 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 component
interface 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 hooks
const [count, setCount] = useState<number>(0);
const userRef = useRef<HTMLInputElement>(null);

NestJS with TypeScript

// Type-safe DTO with class-validator
import { 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 schema
const 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 schema
type User = z.infer<typeof UserSchema>;

// Validate at runtime
function 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)

ToolVersionPurpose
TypeScript5.9+Type checking and compilation
Node.js22 LTSRuntime environment
Vite7.xBuild tool and dev server
pnpm9.xPackage manager
ESLint9.xLinting with flat config
Vitest3.xTesting framework
Prettier3.xCode formatting

ESLint Flat Config (ESLint 9+)

// eslint.config.js
import 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

  1. Add
    allowJs: true
    and
    checkJs: false
    to tsconfig.json
  2. Rename files from
    .js
    to
    .ts
    one at a time
  3. Add type annotations gradually
  4. 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

MistakeProblemFix
Using
any
liberally
Defeats type safetyUse
unknown
and narrow
Ignoring strict modeMisses null/undefined bugsEnable all strict options
Type assertions (
as
)
Can hide type errorsUse
satisfies
or guards
Enum for simple unionsGenerates runtime codeUse literal unions instead
Not validating API dataRuntime type mismatchesUse Zod at boundaries

Cross-Language Comparison

FeatureTypeScriptJavaPython
Type SystemStructuralNominalGradual (duck typing)
NullabilityExplicit (
T | null
)
@Nullable
annotations
Optional via typing
GenericsType-level, erasedType-level, erasedRuntime via typing
InterfacesStructural matchingMust implementProtocol (3.8+)
EnumsAvoid (use unions)First-classEnum class

Reference Files

Assets

Scripts