<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          TypeScript類型標注相關概念一覽

          共 10283字,需瀏覽 21分鐘

           ·

          2020-08-28 04:38

          來源:SegmentFault 思否社區(qū)

          作者:LnEoi




          背景


          用TypeScript重構了一遍業(yè)務后臺,TS很大一部分在處理類型標注,基礎的類型很容易上手,但到泛型一塊,抽象程度一下子就高了起來,提供的許多工具也復雜了起來。重構的時候一直想整理一份方便查詢的筆記一直沒空,現(xiàn)在總算抽出時間整理了一份,將于類型有關的部分整理了出來,其他部分還需要自行翻閱相關文檔。


          筆記類似大綱,知道什么情況下有什么可以用,有一個范圍概念,具體細節(jié)通過筆記提供的關鍵字再搜索相關文檔仔細了解。





          基礎類型


          TS是JS的超集,所以JS基礎的類型都包含在內:
          boolean、number、string、symbol、array([])、object({})、null、undefined


          • TS還提供了其他更加詳細的類型標識:

          • any:任意類型,TS將不對其進行類型檢測

          • unknown:未知類型,在早期對于未知類型我們一般標注any,但標注any將不會對類型進行檢測,unknown則在對變量第一次賦值后,除了any類型,其他類型禁止再賦值給此變量,同時在使用其類型所對應的方法時也需要先收縮類型,才能正常的使用

          • void:無返回,當一個函數沒有返回值的時候此類型標識

          • never:當函數永遠沒有返回值的時候用此類型標識。如始終拋出異常的函數,其返回值就是never

          • tuple:元組,數組的更精確的標識方法,可以限定數組的數量與每個位的類型

          • enum:枚舉,可以給每一個預定值的數值或是字符串提供簡明的名稱,方便編寫時區(qū)分

          • ... | ...:類型字面量,如'Red' | 'Green' | 'Blue',此時這變量就只能賦值這三種字符串,如果賦值其他字符串時將會報錯,也可以是數字字面量 1 | 2 | 3


          其他

          • 默認情況下null和undefined是所有類型的子類型,可以將這兩個值賦值給任意類型。
          • never類型也是任何類型的子類型。但其他類型無法賦值給never類型,只有never才能賦值給never類型。
          • object與Object,這兩個注釋類型只有一個大小寫之分,用Object申明的類型可以訪問Object對象的默認方法,但object則不行。





          基本概念


          : (冒號標注)


          TS有自動類型推斷,當賦上初始值后TS會生成相應的類型標注,所以一般情況下不必手動添加


          let variate: boolean = truelet variate: number = 1let variate: string = 'one'let variate: number[] = [1,3,4,6] // 數組let variate: { first: string, second: number } = { first: 'one', second: 2 } // 對象let sym1 = Symbol('key') // symbollet variate: number[string, number] = ['one', 2] // 元組enum color {Red, Green, Blue} // 枚舉


          函數參數的類型標注


          function func(params: {  first: string;  second: number;}): void {}const func = function(params: {  first: string;  second: number;}): void {}


          函數的類型標注


          const func: (first: string, second: number) => void = function(params) {}const func: (first: string, second: number) => void = () => {}  // 這種寫法很不容易分辨,只要記錄函數的類型標注是必須顯示指定返回值的,所以當看到返回值的標注才是類型標注完全結束





          interface (接口)


          此是最常用的類型標注方式。
          如上例變量標注也可以用接口替代:


          interface IVariate {  first: string;  second: number;}let variate: IVariate = { first: 'one', second: 2 }


          其他特性


          ??可選標識符

          interface IVariate {  first: string;  second?: number;}


          readonly?只讀標識符


          interface IVariate {  first: string;  readonly second: number;}


          [key: string | number]: any?任意數量標識


          有時候類型數量是動態(tài)的,但類型是指定需要約束的,可以這樣寫

          interface IVariate {  first: string;  readonly second: number;  [key: string]: number;}

          extends?接口的繼承


          interface IVariate {  first: string;  second: number;}interface IVariate2 extends IVariate {  third: string}等價于:interface IVariate2 {  first: string;  second: number;  third: string;}


          利用,可以進行多繼承


          interface IVariate3 extends IVariate, IVariate2 {  fourth: string}


          類型合并


          interface IVariate {  first: string;  second: number;}interface IVariate {  third: number;}


          重復申明類型默認會將申明合并起來,如果有重復定義的類型標注,后面的會把前面的覆蓋。


          函數


          函數參數的類型標注


          interface IParams {  first: string;  second: number;}function func(params: IParams) {}const func = function(params: IParams) {}


          函數的類型標注


          完整的標注函數參數以及函數返回值,與參數類型標注一樣,只是將定義移動到interface中。


          interface IFunc {  (first: string, second: number): void  // 這里需要注意,在interface中函數的類型標注是冒號(:)而不是箭頭(=>)}const func: IFunc = function(params) {}const func: IFunc = () => {}

          不定參數的類型標注

          const func = function(...params: any[]) {}
          可選參數的類型標注

          需要注意,可選類型之后不能有必選類型。

          const func = function(first: string, second?: number) {}
          不定參數的類型標注
          const func = function(...params: any[]) {}
          this類型標注

          由于JS的this是動態(tài)指向的,所以this類型默認標注為any,如果需要顯式標注可以在函數第一個參數位進行標注

          const func = function(this: void, first: string, second?: number) {}
          函數重載

          當傳入不同參數需要返回不同類型時,此時可以用函數重載來標識

          function func(data: number): numberfunction func(data: string): stringfunction func(data: number | string): string | number { // 必須要有一個函數支持所有類型    if(typeof data === 'number') return data+1    return data}



          這一塊跟C#很像,基礎的元素都搬過來了,可以直接使用。


          類的申明


          class People {    name: string;    constructor(name: string) {        this.name = name;    }    hello() {        return `Hello ${this.name}`;    }}

          類的繼承

          class Student extends People {    number: number = 0    constructor(name: string, number: number) {        super(name);        this.number = number    }    hello() {        return `Hello ${this.name}, Number ${this.number}`;    }}

          修飾符

          修飾符與其他語言一樣,public、private、protected。不標明時默認為public。
          靜態(tài)屬性static,只讀readonly,可選?。

          存取器get、set

          class People2 {    private _name: string;    constructor(name: string) {        this.name = name;    }    get name(): string {        return this._name    }    set name(newName: string) {        this._name = `name: ${newName}`    }}

          abstract抽象類 抽象方法


          標識abstract關鍵詞,類與方法都不必在定義時被實現(xiàn),必須在繼承的類中實現(xiàn)具體方法

          abstract class People3 {    abstract hello() {}}

          implements類的接口繼承

          類的繼承只能是單繼承,沒辦法多繼承,但實際開發(fā)中常常會有一個功能需要多個類使用,這時候可以用接口


          interface IStudy {    gender: string}interface IGrade {    grade(): string}class Student2 extends People implements IStudy, IGrade // implements 可以用逗號分隔指定多個{    number: number = 0    gender: string    constructor(name: string, number: number, gender: string) {        super(name);        this.number = number        this.gender = gender    }    hello() {        return `Hello ${this.name}, Number ${this.number}`;    }    grade(){        return 'A'    }}


          |?聯(lián)合類型


          通常情況下一個變量一個類型是不夠用的,所以會需要指定多種類型,這時候就需要用到聯(lián)合類型符號


          let variate: number | string // 這樣就能支持number類型與string類型


          &?交叉類型


          有時候新的類型是兩個舊類型的并集,這時候可以用交叉類型運算符生成新的類型,而不必重新申明


          let variate: IVariate & { data: string } // 會生成新的類型,其中包含 first second data 三個key

          as、<>類型斷言


          <>斷言寫在前面variate,但因為與JSX語法會有沖突,所以一般使用as語法,as語法寫在后面variate as string。


          有的場景我們要使用一個類型的內部值,但TS又會報錯時,這時候可以直接強制將類型收縮。


          某些條件下外部傳入的值始終符合預期無需判斷,這時候我們也可以使用斷言。


          interface IA {  first: string;  second: number;}interface IB {  name: string;  age: number;}function func(data: IA | IB) {    if((data as IB).name) // ...省略    else return // ...省略}
          function func2(data: IA) { (data as any as IB).name // 當原本沒有標注類型IB時,無法直接斷言,可以使用兩次斷言強制指定一個類型}


          is關鍵字


          is可以將類型收縮成某一類型


          function isNumber(x: any): x is number {  return typeof x === 'number'}


          !關鍵字


          某些場景類型定義變量是包含undefined的,但我們使用的時候確定這時候無需判斷,可以使用!斷言來消除警告提示。


          interface IVariate {  first: string;  second: number;}let variate: IVariate | undefinedlet second = variate!.second


          type?類型別名


          類型別名與interface一樣,但比interface更加強大通用。
          比如類型字面量想要重新起一個名稱方便使用


          type color = 'Red' | 'Green' | 'Blue'


          甚至可以直接給原始類型起別名


          type name = string


          或是給interface再起別名


          type funcParams = IParams


          也可以使用&、|


          type funcParams2 = IParams & IParams2type funcParams2 = IParams | IParams2





          泛型


          此前的基礎概念大多數熟悉面向對象編程語言的可以很快就上手,但到泛型一塊,概念性的東西是多了起來,語法也越漸變得復雜起來,TS制定了一套語法,除了寫業(yè)務邏輯,我們還需要對JS類型進行編程。


          為什么會有泛型呢?在一般使用中,類型相對固定,是可預期的。但如果要寫通用組件時,我們沒辦法完全預期傳入的類型和返回類型,有一些返回類型可能是需要用戶自定義的,這時候就需要用到泛型來讓用戶在外部標注類型。


          泛型符號


          跟斷言很像,但括號這里是在使用時傳入的定義類型,命名為T,其中符號可以隨意自定義,但有一些常用的關鍵字:T為type,U為T的下一位,K為key,V為Value,N為Number,E為Element


          使用時類似這樣:


          function func(arg1: T, arg2: U): [T, U] {    return [arg1, arg2]}const func = (arg1: T, arg2: U) => [arg1, arg2]
          class People { name: string; data: T}
          // 接口中使用泛型interface IArg { arg1: T; arg2: U;}


          基礎泛型工具


          keyof獲取鍵名工具


          要處理的類型往往是一個集合,所以需要有一個工具可以獲取集合中的鍵名、鍵值


          interface IParams {  first: string;  second: number;}
          // 獲取鍵名type keyList = keyof IParams; // "first" | "second"// 獲取鍵值type keyList = IParams[keyof IParams]; // string | number

          in映射工具

          in可以將keyof每一次循環(huán)出的值映射給新的變量。
          比如我們遇到了一個新場景,同樣是使用IParams類型,但其中所有參數是可選的,并非默認必選的,這時候我們新建一個重復的類型就很麻煩,可以使用in keyof來將舊類型轉換為新類型


          // 可以使用type構建我們處理工具Partialtype Partial = {  [P in keyof T]?: T[P]}// 當做泛型,將定義的類型傳入type IParamsPartial = Partial // 是不是有類型編程的味道了
          // 去除可選可用-號標識type Required = { [P in keyof T]-?: T[P]}type IParamsRequired = Required

          extends繼承

          同樣的邏輯,可以用來約束泛型的格式。用U extends TU繼承自T,具有T中的定義,所以泛型傳入的參數必須實現(xiàn)T中的定義

          // 我們約束傳入的值必須帶name屬性 并且值類型為stinginterface IArg {  name: string}function func(arg: T){  return arg}
          // 使用func<{age: number}>({age: 1}) // 當傳入泛型的類型不符合約束時會提示錯誤 Type '{ age: number; }' does not satisfy the constraint 'IArg'. Property 'name' is missing in type '{ age: number; }' but required in type 'IArg'
          func<{name: string, age: number}>({name: 'name', age: 1}) // 只有泛型類型加上指定的name: string時才會正常


          可以與keyof組合使用。


          比如我們創(chuàng)建一個函數,第一個參數傳入一個對象,第二個參數傳入對象的鍵名,返回此鍵名對應的值。此時鍵名參數就是動態(tài)的了,寫any無法達到類型檢測的目的,可以使用extends keyof來進行約束



          function getObjectValue(Object: T, key: K) {  return Object[key];}
          const lsit = { a: 1, b: 2, c: 3 }getObjectValue(lsit, 'a'); // 通過 返回1getObjectValue(lsit, 'e'); // 報錯 Argument of type '"e"' is not assignable to parameter of type '"a" | "b" | "c"'.

          infer待推斷變量工具


          infer必須與extends結合使用,語句格式T extends (infer U)?true : false,(infer U)部分就是我們填寫將要匹配的類型推斷主體,類型T滿足類型U,執(zhí)行true中的邏輯,否則執(zhí)行false中的邏輯。


          手冊的例子就很好的展示了多種類型的推斷匹配:


          type Unpacked =    T extends (infer U)[] ? U : // 如果傳入的是數組 則將數組的類型命名為U 并且返回U類型    T extends (...args: any[]) => infer U ? U :  // 如果傳入的是函數 則將函數的返回值命名為U 并且返回U類型    T extends Promise ? U :  // 如果傳入的是Promise 則將Promise的泛型參數命名為U 并且返回U類型    T;  // 如果所有皆否 則返回T類型
          type T0 = Unpacked; // stringtype T1 = Unpacked; // stringtype T2 = Unpacked<() => string>; // stringtype T3 = Unpacked>; // stringtype T4 = Unpacked[]>; // Promisetype T5 = Unpacked[]>>; // string


          內置泛型工具


          TS提供了一些常用的類型工具。

          Partial?(TypeScript 2.1)

          將傳入的類型的所有屬性設置為可選。

          type Partial = {  [P in keyof T]?: T[P]}

          Required?(TypeScript 2.8)

          將傳入的類型的所有屬性設置為必選。

          type Required = {  [P in keyof T]-?: T[P]}

          Readonly?(TypeScript 2.1)

          將傳入的類型的所有屬性設置為只讀。


          type Readonly = {  readonly [P in keyof T]: T[P]}Record (TypeScript 2.1)

          將傳入的K類型,重新定義為T類型。


          type Record = {  [P in K]: T;}Exclude (TypeScript 2.8)

          從T類型中排除所有可以賦值給U的類型,生成新類型

          type Exclude = T extends U ? never : T;
          Extract?(TypeScript 2.8)

          從T類型中提取所有可以賦值給U的類型,生成新類型。


          type Extract = T extends U ? T : never;
          Pick?(TypeScript 2.1)

          從T類型中提取K鍵名的元素,生成新類型。

          type Pick = {  [P in K]: T[P];}

          Omit?(TypeScript 3.5)

          從T類型中排除K鍵名的元素,生成新類型。

          type Omit = Pick<  T,  Exclude>

          NonNullable?(TypeScript 2.8)

          從T類型中排除ull或者undefined類型,生成新類型。


          type NonNullable = T extends null | undefined ? never : T;ReturnType (TypeScript 2.8)

          獲取一個函數類型定義的返回類型。


          type ReturnType any> = T extends (...args: any[]) => infer R ? R : any;
          Parameters

          獲取一個函數的參數類型。

          type Parameters any> = T extends (...args: infer P) => any ? P : never;
          ConstructorParameters

          獲取一個構造函數的參數類型,以數組格式返回。


          type ConstructorParameters any> = T extends new (...args: infer P) => any ? P : never;
          InstanceType?(TypeScript 2.8)


          獲取一個類的實例類型。


          type InstanceType any> = T extends new (...args: any[]) => infer R ? R : any;


          相關資料


          TypeScript文檔:https://www.tslang.cn/docs/home.html

          TyepScript Handbook:

          https://www.typescriptlang.org/docs/handbook/intro.html
          TypeScript Handbook 中文版:

          https://zhongsp.gitbooks.io/typescript-handbook/content/

          TypeScript Deep Dive:https://basarat.gitbook.io/typescript/
          TypeScript Deep Dive 中文版:https://jkchao.github.io/typescript-book-chinese/
          TypeScript入門教程:https://ts.xcatliu.com/
          TypeScript Web版:https://www.typescriptlang.org/zh/play
          TypeScript 內置工具泛型:

          https://github.com/chenxiaochun/blog/issues/67





          點擊左下角閱讀原文,到?SegmentFault 思否社區(qū)?和文章作者展開更多互動和交流。

          -?END -


          瀏覽 70
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产乱妇无码毛片A片在线看下载 | 国产精品无码成人久久久 | 熟女视频网 | 色五月婷婷导航 | 人人干人人操人人模 |