1. 【TS】1470- 總結(jié) TS 類(lèi)型體操的9種類(lèi)型運(yùn)算、4種類(lèi)型套路

        共 17646字,需瀏覽 36分鐘

         ·

        2022-10-15 02:33

        今天給大家分享的主題是一起來(lái)做類(lèi)型體操。

        主要分為 4 個(gè)部分進(jìn)行介紹:

        1. 類(lèi)型體操的背景,通過(guò)背景了解為什么要在項(xiàng)目中加入類(lèi)型體操;
        2. 了解類(lèi)型體操的主要類(lèi)型、運(yùn)算邏輯、和類(lèi)型套路;
        3. 類(lèi)型體操實(shí)踐,解析 TypeScript 內(nèi)置高級(jí)類(lèi)型,手寫(xiě) ParseQueryString 復(fù)雜類(lèi)型;
        4. 小結(jié),綜上分享,沉淀結(jié)論。

        一、背景

        在背景章節(jié)介紹的是什么是類(lèi)型,什么是類(lèi)型安全,怎么實(shí)現(xiàn)類(lèi)型安全,什么是類(lèi)型體操?

        以了解類(lèi)型體操的意義。

        1. 什么是類(lèi)型?

        了解什么是類(lèi)型之前,先來(lái)介紹兩個(gè)概念:

        • 不同類(lèi)型變量占據(jù)的內(nèi)存大小不同

        boolean 類(lèi)型的變量會(huì)分配 4 個(gè)字節(jié)的內(nèi)存,而 number 類(lèi)型的變量則會(huì)分配 8 個(gè)字節(jié)的內(nèi)存,給變量聲明了不同的類(lèi)型就代表了會(huì)占據(jù)不同的內(nèi)存空間。

        • 不同類(lèi)型變量可做的操作不同

        number 類(lèi)型可以做加減乘除等運(yùn)算,boolean 就不可以,復(fù)合類(lèi)型中不同類(lèi)型的對(duì)象可用的方法不同,比如 Date 和 RegExp,變量的類(lèi)型不同代表可以對(duì)該變量做的操作就不同。

        綜上,可以得到一個(gè)簡(jiǎn)單的結(jié)論就是,類(lèi)型就是編程語(yǔ)言提供對(duì)不同內(nèi)容的抽象定義。

        2. 什么是類(lèi)型安全?

        了解了類(lèi)型的概念后,那么,什么是類(lèi)型安全呢?

        一個(gè)簡(jiǎn)單的定義就是,類(lèi)型安全就是只做該類(lèi)型允許的操作。比如對(duì)于 boolean 類(lèi)型,不允許加減乘除運(yùn)算,只允許賦值 true、false。

        當(dāng)我們能做到類(lèi)型安全時(shí),可以大量的減少代碼中潛在的問(wèn)題,大量提高代碼質(zhì)量。

        3. 怎么實(shí)現(xiàn)類(lèi)型安全?

        那么,怎么做到類(lèi)型安全?

        這里介紹兩種類(lèi)型檢查機(jī)制,分別是動(dòng)態(tài)類(lèi)型檢查和靜態(tài)類(lèi)型檢查。

        3.1 動(dòng)態(tài)類(lèi)型檢查

        Javascript 就是典型的動(dòng)態(tài)類(lèi)型檢查,它在編譯時(shí),沒(méi)有類(lèi)型信息,到運(yùn)行時(shí)才檢查,導(dǎo)致很多隱藏 bug。

        3.2 靜態(tài)類(lèi)型檢查

        TypeScript 作為 Javascript 的超集,采用的是靜態(tài)類(lèi)型檢查,在編譯時(shí)就有類(lèi)型信息,檢查類(lèi)型問(wèn)題,減少運(yùn)行時(shí)的潛在問(wèn)題。

        4. 什么是類(lèi)型體操

        上面介紹了類(lèi)型的一些定義,都是大家熟悉的一些關(guān)于類(lèi)型的背景介紹,這一章節(jié)回歸到本次分享的主題概念,類(lèi)型體操。

        了解類(lèi)型體操前,先介紹 3 種類(lèi)型系統(tǒng)。

        4.1 簡(jiǎn)單類(lèi)型系統(tǒng)

        簡(jiǎn)單類(lèi)型系統(tǒng),它只基于聲明的類(lèi)型做檢查,比如一個(gè)加法函數(shù),可以加整數(shù)也可以加小數(shù),但在簡(jiǎn)單類(lèi)型系統(tǒng)中,需要聲明 2 個(gè)函數(shù)來(lái)做這件事情。

        int add(int a, int b) {
            return a + b
        }

        double add(double a, double b) {
            return a + b
        }

        4.2 泛型類(lèi)型系統(tǒng)

        泛型類(lèi)型系統(tǒng),它支持類(lèi)型參數(shù),通過(guò)給參數(shù)傳參,可以動(dòng)態(tài)定義類(lèi)型,讓類(lèi)型更加靈活。

        T add<T>(T a, T b) {
            return a + b
        }

        add(12)
        add(1.12.2)

        但是在一些需要類(lèi)型參數(shù)邏輯運(yùn)算的場(chǎng)景就不適用了,比如一個(gè)返回對(duì)象某個(gè)屬性值的函數(shù)類(lèi)型。

        function getPropValue<T>(obj: T, key{
          return obj[key]
        }

        4.3 類(lèi)型編程系統(tǒng)

        類(lèi)型編程系統(tǒng),它不僅支持類(lèi)型參數(shù),還能給類(lèi)型參數(shù)做各種邏輯運(yùn)算,比如上面提到的返回對(duì)象某個(gè)屬性值的函數(shù)類(lèi)型,可以通過(guò) keyof、T[K] 來(lái)邏輯運(yùn)算得到函數(shù)類(lèi)型。

        function getPropValue<
          T extends object
          Key extends keyof T
        >(obj: T, key: Key): T[Key
        {
          return obj[key]
        }

        總結(jié)上述,類(lèi)型體操就是類(lèi)型編程,對(duì)類(lèi)型參數(shù)做各種邏輯運(yùn)算,以產(chǎn)生新的類(lèi)型。

        之所以稱(chēng)之為體操,是因?yàn)樗膹?fù)雜度,右側(cè)是一個(gè)解析參數(shù)的函數(shù)類(lèi)型,里面用到了很多復(fù)雜的邏輯運(yùn)算,等先介紹了類(lèi)型編程的運(yùn)算方法后,再來(lái)解析這個(gè)類(lèi)型的實(shí)現(xiàn)。

        二、了解類(lèi)型體操

        熟悉完類(lèi)型體操的概念后,再來(lái)繼續(xù)了解類(lèi)型體操有哪些類(lèi)型,支持哪些運(yùn)算邏輯,有哪些運(yùn)算套路。

        1. 有哪些類(lèi)型

        類(lèi)型體操的主要類(lèi)型列舉在圖中。TypeScript 復(fù)用了 JS 的基礎(chǔ)類(lèi)型和復(fù)合類(lèi)型,并新增元組(Tuple)、接口(Interface)、枚舉(Enum)等類(lèi)型,這些類(lèi)型在日常開(kāi)發(fā)過(guò)程中類(lèi)型聲明應(yīng)該都很常用,不做贅述。

        // 元組(Tuple)就是元素個(gè)數(shù)和類(lèi)型固定的數(shù)組類(lèi)型
        type Tuple = [number, string];

        // 接口(Interface)可以描述函數(shù)、對(duì)象、構(gòu)造器的結(jié)構(gòu):
        interface IPerson {
            name: string;
            age: number;
        }

        class Person implements IPerson {
            name: string;
            age: number;
        }

        const obj: IPerson = {
            name'aa',
            age18
        }

        // 枚舉(Enum)是一系列值的復(fù)合:
        enum Transpiler {
            Babel = 'babel',
            Postcss = 'postcss',
            Terser = 'terser',
            Prettier = 'prettier',
            TypeScriptCompiler = 'tsc'
        }

        const transpiler = Transpiler.TypeScriptCompiler;

        2. 運(yùn)算邏輯

        重點(diǎn)介紹的是類(lèi)型編程支持的運(yùn)算邏輯。

        TypeScript 支持條件、推導(dǎo)、聯(lián)合、交叉、對(duì)聯(lián)合類(lèi)型做映射等 9 種運(yùn)算邏輯。

        • 條件:T extends U ? X : Y

        條件判斷和 js 邏輯相同,都是如果滿足條件就返回 a 否則返回 b。

        // 條件:extends ? :
        // 如果 T 是 2 的子類(lèi)型,那么類(lèi)型是 true,否則類(lèi)型是 false。
        type isTwo<T> = T extends 2 ? true : false;
        // false
        type res = isTwo<1>;
        • 約束:extends

        通過(guò)約束語(yǔ)法 extends 限制類(lèi)型。

        // 通過(guò) T extends Length 約束了 T 的類(lèi)型,必須是包含 length 屬性,且 length 的類(lèi)型必須是 number。
        interface Length {
            length: number
        }

        function fn1<T extends Length>(arg: T): number{
            return arg.length
        }
        • 推導(dǎo):infer

        推導(dǎo)則是類(lèi)似 js 的正則匹配,都滿足公式條件時(shí),可以提取公式中的變量,直接返回或者再次加工都可以。

        // 推導(dǎo):infer
        // 提取元組類(lèi)型的第一個(gè)元素:
        // extends 約束類(lèi)型參數(shù)只能是數(shù)組類(lèi)型,因?yàn)椴恢罃?shù)組元素的具體類(lèi)型,所以用 unknown。
        // extends 判斷類(lèi)型參數(shù) T 是不是 [infer F, ...infer R] 的子類(lèi)型,如果是就返回 F 變量,如果不是就不返回
        type First<T extends unknown[]> = T extends [infer F, ...infer R] ? F : never;
        // 1
        type res2 = First<[123]>;
        • 聯(lián)合:|

        聯(lián)合代表可以是幾個(gè)類(lèi)型之一。

        type Union = 1 | 2 | 3
        • 交叉:&

        交叉代表對(duì)類(lèi)型做合并。

        type ObjType = { a: number } & { c: boolean }
        • 索引查詢:keyof T

        keyof 用于獲取某種類(lèi)型的所有鍵,其返回值是聯(lián)合類(lèi)型。

        // const a: 'name' | 'age' = 'name'
        const a: keyof {
            name: string,
            age: number
        } = 'name'
        • 索引訪問(wèn):T[K]

        T[K] 用于訪問(wèn)索引,得到索引對(duì)應(yīng)的值的聯(lián)合類(lèi)型。

        interface I3 {
          name: string,
          age: number
        }

        type T6 = I3[keyof I3] // string | number

        • 索引遍歷: in

        in 用于遍歷聯(lián)合類(lèi)型。

        const obj = {
            name'tj',
            age11
        }

        type T5 = {
            [P in keyof typeof obj]: any
        }

        /*
        {
          name: any,
          age: any
        }
        */

        • 索引重映射: as

        as 用于修改映射類(lèi)型的 key。

        // 通過(guò)索引查詢 keyof,索引訪問(wèn) t[k],索引遍歷 in,索引重映射 as,返回全新的 key、value 構(gòu)成的新的映射類(lèi)型
        type MapType<T> = {
            [
            Key in keyof T
            as `${Key & string}${Key & string}${Key & string}`
            ]: [T[Key], T[Key], T[Key]]
        }
        // {
        //     aaa: [1, 1, 1];
        //     bbb: [2, 2, 2];
        // }
        type res3 = MapType<{ a1b2 }>

        3. 運(yùn)算套路

        根據(jù)上面介紹的 9 種運(yùn)算邏輯,我總結(jié)了 4 個(gè)類(lèi)型套路。

        • 模式匹配做提??;
        • 重新構(gòu)造做變換;
        • 遞歸復(fù)用做循環(huán);
        • 數(shù)組長(zhǎng)度做計(jì)數(shù)。

        3.1 模式匹配做提取

        第一個(gè)類(lèi)型套路是模式匹配做提取。

        模式匹配做提取的意思是通過(guò)類(lèi)型 extends 一個(gè)模式類(lèi)型,把需要提取的部分放到通過(guò) infer 聲明的局部變量里。

        舉個(gè)例子,用模式匹配提取函數(shù)參數(shù)類(lèi)型。

        type GetParameters<Func extends Function> =
            Func extends (...args: infer Args) => unknown ? Args : never;

        type ParametersResult = GetParameters<(name: string, age: number) => string>

        首先用 extends 限制類(lèi)型參數(shù)必須是 Function 類(lèi)型。

        然后用 extends 為 參數(shù)類(lèi)型匹配公式,當(dāng)滿足公式時(shí),提取公式中的變量 Args。

        實(shí)現(xiàn)函數(shù)參數(shù)類(lèi)型的提取。

        3.2 重新構(gòu)造做變換

        第二個(gè)類(lèi)型套路是重新構(gòu)造做變換。

        重新構(gòu)造做變換的意思是想要變化就需要重新構(gòu)造新的類(lèi)型,并且可以在構(gòu)造新類(lèi)型的過(guò)程中對(duì)原類(lèi)型做一些過(guò)濾和變換。

        比如實(shí)現(xiàn)一個(gè)字符串類(lèi)型的重新構(gòu)造。

        type CapitalizeStr<Str extends string> =
            Str extends `${infer First}${infer Rest}`
            ? `${Uppercase<First>}${Rest}` : Str;

        type CapitalizeResult = CapitalizeStr<'tang'>

        首先限制參數(shù)類(lèi)型必須是字符串類(lèi)型。

        然后用 extends 為參數(shù)類(lèi)型匹配公式,提取公式中的變量 First Rest,并通過(guò) Uppercase 封裝。

        實(shí)現(xiàn)了首字母大寫(xiě)的字符串字面量類(lèi)型。

        3.3 遞歸復(fù)用做循環(huán)

        第三個(gè)類(lèi)型套路是遞歸復(fù)用做循環(huán)。

        TypeScript 本身不支持循環(huán),但是可以通過(guò)遞歸完成不確定數(shù)量的類(lèi)型編程,達(dá)到循環(huán)的效果。

        比如通過(guò)遞歸實(shí)現(xiàn)數(shù)組類(lèi)型反轉(zhuǎn)。

        type ReverseArr<Arr extends unknown[]> =
            Arr extends [infer First, ...infer Rest]
            ? [...ReverseArr<Rest>, First]
            : Arr;


        type ReverseArrResult = ReverseArr<[12345]>

        首先限制參數(shù)必須是數(shù)組類(lèi)型。

        然后用 extends 匹配公式,如果滿足條件,則調(diào)用自身,否則直接返回。

        實(shí)現(xiàn)了一個(gè)數(shù)組反轉(zhuǎn)類(lèi)型。

        3.4 數(shù)組長(zhǎng)度做計(jì)數(shù)

        第四個(gè)類(lèi)型套路是數(shù)組長(zhǎng)度做計(jì)數(shù)。

        類(lèi)型編程本身是不支持做加減乘除運(yùn)算的,但是可以通過(guò)遞歸構(gòu)造指定長(zhǎng)度的數(shù)組,然后取數(shù)組長(zhǎng)度的方式來(lái)完成數(shù)值的加減乘除。

        比如通過(guò)數(shù)組長(zhǎng)度實(shí)現(xiàn)類(lèi)型編程的加法運(yùn)算。

        type BuildArray<
            Length extends number,
            Ele = unknown,
            Arr extends unknown[] = []
            > = Arr['length'] extends Length
            ? Arr
            : BuildArray<Length, Ele, [...Arr, Ele]>;

        type Add<Num1 extends number, Num2 extends number> =
            [...BuildArray<Num1>, ...BuildArray<Num2>]['length'];


        type AddResult = Add<3225>

        首先通過(guò)遞歸創(chuàng)建一個(gè)可以生成任意長(zhǎng)度的數(shù)組類(lèi)型

        然后創(chuàng)建一個(gè)加法類(lèi)型,通過(guò)數(shù)組的長(zhǎng)度來(lái)實(shí)現(xiàn)加法運(yùn)算。

        三、類(lèi)型體操實(shí)踐

        分享的第三部分是類(lèi)型體操實(shí)踐。

        前面分享了類(lèi)型體操的概念及常用的運(yùn)算邏輯。

        下面我們就用這些運(yùn)算邏輯來(lái)解析 TypeScript 內(nèi)置的高級(jí)類(lèi)型。

        1. 解析 TypeScript 內(nèi)置高級(jí)類(lèi)型

        • partial 把索引變?yōu)榭蛇x

        通過(guò) in 操作符遍歷索引,為所有索引添加 ?前綴實(shí)現(xiàn)把索引變?yōu)榭蛇x的新的映射類(lèi)型。

        type TPartial<T> = {
            [P in keyof T]?: T[P];
        };

        type PartialRes = TPartial<{ name'aa'age18 }>
        • Required 把索引變?yōu)楸剡x

        通過(guò) in 操作符遍歷索引,為所有索引刪除 ?前綴實(shí)現(xiàn)把索引變?yōu)楸剡x的新的映射類(lèi)型。

        type TRequired<T> = {
            [P in keyof T]-?: T[P]
        }

        type RequiredRes = TRequired<{ name?: 'aa', age?: 18 }>
        • Readonly 把索引變?yōu)橹蛔x

        通過(guò) in 操作符遍歷索引,為所有索引添加 readonly 前綴實(shí)現(xiàn)把索引變?yōu)橹蛔x的新的映射類(lèi)型。

        type TReadonly<T> = {
            readonly [P in keyof T]: T[P]
        }

        type ReadonlyRes = TReadonly<{ name?: 'aa', age?: 18 }>
        • Pick 保留過(guò)濾索引

        首先限制第二個(gè)參數(shù)必須是對(duì)象的 key 值,然后通過(guò) in 操作符遍歷第二個(gè)參數(shù),生成新的映射類(lèi)型實(shí)現(xiàn)。

        type TPick<T, K extends keyof T> = {
            [P in K]: T[P]
        }

        type PickRes = TPick<{ name?: 'aa', age?: 18 }, 'name'>
        • Record 創(chuàng)建映射類(lèi)型

        通過(guò) in 操作符遍歷聯(lián)合類(lèi)型 K,創(chuàng)建新的映射類(lèi)型。

        type TRecord<K extends keyof any, T> = {
            [P in K]: T
        }

        type RecordRes = TRecord<'aa' | 'bb', string>
        • Exclude 刪除聯(lián)合類(lèi)型的一部分

        通過(guò) extends 操作符,判斷參數(shù) 1 能否賦值給參數(shù) 2,如果可以則返回 never,以此刪除聯(lián)合類(lèi)型的一部分。

        type TExclude<T, U> = T extends U ? never : T

        type ExcludeRes = TExclude<'aa' | 'bb''aa'>
        • Extract 保留聯(lián)合類(lèi)型的一部分

        和 Exclude 邏輯相反,判斷參數(shù) 1 能否賦值給參數(shù) 2,如果不可以則返回 never,以此保留聯(lián)合類(lèi)型的一部分。

        type TExtract<T, U> = T extends U ? T : never

        type ExtractRes = TExtract<'aa' | 'bb''aa'>
        • Omit 刪除過(guò)濾索引

        通過(guò)高級(jí)類(lèi)型 Pick、Exclude 組合,刪除過(guò)濾索引。

        type TOmit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>

        type OmitRes = TOmit<{ name'aa'age18 }, 'name'>
        • Awaited 用于獲取 Promise 的 valueType

        通過(guò)遞歸來(lái)獲取未知層級(jí)的 Promise 的 value 類(lèi)型。

        type TAwaited<T> =
            T extends null | undefined
                ? T
                : T extends object & { then(onfulfilled: infer F): any }
                    ? F extends ((value: infer V, ...args: any) => any)
                        ? Awaited<V>
                        : never
                    : T;


        type AwaitedRes = TAwaited<Promise<Promise<Promise<string>>>>

        還有非常多高級(jí)類(lèi)型,實(shí)現(xiàn)思路和上面介紹的類(lèi)型套路大多一致,這里不一一贅述。

        2. 解析 ParseQueryString 復(fù)雜類(lèi)型

        重點(diǎn)解析的是在背景章節(jié)介紹類(lèi)型體操?gòu)?fù)雜度,舉例說(shuō)明的解析字符串參數(shù)的函數(shù)類(lèi)型。

        如圖示 demo 所示,這個(gè)函數(shù)是用于將指定字符串格式解析為對(duì)象格式。

        function parseQueryString1(queryStr{
          if (!queryStr || !queryStr.length) {
            return {}
          }
          const queryObj = {}
          const items = queryStr.split('&')
          items.forEach((item) => {
            const [key, value] = item.split('=')
            if (queryObj[key]) {
              if (Array.isArray(queryObj[key])) {
                queryObj[key].push(value)
              } else {
                queryObj[key] = [queryObj[key], value]
              }
            } else {
              queryObj[key] = value
            }
          })
          return queryObj
        }

        比如獲取字符串 a=1&b=2 中 a 的值。

        常用的類(lèi)型聲明方式如下圖所示:

        function parseQueryString1(queryStr: string): Record<stringany{
          if (!queryStr || !queryStr.length) {
            return {}
          }
          const queryObj = {}
          const items = queryStr.split('&')
          items.forEach((item) => {
            const [key, value] = item.split('=')
            if (queryObj[key]) {
              if (Array.isArray(queryObj[key])) {
                queryObj[key].push(value)
              } else {
                queryObj[key] = [queryObj[key], value]
              }
            } else {
              queryObj[key] = value
            }
          })
          return queryObj
        }

        參數(shù)類(lèi)型為 string,返回類(lèi)型為 Record<string, any>,這時(shí)看到,res1.a 類(lèi)型為 any,那么有沒(méi)有辦法,準(zhǔn)確的知道 a 的類(lèi)型是字面量類(lèi)型 1 呢?

        下面就通過(guò)類(lèi)型體操的方式,來(lái)重寫(xiě)解析字符串參數(shù)的函數(shù)類(lèi)型。

        首先限制參數(shù)類(lèi)型是 string 類(lèi)型,然后為參數(shù)匹配公式 a&b,如果滿足公式,將 a 解析為 key value 的映射類(lèi)型,將 b 遞歸 ParseQueryString 類(lèi)型,繼續(xù)解析,直到不再滿足 a&b 公式。

        最后,就可以得到一個(gè)精準(zhǔn)的函數(shù)返回類(lèi)型,res.a = 1。


        type ParseParam<Param extends string> =
            Param extends `${infer Key}=${infer Value}`
                ? {
                    [K in Key]: Value
                } : Record<string, any>;

        type MergeParams<
            OneParam extends Record<string, any>,
            OtherParam extends Record<string, any>
        > = {
          readonly [Key in keyof OneParam | keyof OtherParam]:
            Key extends keyof OneParam
                ? OneParam[Key]
                : Key extends keyof OtherParam
                    ? OtherParam[Key]
                    : never
        }

        type ParseQueryString<Str extends string> =
            Str extends `${infer Param}&${infer Rest}`
                ? MergeParams<ParseParam<Param>, ParseQueryString<Rest>>
                : ParseParam<Str>;
        function parseQueryString<Str extends string>(queryStr: Str): ParseQueryString<Str{
            if (!queryStr || !queryStr.length) {
                return {} as any;
            }
            const queryObj = {} as any;
            const items = queryStr.split('&');
            items.forEach(item => {
                const [key, value] = item.split('=');
                if (queryObj[key]) {
                    if(Array.isArray(queryObj[key])) {
                        queryObj[key].push(value);
                    } else {
                        queryObj[key] = [queryObj[key], value]
                    }
                } else {
                    queryObj[key] = value;
                }
            });
            return queryObj as any;
        }


        const res = parseQueryString('a=1&b=2&c=3');

        console.log(res.a) // type 1

        四、小結(jié)

        綜上分享,從 3 個(gè)方面介紹了類(lèi)型體操。

        • 第一點(diǎn)是類(lèi)型體操背景,了解了什么是類(lèi)型,什么是類(lèi)型安全,怎么實(shí)現(xiàn)類(lèi)型安全;

        • 第二點(diǎn)是熟悉類(lèi)型體操的主要類(lèi)型、支持的邏輯運(yùn)算,并總結(jié)了 4 個(gè)類(lèi)型套路;

        • 第三點(diǎn)是類(lèi)型體操實(shí)踐,解析了 TypeScript 內(nèi)置高級(jí)類(lèi)型的實(shí)現(xiàn),并手寫(xiě)了一些復(fù)雜函數(shù)類(lèi)型。

        從中我們了解到需要?jiǎng)討B(tài)生成類(lèi)型的場(chǎng)景,必然是要用類(lèi)型編程做一些運(yùn)算,即使有的場(chǎng)景下可以不用類(lèi)型編程,但是使用類(lèi)型編程能夠有更精準(zhǔn)的類(lèi)型提示和檢查,減少代碼中潛在的問(wèn)題。

        參考資料+源碼

        這里列舉了本次分享的參考資料及示例源碼,歡迎大家擴(kuò)展閱讀:

        [1]

        參考資料《TypeScript 類(lèi)型體操通關(guān)秘籍》: https://juejin.cn/book/7047524421182947366

        [2]

        示例源碼: https://github.com/jiaozitang/ts-demo


        往期回顧

        #

        如何使用 TypeScript 開(kāi)發(fā) React 函數(shù)式組件?

        #

        11 個(gè)需要避免的 React 錯(cuò)誤用法

        #

        6 個(gè) Vue3 開(kāi)發(fā)必備的 VSCode 插件

        #

        3 款非常實(shí)用的 Node.js 版本管理工具

        #

        6 個(gè)你必須明白 Vue3 的 ref 和 reactive 問(wèn)題

        #

        6 個(gè)意想不到的 JavaScript 問(wèn)題

        #

        試著換個(gè)角度理解低代碼平臺(tái)設(shè)計(jì)的本質(zhì)

        瀏覽 62
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
          
          

            1. 涩涩免费网站 | 大黑逼网 | 超碰人人9 | 日韩成人精品电影 | 亚洲777kkk成人片 |