Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Part of a 72-plugin marketplace with 112 AI agents and 146 skills for Claude Code development automation.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/details.md
1# typescript-advanced-types — detailed worked examples23## Advanced Patterns45### Pattern 1: Type-Safe Event Emitter67```typescript8type EventMap = {9"user:created": { id: string; name: string };10"user:updated": { id: string };11"user:deleted": { id: string };12};1314class TypedEventEmitter<T extends Record<string, any>> {15private listeners: {16[K in keyof T]?: Array<(data: T[K]) => void>;17} = {};1819on<K extends keyof T>(event: K, callback: (data: T[K]) => void): void {20if (!this.listeners[event]) {21this.listeners[event] = [];22}23this.listeners[event]!.push(callback);24}2526emit<K extends keyof T>(event: K, data: T[K]): void {27const callbacks = this.listeners[event];28if (callbacks) {29callbacks.forEach((callback) => callback(data));30}31}32}3334const emitter = new TypedEventEmitter<EventMap>();3536emitter.on("user:created", (data) => {37console.log(data.id, data.name); // Type-safe!38});3940emitter.emit("user:created", { id: "1", name: "John" });41// emitter.emit("user:created", { id: "1" }); // Error: missing 'name'42```4344### Pattern 2: Type-Safe API Client4546```typescript47type HTTPMethod = "GET" | "POST" | "PUT" | "DELETE";4849type EndpointConfig = {50"/users": {51GET: { response: User[] };52POST: { body: { name: string; email: string }; response: User };53};54"/users/:id": {55GET: { params: { id: string }; response: User };56PUT: { params: { id: string }; body: Partial<User>; response: User };57DELETE: { params: { id: string }; response: void };58};59};6061type ExtractParams<T> = T extends { params: infer P } ? P : never;62type ExtractBody<T> = T extends { body: infer B } ? B : never;63type ExtractResponse<T> = T extends { response: infer R } ? R : never;6465class APIClient<Config extends Record<string, Record<HTTPMethod, any>>> {66async request<Path extends keyof Config, Method extends keyof Config[Path]>(67path: Path,68method: Method,69...[options]: ExtractParams<Config[Path][Method]> extends never70? ExtractBody<Config[Path][Method]> extends never71? []72: [{ body: ExtractBody<Config[Path][Method]> }]73: [74{75params: ExtractParams<Config[Path][Method]>;76body?: ExtractBody<Config[Path][Method]>;77},78]79): Promise<ExtractResponse<Config[Path][Method]>> {80// Implementation here81return {} as any;82}83}8485const api = new APIClient<EndpointConfig>();8687// Type-safe API calls88const users = await api.request("/users", "GET");89// Type: User[]9091const newUser = await api.request("/users", "POST", {92body: { name: "John", email: "[email protected]" },93});94// Type: User9596const user = await api.request("/users/:id", "GET", {97params: { id: "123" },98});99// Type: User100```101102### Pattern 3: Builder Pattern with Type Safety103104```typescript105type BuilderState<T> = {106[K in keyof T]: T[K] | undefined;107};108109type RequiredKeys<T> = {110[K in keyof T]-?: {} extends Pick<T, K> ? never : K;111}[keyof T];112113type OptionalKeys<T> = {114[K in keyof T]-?: {} extends Pick<T, K> ? K : never;115}[keyof T];116117type IsComplete<T, S> =118RequiredKeys<T> extends keyof S119? S[RequiredKeys<T>] extends undefined120? false121: true122: false;123124class Builder<T, S extends BuilderState<T> = {}> {125private state: S = {} as S;126127set<K extends keyof T>(key: K, value: T[K]): Builder<T, S & Record<K, T[K]>> {128this.state[key] = value;129return this as any;130}131132build(this: IsComplete<T, S> extends true ? this : never): T {133return this.state as T;134}135}136137interface User {138id: string;139name: string;140email: string;141age?: number;142}143144const builder = new Builder<User>();145146const user = builder147.set("id", "1")148.set("name", "John")149.set("email", "[email protected]")150.build(); // OK: all required fields set151152// const incomplete = builder153// .set("id", "1")154// .build(); // Error: missing required fields155```156157### Pattern 4: Deep Readonly/Partial158159```typescript160type DeepReadonly<T> = {161readonly [P in keyof T]: T[P] extends object162? T[P] extends Function163? T[P]164: DeepReadonly<T[P]>165: T[P];166};167168type DeepPartial<T> = {169[P in keyof T]?: T[P] extends object170? T[P] extends Array<infer U>171? Array<DeepPartial<U>>172: DeepPartial<T[P]>173: T[P];174};175176interface Config {177server: {178host: string;179port: number;180ssl: {181enabled: boolean;182cert: string;183};184};185database: {186url: string;187pool: {188min: number;189max: number;190};191};192}193194type ReadonlyConfig = DeepReadonly<Config>;195// All nested properties are readonly196197type PartialConfig = DeepPartial<Config>;198// All nested properties are optional199```200201### Pattern 5: Type-Safe Form Validation202203```typescript204type ValidationRule<T> = {205validate: (value: T) => boolean;206message: string;207};208209type FieldValidation<T> = {210[K in keyof T]?: ValidationRule<T[K]>[];211};212213type ValidationErrors<T> = {214[K in keyof T]?: string[];215};216217class FormValidator<T extends Record<string, any>> {218constructor(private rules: FieldValidation<T>) {}219220validate(data: T): ValidationErrors<T> | null {221const errors: ValidationErrors<T> = {};222let hasErrors = false;223224for (const key in this.rules) {225const fieldRules = this.rules[key];226const value = data[key];227228if (fieldRules) {229const fieldErrors: string[] = [];230231for (const rule of fieldRules) {232if (!rule.validate(value)) {233fieldErrors.push(rule.message);234}235}236237if (fieldErrors.length > 0) {238errors[key] = fieldErrors;239hasErrors = true;240}241}242}243244return hasErrors ? errors : null;245}246}247248interface LoginForm {249email: string;250password: string;251}252253const validator = new FormValidator<LoginForm>({254email: [255{256validate: (v) => v.includes("@"),257message: "Email must contain @",258},259{260validate: (v) => v.length > 0,261message: "Email is required",262},263],264password: [265{266validate: (v) => v.length >= 8,267message: "Password must be at least 8 characters",268},269],270});271272const errors = validator.validate({273email: "invalid",274password: "short",275});276// Type: { email?: string[]; password?: string[]; } | null277```278279### Pattern 6: Discriminated Unions280281```typescript282type Success<T> = {283status: "success";284data: T;285};286287type Error = {288status: "error";289error: string;290};291292type Loading = {293status: "loading";294};295296type AsyncState<T> = Success<T> | Error | Loading;297298function handleState<T>(state: AsyncState<T>): void {299switch (state.status) {300case "success":301console.log(state.data); // Type: T302break;303case "error":304console.log(state.error); // Type: string305break;306case "loading":307console.log("Loading...");308break;309}310}311312// Type-safe state machine313type State =314| { type: "idle" }315| { type: "fetching"; requestId: string }316| { type: "success"; data: any }317| { type: "error"; error: Error };318319type Event =320| { type: "FETCH"; requestId: string }321| { type: "SUCCESS"; data: any }322| { type: "ERROR"; error: Error }323| { type: "RESET" };324325function reducer(state: State, event: Event): State {326switch (state.type) {327case "idle":328return event.type === "FETCH"329? { type: "fetching", requestId: event.requestId }330: state;331case "fetching":332if (event.type === "SUCCESS") {333return { type: "success", data: event.data };334}335if (event.type === "ERROR") {336return { type: "error", error: event.error };337}338return state;339case "success":340case "error":341return event.type === "RESET" ? { type: "idle" } : state;342}343}344```345346## Type Inference Techniques347348### 1. Infer Keyword349350```typescript351// Extract array element type352type ElementType<T> = T extends (infer U)[] ? U : never;353354type NumArray = number[];355type Num = ElementType<NumArray>; // number356357// Extract promise type358type PromiseType<T> = T extends Promise<infer U> ? U : never;359360type AsyncNum = PromiseType<Promise<number>>; // number361362// Extract function parameters363type Parameters<T> = T extends (...args: infer P) => any ? P : never;364365function foo(a: string, b: number) {}366type FooParams = Parameters<typeof foo>; // [string, number]367```368369### 2. Type Guards370371```typescript372function isString(value: unknown): value is string {373return typeof value === "string";374}375376function isArrayOf<T>(377value: unknown,378guard: (item: unknown) => item is T,379): value is T[] {380return Array.isArray(value) && value.every(guard);381}382383const data: unknown = ["a", "b", "c"];384385if (isArrayOf(data, isString)) {386data.forEach((s) => s.toUpperCase()); // Type: string[]387}388```389390### 3. Assertion Functions391392```typescript393function assertIsString(value: unknown): asserts value is string {394if (typeof value !== "string") {395throw new Error("Not a string");396}397}398399function processValue(value: unknown) {400assertIsString(value);401// value is now typed as string402console.log(value.toUpperCase());403}404```405