阅读 150

TypeScript 类型操作(typescript菜鸟教程)

前言

在掌握好 TypeScript 基础之后,就需要将基础类型联合起来做出更优美的类型声明。

TypeScript 中有很多关键字,如果不去主动接触的话就会少了很多可操作性。例如:inferkeyoftypeofextends 之类的。还有映射和模板字符串这种概念

泛型

利用泛型来达到类型复用的目的,而不是一昧的用 any 大法。这里达到的效果是传入什么类型,就返回该类型

function identify<T>(arg: T): T {   return arg; } 复制代码

接口的形式定义泛型

interface GenericIdentifyFn {     <T>(arg: T): T } function identify<T>(arg: T): T {     return arg } let myIdentify: GenericIdentifyFn = identify 复制代码

在类型的继承中使用

class BeeKeeper {   hasMask!: boolean; } class ZooKeeper {   nametag!: string; } class Animal {   numLegs!: number; } class Bee extends Animal {   keeper: BeeKeeper = new BeeKeeper; } class Lion extends Animal {   keeper: ZooKeeper = new ZooKeeper; } function createInstance<A extends Animal>(c: new () => A): A {   return new c(); } createInstance(Lion).keeper.nametag;  // typechecks! createInstance(Bee).keeper.hasMask;   // typechecks! 复制代码

keyof

运算符:构造一个类型,类型由参数的所有键值

interface Person {   name: string;   age: number; } type T1 = keyof Person; // "name" | "age" type T2 = keyof { [x: string]: 1 }; // string | number type T3 = number; type T4 = keyof T3; // type T4 = "toString" | "toFixed" | "toExponential" | "toPrecision" | "valueOf" | "toLocaleString" function prop<T, K extends keyof T>(obj: T, key: K): T[K] {   return obj[key] } 复制代码

提取键值,组成类型。提取一定要是的类型

typeof

运算符:获取变量的类型

type Predicate = (x: unknown) => boolean; function f() {   return true; } type K = ReturnType<Predicate>; // type K = boolean type T = ReturnType<typeof f>; // type T = boolean 复制代码

JavaScript 中也有 typeofTypeScript 环境下,编辑器能够区分应该使用哪个

keyoftypeof 这两个都是提取类型的,一个是提取类型,一个是提取变量。可以一起记忆

索引

对于对象,通过属性名索引

type Person = { age: number; name: string; alive: boolean }; type Age = Person["age"]; // type Age = number 复制代码

对于数组,通过数值索引

const MyArray = [   { name: "Alice", age1: 15 },   { name: "Bob", age: 23 },   { name: "Eve", age: 38 }, ]; type Person = NonNullable<typeof MyArray[number]["age1"]>; // type Person = number 复制代码

索引的方式可以抽离我们想要的类型

条件类型

条件类型有助于描述输入和输出类型之间的关系,操作起来更加灵活

interface Animal {   live(): void; } interface Dog extends Animal {   woof(): void; } type Example1 = Dog extends Animal ? number : string; type Example2 = RegExp extends Animal ? number : string; 复制代码

上面这个是 extends 的条件用法,类似三元表达式语法

重载函数的例子

interface IdLabel {   id: number; } interface NameLabel {   name: string; }   function createLabel(id: number): IdLabel; function createLabel(name: string): NameLabel; function createLabel(nameOrId: string | number): IdLabel | NameLabel; function createLabel(nameOrId: string | number): IdLabel | NameLabel {   throw "unimplemented"; } 复制代码

这里进行了三个重载。我们可以改造成下述的形式,将输出定义成一个条件类型,可以避免写复杂的重载函数

type nameOrId<T extends number | string> = T extends number   ? IdLabel   : NameLabel; function createLabel<T extends number | number>(idOrName: T): NameOrId {     thorw "unimplemented"; } 复制代码

条件类型约束

更好的限制泛型

type MessageOf<T> = T extends { message: unknown } ? T["message"] : never; 复制代码

type Flatten<T> = T extends any[] ? T[number] : T; 复制代码

在条件类型中进行推断 infer

注意:只有在条件类型 extends 子句中才允许 infer 声明

type Flatten<Type> = Type extends Array<infer Item> ? Item : Type; 复制代码

type GetReturnType<Type> = Type extends (...args: never[]) => infer Return ? Return : never type Num = GetReturnType<() => number>; type Bools = GetReturnType<(a: boolean, b: boolean) => boolean[]>; 复制代码

主要体现了 infer 的用法,作为辅助类型的方式出现

分配条件类型

当我们向条件类型传入联合类型,如果没有使用 [] 会出现映射联合类型的每个成员类型。

type ToArray<T> = T extends any ? T[] : never; type ToArray2<T> = [T] extends [any] ? T[] : never; type StrArrOrNumArr = ToArray<string | number>; // string[] | number[] type StrArrOrNumArr2 = ToArray2<string | number>; // (string | number)[] 复制代码

映射类型

当我们想要将另一个类型的属性都拿过来的时候,可以用到映射的概念。keyof 拿出所有类型,in 表示在这些类型中

