这是我参与「第四届青训营 」笔记创作活动的第3天
TypeScript高级类型
除了 string、number、boolean 这种基础类型外,还有一些类型声明中的一些高级用法。
- 交叉类型(&)
- 联合类型(|)
- 类型保护
- null和undefined
- 类型别名(type)
- 类型索引(keyOf)
- 类型约束(extends)
1. 联合/交叉类型
应用环境:类型声明繁琐,存在较多重复
interface:IHistoryBoook{
author:string;
type:string;
range:string;
}
interface IStoryBook{
author:string;
type:string;
theme:string;
}
type IBookList=Array<IHistoryBook|IStoryBook>;
联合类型:A|B;联合类型表示一个值可以是几种类型之一
交叉类型:A&B;多种类型叠加到一起成为一种类型,它包含了所需所有类型的特性
type IBookList=Array<{
author:string;
}&({
type:'history';
range:string;
}|{
type:'story';
theme:string;
})>
2. 类型保护与类型守卫
类型守卫:定义一个函数,返回值是一个类型谓词,生效范围为子作用域
interface IA{a:1,a1:2}
interface IB{b:1,b1:2}
function getIsIA(arg:IA|IB):arg is IA{
return !!(arg as IA).a;
}
function log2(arg:IA|IB){
if(getIsIA(arg)){
console.log(arg.a1);
}else{
console.log(arg.b1);
}
}
联合类型+类型保护=自动类型推断
function logBook(book:IBookItem){
if(book.type==='history'){
console.log(book.range);
}else{
console.log(book.theme);
}
}
3. 类型索引(keyOf)
keyof类似于Object.keys,用于获取一个接口中的key的联合类型
// 实现merge函数类型
function merge(sourceObj,targetObj){
// 要求sourceObj必须为targetObj的子集
}
// 定义类型
interface ISourceObj{
x?:string;
y?:string;
}
interface ITargetObj{
x:string;
y:string;
}
// 函数类型
type IMerge=(sourceObj:ISourceObj,targetObj:ITargetObj)=>ITargetObj;
// 类型实现繁琐,若obj类型较为复杂,则source和targe需要大量重复
// 若target增加/减少key,则source需要联动去除
动态设置keyof
interface IMerge{
<T extends Record<string,any>>(sourceObj:IPartial<T>,targetObj:T):T;
}
// Partial 是ts的内置工具泛型,将一个接口的所有属性设置为可选状态
type IPartial<T extends Record<string,any>>={
[P in keyof T]?:T[P];
// ? 通过设定对象可选选项,即自动推导出子集类型
}
type IKeys=keyof {a:string;b:number} ;
// type IKeys="a"|"b"
类型映射(in)in用来做类型映射,遍历已有接口的key或者遍历联合类型
4. 类型约束 extends
extends主要用于对泛型加以约束,不像class使用extends是为了达到
// demo
type BaseType = string | number | boolean;
public testGenerics<T extends BaseType>(arg: T): T {
return arg;
}
this.testGenerics('123'); // 成功
this.testGenerics({}); // 失败
extends的应用场景:extends 经常与 keyof 一起使用,例如我们有一个方法专门用来获取对象的值。
但是这个对象并不确定,我们就可以使用 extends 和 keyof 进行约束
// 根据传入的obj来约束key的值
function getValue<T, K extends keyof T>(obj: T, key: K) {
return obj[key]
}
工具泛型
ts还内置了许多工具泛型
1. Partial
用于将一个接口的所有属性都设置成可选状态,执行步骤就是先通过keyof T,取出类型变量T的所有属性,然后通过in进行循环,最后在每个属性上加上?
type Partial<T> = {
[P in keyof T]?: T[P]
}
// demo
interface ITeacher {
age: number;
gender: sex;
}
const teacherA: Partial<ITeacher> = {age: 28}; // 此时用了Partial,即使不给gender赋值也不会报错
2. Required
Required的作用刚好与Partial相反,是将接口中的每个属性都改成,主要区别就是将Partial中的?替换成-?
type Required<T> = {
[P in keyof T]-?: T[P]
}
3. Exclude
Exclude的作用:如果 T 中的类型在 U 不存在,则返回,否则抛弃。
type Exclude<T, U> = T extends U ? never : T
//demo
interface ITeacher {
age: number;
gender: sex;
}
interface IStudent {
age: number;
gender: sex;
homeWork: string;
}
type ExcludeKeys = Exclude<keyof ITeacher, keyof IStudent>; // "homeWork"
4. Pick
Pick主要用于提取接口中的某几个属性
type Pick<T, K extends keyof T> = {
[P in K]: T[P]
}
// demo
interface IStudent {
name: string
age: number
homework: string
}
type StudentA = Pick<IStudent, "name" | "age"> // 提取这个接口中的name和age属性
const zs: StudentA = {
name: 'zs',
age: 18
}
5. Omit
Omit 的作用刚好和 Pick 相反, 用来排除接口中的某些属性
type Omit<T, K extends keyof any> = Pick<
T, Exclude<keyof T, K>
>
// demo
interface IStudent {
name: string
age: number
homework: string
}
type StudentA = Omit<IStudent, "name"> // 排除掉这个接口中的name属性
const zs: StudentA = {
age: 18,
homework: '背诵课文'
}