前言
大家好,多日未见,这一次我们继续进行TS的学习,今天讲述的东西可能稍微有些难懂(笔者刚开始学习的时候也是感觉困难)
泛型⚡️⚡️
泛型个人认为是TS中非常重要的一部分,因此我觉得如果泛型学习的差不多,那么TS才叫真正意义上的学好。
这部分内容对于学习过java的同学相信并不陌生
什么是泛型
在学习之前我们先来了解泛型的定义:泛型是指附属于函数,类,接口,类型别名之上的类型,当某个函数的参数,返回值和内部使用的类型无法确定的情况下,就可以使用泛型进行约束。
如何定义泛型呢?
其实也很简单我们可以在函数名称之后加上,这个T就是泛型名称,当然你可以随意的定义名称,我们在向函数传递参数的时候也可以这样传递
举一个例子
php
function fn<T>(args: T): T {
return args;
}
fn<string>("hello");
这样T的类型就被限制在字符串类型中,如果我们传递的类型不是string类型的,TS就会报错。
接口中使用泛型
当然我们不仅可以在函数中使用泛型,我们同样可以在接口中定义泛型,我们来看下面的这个例子
typescript
interface idfuc<T, Y> {
id: (value: T) => T;
name: (value: Y) => Y;
}
let obj2: idfuc<number, string> = {
id(value) { return value; },
name(value) { return value; },
};
这里我们在接口中定义使用了泛型,我们进行了类型的约束,这样做的目的就是我们可以提高这个接口的复用性,不事先定义好,而是可以灵活的控制。更加的方便灵活。
类中使用泛型
同样我们在类中也可以使用泛型约束,就比如下面的这个例子:
ini
class Article<T, Y> {
id: T;
author: Y;
constructor(id: T, author: Y) {
this.id = id;
this.author = author;
}
getAuthor<Y>(name: Y) {
console.log(name);
}
}
const article = new Article(12, "de");
这样当我们传入相应的值的时候,TS的类型推断会给我们自动的推断,判断传入的值的类型,这里面我们传递id的值为number类型,传入author的值的类型为string类型,TS就可以自动的给我们推断出来,包括上面举得函数的例子也是如此fn("hello"),如果我们不写,那么TS还是会自动推断为string类型。这就是TS类型推断的好处。
数组泛型
在数组中使用泛型个人认为不多见,下面我们举个例子
ini
let arr: Array<number> = [1, 2, 3, 4] === arr:number[]=[1,2,3];
泛型约束🌾🌾
- 因为在默认情况下T可以代表任何类型,这就导致了无法访问任何属性,所以我们有时候需要进行类型的约束
比如下面这种情况:
matlab
function getlength<T>(str:T){
console.log(str.length);
}
getlength("sdad")
这个时候编译器就会报错,提示T这个类型上面并不存在这个属性,当然我们可以通过判断str的类型来决定是否打印length,但是这样做太过于繁琐,而且使用场景有限,因此泛型约束就应运而生。
那么进行类型约束主要有两种方式:
- 使用extends关键字进行类型的约束
- 进行更加具体的类型定义。
extends关键字
注意这里面的extends和我们之前学习的类中的extends不是一个含义,这里的可以理解为约束于,而之前学习的是继承的含义
这里面我们使用第一种方式来进行
javascript
function getlength<T extends String>(str: T) {
console.log(str.length);
}
getlength("sdsd");
如果我们采取这种方式就相当于是将T类型约束为String类型,这样可以获取String上面的所有属性,但同时我们只能传入string类型的值,而且如果我们不需要这么多属性,我们其实可以这样做:
typescript
interface IsLength {
length: number;
}
function getlength<T extends IsLength>(str: T) {
console.log(str.length);
}
我们可以自己定义接口,然后泛型约束为这个接口以内,这个含义就是传入的参数中的属性必须有length这一项才可以满足需求,这样我们传入数组这种有长度的值的时候也可以满足需求。
第二种方式种提到的更加具体的类型定义是什么意思呢?
比如如果我们想要约束传入的值是一个数组,我们就可以这样做:
matlab
function getlength<T>(str:T[]){
console.log(str.length);
}
这样如果我们传递的不是数组,那么此时就会报错。
keyof关键字
这个部分是对类型约束的补充,也是很重要,那么这个关键字是有什么用呢?
这个关键字可以获取对象中的key值
ini
let obj = {
name: "sda",
sex: "男",
};
type Key = keyof typeof obj;
这里就可以获取Key的类型为联合类型,为"name"|"sex",这里面type将他推断为联合类型。
那他的使用场景是什么呢?比如现在我们需要获取一个对象中的某个值,我们就可以使用这个关键字
typescript
interface Data {
name: string;
age: number;
sex: string;
}
function ob<T extends Data, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
这里面我们首先将T约束为Data类型,其次我们这里的key值约束为传递的T中的属性值,这样我们在返回obj[key]的时候才不会报错,否则TS会认为这是不安全的而报错。而且这样做了之后我们在使用函数的时候,传递第二个值会有提示,还是很爽的。
内置工具类型🍆🍆
Record
Record<K extends keyof any, T>
这个工具类型的作用是什么呢?
首先我们要明白K extends keyof any
的含义,这部分也是用到了类型约束,表明传入的K的类型只能为string|number|symbol 这几个类型中的一个,
用途:Record
工具类型用于创建一个对象类型,其中键为 Key
类型的值,值为 Type
类型的值。
举一个例子:
typescript
interface E {
title: string;
id: number;
}
type article = "home" | "other" | "about";
type ded=record<article,E>
//等价于
{
home:{title:string,id:number},
other:{title:string,id:number},
about:{title:string,id:number}
}
这样演示一下应该就比较清楚了,其实就是创造了一个对象类型,使用这个工具可以更加的方便,不需要我们在手动的定义了
Partial
Partial
用途:Partial
工具类型将泛型类型的所有属性都变为可选属性。
举一个例子:
ini
interface Person {
name: string;
age: number;
}
type PartialPerson = Partial<Person>;
// PartialPerson 的类型是 { name?: string; age?: number; }
这样做就将全部的属性上面加上可选,不需要我们在重复的定义,节省时间,其实内部实现也是比较容易理解的
ini
type Partial<T> = {
[P in keyof T]?: T[P];
};
首先通过keyof获取传递进来的对象类型中的属性名,然后通过in关键字进行遍历,其实和for in很像,[[P in keyof T]]代表的就是每次遍历的属性名然后T[P]就是属性值,在加上?就实现了功能
Pick
Pick<T,K>
用途:Pick
工具类型用于从泛型类型中选取指定属性键集合 Keys
对应的属性。
ini
interface Person {
name: string;
age: number;
address: string;
}
type PersonInfo = Pick<Person, 'name' | 'age'>;
// PersonInfo 的类型是 { name: string; age: number; }
从字面上的意思理解就是挑选,其实就是从对象中挑选一部分来作为新的类型。
Omit
Omit<T,K>
用途:Omit
工具类型用于从泛型类型中排除指定属性键集合 Keys
对应的属性。
ini
interface Person {
name: string;
age: number;
address: string;
}
type PersonWithoutAddress = Omit<Person, 'address'>;
// PersonWithoutAddress 的类型是 { name: string; age: number; }
比如有些时候我们可能需要排除一些属性,可以利用大部分属性,又不想要重新在写,这个时候就可以使用这个内置工具类。
小结
今天我们学习了一下泛型以及相关的知识,相信大家还是会有收获的。