type Horse = {}; type OnlyBoolsAndHorses = {   [key: string]: boolean | Horse; }; const conforms: OnlyBoolsAndHorses = {   del: true,   rodney: false, }; type OptionsFlags<Type> = {   [Property in keyof Type]: boolean; }; 复制代码

映射修饰符

readonly? 可以在映射的过程中使用。用 - + 表示减少或添加,默认 +

type CreateMutable<Type> = {   -readonly [Property in keyof Type]?: Type[Property]; }; type LockedAccount = {   readonly id: string;   readonly name: string; }; type UnlockedAccount = CreateMutable<LockedAccount>; // type UnlockedAccount = { //   id?: string | undefined; //   name?: string | undefined; // } 复制代码

通过 as 来重新设置键名

as 可以将键名设置成我们想要的形式,或者通过 never 过滤

联合模式

type EventConfig<Events extends { kind: string }> = {   [E in Events as E["kind"]]: (event: E) => void; }; type SquareEvent = { kind: "square"; x: number; y: number }; type CircleEvent = { kind: "circle"; radius: number }; type Config = EventConfig<SquareEvent | CircleEvent>; // type Config = { //   square: (event: SquareEvent) => void; //   circle: (event: CircleEvent) => void; // } 复制代码

这个很好用,也很好玩

模板字符串

类似 js 中的模板字符串语法。遇到联合类型会算出每种组合方式

type World = "world"; type Greeting = `hello ${World}`; // type Greeting = "hello world" type EmailLocaleIDs = "welcome_email" | "email_heading"; type FooterLocaleIDs = "footer_title" | "footer_sendoff"; type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id`; // type AllLocaleIDs = "welcome_email_id" | "email_heading_id" | "footer_title_id" | "footer_sendoff_id" type Lang = "en" | "ja" | "pt"; type LocaleMessageIDs = `${Lang}_${AllLocaleIDs}`; // type LocaleMessageIDs = "en_welcome_email_id" | "en_email_heading_id" | "en_footer_title_id" | "en_footer_sendoff_id" | "ja_welcome_email_id" | "ja_email_heading_id" | "ja_footer_title_id" | "ja_footer_sendoff_id" | "pt_welcome_email_id" | "pt_email_heading_id" | "pt_footer_title_id" | "pt_footer_sendoff_id" 复制代码

类型中的字符串联合

意思是将类型的属性拿出来进行字符串拼接,可以得到一个联合类型。

type PropEventSource<Type> = {   on(     eventName: `${string & keyof Type}Changed`,     callback: (newValue: any) => void   ): void; }; declare function makeWatchedObject<Type>(   obj: Type ): Type & PropEventSource<Type>; const person = makeWatchedObject({   firstName: "Saoirse",   lastName: "Ronan",   age: 26, }); person.on("firstNameChanged", (newValue: any) => {   console.log(`firstName was changed to ${newValue}!`); }); 复制代码

额,好像没啥不好理解的

使用模板文字进行推理

用模板字符串中的变量来做泛型的类型推断

内在字符串操作类型

intrinsic 是一个关键字,是一种编译

intrinsic 关键字只能用于声明编译器提供的内部类型。

Uppercase<StringType>

将字符串中的每个字符转换为大写版本

type Greeting = "Hello, world"; type ShoutyGreeting = Uppercase<Greeting>; // type ShoutyGreeting = "HELLO, WORLD" type ASCIICacheKey<Str extends string> = `ID-${Uppercase<Str>}`; type MainID = ASCIICacheKey<"my_app">; // type MainID = "ID-MY_APP" 复制代码

源码

type Uppercase<S extends string> = intrinsic; 复制代码

Lowercase<StringType>

将字符串中的每个字符转换为小写版本

type Greeting = "Hello, world"; type QuietGreeting = Lowercase<Greeting>; // type QuietGreeting = "hello, world" type ASCIICacheKey<Str extends string> = `id-${Lowercase<Str>}`; type MainID = ASCIICacheKey<"MY_APP">; // type MainID = "id-my_app" 复制代码

源码

type Lowercase<S extends string> = intrinsic; 复制代码

Capitalize<StringType>

将字符串中的第一个字符转换为等效的大写字符

type LowercaseGreeting = "hello, world"; type Greeting = Capitalize<LowercaseGreeting>; // type Greeting = "Hello, world" 复制代码

源码

type Capitalize<S extends string> = intrinsic; 复制代码

Uncapitalize<StringType>

将字符串中的第一个字符转换为等效的小写字符

type UppercaseGreeting = "HELLO WORLD"; type UncomfortableGreeting = Uncapitalize<UppercaseGreeting>; // type UncomfortableGreeting = "hELLO WORLD" 复制代码

源码

type Uncapitalize<S extends string> = intrinsic; 复制代码

小结

类型操作让我们更加灵活的使用类型,优化很多繁琐的声明。对于每个知识点都需要熟记,毕竟存在即合理,肯定有它的妙用所在

 伪原创工具 SEO网站优化  https://www.237it.com/ 

作者:lankongclub
链接:https://juejin.cn/post/7035995700630388743


文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