TypeScript 第四章 高级类型

TS官网链接

1. 高级类型

1. 重载签名

  • 重载签名允许你在同一个函数名称下定义多个不同的函数签名,每个签名有不同的参数列表和返回类型。实际的函数实现只需要一个,并且必须匹配其中一个签名。

在 TypeScript 中,函数签名定义了函数的参数列表和返回类型。一个函数签名包括:

  1. 参数列表(参数名和类型)
  2. 返回类型
function sum(arg1: number, arg2: number):number;
function sum(arg1: string, arg2: string):string;

function sum (arg1,arg2){
  return arg1+arg2;
}
// 在 TypeScript 中,当你使用重载签名时,编译器会根据实际传递的参数类型来决定使用哪个重载签名。具体来说,编译器会根据参数的实际类型匹配最合适的重载签名。
console.log(sum(1,2));  // 3
console.log(sum('1','2'))   // '12'
  • 在 TypeScript 中,当你看到 +1 overload 的提示时,这意味着当前函数定义中存在一个或多个重载签名(overload signatures)。具体来说,这个提示表明除了当前显示的函数签名外,还有至少一个额外的重载签名。
    在这里插入图片描述

2. 条件类型 T extends U ? X : Y

  • 语法:T extends U ? X : Y 使用了继承关键字extend和三元运算符
  • 含义:T类型是否继承自U类型,如果是则TX类型,不是则TY类型
  • 简单的示例
type ExtractNumberOrString<T> = T extends number | string ? T : never;

// 测试
type Test1 = ExtractNumberOrString<number>; // 结果为 number
type Test2 = ExtractNumberOrString<string>; // 结果为 string
type Test3 = ExtractNumberOrString<boolean>; // 结果为 never
  • 将上面的重载签名的函数改为泛型的条件类型
function sum<T extends number | string>(
  arg1: T,
  arg2: T
): T extends number ? number : string;

function sum(arg1,arg2){
  return arg1 + arg2
}

console.log(sum(1,2));  // 3

console.log(sum('1','2'))   // '12'

3. 类型推断 infer

  • infer 要和条件类型 T extends U ? X : Y一起使用
  • 自己的理解:infer的作用确实是做类型推断,在实际使用中可以理解为声明了一个类型参数,比如下面的例子中就理解为在返回值的地方声明了一个类型参数R,等实参传过来的时候,R就是实参的类型
  1. 示例:计算函数的返回值类型
type ReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : any;
  • 分析:
    1. <T extends (...args: any[]) => any>:T类型被约束为一个函数类型
    2. T extends (...args: any[]) => any ? X : Y:这种结构是条件类型,如果T类型是函数类型,则返回X类型,不是则返回Y类型;因为T类型已经在传入时就确定为函数类型,所以此处必定为真
    3. T extends (...args: any[]) => infer Rinfer R代替了上面的any,infer R推断此处的类型为R
    4. T extends (...args: any[]) => infer R ? R : any连起来就是,首先T肯定是个函数类型, infer R:返回值类型推断为R,下面做条件判断,T如果是函数类型,则返回推导出的R类型,不是则无所谓因为T肯定是函数类型。
type ReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : any;

function foo(){
  return 1
}

type Foo = ReturnType<typeof foo>

4. 分发条件类型

  • |:虽然|不是泛型中的关键字,分发类型中必须有它
  • 也必须与T extends U ? X : Y相结合
type toArray<T> = T[]

type newType = toArray<number|string> //newType = (string|number)[]

type toArray1<T> = T extends any ? T[] : never
// 联合类型遇到条件类型就变成了分发类型,会将联合类型中每一项单独运算然后再联合
type newType1 = toArray1<number|string> // newType1 = string[] | number[]


// 区别
// [1,2,3] 是 newType类型也是newType1类型
// ['1',2,3] 是newType类型,但不是newType1类型

2. TypeScript内置的类型工具

