内置类型

作者: shuaiutopia | 来源:发表于2020-06-29 13:52 被阅读0次

    问题引出

    我们需要渲染一个表格,往往需要定义:

    interface Row {
      user: string
      email: string
      id: number
      vip: boolean
      // ...
    }
    
    const tableDatas: Row[] = []
    

    有时候我们也需要表格对应的搜索表单,需要其中一两个搜索项,如果刚接触 typescript 的同学可能会立刻这样写:

    interface SearchModel {
      user?: string
      id?: number 
    }  
    const model: SearchModel = {
      user: '',
      id: undefined 
    }
    

    这样写会出现一个问题,如果后面 id 类型要改成 string,我们需要改 2 处地方,不小心的话可能就会忘了改另外一处。所以,有些人会这样写:

    interface SearchModel {
      user?: Row['user']
      id?: Row['id']
    } 
    

    这固然是一个解决方法,但事实上,我们前面已经定义了 Row 类型,这其实是可以更优雅地复用的:

    // Partial 和 Pick 为内置类型
    const model: Partial<Row> = {
      user: '',
      id: undefined 
    }
    // 或者需要明确指定 key 的,可以
    const model2: Partial<Pick<Row, 'user'|'id'>>
    

    上面使用到的 PartialPick 都是 typescript 内置的类型别名。

    内置类型

    Partial<T>

    将类型 T 的所有属性标记为可选属性

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

    使用场景:

    // 账号属性
    interface AccountInfo {
        name: string 
        email: string 
        age: number 
        vip: 0|1 // 1 是vip ,0 是非vip
    }
    
    // 当我们需要渲染一个账号表格时,我们需要定义
    const accountList: AccountInfo[] = []
    
    // 但当我们需要查询过滤账号信息,需要通过表单,
    // 但明显我们可能并不一定需要用到所有属性进行搜索,此时可以定义
    const model: Partial<AccountInfo> = {
      name: '',
      vip: undefind
    }
    

    Required<T>

    Partial 相反,Required 将类型 T 的所有属性标记为必选属性

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

    Readonly<T>

    将所有属性标记为 readonly, 即不能修改

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

    Pick<T, K>

    从 T 中过滤出属性 K

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

    使用场景:

    interface AccountInfo {
      name: string 
      email: string 
      age: number 
      vip?: 0|1 // 1 是vip ,0 是非vip
    }
    
    type CoreInfo = Pick<AccountInfo, 'name' | 'email'>
    /* 
    { 
      name: string
      email: string
    }
    */
    

    Record<K, T>

    同时标记对象的 keyvalue 的类型

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

    使用场景:

    // 定义 学号(key)-账号信息(value) 的对象
    const accountMap: Record<number, AccountInfo> = {
      10001: {
        name: 'xx',
        email: 'xxxxx',
        // ...
      }    
    }
    const user: Record<'name'|'email', string> = {
        name: '', 
        email: ''
    }
    // 复杂点的类型推断
    function mapObject<K extends string | number, T, U>(obj: Record<K, T>, f: (x: T) => U): Record<K, U>
    
    const names = { foo: "hello", bar: "world", baz: "bye" };
    // 此处推断 K, T 值为 string , U 为 number
    const lengths = mapObject(names, s => s.length);  // { foo: number, bar: number, baz: number }
    

    Exclude<T, U>,Omit<T, K>

    移除 T 中的 U 属性

    type Exclude<T, U> = T extends U ? never : T;
    

    使用场景:

    // 'a' | 'd'
    type A = Exclude<'a'|'b'|'c'|'d' ,'b'|'c'|'e' >  
    

    乍一看好像这个没啥卵用,但是,我们通过一番操作,之后就可以得到 Pick 的反操作:

    type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
    
    type NonCoreInfo = Omit<AccountInfo, 'name' | 'email'>
    /*
    {
      age: number 
      vip: 0|1,
    }
    */
    

    Extract<T, U>

    Exclude 的反操作,取 T,U 两者的交集属性

    type Extract<T, U> = T extends U ? T : never;
    

    使用 demo:

    // 'b'|'c'
    type A = Extract<'a'|'b'|'c'|'d' ,'b'|'c'|'e' >  
    

    这个看起来没啥用,实际上还真没啥卵用,应该是我才疏学浅,还没发掘到其用途。

    NonNullable<T>

    排除类型 Tnull | undefined 属性

    type NonNullable<T> = T extends null | undefined ? never : T;
    

    使用 demo

    type A = string | number | undefined 
    type B = NonNullable<A> // string | number
    
    function f2<T extends string | undefined>(x: T, y: NonNullable<T>) {
        let s1: string = x;  // Error, x 可能为 undefined
        let s2: string = y;  // Ok
    }
    

    Parameters<T>

    获取一个函数的所有参数类型

    // 此处使用 infer P 将参数定为待推断类型
    // T 符合函数特征时,返回参数类型,否则返回 never
    type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
    

    使用demo:

    interface IFunc {
      (person: IPerson, count: number): boolean
    }
    
    type P = Parameters<IFunc> // [IPerson, number]
    
    const person01: P[0] = {
      // ...
    }
    

    另一种使用场景是,快速获取未知函数的参数类型

    import { somefun } from 'somelib'
    // 从其他库导入的一个函数,获取其参数类型
    type SomeFuncParams = Parameters<typeof somefun>
    
    // 内置函数
    // [any, number?, number?]
    type FillParams = Parameters<typeof Array.prototype.fill>
    

    ConstructorParameters<T>

    类似于 Parameters<T>, ConstructorParameters 获取一个类的构造函数参数

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

    使用 demo:

    // string | number | Date 
    type DateConstrParams = ConstructorParameters<typeof Date>
    

    ReturnType<T>

    获取函数类型 T 的返回类型

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

    使用方式和 Parameters<T> 类似,不再赘述

    InstanceType<T>

    获取一个类的返回类型

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

    使用方式和 ConstructorParameters<T> 类似,不再赘述

    相关文章

      网友评论

        本文标题:内置类型

        本文链接:https://www.haomeiwen.com/subject/upuofktx.html