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 工作方式的核心问题。
问题
我想定义一个拥有宠物的人扩展 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 工作方式的核心问题。