TypeScript泛型与类型守卫实战:高级类型在业务逻辑中的应用

TypeScript的高级类型系统是大型前端项目的类型安全基石。泛型让组件和函数具备类型复用能力,类型守卫在运行时收窄类型范围,条件类型和映射类型则实现类型层面的逻辑运算。实际开发中合理使用这些特性,可以在编译阶段消除大量运行时错误。

泛型函数与泛型约束

泛型函数允许类型参数化,在不丢失类型信息的前提下实现逻辑复用:

// 基础泛型:类型参数T在调用时确定
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const user = { name: "张三", age: 30, email: "zhang@example.com" };
const userName = getProperty(user, "name");    // 类型推断为 string
const userAge = getProperty(user, "age");       // 类型推断为 number

// 泛型约束:限制类型参数必须满足特定结构
interface HasId {
  id: number;
}

function findById<T extends HasId>(items: T[], id: number): T | undefined {
  return items.find(item => item.id === id);
}

// 泛型默认类型
function createArray<T = string>(length: number, value: T): T[] {
  return Array.from({ length }, () => value);
}

const strArray = createArray(3, "hello");       // string[]
const numArray = createArray<number>(3, 42);    // number[]

API请求函数中的泛型应用:

interface ApiResponse<T> {
  code: number;
  message: string;
  data: T;
}

async function request<T>(url: string, options?: RequestInit): Promise<T> {
  const res = await fetch(url, options);
  const json: ApiResponse<T> = await res.json();
  if (json.code !== 0) {
    throw new Error(json.message);
  }
  return json.data;
}

// 调用时自动推断返回类型
interface User { id: number; name: string; }
const user = await request<User>("/api/user/1");  // 类型为 User

类型守卫与类型收窄

类型守卫在运行时检查类型,帮助TypeScript编译器收窄联合类型:

// typeof 类型守卫
function processValue(value: string | number) {
  if (typeof value === "string") {
    return value.toUpperCase();  // 此处 value 类型收窄为 string
  }
  return value.toFixed(2);       // 此处 value 类型收窄为 number
}

// instanceof 类型守卫
class ValidationError extends Error {
  constructor(public field: string, message: string) {
    super(message);
  }
}

function handleError(error: Error | ValidationError) {
  if (error instanceof ValidationError) {
    return `${error.field}: ${error.message}`;  // 可访问 field 属性
  }
  return error.message;
}

// in 操作符类型守卫
interface ApiSuccess { data: unknown; }
interface ApiError { error: string; }

function handleResponse(response: ApiSuccess | ApiError) {
  if ("data" in response) {
    return response.data;    // 类型收窄为 ApiSuccess
  }
  throw new Error(response.error);  // 类型收窄为 ApiError
}

自定义类型守卫函数处理复杂联合类型:

interface TextNode {
  type: "text";
  content: string;
}

interface ImageNode {
  type: "image";
  url: string;
  width: number;
  height: number;
}

interface VideoNode {
  type: "video";
  url: string;
  duration: number;
}

type ContentNode = TextNode | ImageNode | VideoNode;

// 自定义类型谓词
function isImageNode(node: ContentNode): node is ImageNode {
  return node.type === "image";
}

function renderNode(node: ContentNode): string {
  switch (node.type) {
    case "text":
      return `<p>${node.content}</p>`;
    case "image":
      return `<img src="${node.url}" width="${node.width}" height="${node.height}"/>`;
    case "video":
      return `<video src="${node.url}" duration="${node.duration}"></video>`;
  }
}

// 类型谓词在数组过滤中的应用
const nodes: ContentNode[] = [...];
const images = nodes.filter(isImageNode);  // 类型为 ImageNode[]

条件类型与映射类型

条件类型实现类型层面的条件判断:

// 提取Promise的泛型参数
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;

type R1 = UnwrapPromise<Promise<string>>;   // string
type R2 = UnwrapPromise<number>;             // number

// 提取函数返回类型
type ReturnTypeOf<T> = T extends (...args: any[]) => infer R ? R : never;

// 排除特定属性
type OmitMethod<T> = {
  [K in keyof T]: T[K] extends Function ? never : K
}[keyof T];

type DataProps = OmitMethod<{ name: string; age: number; greet(): void }>;
// "name" | "age"

映射类型实现类型转换:

// 所有属性变为可选
type Partial<T> = { [K in keyof T]?: T[K] };

// 所有属性变为只读
type Readonly<T> = { readonly [K in keyof T]: T[K] };

// 所有属性变为可空
type Nullable<T> = { [K in keyof T]: T[K] | null };

// 将对象键名转为联合类型
type ObjectValues<T> = T[keyof T];

const config = {
  apiBase: "/api",
  timeout: 5000,
  retryCount: 3
} as const;

type ConfigValue = ObjectValues<typeof config>;
// "/api" | 5000 | 3

实际业务场景中的类型设计

表单验证场景,利用泛型和映射类型构建类型安全的验证器:

type ValidationRule<T> = {
  [K in keyof T]?: {
    required?: boolean;
    min?: number;
    max?: number;
    pattern?: RegExp;
    validator?: (value: T[K]) => boolean | string;
  };
};

interface LoginForm {
  username: string;
  password: string;
}

const rules: ValidationRule<LoginForm> = {
  username: {
    required: true,
    min: 3,
    max: 20,
    pattern: /^[a-zA-Z0-9_]+$/
  },
  password: {
    required: true,
    min: 8,
    validator: (value) => value.length >= 8 || "密码至少8位"
  }
};

function validate<T>(data: T, rules: ValidationRule<T>): Record<string, string> {
  const errors: Record<string, string> = {};
  for (const key in rules) {
    const rule = rules[key];
    const value = data[key];
    if (rule?.required && !value) {
      errors[key] = `${key}不能为空`;
    }
    if (rule?.min && typeof value === "string" && value.length < rule.min) {
      errors[key] = `${key}最少${rule.min}个字符`;
    }
  }
  return errors;
}

泛型和高级类型的使用需要控制复杂度。简单业务逻辑用基础类型即可,过度使用泛型嵌套会降低代码可读性。核心原则:泛型用于可复用的工具函数和组件,类型守卫用于联合类型处理,条件类型用于类型工具库,业务代码以清晰可维护优先。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/typescript-fan-xing-yu-lei-xing-shou-wei-shi-zhan-gao-ji/

(0)
小编小编
上一篇 5小时前
下一篇 5小时前

相关推荐