1. Partial<T>:把类型T内所有属性变为可选

interface Person {
    name: string;
    age: number;
}
type newType = Partial<Person>
// newType相当于
type newType = {
    name?: string;
    age?: number;
}
  • 实现过程
interface Person {
    name: string;
    age: number;
}
type ToPartial<T> = {
  [P in keyof T]?: T[P];
};
type MyPartial = ToPartial<Person>;
  • 分析
    1. 传入一个对象类型T
    2. keyof T:baT类型中key组成一个联合类型,例子中就组成: name | age
    3. [P in keyof T]?关键字in就是遍历,P遍历key的联合类型;这里相当于把T的key都遍历一次,并且在后面加上?可选属性
    4. T[P]:T是整个对象,P是key,那 T[P]就是value

2. Required<T> :把类型T所有属性变为必选

  • 示例
interface Person {
    name?: string;
    age?: number;
}

type RequiredPerson = Required<Person>;
// RequiredPerson 等价于
// {
//   name: string;
//   age: number;
// }
  • 实现过程
type myRequired<T> = {
    [P in keyof T]-?: T[P];
}
  • 解析
    1. 过程类似于Partial
    2. 不同之处-?,去掉属性的可选

3. ReadOnly<T> :把类型T 内所有属性变为只读

interface Person {
    name: string;
    age: number;
}

type ReadonlyPerson = Readonly<Person>;
// ReadonlyPerson 等价于
// {
//   readonly name: string;
//   readonly age: number;
// }
  • 实现过程
type Readonly<T> = {
    readonly [P in keyof T]: T[P];
};
  • 解析
    1. 过程类似于Partial
    2. 不同之处readonly,所有属性加上readonly

4. Record 把两种类型组合成一个新类型

  • Record<K extends keyof any, T>
type RecordType = Record<Keys, Value>

// 其中keys满足联合类型的约束条件
// keys = "name" | "age" | "adress"
  • 示例1:合并两种类型
type keys = "上海" | "北京";

type value = {
  人口: number;
  面积: number;
};

type newType = Record<keys, value>;

const obj: newType = {
  上海: {
    人口: 100,
    面积: 200,
  },
  北京: {
    人口: 200,
    面积: 300,
  },
};
  • 示例2:将属性的类型一起改变
type value = {
  人口: number;
  面积: number;
};
type newType2 = NewRecord<keyof value, string>;

// type newType2 = {
//   人口: string;
//   面积: string;
// }
  • 实现过程
type NewRecord<T extends keyof any, R> = {
  [key in T]: R;
};
  • 解析
    1. keyof anykeyof的作用是取对象的属性,返回的是联合类型, keyof any这里的作用主要是想说明这里是联合类型
    2. T extends keyof any:T被约束为联合类型
    3. [key in T]:in关键字的后面只能是联社类型,这里就是在遍历联合类型T

5. Pick 挑选对象内的部分属性

  • Pick<T, K extends keyof T>
interface Info {
  name: string;
  age: number;
  address: string;
}
// 选择了对象中 name和 age属性
type newInfo = Pick<Info, "name" | "age">

// type newInfo = {
//   name: string;
//   age: number;
// }
  • 自己实现pick
interface Info {
  name: string;
  age: number;
  address: string;
}
type newPick<T, V extends keyof T> = {
  [k in V]: T[k];
};

type newInfo2 = newPick<Info, "name" | "age">;
  • 解析:
    1. V extends keyof T:V被约束为T类型中key的联合类型也就是:‘name’|‘age’|‘address’
    2. [k in V]:遍历所有的V,形成新的key
    3. T[k]:T类型中K属性对应的值

6. Omit 把对象内部分属性删除

  • 官方实现:type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
interface Info {
  name: string;
  age: number;
  gender: string;
}

type newInfo = Omit<Info, "name" | "age">;
// type newInfo = {
//   gender: string;
// }
  • 自己实现omit
type newOmit<T, K extends keyof T> = {
  [P in keyof T as P extends K ? never : P]: T[P];
};
type newInfo1 = newOmit<Info, "name" | "age">;
  • 解析:
    1. P in keyof T:P遍历整个T的key
    2. P in keyof T as P:遍历后命名为P,且只能命名为P,我觉得最后这个P就是前面P in keyof T遍历时的返回值,像是filfter里面的item
    3. P extends K ? never : P:P是否在K的组合类型中,在则弃用,不在则返回
    4. [P in keyof T as P extends K ? never : P]:整个给我的感觉就像是使用filfter来求差集,遍历一个集合,另一个集合中是否也有这个元素,有则排除,没有则留下,最后返回了两个集合的差集

7. Exclude 排除联合类型中的类型

  • type Exclude<T, U>:排除在T类型中的U类型
  • T 必须是联合类型,不然达不到想要的结果
type Info =  string | number | boolean 

type newInfo = Exclude<Info, string>

// type newInfo = number | boolean

  • 自己实现
type Info = string | number | symbol ;

type newExclude<T, U> = T extends U ? never : T;

type newInfo2 = newExclude<Info, string>;

8. Extract 提取两个联合类型中共有的类型,求交集

  • type Extract<T, U> :在源码中并没有对T和U做类型约束,但是如果T和U不是联合类型,这么做就没有必要
type Info =  string | number | boolean 
type Info1 = string | symbol

type newInfo = Extract<Info, Info1>

// type newInfo = string
  • 自己实现
type newExtract<T, U> = T extends U ? T : never 
// 觉得还可以使用
type newExtract1<T,U> = T & U

9. NonNullable 排除联合类型中的null和undefined

  • type NonNullable<T>
type Info = string | number | boolean | null | undefined;

type newInfo = NonNullable<Info>;d

// type newInfo = string | number | boolean
  • 自己实现
type myNonNullable<T> = T extends null | undefined ? never : T;
  • 源码实现方法比较特殊
type NonNullable<T> = T & {};	// 交叉类型:既满足T
  • 解释:

string & {} 的结果是 string。
number & {} 的结果是 number。
boolean & {} 的结果是 boolean。
null & {} 的结果是 never(因为 null 和 {} 没有公共部分)。
undefined & {} 的结果也是 never(同理)。

10. ReturnType 获取返回值的类型

  • ReturnType<T extends (…args: any) => any>
  • 参考上面的类型推断infer章节

11. InstanceType 类的实例对象的类型

  • type InstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any;
  • 解释源码:
    1. new (...args: any) => any 表示构造函数,直接理解为类
    2. abstract new (...args: any) => any:抽象类的构造函数
    3. T extends abstract new (...args: any) => any:传入的类型T既可以是抽象类,也可以是普通的类
    4. R:类的实例类型
  • 示例
class Person {}
class Dog {}
// 工厂函数
function factory<T extends new (...arg: any[]) => any>(
  ctor: T
): InstanceType<T> {
  return new ctor();
}

// 如果没有InstanceType<T>,通过工厂函数实例化的person和dog都是any类型
const person = factory(Person); // person是Person类型
const dog = factory(Dog); // dog是Dog类型

类型中的关键字

  • extends:表示类型约束,用于限制泛型参数的范围。T extends R :T类型必须是R类型的子集
  • keyof:表示键类型,用于获取对象类型的键。返回的是联合类型:"类型1" |"类型2"
  • extends ?:条件类型。
  • infer:用于条件类型中的类型推断。
  • never:表示永远不出现的类型,通常用于错误处理或类型排除。
  • this:表示当前上下文的对象类型。
  • readonly:表示只读属性。
  • in:用于迭代类型或键。in后面必须是联合类型,或经过keyof处理的类型,keyof处理后的类型也是联合类型

泛型的语法

  • T extends keyof any:T 约束为联合类型(大范围可用,有些实际情况不行,参考Exclude中的示例)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值