class Animal {
kind = 'animal'
constructor(kind){
this.kind = kind;
sayHello(){
console.log(`Hello, I am a ${this.kind}!`);
class Dog extends Animal {
constructor(kind){
super(kind)
bark(){
console.log('wang wang')
const dog = new Dog('dog');
dog.name; // => 'dog'
dog.sayHello(); // => Hello, I am a dog!
When the type on
the left
of the
extends
is
assignable to the one on the right
, then you’ll get the type in the first branch (the “true” branch); otherwise you’ll get the type in the latter branch (the “false” branch).
type Human = {
name: string;
type Duck = {
name: string;
type Bool = Duck extends Human ? 'yes' : 'no'; // Bool => 'yes'
在 vscode 里或者 ts playground 里输入这段代码,你会发现 Bool 的类型是
'yes'
。这是因为 Human 和 Duck 的类型完全相同,或者说 Human 类型的一切约束条件,Duck 都具备;换言之,类型为 Human 的值可以分配给类型为 Duck 的值
(分配成功的前提是,Duck里面得的类型得有一样的)
,反之亦然。需要理解的是,这里
A extends B
,是指
类型
A
可以分配给类型
B
,而不是说类型
A
是类型
B
的子集
。稍微扩展下来详细说明这个问题:
type Human = {
name: string;
occupation: string;
type Duck = {
name: string;
type Bool = Duck extends Human ? 'yes' : 'no'; // Bool => 'no'
当我们给
Human
加上一个
occupation
属性,发现此时
Bool
是
'no'
,这是因为 Duck 没有类型为
string
的
occupation
属性,类型
Duck
不满足类型
Human
的类型约束。因此,
A extends B
,是指
类型
A
可以
分配给
类型
B
,而不是说类型
A
是类型
B
的子集
,理解
extends
在类型三元表达式里的用法非常重要。
继续看示例
type A1 = 'x' extends 'x' ? string : number; // string
type A2 = 'x' | 'y' extends 'x' ? string : number; // number
type P<T> = T extends 'x' ? string : number;
type A3 = P<'x' | 'y'> // ?
type A = Exclude<'key1' | 'key2', 'key2'> // 'key1'
Exclude的定义是
type Exclude<T, U> = T extends U ? never : T
这个定义就利用了条件类型中的分配原则,来尝试将实例拆开看看发生了什么:
type A = `Exclude<'key1' | 'key2', 'key2'>`
// 等价于
type A = `Exclude<'key1', 'key2'>` | `Exclude<'key2', 'key2'>`
type A = ('key1' extends 'key2' ? never : 'key1') | ('key'2 extends 'key2' ? never : 'key2')
// never是所有类型的子类型
type A = 'key1' | never = 'key1'