TypeScript 中是否有一种使用 "extends T" 作为类型的方法?

Is there in TypeScript a way to use "extends T" as a type?

问题

我想定义一个拥有宠物的人扩展 Animal,而不仅仅是Animal:

interface Person {
  name: string
  pet: extends Animal  //I'm looking for something that would work here
}

我试过的

假设我们有这些接口:

interface Animal {
  name: string
}

interface Fish extends Animal {
  swim: () => void
}

interface Bird extends Animal {
  fly: () => void
}
1。使用父类型

我知道我能做到:

interface Person {
  name: string
  pet: Animal // Not what I want, because it needs to be only things that extend Animal
}

但这会妨碍推理。因为如果 pet 不是 Fish,它应该是 Bird。但是这样它也可以是一个普通的 Animal.

2。联合类型

要仅使用扩展 Animal 的类型,我可以自己选择联合类型:

interface Person {
  name: string
  pet: Fish | Bird // Not what I want, because I would need to update it with new animals
}

但是如果我想创造更多的动物类型,我就必须每次都更新Person

例如,如果我添加:

interface Mole extends Animal {
  dig: () => void
}

我希望 Person 自动接受 Mole 作为宠物,而不是通过将 pet: Fish | Bird 更改为 pet: Fish | Bird | Mole 来明确添加它。

总结

我正在寻找一种方法来接受任何扩展 Animal,但不接受父 Animal 本身,而不必在每次出现新动物时都对其进行更新。 我如何在 Typescript 中执行此操作?

备注

我不需要Animal作为界面存在,但如果它能保持界面就好了。

这样的东西行得通吗?

interface Person<T extends Animal> {
    name: string;
    pet: T;
}

这就是泛型的用武之地:

interface Person<T extends Animal> {
  name: string
  pet: T
}

let personWithBirdPet: Person<Bird>;
let personWithFishPet: Person<Fish>;
let personWithMolePet: Person<Mole>;

泛型有点像“类型参数”,就像您可以为函数定义“值参数”一样 (function(foo, bar) {}) 我们可以为大多数类型结构定义称为泛型的“类型参数” (包括函数 function<A, B>(foo: A, bar: B) {},以及如上所示的接口)。

使用泛型时,我们可以使用 extends 关键字对泛型应用约束。例如 function<A extends number>(value: A) {} 与写作 function(value: number) {} 几乎相同。使用泛型的版本更强大,但也更冗长。在这个简单的 function 示例中,使用泛型就有些过分了。但是,在您的 interface 情况下,使用泛型正是您所需要的。

切记鸭子打字

因为打字稿是鸭子类型的(如果它走路像鸭子,说话像鸭子,那么它就是鸭子)。您可能会遇到一些需要注意的意外行为。

例如,您可以将拥有普通动物的人作为宠物:

let personWithGenericPet: Person<Animal>;

..或者你可以让一个人养一只不“延伸”动物但看起来像动物的宠物:

let personWithStrangePet: Person<{ name: 'bob' }>;

..说起来,Person长得像动物,所以你可以养一个人和一个人当宠物:

let personWithPersonPet: Person<Person<Animal>>;

这些不是由泛型引起的问题,而是 typescript 工作方式的核心问题。

playground