正在加载,请稍候…

Advanced TypeScript: Generics, Conditional Types, and Template Literals

Master TypeScript advanced type system: generic constraints, conditional types, mapped types, template literal types, and the infer keyword for type-safe code.

Advanced TypeScript Type System

TypeScript's type system is incredibly powerful. Mastering generics, conditional types, and template literals eliminates entire classes of bugs.

Generics with Constraints

// Generic with constraint
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const user = { name: "Alice", age: 30 };
const name = getProperty(user, "name"); // type: string

// Multiple constraints
interface Serializable { serialize(): string; }
interface Identifiable { id: string; }

function saveEntity<T extends Serializable & Identifiable>(entity: T): void {
  const data = entity.serialize();
  database.save(entity.id, data);
}

Conditional Types

// Basic conditional type
type IsString<T> = T extends string ? "yes" : "no";

// Extract array element type
type ArrayElement<T> = T extends Array<infer E> ? E : never;
type Items = ArrayElement<string[]>; // string

// Deep readonly
type DeepReadonly<T> = T extends object
  ? { readonly [P in keyof T]: DeepReadonly<T[P]> }
  : T;

// Async return type
type AsyncReturn<T extends (...args: any) => Promise<any>> = 
  T extends (...args: any) => Promise<infer R> ? R : never;

The infer Keyword

// Extract function parameters
type Parameters<T extends (...args: any) => any> =
  T extends (...args: infer P) => any ? P : never;

type ReturnType<T extends (...args: any) => any> =
  T extends (...args: any) => infer R ? R : never;

function greet(name: string, age: number): string { return name; }
type GreetParams = Parameters<typeof greet>; // [string, number]
type GreetReturn = ReturnType<typeof greet>;  // string

// Unwrap Promise
type Awaited<T> = T extends Promise<infer V> ? Awaited<V> : T;
type Unwrapped = Awaited<Promise<Promise<string>>>; // string

Mapped Types

// Custom mapped types
type Nullable<T> = { [P in keyof T]: T[P] | null };

// Re-map keys with 'as'
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 }

// Filter keys by value type
type KeysOfType<T, ValueType> = {
  [K in keyof T]: T[K] extends ValueType ? K : never
}[keyof T];

interface Mixed { id: number; name: string; active: boolean; }
type StringKeys = KeysOfType<Mixed, string>; // "name"

Template Literal Types

// Event system with type safety
type EventName = "click" | "focus" | "blur";
type HandlerName = `on${Capitalize<EventName>}`;
// "onClick" | "onFocus" | "onBlur"

// CSS property builder
type Side = "top" | "right" | "bottom" | "left";
type Margin = `margin-${Side}`;

// Type-safe object paths
type PathKeys<T, Prefix extends string = ""> = {
  [K in keyof T & string]: T[K] extends object
    ? PathKeys<T[K], `${Prefix}${K}.`>
    : `${Prefix}${K}`;
}[keyof T & string];

interface Config { db: { host: string; port: number }; }
type ConfigPaths = PathKeys<Config>; // "db.host" | "db.port"

Discriminated Unions

type LoadingState<T> =
  | { status: "idle" }
  | { status: "loading" }
  | { status: "success"; data: T }
  | { status: "error"; error: Error };

function renderUser(state: LoadingState<User>) {
  switch (state.status) {
    case "success": return state.data.name; // TypeScript knows data exists
    case "error": return state.error.message;
    default: return "Loading...";
  }
}

// Exhaustive check
function assertNever(x: never): never {
  throw new Error(`Unexpected: ${x}`);
}

Summary

TypeScript's type system rewards investment:

  • Generics eliminate duplication while maintaining type safety
  • Conditional types transform types based on runtime shapes
  • infer extracts types from complex generics
  • Mapped types transform existing types systematically
  • Template literals bring type safety to string patterns