泛型作用非常广泛 ,它有着很多的应用场景
// 泛型可定义多个 在这定义两个泛型T,U
function identity<T, U>(value: T, message: U): T {
console.log(message);
return value;
}
console.log(identity < Number, string > (68, "Semlinker"));
// console.log(identity(68, "Semlinker")); 简写 类型可根据参数类型自动检测 不用传递类型
我们在声明identity
函数名后<>
里声明两个泛型T,U
。在不调用该函数时我们不知道T,U
的类型。只有我们在调用 identity
时,在<>中传递Number,string
时才知道 T的类型时Number, U的类型时string
。使用泛型时你就把它看成参数传递!!
当然在你调用函数时也可以不向泛型传递类型,ts 会自动帮你检测你的参数类型!!
interface Identities<V, M> {
value: V;
message: M;
}
let a: Identities = {}; // 报错 缺少参数
let a: Identities<number, string> = {
value:2
message:"7777"
};
// 接口泛型接口可以作用 很多地方 在这例子比较简单 但理解都是一样的
class A<T> {
// T 接受类型是string
value: T; // 限制value 的类型string
constructor(value: T) {
// 限制了类A接受的参数得是一个string类型
this.value = value;
}
getIdentity(): T {
// 限制返回值是string
return this.value;
}
}
let newA = new A("777"); // ts自动检测 泛型 参数是string 省略不写
// let newA = new A("777");
// arg类型是T ,T的类型是根据identity传递参数时的类型,
function identity<T>(arg: T): T {
console.log(arg.length); // Error 但这个参数可能是一个数字 数字是不存在length属性
return arg;
}
使用接口来进行 泛型约束 来限制使用该函数时传递的参数必须有 length 属性
interface Length {
length: number;
}
// extends 继承 在这可理解为T继承了 Length 接口的类型限制 ,多了一种条件
function identity<T extends Length>(arg: T): T {
console.log(arg.length); // 可以获取length属性
return arg;
}
// 在我们调用该函数时传入的参数就必须要含有length属性 不然会报错提示
interface Person {
name: string;
age: number;
location: string;
}
type K1 = keyof Person; // "name" | "age" | "location"
通过 keyof 操作符,我们就可以获取指定类型的所有键。结合前面介绍的 extends 约束,即限制输入的属性名包含在 keyof 返回的联合类型中。
let tsInfo = {
name: "Typescript",
age: 7,
}
// k 继承了 keyof T 的类型, 而 keyof T 返回联合类型 name | age 存放在 T 里。
// 这就限制了我们第二个参数只能是tsInfo的键名
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
getProperty(tsInfo, 'name'); // OK Typescript
getProperty(tsInfo, 'n_ame'); // Error
interface A<T = string> {
name: T;
}
const strA: A = { name: "Semlinker" }; // 没有指定类型 那么就是默认类型string
const numB: A<number> = { name: 101 }; // 指定了类型number
interface INamed {
name: string;
}
let x: INamed = { name: "77" };
let y = {
name: "funlee",
age: 18,
};
x = y; // ok
y = x; // error x想要赋值给y 就要包含y中所有的值
T extends U ? X : Y
结合类型兼容的知识来理解 ,若 T 能赋值给 U,则类型为 X,否则类型为 Y 有点类似于 JavaScript 中的三元条件运算符。在这里 extends 你理解为 包含 会更明白点。
interface Water {
water: string
}
interface Fish {
water: string
fish: string,
}
interface Sky {
sky: string
}
// 当 T 接受传递的类型 它会看看该类型是否包含着 Fish , true 返回 warter, false返回Sky
type Condition<T> = T extends Fish ? Water : Sky;
let condition1: Condition<Water> = { water: '水' };
// error T 赋值不了 Fish 缺少 fish 字段 ,所以此时的类型是 Sky
let condition2: Condition<Fish> = { water: '水' };
// ok T 能够赋值 Fish
// infer R 表示声明一个变量 R 并将抽取(推断)的类型保存在里面
type Type<T> = T extends Array<infer R> ? R : T;
type TypeA = Type<string[]> // string
type TrypB = Type<never> // nerver
我们来分析一下以上代码:
案例可能比较简单但是就是这个理,大家可以在网上寻找更多例子
interface IPeople {
title: string;
name: string;
}
const people: IPeople = {
title: 'Delete inactive users'
};
// error 缺少了name属性
const people: Partial<IPeople> = {
title: 'Delete inactive users'
};
// ok 通过 Partial 将接口 Ipeople 的属性变为可选类型
interface Person {
name: string;
}
// 传递 x y,此时的 x y 的类型都是Person
type Peoples = Record<"x" | "y", Person>; // 等于下面写法
// Peoples={
// x:Person,
// Y:Person,
// }
const P: Peoples = {
x: {
name: '张三'
},
y: {
name: '李四'
}
}
interface Person {
name: string;
age: number;
}
// 设置值后就不可以再次更改了
const p: Readonly<Person> = {
name: '张三',
age: 22
}
p.name = '李四'; // 无法分配到 "name" ,因为它是只读属性
interface Person {
name?: string;
age?: number;
}
const p: Required<Person> = {
name: '张三',
age: 22
}
type T0 = Exclude<"a" | "b" | "c", "a">;
// 相当于 type T0 = "b" | "c"
type T1 = Exclude<"a" | "b" | "c", "a" | "b">;
// 相当于 type T1 = "c"
type T2 = Exclude<string | number | (() => void), Function>;
// 相当于 type T2 = string | number
type T0 = Extract<'a' | 'b' | 'c', 'a' | 'f'>;
// 相当于 type T0 = 'a';
type T1 = Extract<string | (() => void), Function>;
// 相当于 type T1 = () => void;
type T2 = Extract<string | number, boolean>;
// 因为没有交集,相当于 type T2 = never;
联合类型的话 它会一个个去比较 若能赋值他就会保留 该类型,它和 Exclude 类似。
Exclude 去除相同的
Extract 保留相同的
interface IPerson {
name: string;
age: number;
}
type TP = Omit<IPerson, 'age'>;
const p: TP = {
name: '张三',
age:3,// 报错 没有该类型
}
// string
type T0 = ReturnType<() => string>;
// { a: string; b: number }
type T1 = ReturnType<() => { a: string; b: number}>;
文章借鉴
知乎一文读懂泛型
掘金泛型工具及实现原理