TypeScript 之 extends
extends 在 TS 中比较常用,但是也拥有不同的使用场景
- 表示继承/拓展的含义
- 表示约束的含义
- 表示分配的含义
基础使用
extends 是在 ES6 中引用的关键字,常常 class 用于继承
继承父类
和在 JS 中的使用一致
class Animal {
name = 'animal';
constructor(name) {
this.name = name;
}
sayHello() {
console.log(`Hello, I am a ${this.name}!`);
}
}
class Dog extends Animal {
constructor(props) {
super(props);
}
bark() {
console.log('wang wang');
}
}
const dog = new Dog('dog');
console.log(dog.name); // dog
dog.sayHello(); // Hello, I am a dog!
dog.bark(); // wang wang
继承类型
interface Animal {
sayHello: () => void;
}
interface Dog extends Animal {
bark: () => void;
}
// Dog
// {
// sayHello: () => void;
// bark: () => void
// }
条件判断
如果是 extends 两侧类型相同的,此时的 extends 等同于 ===
type result1 = 'ab' extends 'abc' ? true : false; // false
type result2 = 123 extends 123 ? true : false; // true
如果位于 extends 右侧的类型包含位于 extends 左侧的类型,即狭窄类型 extends 宽泛类型
type result1 = string extends string | number ? true : false; // true
type result2 = boolean extends string | number ? true : false; // false
如果左右侧是对象时,如果对象中的 key 越多说明该对象定义的范围更为狭窄
type result1 = { name: string; age: number } extends { name: string }
? true
: false; // true
type result2 = { name: string } extends { name: string; age: number }
? true
: false; // false
能够发现为对象时,A extends B 表示的是 A 是否可以分配给 B,而不是 A 是否为 B 的子集
泛型类型
在使用泛型的时候,往往希望对某一个泛型做一个限制,例如我们在使用 Redux 的时候都会给 dispatch 传入一个 type
interface Dispatch<T extends { type: string }> {
(action: T): T;
}
当泛型用于条件判断时,例如下述例子:
type Test<T, U> = T extends U ? never : T;
type A = Test<'a' | 'b' | 'c', 'a'>;
那么 A 会是什么类型呢?在第一个条件判断中我们讲到的方式解答该问题即为 T("a" | "b" | "c")。可正确答案却为 "b" | "c"
When conditional types act on a generic type, they become distributive when given a union type.
在官网中,当传入的类型参数为联合类型时,他们会被分配类型,即联合类型会被拆分。
Test<"a" | "b" | "c", "a">
// 等价于
'a' extends 'a' ? never : 'a'
'b' extends 'a' ? never : 'b'
'c' extends 'a' ? never : 'c'
那么答案即为 never | 'b' | 'c',never 可以分配给任何类型,但是没有类型可以分配给 never,因此答案为 'b' | 'c'
只有在满足两个条件的时候才能适用分配律
- 参数是泛型类型
- 代入参数的是联合类型
上述的实现则为 TS 中工具类型 Exclude 的实现
type Exclude<T, U> = T extends U ? never : T;
另一个工具类型 Omit 则是借助 Exclude 实现的
interface Person {
name: string;
age: number;
description: string;
}
type Person1 = Omit<Person, 'description'>;
Omit<T,U> 需要过滤掉 T 中的 U
type Omit<T, K extends keyof T> = {
[P in Exclude<keyof T, K>]: T[P];
};
如何终止这种分配?
type P<T> = [T] extends ['x'] ? string : number;
type A1 = P<'x' | 'y'>; // number
在条件判断类型的定义中,将泛型参数使用[]括起来,即可阻断条件判断类型的分配,此时,传入参数 T 的类型将被当做一个整体,不再分配。
类型推导
infer表示在extends条件语句中待推断的类型变量
在 TS 中,一般会结合 extends 来使用类型推导 infer 语法,实现自动推导类型。
例如工具类型 ReturnType
type ReturnType<T> = T extends (...args: any) => infer R ? R : never;
其中 infer R 表示待推导的类型,表示 T 如果能赋值给 (...args: any) => infer R,则结果为 R 否则为 never
另一个例子,获取参数
type ParamType<T> = T extends (...args: infer P) => P : never