TypeScript
泛型
使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据
泛型就是解决类 接口 方法的复用性,以及对不特定数据类型的支持
泛型可以解决any类型放弃类型检查的问题 使传入的参数类型和返回的参数类型一致
举个例子,比如我们现在有个这样的需求,我们要实现一个这样的函数,函数的参数可以是任何值,返回值就是将参数原样返回,并且参数的类型是 string,函数返回类型就为 string?
很容易写下:
function getValue(arg:string):string {
return arg;
}
现在需求有变,需要返回一个 number 类型的值,你会说,联合类型就完事了:
function getValue(arg:string | number):string | number {
return arg;
}
但是这样又有一个问题,就是如果我们需要返回一个 boolean 类型,string 数组甚至任意类型呢,难道有多少个就写多少个联合类型?
是的,我们直接用 any 就行了!
function getValue(arg:any):any {
return arg;
}
尽管 any 大法好,很多时候 any 也确实能够解决不少问题
但是这样也不符合我们的需求了,传入和返回都是 any 类型,传入和返回并没有统一,我们还能不能有其他解决办法呢?
这个时候就要祭出我们的泛型了
基本使用
泛型是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性
上面的需求,我们如果用泛型来解决的话:
function getValue<T>(arg:T):T {
return arg;
}
泛型的语法是尖括号<>
里面写类型参数,一般用 T
来表示第一个类型变量名称,其实它可以用任何有效名称来代替
泛型就像一个占位符一个变量,在使用的时候我们可以将定义好的类型像参数一样传入,原封不动的输出
使用:
我们有两种方式来使用:
定义要使用的类型,比如:
getValue<string>('树哥'); // 定义 T 为 string 类型
利用 typescript 的类型推断,比如:
getValue('树哥') // 自动推导类型为 string
多个参数
其实并不是只能定义一个类型变量,我们可以引入希望定义的任何数量的类型变量。比如我们引入一个新的类型变量 U
function getValue<T, U>(arg:[T,U]):[T,U] {
return arg;
}
使用:
const str = getValue(['树哥', 18]);
typescript 给我们自动推断出输入、返回的类型
箭头函数
const foo = <T,>(x: T): T => x;
const foo = <T extends {}>(x: T): T => x;
const foo = <T extends Record<string, unknown>>(x: T): T => x;
const foo: <T>(x: T) => T = x => x;
const identity = <T,>(arg: T): T => {
console.log(arg);
return arg;
};
const renderAuthorize = <T>(Authorized: T): ((currentAuthority: CurrentAuthorityType) => T) => (
currentAuthority: CurrentAuthorityType,
): T => {
return
};
泛型约束
在函数内部使用泛型变量的时候,由于事先不知道它是哪种类型,所以不能随意的操作它的属性或方法:
function getLength<T>(arg:T):T {
console.log(arg.length); // 报错,不能调用 length 属性
}
因为泛型 T
不一定包含属性 length
,那么我想 getLength
这个函数只允许传入包含 length
属性的变量,该怎么做呢
这时,我们可以使用extends
关键字来对泛型进行约束
interface Lengthwise {
length: number;
}
function getLength<T extends Lengthwise>(arg:T):T {
console.log(arg.length);
return arg;
}
使用:
const str = getLength('树哥')
const arr = getLength([1,2,3])
const obj = getLength({ length: 5 })
这里可以看出,不管你是 str
,arr
还是obj
,只要具有 length
属性,都可以
泛型接口
在定义接口的时候指定泛型
interface KeyValue<T,U> {
key: T;
value: U;
}
const person1:KeyValue<string,number> = {
key: '树哥',
value: 18
}
const person2:KeyValue<number,string> = {
key: 20,
value: '张麻子'
}
泛型类
class Test<T> {
value: T;
add: (x: T, y: T) => T;
}
let myTest = new Test<number>();
myTest.value = 0;
myTest.add = function (x, y) {
return x + y;
};
泛型类型别名
type Cart<T> = { list: T[] } | T[];
let c1: Cart<string> = { list: ["1"] };
let c2: Cart<number> = [1];
泛型参数的默认类型
我们可以为泛型中的类型参数指定默认类型。
当使用泛型时没有在代码中直接指定类型参数,从实际值参数中也无法推测出时,这个默认类型就会起作用。有点 js 里函数默认参数的意思。
function createArray<T = string>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
泛型工具类型
typeof
关键词除了做类型保护,还可以从实现推出类型,
//先定义变量,再定义类型
let p1 = {
name: "树哥",
age: 18,
gender: "male",
};
type People = typeof p1;
function getName(p: People): string {
return p.name;
}
getName(p1);
keyof
可以用来获取一个对象接口中的所有 key 值
interface Person {
name: string;
age: number;
gender: "male" | "female";
}
type PersonKey = keyof Person; //type PersonKey = 'name'|'age'|'gender';
function getValueByKey(p: Person, key: PersonKey) {
return p[key];
}
let val = getValueByKey({ name: "树哥", age: 18, gender: "male" }, "name");
console.log(val); // 树哥
in
用来遍历枚举类型:
type Keys = "a" | "b" | "c"
type Obj = {
[p in Keys]: any
} // -> { a: any, b: any, c: any }
infer
在条件类型语句中,可以用 infer 声明一个类型变量并且对它进行使用。
type ReturnType<T> = T extends (
...args: any[]
) => infer R ? R : any;
infer R
就是声明一个变量来承载传入函数签名的返回值类型,简单说就是用它取到函数返回值的类型方便之后使用。
extends
有时候我们定义的泛型不想过于灵活或者说想继承某些类等,可以通过 extends
关键字添加泛型约束。
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
现在这个泛型函数被定义了约束,因此它不再是适用于任意类型:
loggingIdentity(3); // Error, number doesn't have a .length property
当我们传入合法的类型的值,即包含 length 属性的值时:
loggingIdentity({length: 10, name: '张麻子'}); // 编译正确
索引访问操作符
使用 []
操作符可以进行索引访问:
interface Person {
name: string;
age: number;
}
type x = Person["name"]; // x is string
内置工具类型
Required
将类型的属性变成必选
interface Person {
name?: string,
age?: number,
hobby?: string[]
}
const user: Required<Person> = {
name: "树哥",
age: 18,
hobby: ["code"]
}
Partial
与 Required 相反,将所有属性转换为可选属性
interface Person {
name: string,
age: number,
}
const shuge:Person = {
name:'树哥'
} // error Property 'age' is missing in type '{ name: string; }' but required in type 'Person'.
从上面知道,如果必传而我们少穿传了的话,就会报错
我们使用 Partial
将其变为可选
type User = Partial<Person>
const shuge: User={
name:'树哥'
} // 编译正确
Exclude
Exclude<T, U>
的作用是将某个类型中属于另一个的类型移除掉,剩余的属性构成新的类型
type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
type T1 = Exclude<"a" | "b" | "c", "a" | "b">; // "c"
type T2 = Exclude<string | number | (() => void), Function>; // string | number
Extract
和Exclude
相反,Extract<T,U>
从 T
中提取出 U
。
type T0 = Extract<"a" | "b" | "c", "a" | "f">; // "a"
type T1 = Extract<string | number | (() => void), Function>; // () =>void
适用于:并集类型
Readonly
把数组或对象的所有属性值转换为只读的,这就意味着这些属性不能被重新赋值。
interface Person {
name: string;
age: number;
gender?: "male" | "female";
}
let p: Readonly<Person> = {
name: "hello",
age: 10,
gender: "male",
};
p.age = 11; // error Cannot assign to 'age' because it is a read-only property.
Record
Record<K extends keyof any, T>
的作用是将 K
中所有的属性的值转化为 T
类型。
type Property = 'key1'|'key2'
type Person = Record<Property, string>;
const p: Person = {
key1: "hello 啊",
key2: "树哥",
};
Pick
从某个类型中挑出一些属性出来
type Person = {
name: string;
age:number;
gender:string
}
type P1 = Pick<Person, "name" | "age">; // { name: string; age: number; }
const user:P1={
name:'树哥',
age:18
}
Omit
与Pick
相反,Omit<T,K>
从T中取出除去K的其他所有属性。
interface Person {
name: string,
age: number,
gender: string
}
type P1 = Omit<Person, "age" | "gender">
const user:P1 = {
name: '树哥'
}
NonNullable
去除类型中的 null
和 undefined
type P1 = NonNullable<string | number | undefined>; // string | number
type P2 = NonNullable<string[] | null | undefined>; // string[]
ReturnType
用来得到一个函数的返回值类型
type Func = (value: string) => string;
const test: ReturnType<Func> = "1";
Parameters
用于获得函数的参数类型所组成的元组类型。
type P1 = Parameters<(a: number, b: string) => void>; // [number, string]
InstanceType
返回构造函数类型T的实例类型
class C {
x = 0;
y = 0;
}
type D = InstanceType<typeof C>; // C
type T1 = InstanceType<new (s?: string) => object>; // object
ThisType
声明this对象类型
// 没有ThisType情况下
const foo = {
bar() {
console.log(this.a); // error,在foo中只有bar一个函数,不存在a
}
}
// 使用ThisType
const foo: { bar: any } & ThisType<{ a: number }> = {
bar() {
console.log(this.bar) // error,因为没有在ThisType中定义
console.log(this.a); // ok
}
}
foo.bar // ok
foo.a // error,在外面的话,就跟ThisType没有关系了