1.泛型是什么?
泛型的关键目的是在成员之间提供有意义的约束,这些成员可以是:类的实例成员、类的方法、函数参数和函数返回值。‘
// 举个例子
// 定义一个 demo 函数,接受一个参数并且返回
function demo(value){
return value;
}
console.log(demo(1)); //1
console.log(demo('kjh')); //'kjh'
// 接着我们对 demo 函数做适当的调整,以支持 TypeScript 的 Number 类型的参数:
function demoWithTS(value:number):number{
return value;
}
console.log(demoWithTS(100)); //100
// console.log(demoWithTS('kjh')); //type error
// 但是当前的函数无法拓展,即当我们想要传入一个字符串,布尔值的时候不能满足。
// 解决方法1: 采用any 否定:丢弃了 ts 类型检查的好处
// 解决方法2: 采用泛型
function demoWithGeneric<T>(value: T): T {
return value;
}
console.log(demoWithGeneric(1)); //1
console.log(demoWithGeneric<string>('kjh')); //'kjh'
2.泛型接口
// 泛型接口
// 我们新的函数增加了一个类型变量 U,我们如果想要返回两种类型的对象该怎么办呢?
// 采用元组的方法
function newDemo01 <T,U>(name:T, age:U):[T,U] {
return [name,age];
}
console.log(newDemo01<string,number>('kjh',22)); // ['kjh',22]
// 我们也可以返回一个新的对象类型
function newDemo02<T,U>(name:T,age:U):{name:T,age:U}{
return {
name,
age
}
}
console.log(newDemo02<string,number>('kangjiahao',100));
//{ name: 'kangjiahao', age: 100 }
// 将这个对象类型提取出来
interface DemoType<T,U>{
name:T,
age:U
}
function newDemo03<T,U>(name:T,age:U):DemoType<T,U>{
return {
name,
age
}
}
console.log(newDemo03<string,number>('kangjiahao',200));
//{ name: 'kangjiahao', age: 100 }
3.泛型类
// 定义一个队列类
class Queue {
public queueItems;
constructor(){
this.queueItems = [];
}
enqueue(value){
this.queueItems.push(value);
return true;
}
dequeue(){
const result = this.queueItems.shift();
return result;
}
size(){
return this.queueItems.length;
}
}
const queue = new Queue();
// 定义泛型类
interface QueueType<T> {
queueItems:T[],
enqueue(value:T):boolean,
dequeue():T,
size():number
}
class QueueWithTs<T> implements QueueType<T>{
public queueItems: T[];
enqueue(value:T){
this.queueItems.push(value);
return true;
}
dequeue(){
const result = this.queueItems.shift();
return result;
}
size(){
return this.queueItems.length;
}
}
4.泛型约束
4.1 type 和 interface 的区别和联系
// 联系1: 都可以描述一个对象
interface User {
name:string;
age:number;
}
type Person = {
name:string;
age:number;
}
// 联系2: 都可以进行拓展
// interface 拓展
interface Sex extends User{
sex:string;
}
interface Home extends Person {
home:string;
}
// type 拓展
type Party = Person & {party:string};
type Car = User & {car:string};
// 区别1: type 可以但是 interface 不可以的
// type 可以声明基本类型别名,联合类型,元组等类型
type Name = string;
type Kinds = number | string;
// type 可以使用 typeof 获取实例的理性之后进行赋值
let div = document.createElement('div');
type B = typeof div;
// 区别2: interface 能够声明合并
interface Animal {
eat():void;
drink():void;
}
interface Animal {
run():void;
}
const dog : Animal = {
// eat, drink, run .....
}
4.2 确保属性存在
// 有时候我们在处理字符串或者数组的时候,我们会假设length属性是可用的。
function demo<T>(arg:T):T {
// console.log(arg.length); // error
return arg;
}
// 编译器不会知道T确实韩式含有 length 属性,我们需要让类型变量 extends 一个含有我们所需要属性的接口
interface ProviderLength {
length:number;
}
function demo2<T extends ProviderLength>(arg:T):T{
console.log(arg.length)
return arg;
}
//这样编译器就不会报错了,当然我们传入没有length的数据类型也会包error
4.3 keyof
// keyof 操作符是在 TypeScript 2.1 版本引入的,该操作符可以用于获取某种类型的所有键,其返回类型是联合类型
interface Person {
name:string;
age:number;
location:string;
}
type K1 = keyof Person; // "name" | "age" | "location"
4.4 检查对象上的键是否存在
// 举个例子,我们想要返回一个对象身上的某个属性
function getProperty<T,K extends keyof T>(obj:T, key:K): T[K]{
return obj[key];
}
getProperty({name:'ts'},'name'); // allow
5 泛型条件类型
条件类型会以一个条件表达式进行类型关系检测,从而在两种类型中选择其一
interface Father {
blood:'O',
}
interface Mather {
blood2:'X'
}
type Son<T> = T extends Father ? Father :Mather;
type boy = Son<{}>; //Mather
type girl = Son<{blood:'O'}>; // Father
结合泛型推断infer使用
// infer 的中文是“推断”的意思,一般是搭配上面的泛型条件语句使用的,所谓推断,就是你不用预先指定在泛型列表中,在运行时会自动判断,不过你得先预定义好整体的结构。
type Foo<T> = T extends {t: infer Test} ? Test: string;
// 首选看 extends 后面的内容,{t: infer Test}可以看成是一个包含t属性的类型定义,这个t属性的 value 类型通过infer进行推断后会赋值给Test类型,
// 如果泛型实际参数符合{t: infer Test}的定义那么返回的就是Test类型,否则默认给缺省的string类型。
// 举个例子
type MyNumber = Foo<number>; // string
type YourNumber = Foo<{t:number}>; // number
6.泛型工具类型
6.1 Partial
Partial 的作用就是将某个类型里的属性全部变为可选项 ?。
// type Partial<T> = {
// [P in keyof T]?: T[p];
// }
举例:
interface StudentType {
name:string;
age:number;
home:string;
}
const xiaoming:Partial<StudentType> = {
name:'string',
//......
}
6.2 Record
Record<K extends keyof any, T> 的作用是将 K 中所有的属性的值转化为 T 类型。
type Record<K extends keyof any, T> = {
[P in K]: T;
};
举例:
interface PageInfo {
info:string;
}
type Page = "home" | "about";
const newPage: Record<Page,PageInfo> = {
home:{info:"home"},
about:{info:"about"}
}
// 实际应用
// 我现在需要一个对象,有ABC三个属性,且属性的值必须都是数字。
type Keys = 'A' | 'B' | 'C';
const obj : Record<Keys,number> = {
A:1,
B:2,
C:3
}
6.3 Pick
Pick<T, K extends keyof T> 的作用是将某个类型中的子属性挑出来,变成包含这个类型部分属性的子类型。
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
举例:
interface Animal {
dog:string;
cat:string;
bird:string;
fish:string;
}
const FiledAnimal : Pick<Animal,"dog" | "cat"> = {
dog:"xiaohei",
cat:"xiaohua"
}
6.4 Exclude<T,U>
此工具是在 T 类型中,去除 T 类型和 U 类型的交集,返回剩余的部分。
type Exclude<T, U> = T extends U ? never : T
举例:
type Test1 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
6.5 ReturnType
ReturnType 的作用是用于获取函数 T 的返回类型。
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
举例:
type Test2 = ReturnType<() => string>; // string
6.6 Required
此工具可以将类型 T 中所有的属性变为必选项。
type Required<T> = {
[P in keyof T]-?: T[P]
}
举例:
interface StudentType {
name:string;
age?:number;
home?:string;
}
const xiaoming:Required<StudentType> = {
name:'string',
age:18,
home:'xian'
}