溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

使用typescript的小技巧有哪些

發(fā)布時間:2021-08-26 13:59:09 來源:億速云 閱讀:130 作者:小新 欄目:開發(fā)技術(shù)

小編給大家分享一下使用typescript的小技巧有哪些,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

    函數(shù)重載

    當(dāng)希望傳 user 參數(shù)時,不傳 flag,傳 para 時,傳 flag。就可以這樣寫:

    interface User {
      name: string;
      age: number;
    }
    
    const user = {
      name: 'Jack',
      age: 123
    };
    
    class SomeClass {
    
      public test(para: User): number;
      public test(para: number, flag: boolean): number;
    
      public test(para: User | number, flag?: boolean): number {
        // 具體實(shí)現(xiàn)
        return 1;
      }
    }
    
    const someClass = new SomeClass();
    
    // ok
    someClass.test(user);
    someClass.test(123, false);
    
    // Error
    // someClass.test(123); 
    //Argument of type 'number' is not assignable to parameter of type 'User'.
    // someClass.test(user, false);
    //Argument of type '{ name: string; age: number; }' is not assignable to parameter of type 'number'.

    映射類型

    在了解映射類型之前,需要了解 keyof, never, typeof, in。

    keyof:keyof 取 interface 的鍵

    interface Point {
        x: number;
        y: number;
    }
    
    // type keys = "x" | "y"
    type keys = keyof Point;

    never:永遠(yuǎn)不存在的值的類型

    官方描述:

    the never type represents the type of values that never occur.

    // 例子:進(jìn)行編譯時的全面的檢查
    type Foo = string | number;
    
    function controlFlowAnalysisWithNever(foo: Foo) {
      if (typeof foo === "string") {
        // 這里 foo 被收窄為 string 類型
      } else if (typeof foo === "number") {
        // 這里 foo 被收窄為 number 類型
      } else {
        // foo 在這里是 never
        const check: never = foo;
      }
    }

    使用 never 避免出現(xiàn)新增了聯(lián)合類型沒有對應(yīng)的實(shí)現(xiàn),目的就是寫出類型絕對安全的代碼。

    typeof:取某個值的 type

    const a: number = 3
    
    // 相當(dāng)于: const b: number = 4
    const b: typeof a = 4

    in:檢查一個對象上是否存在一個屬性

    interface A {
      x: number;
    }
    
    interface B {
      y: string;
    }
    
    function doStuff(q: A | B) {
      if ('x' in q) {
        // q: A
      } else {
        // q: B
      }
    }

    映射類型就是將一個類型映射成另外一個類型,簡單理解就是新類型以相同的形式去轉(zhuǎn)換舊類型的每個屬性。

    Partial, Readonly, Nullable, Required

    • Partial 將每個屬性轉(zhuǎn)換為可選屬性

    • Readonly 將每個屬性轉(zhuǎn)換為只讀屬性

    • Nullable 轉(zhuǎn)換為舊類型和null的聯(lián)合類型

    • Required 將每個屬性轉(zhuǎn)換為必選屬性

    type Partial<T> = {
        [P in keyof T]?: T[P];
    }
    
    type Readonly<T> = {
        readonly [P in keyof T]: T[P];
    }
    
    type Nullable<T> = { 
      [P in keyof T]: T[P] | null 
    }
    
    type Required<T> = {
      [P in keyof T]-?: T[P]
    }
    
    interface Person {
        name: string;
        age: number;
    }
    
    type PersonPartial = Partial<Person>;
    type PersonReadonly = Readonly<Person>;
    type PersonNullable = Nullable<Person>;
    
    type PersonPartial = {
        name?: string | undefined;
        age?: number | undefined;
    }
    
    type PersonReadonly = {
        readonly name: string;
        readonly age: number;
    }
    
    type PersonNullable = {
          name: string | null;
          age: number | null;
    }
    
    interface Props {
      a?: number;
      b?: string;
    }
    
    const obj: Props = { a: 5 };
    
    const obj2: Required<Props> = { a: 5 };
    // Property 'b' is missing in type '{ a: number; }' but required in type 'Required<Props>'.

    Pick, Record

    • Pick 選取一組屬性指定新類型

    • Record 創(chuàng)建一組屬性指定新類型,常用來聲明普通Object對象

    type Pick<T, K extends keyof T> = {
      [P in K]: T[P];
    }
    
    type Record<K extends keyof any, T> = {
      [P in K]: T;
    }
    
    interface Todo {
      title: string;
      description: string;
      completed: boolean;
    }
    
    type TodoPreview = Pick<Todo, "title" | "completed">;
    
    const todo: TodoPreview = {
      title: "Clean room",
      completed: false,
    };
    
    todo; // = const todo: TodoPreview
    
    
    interface PageInfo {
      title: string;
    }
    
    type Page = "home" | "about" | "contact";
    
    const nav: Record<Page, PageInfo> = {
      about: { title: "title1" },
      contact: { title: "title2" },
      home: { title: "title3" },
    };
    
    nav.about; // = const nav: Record

    Exclude, Omit

    • Exclude 去除交集,返回剩余的部分

    • Omit 適用于鍵值對對象的Exclude,去除類型中包含的鍵值對

    type Exclude<T, U> = T extends U ? never : T
    type Omit = Pick<T, Exclude<keyof T, K>>
    
    // 相當(dāng)于: type A = 'a'
    type A = Exclude<'x' | 'a', 'x' | 'y' | 'z'>
    
    interface Todo {
      title: string;
      description: string;
      completed: boolean;
    }
    
    type TodoPreview = Omit<Todo, "description">;
    
    const todo: TodoPreview = {
      title: "a",
      completed: false,
    };

    ReturnType

    獲取返回值類型,一般為函數(shù)

    type ReturnType<T extends (...args: any) => any>
      = T extends (...args: any) => infer R ? R : any;
    
    declare function f1(): { a: number; b: string };
    type T1 = ReturnType<typeof f1>;
    //    type T1 = {
    //        a: number;
    //        b: string;
    //    }

    還有很多映射類型,可查看Utility Types參考。

    類型斷言

    類型斷言用來明確的告訴 typescript 值的詳細(xì)類型,合理使用能減少我們的工作量。

    比如一個變量并沒有初始值,但是我們知道它的類型信息(它可能是從后端返回)有什么辦法既能正確推導(dǎo)類型信息,又能正常運(yùn)行了?有一種網(wǎng)上的推薦方式是設(shè)置初始值,然后使用 typeof 拿到類型(可能會給其他地方用)。也可以使用類型斷言可以解決這類問題:

    interface User { 
        name: string; 
        age: number; 
    }
    
    export default class someClass { 
        private user = {} as User;
    }

    枚舉

    枚舉類型分為數(shù)字類型與字符串類型,其中數(shù)字類型的枚舉可以當(dāng)標(biāo)志使用:

    enum AnimalFlags {
        None = 0, 
        HasClaws = 1 << 0, 
        CanFly = 1 << 1, 
        HasClawsOrCanFly = HasClaws | CanFly 
    }
    
    interface Animal { 
        flags: AnimalFlags; 
       [key: string]: any; 
    }
    
    function printAnimalAbilities(animal: Animal) { 
        var animalFlags = animal.flags; 
        if (animalFlags & AnimalFlags.HasClaws) { 
            console.log('animal has claws'); 
        } 
        if (animalFlags & AnimalFlags.CanFly) { 
            console.log('animal can fly'); 
        } 
        if (animalFlags == AnimalFlags.None) { 
            console.log('nothing'); 
        } 
    }
    
    var animal = { flags: AnimalFlags.None }; 
    printAnimalAbilities(animal); // nothing 
    animal.flags |= AnimalFlags.HasClaws; 
    printAnimalAbilities(animal); // animal has claws 
    animal.flags &= ~AnimalFlags.HasClaws; 
    printAnimalAbilities(animal); // nothing 
    animal.flags |= AnimalFlags.HasClaws | AnimalFlags.CanFly; 
    printAnimalAbilities(animal); // animal has claws, animal can fly
    • 使用 |= 來添加一個標(biāo)志;

    • 組合使用 &= 和 ~ 來清理一個標(biāo)志;

    • | 來合并標(biāo)志。

    這個或許不常用,在 typescript 關(guān)于 types 源碼中我們也可以看到類似的代碼:

    使用typescript的小技巧有哪些

    字符串類型的枚舉可以維護(hù)常量:

    const enum TODO_STATUS {
      TODO = 'TODO',
      DONE = 'DONE',
      DOING = 'DOING'
    }
    
    function todos (status: TODO_STATUS): Todo[];
    
    todos(TODO_STATUS.TODO)

    元組

    表示一個已知元素數(shù)量和類型的數(shù)組,各元素的類型不必相同。

    let x: [string, number];
    x = ['hello', 10];

    在發(fā)出不固定多個請求時,可以應(yīng)用:

    const requestList: any[] = [http.get<A>('http://some.1')]; // 設(shè)置為 any[] 類型 
    if (flag) { 
        requestList[1] = (http.get<B>('http://some.2')); 
    } 
    const [ { data: a }, response ] = await Promise.all(requestList) as [Response<A>, Response<B>?]

    范型

    在定義泛型后,有兩種方式使用,一種是傳入泛型類型,另一種使用類型推斷。

    declare function fn<T>(arg: T): T; // 定義一個泛型函數(shù) 
    const fn1 = fn<string>('hello'); // 第一種方式,傳入泛型類型 
    string const fn2 = fn(1); // 第二種方式,從參數(shù) arg 傳入的類型 number,來推斷出泛型 T 的類型是 number

    一個扁平數(shù)組結(jié)構(gòu)建樹形結(jié)構(gòu)例子:

    // 轉(zhuǎn)換前數(shù)據(jù) 
    const arr = [ 
    { id: 1, parentId: 0, name: 'test1'}, 
    { id: 2, parentId: 1, name: 'test2'}, 
    { id: 3, parentId: 0, name: 'test3'} 
    ]; 
    // 轉(zhuǎn)化后 
    [ { id: 1, parentId: 0, name: 'test1', 
        childrenList: [ { id: 2, parentId: 1, name: 'test2', childrenList: [] } ] }, 
        { id: 3, parentId: 0, name: 'test3', childrenList: [] } 
    ]
    
    
    interface Item { 
        id: number; 
        parentId: number; 
        name: string; 
    }
    
    // 傳入的 options 參數(shù)中,得到 childrenKey 的類型,然后再傳給 TreeItem
    
    interface Options<T extends string> { 
        childrenKey: T; 
    } 
    type TreeItem<T extends string> = Item & { [key in T]: TreeItem<T>[] | [] }; 
    declare function listToTree<T extends string = 'children'>(list: Item[], options: Options<T>): TreeItem<T>[]; 
    listToTree(arr, { childrenKey: 'childrenList' }).forEach(i => i.childrenList)

    infer

    表示在 extends 條件語句中待推斷的類型變量。

    type ParamType<T> = T extends (param: infer P) => any ? P : T;

    這句話的意思是:如果 T 能賦值給 (param: infer P) => any,則結(jié)果是 (param: infer P) => any 類型中的參數(shù) P,否則返回為 T。

    interface User { 
        name: string; 
        age: number; 
    } 
    type Func = (user: User) => void 
    type Param = ParamType<Func>; // Param = User 
    type AA = ParamType<string>; // string

    例子:

    // [string, number] -> string | number
    type ElementOf<T> = T extends Array<infer E> ? E : never;
    
    type TTuple = [string, number];
    
    type ToUnion = ElementOf<TTuple>; // string | number
    
    
    // T1 | T2 -> T1 & T2
    type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
    
    type Result = UnionToIntersection<T1 | T2>; // T1 & T2

    以上是“使用typescript的小技巧有哪些”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

    向AI問一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI