类型变换 - 11
类型变换
前言:TS 要区分类型和值
关键字 | 作为类型使用 | 作为值使用 |
---|---|---|
class | y | y |
enum(枚举) | y | y |
interface(接口) | y | n |
type | y | n |
function | n | y |
var/let/const | n | y |
接口是不能当做 值 来用的,比如我们不能把一个接口赋值给另一个接口或变量
1 交叉类型
- 交叉类型(Intersection Types)表示将多个类型合并为一个类型(其实就是两个接口类型的属性的并集)
interface Bird {
name: string
fly(): void
}
interface Person {
name: string
eat(): void
}
// 将多个类型合并为一个类型
type BirdMan = Bird & Person;
let p: BirdMan = {
name: "ruhua",
fly() { },
eat() { }
}
2 typeof
- 可以获取一个变量的类型
let p = {
name: "ruhua",
age: 10
}
// 通过 typeof 获取变量 p 的类型
// type 是用来定义类型的。 let 只能定义值。
type Person = typeof p;
let p2: Person = {
name: "ruhua2",
age: 20
}
3 索引访问操作符 []
- 可以通过[]来获取一个类型的子类型
interface Person {
name: string;
age: number;
job: {
name: string
};
interests: {
name: string
level: number
}[];
// interests: Array<{ name: string; level: number}> // 另一种写法
}
let myname: Person["job"]["name"] = "ruhua";
let myLevel: Person["interests"][0]["level"] = 10;
4 keyof 索引类型查询操作符
interface Person {
name: string;
age: number;
gender: "male" | "female";
// [orooName: string]: any; // 不能使用 任意属性
}
// type PersonKeys = "name" | "age" | "gender"
type PersonKeys = keyof Person; // 返回一个接口的 key 的集合
function getValueByKey(val: Person, key: PersonKeys): any {
return val[key]
}
let person: Person = {
name: "ruhua",
age: 10,
gender: "male"
};
getValueByKey(person, "name")
5 映射类型
- 在定义的时候用 in 操作符去批量定义
interface Person {
name: string;
age: number;
gender: "male" | "female";
}
type PartialPerson = { // 通过 映射类型 批量操作 接口 的属性,将其改为可选
[key in keyof Person]?: Person[key] // ? 代表可选的意思
}
let p: Person = { // p 的属性必须完全和 Person 接口的一样
name: "ruhua",
age: 10,
gender: "male"
}
let p2: PartialPerson = {
name: "ruhua",
}
6 内置工具类型
- TS 中内置了一些工具类型来帮助我们更好的使用类型系统
6.1 Partial (部分的)
- Partial 可以将传入的属性由非可选变为可选,具体使用如下:
interface Person {
name: string;
age: number;
gender: "male" | "female";
}
// Partial 的实现原理:
// type Partial<T> = {
// [key in keyof T]?: T[key]
// }
type PartialPerson = Partial<Person>
let p: PartialPerson = {
name: "ruhua",
}
6.2 Required (要求的)
- Required 可以来将传入的属性中的可选项变为必选项,这里用了 -?修饰符来实现
interface Person {
name?: string;
age?: number;
gender: "male" | "female";
}
// Required 的实现原理:(通过 -? 来实现)
// type Required<T> = {
// [key in keyof T]-?: T[key]
// }
// 通过 Required 将接口 Person 里的每个属性都改成必选项
type RequiredPerson = Required<Person>
let p: RequiredPerson = {
name: "ruhua",
age: 0,
gender: "male"
}
6.3 Readonly
- 将属性变成只读属性
interface Person {
name: string;
age: number;
gender: "male" | "female";
}
// Readonly 实现原理
// type Readonly<T> = {
// readonly [key in keyof T]: T[key]
// }
// Readonly
type ReadonlyPerson = Readonly<Person>
let p: ReadonlyPerson = {
name: "ruhua",
age: 0,
gender: "male"
}
p.name = "xiaomei" // 报错:无法分配到 "name" ,因为它是只读属性
6.4 Pick (挑选)
- 将接口的某一项属性挑选出来
interface Person {
name: string;
age: number;
gender: "male" | "female";
}
// Pick 实现原理
// type Pick<T, K extends keyof T> = {
// [key in K]: T[key]
// }
// Readonly
type PickPerson = Pick<Person, "name">
let p: PickPerson = {
name: "ruhua"
}
6.5 映射类型修饰符的控制
- TS 中增加了对映射类型修饰符的控制
- 具体而言,一个
readonly
或?
修饰符在一个映射类型里可以使用前缀+
或-
来表示这个修饰符应该被添加或移除 - TS 中部分内置工具类型就利用了这个特性(Partial、Required、Readonly、...),这里我们可以参考 Partial、Required 的实现
7 条件类型
- 在定义泛型的时候能够添加进逻辑分支,以后泛型更加灵活
7.1 定义条件类型
namespace e {
interface Fish {
name1: string;
}
interface Water {
name2: string;
}
interface Bird {
name3: string;
}
interface Sky {
name4: string;
}
// TS 中的 extends 并不是真的继承,而是判断属性的: `obj1 extends obj2 ? true : false`, obj1中的属性是否包含obj2中的全部属性
type Condition<T> = T extends Fish ? Water : Sky
let condition: Condition<Fish> = {
name2: "waooo water"
}
}
7.2 条件类型的分发
interface Fish {
name1: string;
}
interface Water {
name2: string;
}
interface Bird {
name3: string;
}
interface Sky {
name4: string;
}
type Condition<T> = T extends Fish ? Water : Sky
let c: Condition<Fish | Bird> = {
name2: "water"
}
let c2: Condition<Fish | Bird> = {
name4: "sky"
}
// 等同于如下写法
let c3: Water | Sky = {
name2: "water"
}
7.3 内置条件类型
- TS 内置了一些常用的条件类型
7.3.1 Exclude 排除类型
- 从 T 可分配给的类型中排除 U
type Exclude<T, U> = T extends U ? never : T;
type MyExclude = Exclude<'1' | '2' | '3', '1' | '2'> // type MyExclude = "3"
7.3.2 Extract 抽取类型 取交集
- 从 T 可分配的类型中提取 U
type Extract<T, U> = T extends U ? T : never;
type MyExtract = Extract<'1' | '2' | '3', '1' | '2'> // type MyExtract = "1" | "2"
7.3.3 NonNullable 非空检测
- 从 T 中排除 null 和 undefined
type NonNullable<T> = T extends null | undefined ? never : T
type MyNone = NonNullable<'a' | null | undefined> // type MyNone = "a"
7.3.4 ~ 7.3.7 下面的内置条件类型,将用到 infer 类型推断
- ReturnType 返回值类型(获取函数类型的返回类型,redux 中会用到)
function getUser(a: number, b: number) {
return { name: 'xiaomi', age: 10 }
}
// 实现原理
type ReturnType<T> = T extends (...args: any) => infer R ? R : never
type MyReturn = ReturnType<typeof getUser> // type MyReturn = { name: string; age: number; }
- Parameters 参数类型
function getUser(a: number, b: number) {
return { name: 'xiaomi', age: 10 }
}
// 实现原理
type Parameters<T> = T extends (...args: infer R) => any ? R : any;
// 使用举例
type MyParams = Parameters<typeof getUser>; // type MyParams = [a: number, b: number]
- ConstructorParameters 构造函数参数类型
class Person {
constructor(name: string, age: number) { }
}
// 实现原理
type ConstructorParameters<T> = T extends { new(...args: infer R): any } ? R : never;
// 使用举例
type MyConstructor = ConstructorParameters<typeof Person> // type MyConstructor = [name: string, age: number]
- InstanceType 实例类型(获取构造函数的实例类型)
class Person {
constructor(public name: string, public age: number) {}
}
// 实现原理
type InstanceType<T> = T extends { new(...args: any): infer R } ? R : any;
// 使用举例
type MyInstance = InstanceType<typeof Person> // type MyInstance = Person
let p: MyInstance = new Person("ruhua", 12);
8 infer实践
// 将数组类型转化为联合类型
// 实现原理
type ElementOf<T> = T extends Array<infer E> ? E : never;
// 使用举例
type TupleToUnion = ElementOf<[string, number, boolean]>; // type TupleToUnion = string | number | boolean
// 将两个函数的参数转化为交叉类型
type T1 = { name: string };
type T2 = { age: number };
// 实现原理
type ToIntersection<T> = T extends ([(x: infer U) => any, (x: infer U) => any]) ? U : never;
// 使用举例
type t3 = ToIntersection<[(x:T1)=>any,(x:T2)=>any]> // type t3 = T1 & T2
// 表示要把T1、T2赋予给x,那么x的值就是T1、T2的交集。(参数是逆变的可以传父类)
// TS的类型:TS主要是为了代码的安全性来考虑。所以所有的兼容性问题都要从安全性来考虑!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!