TypeScript实战系列之ts高手必备技能(最终篇)

目录

  • 介绍
    • keyof 类型运算符
    • typeof 类型运算符
    • 通过索引获得类型
    • Conditional Types 条件类型(重点)
      • infer 关键字 (重中之重)
      • 在条件类型中,如果入参是联合类型,则会被拆解为一个个独立的(原子)类型(成员),然后再进行类型运算。
      • as 和 in 关键字 在 循环里 配合 条件语句使用
    • Mapping Modifiers 映射修改器(重点)
    • Template Literal Types 模板文字类型
  • 总结

介绍

ts 中 有一组关键字 (keyof typeof infer extends in as ) 用于利用已有的类型生成新的类型。
ts中知识比较多, 这里只介绍个人觉得日常开发需要用到的部分。

先来个开胃菜 下面这个类型的作用

type PartialByKeys<T extends { [p: string]: any }, K extends keyof T> = {[P in keyof T as P extends K ? never : P]: T[P];
} & {[P in K]?: T[P];
};interface User {name: string;age: number;email: string;
}
// 示例用法
type PartialUser = PartialByKeys<User, "name" | "age">;const user = {email: "123",
};

作用就是 将类型指定的键的属性变为可选属性
在这里插入图片描述
学完这一篇,你就能随性所欲的来封装属于你的类型工具!

keyof 类型运算符

keyof 关键字用于获取一个对象的所有可枚举属性的键的类型


type People = {name:string;age:number;
}type DD = keyof People //  "name" | "age"type ArrayObj = { [n: number]: unknown };
type A = keyof ArrayObj;// numbertype MapObj = { [k: string]: boolean };
type M = keyof MapObj;  // string | number
// M 是 string | number — 这是因为 JavaScript 对象键始终强制转换为字符串, obj[0] 等价于 obj["0"]// 特殊 用途type B  = keyof any //string | number | symbol

typeof 类型运算符

使用 typeof 运算符来获取一个变量或表达式的类型

// 获取基本类型的类型
let myNumber: number = 10;
type MyNumberType = typeof myNumber; // numberlet myString: string = "Hello, World!";
type MyStringType = typeof myString; // string// 获取对象和数组的类型
let myObject = { key: "value" };
type MyObjectType = typeof myObject; // {key:string}let myArray = [1, 2, 3];
type MyArrayType = typeof myArray; // number[]// 获取函数的类型
function myFunction() {}
type MyFunctionType = typeof myFunction; // ()=> void

通过索引获得类型

可以使用索引访问类型来查找另一种类型的特定属性

type Person = { age: number; name: string; alive: boolean };
type Age = Person["age"];// numbertype I1 = Person["age" | "name"];type I1 = string | numbertype I2 = Person[keyof Person];type I2 = string | number | booleantype AliveOrName = "alive" | "name";
type I3 = Person[AliveOrName];type I3 = string | boolean

使用 number 来获取数组元素的类型


type PersonType = Array<string|number>type Person = PersonType[number] // string|number

Conditional Types 条件类型(重点)

Conditional Types 形式看起来有点像 js 中的条件表达式 ( condition ? trueExpression : falseExpression )
ts 主要使用 extends 关键字


interface IdLabel {id: number /* some fields */;
}
interface NameLabel {name: string /* other fields */;
}// 意思是 接受一个类型 被限制在 number 或者 string 如何 是 number 就 返回 IdLabel 类型 string 就返回 NameLabel
type NameOrId<T extends number | string> = T extends number? IdLabel: NameLabel;type a = NameOrId<number> // IdLabeltype b = NameOrId<string> // NameLabel

infer 关键字 (重中之重)

ts 2.8 版本 新增 关键字 infer. 用于在extends的条件语句中推断待推断的类型。
参考链接:https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html

// 使用infer来推断函数的返回值类型
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : T;type func = () => number;
type variable = ()=>string;
type funcReturnType = MyReturnType<func>; // funcReturnType 类型为 number
type varReturnType = MyReturnType<variable>; // varReturnType 类型为 string

