1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        成為優(yōu)秀的TS體操高手 之 TS 類型體操前置知識(shí)儲(chǔ)備

        共 16400字,需瀏覽 33分鐘

         ·

        2022-07-24 18:50

        TS 類型體操前置知識(shí)儲(chǔ)備

        如果你正在學(xué)習(xí) TS,可是像我一樣僅僅停留在定義類型,定義 interface/type 的層面的話, 這份體操類型練習(xí)題一定不要錯(cuò)過(guò) !type-challenges[1]

        寫這篇文章的時(shí)候我只做完了體操類型的中等級(jí)別的題目(還有 2-3 道其實(shí)沒完全解出來(lái))

        簡(jiǎn)單搭建一下做題環(huán)境

        我喜歡做一題拷貝一題,所以我 clone 了一份 type-challenges[2]。然后在 type-challenges 新建了一個(gè)文件夾(my-type-challenges),專門用來(lái)做 TS 體操

        每次想做題的時(shí)候都只需要記住編號(hào),比如第一題 13-helloword 。只需要輸入就可以了

        npm run copy 13
        復(fù)制代碼

        詳細(xì)的腳本在 Jioho/my-type-challenges[3]。寫了腳本后,就可以愉快的寫代碼了

        體操入門基本語(yǔ)法

        • 以下的內(nèi)容純粹是個(gè)人的見解,如有說(shuō)錯(cuò)或理解不到位的地方請(qǐng)指出

        雖然說(shuō) TS 圖靈完備(如果一個(gè)計(jì)算系統(tǒng)可以計(jì)算每一個(gè)圖靈可計(jì)算函數(shù),那么這個(gè)系統(tǒng)就是圖靈完備的)

        不過(guò) TS 并沒有那么多語(yǔ)法,比如 if,switch,return 之類的。

        TS 用的最多的都是三目運(yùn)算符,判斷相等,數(shù)組,遞歸等一些技巧后面都會(huì)一一介紹到

        TS 內(nèi)置的高級(jí)類型(內(nèi)置的體操)

        入門第一步一定要看文檔(雖然我也不愛看,看不懂),不過(guò)還是需要有基礎(chǔ)的了解 utility-types[4]

        目前的內(nèi)置方法就如下:

        ------------
        Partial<Type>Required<Type>Readonly<Type>Record<Keys, Type>
        Pick<Type, Keys>Omit<Type, Keys>Exclude<UnionType, ExcludedMembers>Extract<Type, Union>
        NonNullable<Type>Parameters<Type>ConstructorParameters<Type>ReturnType<Type>
        InstanceType<Type>ThisParameterType<Type>OmitThisParameter<Type>ThisType<Type>
        Uppercase<StringType>Lowercase<StringType>Capitalize<StringType>Uncapitalize<StringType>

        比如拿一個(gè)后續(xù)可能用的比較多的來(lái)說(shuō)一下:

        Pick[5] 方法

        官網(wǎng)的 demo:

        interface Todo {
          title: string;
          description: string;
          completed: boolean;
        }

        type TodoPreview = Pick<Todo, "title" | "completed">;

        const todo: TodoPreview = {
          title: "Clean room",
          completed: false,
        };
        復(fù)制代碼

        Pick 的作用就是從一個(gè)對(duì)象中,挑選需要的字段出來(lái),比如從 TODO 里面只取出 titlecompleted

        如果沒有類型體操的話,TodoPreview 還得額外定義一個(gè)類型

        interface TodoPreview {
          title:string;
          completed: boolean;
        }
        復(fù)制代碼

        之前有一個(gè)練手項(xiàng)目我就是遇到了這樣的情況,明明都是同一個(gè)對(duì)象上的字段,為了適應(yīng)不同場(chǎng)景,硬是定義了一大堆的附屬字段,關(guān)鍵是如果想改一個(gè)類型,還得全部跟著改。。如果有 Pick 就一了百了,想要啥就 pick 啥

        想看 Pick 實(shí)現(xiàn)也很簡(jiǎn)單,隨便起一個(gè) type,按住 ctrl+點(diǎn)擊 Pick 就可以看到 Pick 實(shí)現(xiàn)

        代碼實(shí)現(xiàn)如下:

        /**
         * From T, pick a set of properties whose keys are in the union K
         */

        type Pick<T, K extends keyof T> = {
            [P in K]: T[P];
        };
        復(fù)制代碼

        不過(guò)第一次看到這代碼, extendskeyof , in 這都是什么東西?這就是這篇文章存在的意義。往下看

        判斷常用 extends 關(guān)鍵字

        extends 翻譯是延伸的意思

        在 TS 充當(dāng)了 if在 XXX 范圍內(nèi) 的一個(gè)作用。extends 后面通常就要接三目運(yùn)算符了(也有例外)

        舉個(gè) ?? 子

        type mytype1 = 1 extends string ? true : false // false

        type mytype2 = '1' extends string ? true : false // true

        type mytype2_1 = string extends '1' ? true : false // false

        type mytype3 = mytype1 extends any ? 1 : 2 // 1

        type mytype4 = [90extends unknown[] ? true : false // true

        type mytype5 = [90extends string[] ? true : false // false
        復(fù)制代碼

        上面簡(jiǎn)單的舉了幾個(gè)例子,簡(jiǎn)單解釋下:

        • 1 是否屬于 string 類型 得到 false 因?yàn)?1 是數(shù)字類型
        • '1' 屬于是 string 類型的 三目運(yùn)算符判斷為 true
        • string 類型 屬于 '1' 肯定是 false 的,string 類型范圍比'1'更大,不在屬于的范疇了
        • mytype1 屬于 any 類型是對(duì)的,因?yàn)?any 包含一切~
        • [90] 是一個(gè)數(shù)值型的數(shù)組,屬于一個(gè) unknown 未知類型的數(shù)組中,這個(gè)也是對(duì)的,因?yàn)槲粗愋鸵矔?huì)包含數(shù)字類型
        • 而 [90] 就不屬于 string[] 的范疇了

        extends 也有不接三目運(yùn)算符的時(shí)候

        比如寫一個(gè)限制 string 類型的 push 方法

        type StrPush<T extends string[], U extends string> = [...T, U]
        type myarr = StrPush<[123], 4>
        復(fù)制代碼

        比如在這個(gè)例子中,直接會(huì)報(bào)錯(cuò)。因?yàn)檫€沒進(jìn)到 StrPush 的判斷中,T 泛型就已經(jīng)被約束為 strinig 類型了,U 也被約束為 string 類型

        extends 總結(jié)

        • extends 在 TS 的 函數(shù)體中的時(shí)候起到的是判斷范疇的一個(gè)作用
        • 在一些特殊位置 (比如接收泛型的時(shí)候,在函數(shù)運(yùn)算過(guò)程中斷言變量類型的時(shí)候)起到的是一個(gè) 約束類型 的作用

        循環(huán)對(duì)象的鍵 keyof 和 in

        只要了解 keyof 和 in 之后,Pick 的所有關(guān)鍵字也就講解完了,體操練習(xí)中簡(jiǎn)單的第一題 MyPick 也就完成了

        • 還是上面的 demo 代碼
        /**
         * From T, pick a set of properties whose keys are in the union K
         */

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

        interface Todo {
          title: string;
          description: string;
          completed: boolean;
        }

        type TodoPreview = Pick<Todo, "title" | "completed">;

        const todo: TodoPreview = {
          title: "Clean room",
          completed: false,
        };
        復(fù)制代碼

        extends 上面講過(guò),屬于范疇判斷/約束類型,在泛型定義<>里面明顯就是為了約束類型

        keyof 的作用可以理解為 把一個(gè)對(duì)象中的所有 提取出來(lái)。

        • 直接用 keyof 提取一個(gè)對(duì)象看看效果:
        type TodoKeys = keyof Todo
        //type TodoKeys =  'title' | 'description' | 'completed'

        type testkeys = keyof {[k:number]: any; name:string}
        // type testkeys = number | "name"

        // 特殊的例子
        type Mapish = { [k: string]: boolean; name:string };
        type M = keyof Mapish;
        // type M = string | number
        復(fù)制代碼

        上面的幾個(gè)例子中,第一個(gè)是最好理解的,提取所有的

        第二個(gè)案例中,k 作為未知的內(nèi)容,提取 number 作為 key 的范圍,加上 "name" 所以就得出 number | "name"

        特殊的例子也是官網(wǎng)的例子,為啥會(huì)有個(gè) number 類型?

        原話:Note that in this example, M is string | number — this is because JavaScript object keys are always coerced to a string, so obj[0] is always the same as obj["0"].
        翻譯:請(qǐng)注意,在此示例中,M string | number — 這是因?yàn)?JavaScript 對(duì)象鍵總是被強(qiáng)制轉(zhuǎn)換為字符串,所以 obj[0] 總是與 obj["0"] 相同

        結(jié)合 demo ,K extends keyof T 意思也就是說(shuō),K 參數(shù)的取值范圍只能在 Todo 的鍵中取('title' | 'description' | 'completed'),限制為字符串,并且是這 3 個(gè)鍵中的其中一個(gè)/多個(gè),只能少,不能多

        如果是下面這種

        type Mapish = { [k: string]: boolean; name:string };
        // K extends keyof Mapish;
        復(fù)制代碼

        K 的范圍則是 string | number 類型(因?yàn)闆]有具體的鍵名,所以只能不限制具體的鍵名,只限制類型

        像這種用 | 拼起來(lái)的(或類型)規(guī)范點(diǎn)叫做 unio 聯(lián)合數(shù)據(jù)類型,想要循環(huán)這些數(shù)據(jù),可以用到 in 關(guān)鍵字


        回到 demo 的講解

        • Pick 中 泛型 T 傳入的是 Todo 類型
        • K extends keyof T :K 限制為 Todo 的鍵值(傳入的是 "title" | "completed" 符合要求,因?yàn)橹荒苌俨荒芏嗦铮?/section>
        • P in k 可以理解為 循環(huán)
        • P 會(huì)依次被賦值為 title
        • 然后賦值 completed
        • T[P] 的意思和 JS 的[]取值一樣,獲取 T['title'] => string 和 T['completed'] => completed
        • {} 會(huì)包裹循環(huán)出來(lái)的結(jié)果
        type Pick<T, K extends keyof T> = {
            [P in K]: T[P];
        };

        type TodoPreview = Pick<Todo, "title" | "completed">;
        // TodoPreview = {title:string,completed:boolean}
        復(fù)制代碼

        Pick 的實(shí)現(xiàn)就完成了。[P in K] 這個(gè)循環(huán)和我們 JS 常規(guī)的寫法很不一樣,需要消化一下,其他應(yīng)該都好理解


        • 以此內(nèi)推,完成 Readonly
          • 眾所周知 readonly 只需要在鍵值前面加上 readonly 參數(shù)即可

        實(shí)現(xiàn)如下:keyof 寫到了中括號(hào)里面,這個(gè)是允許的,這些關(guān)鍵字無(wú)論寫在哪里只要符合規(guī)范都 OK,比如 MyReadonly2

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

        // 這個(gè)是畫蛇添足的,因?yàn)镻肯定是屬于keyof T的,就是從keyof T中循環(huán)出來(lái)的
        // 不過(guò)證明keyof不僅僅在 <> 中能用
        type MyReadonly2<T> = {
          readonly [P in keyof T]: P extends keyof T ?  T[P] : never
        }
        復(fù)制代碼

        小拓展:'name' extends keyof T ? true : false 也能判斷 T 這個(gè)泛型對(duì)象中有沒有 name 這個(gè)屬性

        keyof 和 in 小結(jié)

        • keyof 是為了拿到一個(gè)對(duì)象中的所有的鍵名,當(dāng)鍵名是一個(gè)類型的時(shí)候,則會(huì)全部被升級(jí)為對(duì)應(yīng)的 類型
        • in 則是為了循環(huán) unio 聯(lián)合類型數(shù)據(jù)的,多數(shù)都用于循環(huán)重新生成一個(gè)對(duì)象

        JS 轉(zhuǎn) TS -- typeof 關(guān)鍵字

        typeof 作為從 js 世界轉(zhuǎn)換為 ts 世界的內(nèi)容。

        假設(shè)一個(gè)場(chǎng)景,我們使用一個(gè) obj 對(duì)象作為數(shù)據(jù)映射的存儲(chǔ),比如使用一個(gè) map,存儲(chǔ)狀態(tài)碼返回對(duì)應(yīng)的 msg:

        const statusMap = {
          200'操作成功',
          404'內(nèi)容找不到',
          500'操作失敗',
          10001'登錄失效'
        }

        var status1 = 200
        console.log(statusMap[status1]) // 操作成功

        var status2 = 10001
        console.log(statusMap[status2]) // 登錄失效
        復(fù)制代碼

        類似上面的場(chǎng)景,那這時(shí)候statusMap[] 中間的值肯定只有 200,404,500,10001 才符合要求,在 TS 層面自然我們就要約定 status 在這個(gè)范圍中

        如果不用 typeof ,我們可以會(huì)寫出這樣的 TS:

        type Status = 200 | 404 | 500 | 10001

        var status1: Status = 200
        console.log(statusMap[status1]) // 操作成功

        var status2: Status = 10001
        console.log(statusMap[status2]) // 登錄失效

        var status3: Status = 301 // 報(bào)紅 (Type '301' is not assignable to type 'Status')
        復(fù)制代碼

        這時(shí)候假如我們?cè)谛略隽藥讉€(gè)鍵值,那 TS 還得在同步跟著改一次(太麻煩了),用上 typeof

        const statusMap = {
          200'操作成功',
          404'內(nèi)容找不到',
          500'操作失敗',
          10001'登錄失效'
        }

        type MyStatusMap = typeof statusMap
        // 這時(shí)候 MyStatus 的值會(huì)變成
        // type MyStatus = {
        //     200: string;
        //     404: string;
        //     500: string;
        //     10001: string;
        // }

        // 然后接上keyof關(guān)鍵字,提取對(duì)象的鍵名
        type MyStatus = keyof MyStatusMap
        // type MyStatus = 200 | 404 | 500 | 10001

        // 上面的2步可以簡(jiǎn)寫一步到位
        type MyStatus2 = keyof typeof statusMap
        // type MyStatus2 = 200 | 404 | 500 | 10001
        復(fù)制代碼

        這樣只需要我們改動(dòng) JS 的內(nèi)容,TS 將會(huì)自動(dòng)獲取對(duì)新的對(duì)象。返回對(duì)象后還不夠,因?yàn)槲覀兩厦嫦爰s束的是傳入的鍵值(想獲取鍵值,剛好上面學(xué)習(xí)了 keyof 關(guān)鍵字),就能動(dòng)態(tài)獲取所有符合規(guī)范的鍵值了!

        typeof 小結(jié)

        typeof 是一個(gè)可以動(dòng)態(tài)把 JS 的對(duì)象轉(zhuǎn)換為 TS 的關(guān)鍵字。

        不過(guò)也有限制場(chǎng)景,那就是轉(zhuǎn)換的前提是這部分 JS 是已固定的內(nèi)容。就好比例子中的一個(gè)對(duì)象映射,那是固定的內(nèi)容,然后讓 TS 去推導(dǎo)

        而且打包后的代碼是不可能存在 TS 的,如果想實(shí)現(xiàn)后端接口動(dòng)態(tài)返回內(nèi)容在用 typeof ,這是實(shí)現(xiàn)不了的

        數(shù)組和字符串的循環(huán) 推斷類型 infer

        infer 應(yīng)用場(chǎng)景非常多

        簡(jiǎn)單一句話概括 infer 只是一個(gè) 占位 的工具,我就站在這個(gè)位置,至于這個(gè)位置是什么內(nèi)容 infer 并不關(guān)心,可以留給后面的程序去判斷

        用簡(jiǎn)單題的 00014-easy-first[6] 來(lái)講解一下。實(shí)現(xiàn)一個(gè) First 工具類型

        通常獲取第一個(gè)元素,我們想到的就是 T[0] 當(dāng)然在 TS 這個(gè)語(yǔ)法是可以行得通的

        可以在測(cè)試用例中有一項(xiàng)

        Equal<First<[]>, never> // 使用 T[0] 的話這個(gè)會(huì)報(bào)錯(cuò),因?yàn)?nbsp;First<[]> 返回的是 undefined
        復(fù)制代碼

        也就是說(shuō)當(dāng)這個(gè)元素非指定聲明為 undefined 時(shí),在 TS 多數(shù)都是要用 never 代替

        正確答案如下:

        type First<T extends any[]> = T extends [infer F,...infer Rest] ? F : never
        復(fù)制代碼

        簡(jiǎn)單的說(shuō)一下

        • infer 必須在 TS 函數(shù)運(yùn)算過(guò)程中使用(在定義泛型的<>中不能使用,而 extends 就可以)
        • infer 可以配合 ... 進(jìn)行運(yùn)算
        • T extends [infer F,...infer Rest]
          • 表達(dá)的意思就是 T extends [F,...Rest 剩余的值]。T 肯定是存在一個(gè)屬性F的數(shù)組,...Rest 是剩下的內(nèi)容,可有可無(wú)
          • ...infer Rest 就是把除了 F 之外的元素在歸集為一個(gè)數(shù)組(這個(gè)是 ES6 的知識(shí)了)
          • infer F 的意思就是,我拿 F 在這個(gè)數(shù)組里面 占位 ,數(shù)組的第一項(xiàng)的內(nèi)容,就是被 F 占了
        • 回歸到題目,我們要拿的也正是第一項(xiàng),所以直接 return F 類型
        • 如果 T 是一個(gè)空數(shù)組,那么 extends 那一步就都判斷不通過(guò),自然返回的就是 never,符合測(cè)試用例的要求。

        infer 的其他妙用

        infer 還能遍歷字符串

        比如起一個(gè) 字符串切割為數(shù)組的需求:

        type Split<T extends string, U extends unknown[] = []> =
          T extends `${infer F}${infer Rest}` ? Split<Rest, [...U, F]> : U


        type testSplit = Split<'123456'>

        // type testSplit = ["1", "2", "3", "4", "5", "6"]
        復(fù)制代碼

        其中 ${infer F}${infer Rest} 的意思就是,F(xiàn) 占第一個(gè)字符,Rest 占剩下的字符,因?yàn)樵谧址胁淮嬖?code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">...的概念,所以 Rest 基本上就是占據(jù)了剩下的字符了

        像這樣的一個(gè)測(cè)試?yán)?,一共?3 個(gè)占位符,

        type testInfer<T extends string> = T extends `${infer F}${infer S}${infer R}` ? [F, S, R] : T

        type testInfer1 = testInfer<'123456'>
        // 按照占位符的特性,前面F和S分別占據(jù)2個(gè)字符,剩余的都給R占去了
        // type testInfer1 = ["1", "2", "3456"]

        // 稍作改動(dòng),在S占位符后面添加一個(gè)5
        type testInfer2<T extends string> = T extends `${infer F}${infer S}5${infer R}` ? [F, S, R] : T
        type testInfer3 = testInfer<'123456'>
        // F 占第一個(gè)字符 = 1
        // S 占據(jù)2-4,因?yàn)樵赗之前有一個(gè)5,所以S代表了第二個(gè)字符開始到5的所有字符
        // 那么R就是從5開始,到末尾,所以得出的結(jié)果如下:
        // type testInfer1 = ["1", "234", "6"]
        復(fù)制代碼

        后面的習(xí)題還有很多會(huì)用到 infer ,所以先了解好 infer 占位 的特性就好

        infer 小結(jié)

        infer 相當(dāng)于一個(gè)占位置的關(guān)鍵字,把占下來(lái)的位置復(fù)制給對(duì)應(yīng)的運(yùn)算變量。

        其中對(duì)于數(shù)組或者其他的類型來(lái)說(shuō),還能用 ... 把所有的位置歸結(jié)起來(lái)形成一個(gè)數(shù)組

        對(duì)于字符串這種不存在 ... 拓展運(yùn)算符的來(lái)說(shuō),只要前面占了一個(gè)位置,剩下的字符就會(huì)被第二個(gè)占位符全部代替

        數(shù)組的用法

        數(shù)組也是 TS 體操的一個(gè)很重要的特性,因?yàn)樵?TS 體操中并沒有加減法的概念,實(shí)際運(yùn)算中少不了加減法的操作,包括獲取長(zhǎng)度之類的。

        所以數(shù)組還充當(dāng)了 計(jì)數(shù)器 的作用。關(guān)于數(shù)組的計(jì)數(shù)器還有一個(gè)非常有用的技巧,有用到我覺得可以單獨(dú)再起一個(gè)文章細(xì)細(xì)分析,下面就先簡(jiǎn)單的介紹一下數(shù)組的功能

        先來(lái)一道簡(jiǎn)單題,學(xué)會(huì)數(shù)組的基本屬性和用法 00018-easy-tuple-length[7]

        題目給出 2 個(gè)數(shù)組,用了 as const。需要求出這 2 個(gè)數(shù)組的長(zhǎng)度

        const tesla = ['tesla''model 3''model X''model Y'as const
        const spaceX = ['FALCON 9''FALCON HEAVY''DRAGON''STARSHIP''HUMAN SPACEFLIGHT'as const

        type teslaLength = Length<typeof tesla>
        type spaceXLength = Length<typeof spaceX>

        // 答案:
        type Length<T extends readonly any[]> = T['length']
        復(fù)制代碼

        是不是很簡(jiǎn)單?!T['length'] 完事~

        所以數(shù)組一個(gè)很重要的特性就是,他有 length 屬性。

        想用 length 屬性的前提:T extends any[] T 類型的范圍,肯定是一個(gè)數(shù)組,any[] 或者 unknown[]。都行,反正是數(shù)組,就有 length 屬性。


        基于這個(gè)求長(zhǎng)度的問(wèn)題,延伸一下,求 2 個(gè)數(shù)組合并后的長(zhǎng)度

        type MergeArray<T extends readonly any[],U extends readonly any[]> = [...T,...U]['length']

        type arrLength = MergeArray<typeof tesla, typeof spaceX> // 9
        復(fù)制代碼

        稍微來(lái)點(diǎn)有意思的題目在感受一下數(shù)組的作用 難度為中等的題 05153-medium-indexof[8]

        實(shí)現(xiàn)一個(gè) indexOf(原題還有另外一個(gè)需要注意的點(diǎn)就是判斷類型的時(shí)候需要注意的地方,我后面還會(huì)起文章來(lái)講解,現(xiàn)在先看最簡(jiǎn)單的實(shí)現(xiàn)):

        既然要算索引位置,自然就涉及到了一個(gè) 計(jì)數(shù)器 的問(wèn)題,看下面的答案

        type numberIndex = IndexOf<[123], 1// 需要得到的答案是 0
        type numberIndex2 = IndexOf<[123], 99// 需要得到的答案是 -1
        type numberIndex3 = IndexOf<[123], 1// 需要得到的答案是 0

        // 題目給出的初始模版(缺少計(jì)數(shù)器)
        // type IndexOf<T extends unknown[],U> = any

        // 對(duì)于這些缺參數(shù)的,我們完全可以自己補(bǔ)一個(gè)參數(shù),而且補(bǔ)充默認(rèn)值
        type IndexOf2<T extends unknown[], U, C extends 1[] = []> =         
          T extends [infer F, ...infer Rest] ?             
            (F extends U ? C['length'] : IndexOf<Rest, U, [...C, 1]>) : -1
        復(fù)制代碼

        講解部分:

        • 因?yàn)轭}目給出的模版缺少了一個(gè)計(jì)數(shù)器,我們可以補(bǔ)充一個(gè) C 變量,并且默認(rèn)賦值為 []。
          • 只要有默認(rèn)值,就非必填,非必填的話測(cè)試用例就不會(huì)報(bào)錯(cuò)了
        • T extends 和 infer 部分上面有講解過(guò),如果 T 已經(jīng)不滿足至少有一個(gè)F的時(shí)候,說(shuō)明 T 數(shù)組已經(jīng)空了,空了之后就說(shuō)明可以得出結(jié)果了
          • T 為空了,還沒匹配到數(shù)據(jù),按 indexOf 的方法應(yīng)該是返回 -1
          • T 如果符合了至少有一個(gè) F 變量的要求的話,判斷F與傳入的U數(shù)據(jù)相比,相同的話返回當(dāng)前計(jì)數(shù)器的長(zhǎng)度(也就是要計(jì)算的索引了) C['length']
          • 不符合的話,拿Rest 數(shù)組繼續(xù)循環(huán)(遞歸調(diào)用 IndexOf),與此同時(shí) 遞歸調(diào)用的時(shí)候 C 的入?yún)⒆兂闪?[...C,1] 數(shù)組長(zhǎng)度遞增1
        • [...C,1] 的作用,就是保留原數(shù)組的內(nèi)容,在添加一個(gè) 1,使得原數(shù)組的 length + 1 計(jì)數(shù)器 效果達(dá)成

        通過(guò)一個(gè)簡(jiǎn)單的拓展運(yùn)算符,加上一個(gè) 1 使得數(shù)組的長(zhǎng)度不斷的變化。達(dá)到計(jì)數(shù)器/加法運(yùn)算的效果

        比如在第 4182 的題目中,需要實(shí)現(xiàn)一個(gè) 斐波那契數(shù)列。斐波那契數(shù)列的特性就是 n = (n-1)+(n-2)。如何實(shí)現(xiàn)這個(gè)加法?用代碼來(lái)說(shuō)就是 [...(N-1),...(N-2)]。(不理解沒關(guān)系,后面還有會(huì)詳細(xì)的文章來(lái)講)

        數(shù)組小結(jié)

        • 只要對(duì)應(yīng)的類型 extends [] 的話,就可以使用 ['length'] 屬性獲取長(zhǎng)度
        • 數(shù)組在體操中不僅僅充當(dāng)了數(shù)組的作用,還充當(dāng)了 計(jì)數(shù)器加法實(shí)現(xiàn) 的作用,用法千變?nèi)f化
        • 數(shù)組的累加依賴于遞歸方法的實(shí)現(xiàn),在每一次遞歸的過(guò)程中往新的方法里面?zhèn)魅胄碌拈L(zhǎng)度(不過(guò)遞歸容易造成內(nèi)存溢出,比如02257-medium-minusone[9]這一題)。需要一個(gè)更加高級(jí)的技巧處理

        as 關(guān)鍵字

        在上面講解數(shù)組的時(shí)候看到有 as const 的出現(xiàn),那就順便講講 as

        在 TS 使用中,as 就是一個(gè) 斷言

        假設(shè)這樣的一個(gè)場(chǎng)景,有一個(gè)變量 todo ,設(shè)置為了 Todo 類型,有對(duì)應(yīng)的屬性

        然后某個(gè)函數(shù)的副作用,導(dǎo)致了我的 todo 變成了一個(gè) string 類型(實(shí)際代碼應(yīng)該規(guī)避這種副作用函數(shù))

        but 事情就這么發(fā)生了,而且不能改,這時(shí)候的 todo 應(yīng)該是 string 類型。todo 還需要調(diào)用一個(gè)方法,需要傳入 stirng 類型的 function todoFn(str:string){} 。這時(shí)候直接傳入 todo 肯定會(huì)報(bào)錯(cuò),類型不符合

        解決辦法就是 todoFn(todo as string)??聪路降拇a截圖會(huì)好理解一點(diǎn)

        在我斷定了這個(gè)類型就是 xxx 類型的時(shí)候就能用 as 關(guān)鍵字(當(dāng)然不推薦使用),盡可能還是用 TS 的類型推導(dǎo)


        在 TS 的類型定義的時(shí)候,as 又有別的含義

        比如說(shuō)這個(gè)

        const teslaConst = ['tesla''model 3''model X''model Y'as const
        // const teslaConst: readonly ["tesla", "model 3", "model X", "model Y"]

        // 用var變量和const推導(dǎo)是一樣的,不過(guò)會(huì)留下代碼隱患,不推薦
        var teslaConst2 = ['tesla''model 3''model X''model Y'as const
        // var teslaConst: readonly ["tesla", "model 3", "model X", "model Y"]

        const tesla = ['tesla''model 3''model X''model Y']
        // const tesla: string[]
        復(fù)制代碼

        區(qū)別很明顯,as const 的會(huì)把所有的值拿出來(lái),而且變成 readonly。因?yàn)?const 確實(shí)是只讀的標(biāo)記。

        不過(guò) TS 不吃 js 變量類型那一套,所以還得通過(guò) as const 來(lái)告訴 TS,我斷言這個(gè)就是一個(gè) const 數(shù)組了,里面的元素都不會(huì)改了,你可以遍歷這里面的值

        沒用 as const 的只會(huì)認(rèn)為是個(gè) string[]的數(shù)組。這是一個(gè)很大的區(qū)別

        最后

        TS 類型體操前置知識(shí)儲(chǔ)備大概就介紹了extends,infer,typeof,keyof和in,數(shù)組的使用,as關(guān)鍵字

        了解了這部分關(guān)鍵字作用之后,完成 TS 體操練習(xí)的中等難度的題目不在話下!(起碼完成 80%的題目沒得問(wèn)題),剩下的 20% 還需要學(xué)習(xí)更多的 TS 體操技巧

        這種感覺就好像如果你要解開一道一元二次方程之前,你得學(xué)習(xí)加減乘除的用法和規(guī)則。上面介紹的就是加減乘除的入門規(guī)則,后面還要學(xué)習(xí)更加巧妙地技能完成更復(fù)雜的 TS 體操

        感興趣的可以到主頁(yè)看看關(guān)于 TS 體操的其他文章

        以上的知識(shí)點(diǎn)也是我作為一個(gè) TS 小白在摸索完中等題目后總結(jié)的一些筆記
        如有說(shuō)錯(cuò)或理解不到位的地方請(qǐng)指出

        關(guān)于本文

        作者:Jioho

        https://juejin.cn/post/7115789691810480135


        最后


        歡迎關(guān)注【前端瓶子君】??ヽ(°▽°)ノ?
        回復(fù)「算法」,加入前端編程源碼算法群,每日一道面試題(工作日),第二天瓶子君都會(huì)很認(rèn)真的解答喲!
        回復(fù)「交流」,吹吹水、聊聊技術(shù)、吐吐槽!
        回復(fù)「閱讀」,每日刷刷高質(zhì)量好文!
        如果這篇文章對(duì)你有幫助,在看」是最大的支持
         》》面試官也在看的算法資料《《
        “在看和轉(zhuǎn)發(fā)”就是最大的支持
        瀏覽 44
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

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

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            嫩草网站入口一区二区 | 国 产 肏 艹 自 拍 | 五月天综合 | 男同啪啪网站 | 日韩在线免费观看视频 | 97人妻精品一区二区三区 | 国产色中色 | 免费成人在线视频观看 | 上床网站 | 粉嫩精品国产色综合久久不8 |