阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 类型参数约束

类型参数约束

作者:陈川 阅读数:10935人阅读 分类: TypeScript

类型参数约束的基本概念

TypeScript的类型参数约束允许我们限制泛型类型参数的范围。通过在泛型类型参数后面使用extends关键字,可以指定该类型参数必须满足的条件。这种机制确保了类型安全,同时保持了泛型的灵活性。

interface Lengthwise {
  length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
  console.log(arg.length);
  return arg;
}

单一约束与多重约束

类型参数可以受单一接口或类型的约束,也可以通过联合类型实现多重约束。当需要满足多个条件时,可以使用交叉类型。

// 单一约束
function process<T extends string>(value: T): void {
  console.log(value.toUpperCase());
}

// 多重约束
interface Serializable {
  serialize(): string;
}

interface Identifiable {
  id: number;
}

function persist<T extends Serializable & Identifiable>(item: T): void {
  const serialized = item.serialize();
  localStorage.setItem(`item_${item.id}`, serialized);
}

约束与默认类型结合

类型参数约束可以与默认类型结合使用,这在创建灵活且类型安全的API时特别有用。

interface PaginationOptions<T = any> {
  pageSize?: number;
  currentPage?: number;
  filter?: T extends object ? Partial<T> : never;
}

function paginate<T extends object = {}>(options: PaginationOptions<T> = {}) {
  // 实现逻辑
}

使用keyof进行属性约束

keyof操作符常与类型约束一起使用,确保只能访问对象上存在的属性。

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

const person = { name: "Alice", age: 30 };
getProperty(person, "name"); // 正确
getProperty(person, "gender"); // 错误:gender不是person的属性

构造函数约束

通过new约束可以限制类型参数必须是构造函数,这在工厂模式中特别有用。

interface Constructor<T> {
  new (...args: any[]): T;
}

function createInstance<T>(ctor: Constructor<T>, ...args: any[]): T {
  return new ctor(...args);
}

class Person {
  constructor(public name: string) {}
}

const alice = createInstance(Person, "Alice");

递归类型约束

类型约束可以用于定义递归类型,这在处理树形结构或嵌套数据时非常有用。

interface TreeNode<T> {
  value: T;
  children?: TreeNode<T>[];
}

function walkTree<T>(node: TreeNode<T>, callback: (value: T) => void) {
  callback(node.value);
  node.children?.forEach(child => walkTree(child, callback));
}

条件类型中的约束

在条件类型中使用约束可以创建更复杂的类型逻辑。

type NonNullablePropertyKeys<T> = {
  [K in keyof T]: null extends T[K] ? never : K
}[keyof T];

interface User {
  name: string;
  age: number | null;
  email: string | null;
}

type RequiredUserKeys = NonNullablePropertyKeys<User>; // "name"

约束与映射类型

结合映射类型,约束可以用于转换对象类型的属性。

type ReadonlyProps<T, K extends keyof T> = {
  readonly [P in K]: T[P];
} & {
  [P in Exclude<keyof T, K>]: T[P];
};

interface Config {
  apiUrl: string;
  timeout: number;
  retries: number;
}

type ReadonlyConfig = ReadonlyProps<Config, "apiUrl">;

泛型函数中的高级约束

复杂的约束可以用于创建高度类型安全的实用函数。

function mergeObjects<T extends object, U extends object>(
  obj1: T,
  obj2: U
): T & U {
  return { ...obj1, ...obj2 };
}

const merged = mergeObjects({ a: 1 }, { b: 2 }); // { a: 1, b: 2 }

类型约束与函数重载

类型约束可以与函数重载结合,为不同输入类型提供精确的类型推断。

function formatInput<T extends string | number>(input: T): string;
function formatInput(input: boolean): "yes" | "no";
function formatInput(input: any): string {
  if (typeof input === "boolean") {
    return input ? "yes" : "no";
  }
  return String(input);
}

约束在React组件中的应用

在React组件中,类型约束可以确保props的正确性。

interface BaseProps {
  className?: string;
  style?: React.CSSProperties;
}

function withCustomProps<T extends BaseProps>(
  Component: React.ComponentType<T>
) {
  return (props: T) => <Component {...props} />;
}

类型约束与索引签名

约束可以用于限制索引签名的键类型。

type StringMap<T> = {
  [K in keyof T as T[K] extends string ? K : never]: T[K];
};

interface MixedData {
  id: string;
  count: number;
  name: string;
  timestamp: Date;
}

type StringOnly = StringMap<MixedData>; // { id: string; name: string }

约束与Promise结合

在异步编程中,约束可以确保Promise解析值的类型。

async function fetchData<T extends { id: string }>(url: string): Promise<T[]> {
  const response = await fetch(url);
  return response.json();
}

interface Product {
  id: string;
  name: string;
  price: number;
}

fetchData<Product>("/api/products"); // 返回Promise<Product[]>

类型约束的性能考量

虽然类型约束提供了安全性,但过度复杂的约束可能会影响编译性能。对于大型项目,需要平衡类型安全性和编译速度。

// 简单的约束编译更快
function simpleConstraint<T extends number | string>(value: T) {}

// 复杂的约束可能需要更多编译时间
type ComplexConstraint<T> = T extends infer U 
  ? U extends object 
    ? keyof U extends string 
      ? { [K in keyof U]: U[K] } 
      : never 
    : U 
  : never;

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