infer 可以理解为占位符

type Unpacked<T> = T extends (infer U)[]? U: T extends (...args: any[]) => infer U? U: T extends Promise<infer U>? U: T;type T0 = Unpacked<string>; // string
type T1 = Unpacked<string[]>; // string
type T2 = Unpacked<() => string>; // string
type T3 = Unpacked<Promise<string>>; // string
type T4 = Unpacked<Promise<string>[]>; // Promise<string>
type T5 = Unpacked<Unpacked<Promise<string>[]>>; // string

协变位置中同一类型变量的多个候选者如何导致推断出联合类型

type ElementOf<T> = T extends Array<infer R> ? R : never;type TTuple = [string, number];
type Union = ElementOf<TTuple>; // string | number

逆变位置中同一类型变量的多个候选会导致推断交集类型

type Bar<T> = T extends { a: (x: infer U) => void; b: (x: infer U) => void }? U: never;
type T1 = Bar<{ a: (x: string) => void; b: (x: string) => void }>; // string/*** {age: number;
} & {address: string;
}*/
type T2 = Bar<{ a: (x: {age:number}) => void; b: (x: {address:string}) => void }>; 

在条件类型中,如果入参是联合类型,则会被拆解为一个个独立的(原子)类型(成员),然后再进行类型运算。

type Diff<T, U> = T extends U ? never : T; // Remove types from T that are assignable to U
type Filter<T, U> = T extends U ? T : never; // Remove types from T that are not assignable to U
type T0 = Diff<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "b" | "d"
type T1 = Filter<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "a" | "c"
type T2 = Diff<string | number | (() => void), Function>; // string | number
type T3 = Filter<string | number | (() => void), Function>; // () => void

条件类型与映射类型结合使用

type FunctionPropertyNames<T> = {[K in keyof T]: T[K] extends Function ? K : never;}[keyof T];type FunctionProperties<T> = Pick<T, FunctionPropertyNames<T>>;type NonFunctionPropertyNames<T> = {[K in keyof T]: T[K] extends Function ? never : K;}[keyof T];type NonFunctionProperties<T> = Pick<T, NonFunctionPropertyNames<T>>;interface Part {id: number;name: string;subparts: Part[];updatePart(newName: string): void;}type T0 = FunctionPropertyNames<Part>; // "updatePart"type T1 = NonFunctionPropertyNames<Part>; // "id" | "name" | "subparts"type T2 = FunctionProperties<Part>; // { updatePart(newName: string): void }type T3 = NonFunctionProperties<Part>; // { id: number, name: string, subparts: Part[] }

as 和 in 关键字 在 循环里 配合 条件语句使用

as 是 4.1 版本后才能使用
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-1.html

作用: 在 as 后面 遍历的变量类型 可以配合 条件语句使用

//那么如何定义一个 ConditionalPick 工具类型,支持根据指定的 Condition 条件来生成新的类型,对应的使用示例如下:
interface Example {a: string;b: string | number;c: () => void;d: {};
}type ConditionalPick<T,K extends T[keyof T]> = {[P in keyof T as T[P] extends K ? P:never ]: T[P] 
}
// 测试用例:
type StringKeysOnly = ConditionalPick<Example, string>;
//=> {a: string}

Mapping Modifiers 映射修改器(重点)

在映射过程中可以应用两个附加修饰符: readonly 和 ? ,它们分别影响可变性和可选性。可以通过添加 - 或 + 前缀来删除或添加这些修饰符。如果不添加前缀,则假定为 + 。

例子 : 去除 readonly 只读

type CreateMutable<Type> = {-readonly [Property in keyof Type]: Type[Property];
};type LockedAccount = {readonly id: string;readonly name: string;
};type UnlockedAccount = CreateMutable<LockedAccount>;

去除 ? 可选

type Concrete<Type> = {[Property in keyof Type]-?: Type[Property];};type MaybeUser = {id: string;name?: string;age?: number;};type User = Concrete<MaybeUser>;

