🔥TS进阶之「类型演算」

类型演算

根据已知的信息,计算出新的类型

三个关键字

typeof

Ts中的typeof,书写的位置在类型约束的位置上

表示: 获取某个数据的类型

ts 复制代码
const a: number = 12
// 表示b获取a的类型
let b: typeof a = 22

场景: 通过一个函数的参数传递一个用户类进来创建一个用户对象,函数返回的是一个用户对象

ts 复制代码
class User{
  loginId: string
  loginPwd: string
}

function createUser(className: ???): User{
  return new className();
}

const u = createUser(User)

第一种约束参数: 使用构造函数进行约束

ts 复制代码
function createUser(className: new () => User): User{
  return new className();
}

第二种约束参数: 使用typeof,表示计算的类型是这个类的构造函数

typeof作用于类的时候,得到的类型,是该类的构造函数

ts 复制代码
function createUser(className: typeof User): User{
  return new className();
}

keyof

作用于类、接口、类型别名,用于获取其他类型中的所有成员名组成的联合类型

场景:使用一个函数,传递一个类对象和属性名,打印出该属性的值

ts 复制代码
class User{
  loginId: string
  loginPwd: string
  age: number
}

// 打印对象的属性值
function printUserProperty(obj: User, prop: string){
  console.log(obj[prop])
}

const u: User = {
  loginId: 'sss',
  loginPwd: '123456',
  age: 18
}

printUserProperty(u, "age")

在以往的写法中,我们在函数传递类的属性名约束为字符串,TS会进行报错,因为后续人为的我们进行修改属性名,TS推断不出一个类里面所有的属性名,导致报错,可以使用之前的联合类型对类中的属性进行约束,比如:

ts 复制代码
function printUserProperty(obj: User, prop: "loginId" | "loginPwd" | "age"){
  console.log(obj[prop])
}

但这样也会存在隐患,后续我们进行修改属性名,函数中也需要进行对应的修改,会带来很多的时间成本。这时,可以使用keyof进行获取类中的所有的成员组合成联合类型,如下:

ts 复制代码
function printUserProperty(obj: User, prop: keyof User){
  console.log(obj[prop])
}

in

该关键字往往和keyof联用,用来限制某个索引类型的取值范围。

场景: 将User的所有属性值类型变成字符串,得到一个新类型

ts 复制代码
interface User{
  loginId: string
  loginPwd: string
  age: number
  pid: string
}

// 对索引器的属性名进行约束
type UserString = {
  // [p: string]: string
  // [p in "loginId" | "loginPwd" | "age"]: string
  [p in keyof User]: string
}

const u: UserString = {
  loginId: "123",
  loginPwd: '123123',
  age: '12',
  pid: "123123"
}

将属性的名字和类型全部来自用户类:

ts 复制代码
type UserReadonly = {
  [p in keyof User]: User[p]
}

将属性的名字和类型变为只读属性,变成一个新的类型:

ts 复制代码
type UserReadonly = {
  readonly [p in keyof User]: User[p]
}

结合泛型使用:

ts 复制代码
interface User{
  loginId: string
  loginPwd: string
  age: number
  pid: string
}
// 属性的名字和类型全部来自用户类
type UserReadonly<T> = {
   [p in keyof T]?: T[p]
}

const u: UserReadonly<User> = {
  loginId: "12322",
  loginPwd: '123123',
}

TS中预设的类型演算

Partial<T> 将类型T中的成员变为可选

本质:

ts 复制代码
type Partial<T> = {
    [p in keyof T]: T[p]
}

实例:

ts 复制代码
interface User{
  age: number
  name: string
}
let u: Partial<User>;
u = {
  name: 'huahua'
}

Required<T> 将类型T中的成员变为必填

本质:

ts 复制代码
type Required<T> = {
    [p in keyof T]-?: T[p]
}

实例:

ts 复制代码
interface User{
  age: number
  name: string
}
let u: Required<User>;
u = {
  name: 'huahua',
  age: 12
}

Readonly<T> 将类型T中的成员变为只读

本质:

ts 复制代码
type Readonly<T> = {
    readonly [P in keyof T]: T[P];
};

实例:

ts 复制代码
interface User{
  age: number
  name: string
}

let u: Readonly<User>;
u = {
  name: 'huahua',
  age: 12
}
u.age = 18 // 无法为"age"赋值,因为它是只读属性

Exclude<T, U> 将T中剔除可以赋值给U的类型

本质:

ts 复制代码
type Exclude<T, U> = T extends U ? never : T;

实例:

ts 复制代码
let u: Exclude< "a"| "b"|"c"|"d",  "b"|"c">
// let u: "a" | "d"

type T = "男" | "女" | null | undefined
type NEWT = Exclude<T, null | undefined>
// NEWT = "男" | "女"

Extract<T, U> 提取T中可以赋值给U的类型

本质:

ts 复制代码
type Extract<T, U> = T extends U ? T : never;

实例:

ts 复制代码
type T = "男" | "女" | null | undefined
type NEWT = Extract<T, "男" | "女">
// NEWT = "男" | "女"

NonNullable<T> 从T中剔除 null 和 undefined

本质:

ts 复制代码
type NonNullable<T> = T & {};

实例:

ts 复制代码
type str = string | null | undefined;
type strNotEmpty = NonNullable<str>
// type strNotEmpty = string

ReturnType<T> 获取函数返回值类型

本质:

ts 复制代码
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;

// 关键词infer: 推断出一个类型

实例:

ts 复制代码
type func = () => number
type returnType = ReturnType<func>
// func返回值类型为number 

function sum(a:number, b:number){
  return a + b;
}
let a: ReturnType<typeof sum>;
// sum返回值类型为number 

InstanceType<T> 获取构造函数类型的实例类型

本质:

ts 复制代码
type InstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any;

实例:

ts 复制代码
class User{
  loginId: string
  loginPwd: string
}

let u: InstanceType<typeof User>
相关推荐
OpenTiny社区14 小时前
操作ArkTS页面跳转及路由相关心得
前端·typescript·web·opentiny
柠檬の夏季14 小时前
TypeScript入门
typescript
万物皆对象66614 小时前
切换路由时页面空白问题(vue3)
前端·vue.js·typescript
突然好热14 小时前
TS 调试技巧
前端·javascript·typescript
晓杰'1 天前
从0到1实现Balatro游戏后端(5):得分计算与单局结算流程实现
后端·typescript·node.js·游戏开发·项目实战·nestjs·webscoket
追光者♂1 天前
【测评系列3】CSDN AI数字营销实测体验官:测试 开源项目——Superpowers 游戏引擎从零开发实战指南
人工智能·深度学习·机器学习·typescript·开源·游戏引擎·superpowers
ct9781 天前
TypeScript 中的泛型
前端·javascript·typescript
烛衔溟2 天前
TypeScript 类的类型 —— 作为类型使用
javascript·ubuntu·typescript
阿隅2 天前
TS 深度解析:同为 ? 可选语法,为什么赋值一错一对?类类型与this绑定底层拆解
typescript
Patrick_Wilson2 天前
前端解析接口数据,到底该不该信任后端?聊聊「防御性编程」与「类型契约」的边界
架构·typescript·代码规范