TS — (2)泛型约束,在约束中使用类型参数以及泛型中使用类类型

文章详细介绍了TypeScript中的泛型约束,包括类型约束和属性约束,以及如何在泛型中使用类型参数实现F-boundedpolymorphism。通过示例展示了如何限制泛型只接受特定类型或满足特定条件的类型,以提高代码的安全性和灵活性。此外,还讨论了在泛型中使用类类型的情况,利用new()关键字确保泛型参数是一个可实例化的构造函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.泛型约束

在 TypeScript 中,我们可以对泛型进行约束,使得泛型只能接受某些类型或者符合某些条件的类型。这样可以增强泛型的灵活性和安全性。

泛型约束有两种形式:类型约束和属性约束。

类型约束是指使用 extends 关键字来约束泛型的类型,例如:

interface Lengthwise {
  length: number;
}

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

在上面的示例中,使用 extends Lengthwise 来限制泛型参数 T 必须符合 Lengthwise 接口的定义,也就是必须包含 length 属性。

属性约束是指使用类型参数中的属性来约束其他属性,例如:

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

在上面的示例中,使用 <T, K extends keyof T> 来定义泛型,其中 K 继承自 T 的 key,即 K 只能取 T 中定义过的属性名。

这样,在使用 getProperty 函数时,就可以保证 key 参数只能传入 T 中定义过的属性名。

2.在泛型约束中使用类型参数

在泛型约束中使用类型参数通常称为 F-bounded polymorphism 或递归类型限定。这是指在泛型类型定义中使用类型参数来定义其自身的约束条件。

一个常见的例子是要求泛型类型必须实现某个接口或继承某个类。在这种情况下,我们需要将泛型类型的类型参数约束为该接口或类,同时在定义接口或类的时候也要使用该类型参数。

下面是一个例子:

interface Shape {
  area(): number;
}

class Rectangle implements Shape {
  constructor(private width: number, private height: number) {}

  area() {
    return this.width * this.height;
  }
}

class Circle implements Shape {
  constructor(private radius: number) {}

  area() {
    return Math.PI * this.radius ** 2;
  }
}

function compareShapes<T extends Shape>(shape1: T, shape2: T) {
  return shape1.area() - shape2.area();
}

const rect1 = new Rectangle(10, 20);
const rect2 = new Rectangle(5, 15);
const circle1 = new Circle(10);

console.log(compareShapes(rect1, rect2)); // 50
console.log(compareShapes(rect1, circle1)); // Error: Argument of type 'Circle' is not assignable to parameter of type 'Rectangle'.

在这个例子中,我们定义了一个 Shape 接口,它定义了一个 area() 方法。然后,我们定义了 RectangleCircle 类来实现 Shape 接口,并实现了它们各自的 area() 方法。

接下来,我们定义了一个泛型函数 compareShapes,它接受两个实现了 Shape 接口的对象,并比较它们的面积。我们使用类型参数 T 来约束传递给该函数的对象类型必须是实现了 Shape 接口的类型。因此,我们可以将 RectangleCircle 对象传递给该函数,并对它们进行比较。

注意,我们必须将类型参数 T 约束为 Shape 接口,这样才能保证传递给该函数的对象实现了该接口。同时,我们在 compareShapes 函数的参数中使用了该类型参数,以确保两个传递的对象类型相同。

3.在泛型中使用类类型

在泛型中使用类类型可以通过在泛型约束中使用 new() 关键字来实现。这个约束表示泛型参数必须是一个可以被实例化的构造函数。例如:

interface IName {
  name: string;
}

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

function createInstance<T extends IName>(c: new() => T): T {
  return new c();
}

const person = createInstance(Person);
person.name = "John Doe";
console.log(person); // Output: Person { name: "John Doe" }

在上面的例子中,我们定义了一个接口 IName,它有一个属性 name。我们还定义了一个类 Person,它实现了 IName 接口。然后,我们定义了一个泛型函数 createInstance,它接受一个泛型参数 T,它必须是一个具有 name 属性的类型。这个泛型函数也接受一个构造函数作为参数,该构造函数必须可以实例化一个 T 类型的对象。

在函数内部,我们使用 new c() 实例化一个 T 类型的对象并将其返回。最后,我们使用 createInstance 函数来实例化一个 Person 对象,并为其属性 name 赋值为 "John Doe"。

### 约束的实现方式 TypeScript 中的约束允许开发者对类型参数施加限制,以确保某些操作的安全性和有效性。通过 `extends` 关键字可以定义一个类型参数必须满足的条件。例如,可以要求某个类型具有特定属性或方法[^3]。 #### 示例代码 ```typescript interface LengthInterface { length: number; } function getArray<T extends LengthInterface>(value: T, items: number = 5): T[] { return new Array(items).fill(value); } let arr = getArray<string>('abc'); // 成功 // let arr2 = getArray<number>(6); // 编译错误,因为 number 类型不包含 length 属性 ``` 在这个例子中,`getArray` 函数接受一个参数 `T`,并且该类型必须实现 `LengthInterface` 接口。如果尝试传入不符合条件的类型(如 `number`),编译器将报错。 ### 约束的应用场景 1. **确保对象结构一致性**:当需要处理的对象需要具备某些共同特性时,可以通过约束来保证输入数据的一致性。比如,在上述示例中,所有传递给 `getArray` 的值都必须拥有 `length` 属性[^3]。 2. **提高函数灵活性与安全性**:通过限制可接受类型范围,可以在保持函数通用性的同时避免潜在运行时错误。例如,对于依赖于特定属性或方法的操作来说,约束能够提前捕获不兼容类型使用情况。 3. **构建复用性强的组件**:在设计库或者框架时,利用约束可以让组件更加灵活地适应不同需求,同时维持良好的类型检查体验。这有助于创建既强大又易于维护的抽象层。 4. **结合其他高级类型特性**:约束还可以与其他 TypeScript 高级类型功能(如映射类型、条件类型等)一起工作,从而实现更复杂的逻辑表达和转换任务。 5. **优化开发体验**:借助 IDE 对 TypeScript 的智能感知支持,正确的约束设置可以帮助开发者更快地发现并修复代码中的问题,提升整体开发效率。 ### 相关问题 - 如何在 TypeScript 中定义带有多个约束条件的? - 约束如何影响类型推断过程? - 在类中如何应用约束? - 使用约束时有哪些常见误区需要注意? - 如何结合接口和约束来增强类型检查?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值