在 TypeScript 4.1 及更高版本中,可以使用映射类型中的 as 子句重新映射映射类型中的键

type Getters<Type> = {[Property in keyof Type as `get${Capitalize<string & Property>}`]: () => Type[Property]
};interface Person {name: string;age: number;location: string;
}type LazyPerson = Getters<Person>;

可以通过条件类型生成 never 来过滤键(重点)

去除 kind 属性

// Remove the 'kind' property
type RemoveKindField<T> = {[P in keyof T as P extends 'kind' ? never : P]: T[P]
};interface Circle {kind: "circle";radius: number;
}type KindlessCircle = RemoveKindField<Circle>;

Template Literal Types 模板文字类型

type World = "world";
type Greeting = `hello ${World}`; //"hello world"

开发中的应用
限制 方法入参的类型规则

type PropEventSource<Type> = {on<Key extends string & keyof Type>(eventName: `${Key}Changed`,callback: (newValue: Type[Key]) => void): void;
};function makeWatchedObject<Type extends { [p: string]: any }>(obj: Type) {return {...obj,on:<PropName extends string & keyof Type>(propName: `${PropName}Changed`) =>(callback: (newValue: Type[PropName]) => void) => {// 在这里可以添加对特定属性的监听逻辑},} as Type & PropEventSource<Type>;
}const person = makeWatchedObject({firstName: "Saoirse",lastName: "Ronan",age: 26,
});person.on("firstNameChanged", (newName) => {console.log(`new name is ${newName.toUpperCase()}`);
});person.on("ageChanged", (newAge) => {});

开胃菜 解析

type PartialByKeys<T extends { [p: string]: any }, K extends keyof T> = {[P in keyof T as P extends K ? never : P]: T[P];
} & {[P in K]?: T[P];
};interface User {name: string;age: number;email: string;
}
// 示例用法
type PartialUser = PartialByKeys<User, "name" | "age">;const user = {email: "123",
};

我们需要实现 类型指定的键的属性变为可选属性

  1. 需要接受2个泛型参数 一个是 键值对类型的 一个需要变成可选属性的键名 ,再给 这两个泛型加上限制条件
// T 只能接受 键值 是string 类型的对象 ,K 只能是 T类型的键名
type PartialByKeys<T extends { [p: string]: any }, K extends keyof T> = {}
  1. 需要对 T 的 键值 进行遍历处理 先找出不需要处理的键(涉及到过滤不符合条件的键名 需要 配合条件语句 设置为never 来 忽略掉)
// 现在已经将 不需要处理的键值对 取出来
type PartialByKeys<T extends { [p: string]: any }, K extends keyof T> = {[P in keyof T as P extends K ? never : P]: T[P];
} 
  1. 再使用交互类型 将剩余需要处理成可选的键 一起返回
type PartialByKeys<T extends { [p: string]: any }, K extends keyof T> = {[P in keyof T as P extends K ? never : P]: T[P];
} & {[P in K]?: T[P];
};

总结

以上基本涵盖了90% ts 的使用。配合类型体操练习题 多练练 ts就算ok了。

下面附上 刷题地址
https://github.com/type-challenges/type-challenges/issues

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/479814.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

0208-1-数据结构

数据结构 第一章&#xff1a;数据结构的 基本概念 定义 在任何问题中&#xff0c;数据元素都不是孤立存在的&#xff0c;而是在它们之间存在着某种关系&#xff0c;这种数据元素相互之间的关系称为结构&#xff08;Structure&#xff09;。数据结构是相互之间存在一种或多种…

变形金刚:第 2 部分:变形金刚的架构

目录 一、说明 二、实现Transformer的过程 第 1 步&#xff1a;代币化&#xff08;Tokenization&#xff09; 第 2 步&#xff1a;对每个单词进行标记嵌入 第 3 步&#xff1a;对每个单词进行位置嵌入 第 4 步&#xff1a;输入嵌入 第 5 步&#xff1a;编码器层 2.5.1 多头自注…

嵌入式Linux应用编程基本概念

Linux应用编程涉及到在Linux环境下开发和运行应用程序的一系列概念。以下是一些涵盖Linux应用编程的基本概念&#xff1a; 1. 系统调用 系统调用是用户空间程序与内核之间进行通信的方式。它提供了一组接口&#xff0c;允许应用程序请求内核执行特权操作。在Linux中&#xff0…

【Python笔记-设计模式】对象池模式

一、说明 用于管理对象的生命周期&#xff0c;重用已经创建的对象&#xff0c;从而减少资源消耗和创建对象的开销 (一) 解决问题 主要解决频繁创建和销毁对象所带来的性能开销问题。如数据库连接、线程管理、网络连接等&#xff0c;对象的创建和销毁成本相对较高&#xff0c…

Android基础Adapter适配器详解

一、概念 Adapter是后端数据和前端显示UI的适配器接口。常见的View如ListView、GridView等需要用到Adapter. BaseAdapter&#xff1a;抽象类&#xff0c;实际开发中继承这个类并且重写相关方法&#xff0c;用得最多的一个Adapter&#xff01; ArrayAdapter&#xff1a;支持泛型…

【嵌入式学习】QT-Day1-Qt基础

笔记 https://lingjun.life/wiki/EmbeddedNote/20QT 毛玻璃登录界面实现&#xff1a;

发电机项目 2/19

MQTT 一.MQTT是什么&#xff1f; MQTT&#xff08;Message Queuing Telemetry Transport&#xff0c;消息队列遥测传输协议&#xff09;&#xff0c;是一种基于发布/订阅&#xff08;publish/subscribe&#xff09;模式的“轻量级”通讯协议&#xff0c;该协议构建于TCP/IP协议…

掌握array_walk()函数:解锁PHP数组操作的神奇力量!

掌握array_walk()函数&#xff1a;解锁PHP数组操作的神奇力量&#xff01; 在 PHP 开发过程中&#xff0c;我们经常需要对数组进行遍历和处理。array_walk() 函数是 PHP 函数库中的一个重要工具&#xff0c;它提供了一种便捷的方式来对数组中的每个元素执行自定义操作。本文将深…

java8的 lambda表达式到stream API总结备忘

文章目录 1. Lambda 表达式为什么使用 Lambda 表达式从匿名类到 Lambda 的转换Lambda 表达式语法语法格式一&#xff1a;无参&#xff0c;无返回值&#xff0c;Lambda 体只需一条语句语法格式二&#xff1a;Lambda 需要一个参数语法格式三&#xff1a;Lambda 只需要一个参数时&…

Windows 使设置更改立即生效——并行发送广播消息

目录 前言 1 遍历窗口句柄列表 2 使用 SendMessageTimeout 发送延时消息 3 并行发送消息实现模拟广播消息 4 修改 UIPI 消息过滤器设置 5 托盘图标刷新的处理 6 完整代码和测试 本文属于原创文章&#xff0c;转载请注明出处&#xff1a; https://blog.csdn.net/qq_5907…

单片机stm32智能鱼缸

随着我国经济的快速发展而给人们带来了富足的生活&#xff0c;也有越来越多的人们开始养鱼&#xff0c;通过养各种鱼类来美化居住环境和缓解压力。但是在鱼类饲养过程中&#xff0c;常常由于鱼类对水质、水位及光照强度有着很高的要求&#xff0c;而人们也由于工作的方面而无法…

【STM32】硬件SPI读写W25Q64芯片

目录 基础知识回顾&#xff1a; SPI外设简介 SPI框图 主模式全双工连续传输 非连续传输 初始化SPI外设 核心代码 - 交换一个字节 硬件接线图 Code 程序配置过程 MySPI.c MySPI.h W25Q64.c W25Q64.h W25Q64_Ins.h main.c 基础知识回顾&#xff1a; 【STM32】SP…