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

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

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"。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值