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

泛型约束

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

泛型约束的基本概念

TypeScript中的泛型约束允许我们对泛型类型参数施加限制,确保它们满足特定条件。通过extends关键字,我们可以指定泛型必须符合某种类型结构。这种机制在需要确保泛型参数具备某些属性或方法时特别有用。

interface Lengthwise {
  length: number;
}

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

使用类型参数约束

泛型约束不仅可以基于具体类型,还可以基于其他类型参数。这种技术常用于创建依赖关系的泛型函数,其中一个类型参数必须满足另一个类型参数的要求。

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

let x = { a: 1, b: 2, c: 3 };
getProperty(x, "a"); // 正确
getProperty(x, "m"); // 错误:'m'不在'a'|'b'|'c'中

多重约束的应用

TypeScript支持对泛型参数施加多重约束,通过交叉类型实现。这种方式要求类型参数必须同时满足多个接口或类型的条件。

interface Serializable {
  serialize(): string;
}

interface Loggable {
  log(): void;
}

function process<T extends Serializable & Loggable>(item: T) {
  item.log();
  const serialized = item.serialize();
  // ...
}

构造函数约束

通过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 person = createInstance(Person, "Alice");

约束与条件类型的结合

泛型约束与条件类型结合使用时,可以创建更灵活的类型操作。这种组合允许基于约束条件选择不同的类型分支。

type Flatten<T> = T extends Array<infer U> ? U : T;

function flatten<T>(array: T): Flatten<T> {
  return Array.isArray(array) ? array[0] : array;
}

const str = flatten("hello"); // string
const num = flatten([42]); // number

高级约束模式

在复杂场景中,我们可以使用映射类型和keyof操作符创建高级约束模式。这些技术常用于构建类型安全的实用工具类型。

type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;

interface User {
  id: number;
  name: string;
  age: number;
}

type PartialUser = PartialBy<User, 'id' | 'age'>;
// 等价于 { name: string; id?: number; age?: number }

约束在React组件中的应用

在React开发中,泛型约束常用于创建可复用组件,同时确保props类型的安全性。这种模式在高阶组件和渲染prop组件中尤为常见。

interface WithLoadingProps {
  loading: boolean;
}

function withLoading<P extends object>(
  Component: React.ComponentType<P>
): React.FC<P & WithLoadingProps> {
  return ({ loading, ...props }: P & WithLoadingProps) => 
    loading ? <div>Loading...</div> : <Component {...props as P} />;
}

类型谓词与约束

结合类型谓词,泛型约束可以用于创建用户定义的类型保护函数,这些函数能够在运行时检查类型并缩小类型范围。

interface Bird {
  fly(): void;
  layEggs(): void;
}

interface Fish {
  swim(): void;
  layEggs(): void;
}

function isBird<T extends Bird | Fish>(pet: T): pet is T & Bird {
  return (pet as Bird).fly !== undefined;
}

function move<T extends Bird | Fish>(pet: T) {
  if (isBird(pet)) {
    pet.fly();
  } else {
    pet.swim();
  }
}

约束与默认类型参数

泛型约束可以与默认类型参数结合使用,提供更灵活的API设计。当类型参数未显式指定时,将使用默认类型,但仍需满足约束条件。

interface PaginationOptions<T = any> {
  pageSize: number;
  currentPage: number;
  data: T[];
}

function paginate<T extends { id: string } = { id: string }>(
  options: PaginationOptions<T>
) {
  // 实现分页逻辑
}

递归类型约束

TypeScript支持递归类型约束,允许类型参数约束引用自身。这种技术常用于定义树形结构或递归数据结构。

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

class BinaryNode implements TreeNode<BinaryNode> {
  constructor(
    public value: number,
    public children: [BinaryNode?, BinaryNode?] = []
  ) {}
}

约束与可变元组类型

在TypeScript 4.0引入的可变元组类型中,泛型约束可以用于限制元组元素的类型关系,创建更精确的元组操作函数。

type Shift<T extends any[]> = ((...args: T) => any) extends 
  ((arg: any, ...rest: infer R) => any) ? R : never;

function tail<T extends any[]>(args: [...T]): Shift<T> {
  const [, ...rest] = args;
  return rest as Shift<T>;
}

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

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

上一篇:泛型类

下一篇:泛型参数默认值

前端川

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