最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • TS的高级类型编程

    正文概述 掘金(胡志武98)   2020-11-24   1075

    1.索引类型

    keyof 索引类型查询操作符,可以获取泛型T上所有的 public 属性名构成联合类型

    class Person {
        name: string = "胡先生"
        age: number = 18
        private UserId: number = 123
    }
    
    type PropNames = keyof Person
    

    TS的高级类型编程
    注意: 

    1. keyof只能获取泛型上的 public 属性名,属性名为字符串类型,
    2. keyof返回的是联合类型

    [] 索引类型访问操作符,类似js访问对象的某个属性值的语法,在TS中,它可以用来访问某个属性的类型

    class Person {
        name: string = "胡先生"
        age: number = 18
        time: Date = new Date()
        private UserId: number = 123
    }
    
    type PropNames = keyof Person // "name"|"age"|"time"
    
    type NameType = Person["name"]
    
    type PropTypes = Person[PropNames]
    

    TS的高级类型编程
    T["属性名"] 可以单独获取某个属性名的类型
    TS的高级类型编程
    T[keyof T] 可以拿到泛型T中所有 public属性的类型 的联合类型

    有了上面的基础后,我们可以实现下 pick 函数,pick函数可以从对象上取出指定的属性

    function pick<T, K extends keyof T>(obj: T, prop: K): T[K] {
        return obj[prop]
    }
    

    K extends keyof T 指K可以赋值 给泛型T所有public属性 的联合类型,T[K]返回的就是泛型T上的 属性类型

    const User = {
        name: "胡先生",
        age: 18,
        id: 12345
    }
    const nameValue = pick(User, "name")//胡先生
    

    TS的高级类型编程
    如图
    keyof T = "name"|"age"|"id"  
    所以K = "name"|"age"|"id" ,即prop的可选属性是"name"|"age"|"id"

    2.映射类型

    映射类型的语法是:[K in Keys] ,类似JS的数组方法forEach,遍历keys,并将值赋值给K
    上面我们已经实现了一个pick函数,现在我们想实现一个TS工具类型MyPick,从泛型T中挑选出需要的属性

    //定义一个泛型T,并且要从T中选出需要的属性,则要定义K 的类型是T所有public **属性名** 的联合类型
    type MyPick<T,K extends keyof T>={
    // K是一个联合类型,我们需要遍历K,使用映射类型的语法[K in Keys]
      [P in K]:T[P]// P是属性名,T[p]则可以拿到属性类型
    }
    
    type MyPick<T,K extends keyof T>={
        [P in K]:T[P]
    }
    
    interface User{
        name:string,
        age:number,
        id:number
    }
    
    type name = MyPick<User,"name">
    

    TS的高级类型编程

    TS工具类型中有个 Partial ,可以将所有类型变成可选的

    // 定义一个泛型T
    type MyPartial<T>={
      // keyof T 可以拿到泛型T中所有pubilc的属性名
      // in 可以遍历所有属性名,并将属性名赋值给K
      // 则T[K]就是属性类型
      //?代表可选
      
      [K in keyof T]?:T[K]
    }
    
    type User = {
        id: number,
        name: string
    }
    
    type PartialUser = MyPartial<User>
    

    TS的高级类型编程

    3.条件类型

    T extends U ? X : Y 如果T 能赋值给U,则返回类型X 否则返回类型Y

    type IsStringOrNumber<T> = T extends string ? string : number
    // 字符串"123"传递给了T,即"123"如果能赋值给string,则返回string,否正返回number
    type str = IsStringOrNumber<"123">
    

    TS的高级类型编程
    注意 :是 T能不能赋值给U,而不是T是不是U类型,因为U可能是any,是所有类型的子类型,例如 type IsStringOrNumber = T extends any ? string : number 只要T是能赋值给any的任何类型,该表达式就只返回string类型

    3.1条件类型和联合类型

    条件类型和联合类型结合,可以形成 分布式有条件类型,举个例子

    // 在T中找到不存在U中的类型,并返回
    type Diff<T, U> = T extends U ? never : T
    
    type DiffType = Diff<number | string | boolean, undefined | string>
    

    TS的高级类型编程
    上面的代码相当于

    type DiffType2 = Diff<number, undefined | string> 
    		| Diff<string, undefined | string>
                    | Diff<boolean, undefined | string>
    

    TS的高级类型编程
    当T类型是联合类型number | string | boolean ,会分别将number | string | boolean

    赋值给Diff<T,U>
    注意 :

    1. 只有裸类型参数才能实现分布式有条件类型,
    2. 裸类型参数,即类型参数不能被包裹在其他类型中,如数组,元组,函数,Promise等
    // 泛型T被包裹在[T],所以不在是裸类型参数了
    type Diff<T, U> = [T] extends [U] ? never : T
    
    type DiffType = Diff<number | string | boolean, undefined | string>
    

    TS的高级类型编程
    当T被数组包裹[T]时,就不能实现分布式有条件类型了,上面代码等同于

    type DiffType = [number | string | boolean] extends [undefined | string] 
    	? never
        	: number | string | boolean 
    

    TS的高级类型编程

    3.2 条件类型与映射类型

    现在我们实现一个类型工具,取出必选类型

    interface User {
        id?: number
        name: string
        age: number
    }
    
    
    type NullableKeys<T> = {
      	// 如果k是id,T[k]=number|undefined
    	// 所以只要undefined extends T[k] 就可以知道K是可选属性
        [K in keyof T]-?: undefined extends T[K] ? never : K
    }[keyof T]
    
    type keys = NullableKeys<User>
    // [keyof T] = ["id"|"name"|"age"]
    

    TS的高级类型编程
    注意 :

    1. + - 用于映射类型中,给属性添加修饰符,-?即是减去可选的,将可续属性变成必选,-readonly将可读属性变成非只读
    2. 如果不写 -? ,虽然通过 undefined extends T[k] 找到了属性id,并且属性id的值被赋值为never,但是因为没有去除 ? ,TS会默认给 id 添加上 undefined 的值
    type NullableKeys<T> = {
      //不加-?
        [K in keyof T]: undefined extends T[K] ? never : K
    }//注意这里没有keyof了
    
    type keys = NullableKeys<User>
    

    TS的高级类型编程

    如图,不添加-?,id被赋值为undefined

    type NullableKeys<T> = {
      //加-?
        [K in keyof T]-?: undefined extends T[K] ? never : K
    }//注意这里没有keyof了
    
    type keys = NullableKeys<User>
    

    TS的高级类型编程

    4. infer

    infer可以在条件语句中充当类待推断的类型变量
    假如我们想知道一个数组中的元素类型,我们可能会这么做

    type ElementOf<T> = T extends Array<string> ? string : T extends Array<number> ? number : ...
    

    按上面的写法,需要写出每个类型的条件语句,太过繁琐 我们可以看到数组的元素类型是一个变量,我们可以使用infer去声明这个变量

    type ElementOf<T> = T extends Array<infer E> ? E :never
    
    type strArr= Array<string>
    
    type arrType = ElementOf<strArr>
    

    TS的高级类型编程

    5. 阅读utility-types的源码

    github.com/piotrwitek/…

    1.IfEquals

    type IfEquals<X, Y, A = X, B = never> = (<T>() => T extends X ? 1 : 2)
    	extends 
    	<T>() => T extends Y ? 1 : 2
      	? A
      	: B;
    

    如果泛型X 和 泛型 Y相同,则返回 A,否正返回B

    1. (<T>()=> T extends X ? 1 : 2)这是一个表达式,不会执行
    2. (<T>()=> T extends Y ? 1 : 2) ,同上这是一个表达式
    3. 如果两条表达式相同,则返回A,否则返回B

    2.MutableKeys

    /*
     * @example
     *   type Props = { readonly foo: string; bar: number };
     *
     *   // Expect: "bar"
     *   type Keys = MutableKeys<Props>;
     */
    export type MutableKeys<T extends object> = {
      [P in keyof T]-?: IfEquals<
        { [Q in P]: T[P] },
        { -readonly [Q in P]: T[P] },// -readonly 是将只读属性变成非只读属性
        P
      >;
    }[keyof T];
    
    1. 上面有疑问的地方是为啥需要[Q in P]:T[P],Q其实没有使用到,但如果没有写[Q in P],我们只能拿到P这个字符串,而不能拿到readonly这个属性
    2. 详情可以看这篇知乎www.zhihu.com/question/36…

    3. ReadonlyKeys

    /*
     * @example
     *   type Props = { readonly foo: string; bar: number };
     *
     *   // Expect: "foo"
     *   type Keys = ReadonlyKeys<Props>;
     */
    export type ReadonlyKeys<T extends object> = {
      [P in keyof T]-?: IfEquals<
        { [Q in P]: T[P] },
        { -readonly [Q in P]: T[P] },
        never,
        P
      >;
    }[keyof T];
    
    

    4.NonUndefined

    /** 
     * @example
     *   // Expect: "string | null"
     *   SymmetricDifference<string | null | undefined>;
     */
    export type NonUndefined<A> = A extends undefined ? never : A;
    

    5. FunctionKeys

    /** 
     * @example
     *  type MixedProps = {name: string; setName: (name: string) => void; someKeys?: string; someFn?: (...args: any) => any;};
     *
     *   // Expect: "setName | someFn"
     *   type Keys = FunctionKeys<MixedProps>;
     */
    export type FunctionKeys<T extends object> = {
      [K in keyof T]-?: NonUndefined<T[K]> extends Function ? K : never;
    }[keyof T];
    
    
    1. T extends object 要求泛型T 必须能赋值给object,这样才能遍历T的属性名
    2. [K in keyof T] 遍历T的所有public属性名,并将属性名赋值给K
    3. -? T的每个字段都是必选,使得TS不会自动给字段赋值undefined
    4. 如果某个函数是一个可选属性,则 T[K] = ()=>{}|undefined ,那T[K] extends Function便不成立,所以需要用 NonUndefined 去除undefined的值

    6.Pick

    type Pick<T, K extends keyof T> = {
        [P in K]: T[P];
    };
    
    interface Person{
      name:string,
      id:number,
      age:number
    }
    
    type NewPerson = Pick<Person,"name"|"age">//{name:string,age:number}
    

    7. RequiredKeys

    /**
     * @example
     *   type Props = { req: number; reqUndef: number | undefined; opt?: string; optUndef?: number | undefined; };
     *
     *   // Expect: "req" | "reqUndef"
     *   type Keys = RequiredKeys<Props>;
     */
    export type RequiredKeys<T> = {
      [K in keyof T]-?: {} extends Pick<T, K> ? never : K;
    }[keyof T];
    
    1. {} extends {id:number} ? true : false 会返回 false ,因为id是必选属性,而 {} 对象中没有
    2. {} extends {id?:number} ? true : false 会返回 true ,因为id是可选属性,空对象也能赋值
    3. {} extends Pick<T, K> ? never : K ,如果T[K]是可选属性则返回never,否则返回K
    4. -? 是让可选属性变成必选,不然 ,K?:neverK前面有个 ? TS会将其变成 K?:never|undefined ,这样子[keyof T]取出的值就含有undefined ,所以要加上 -? 

    8.PickByValue

    /**
     * @example
     *   type Props = { req: number; reqUndef: number | undefined; opt?: string; };
     *
     *   // Expect: { req: number }
     *   type Props = PickByValue<Props, number>;
     *   // Expect: { req: number; reqUndef: number | undefined; }
     *   type Props = PickByValue<Props, number | undefined>;
     */
    export type PickByValue<T, ValueType> = Pick<
      T,
      { [Key in keyof T]-?: T[Key] extends ValueType ? Key : never }[keyof T]
    >;
    
    1. Pick的第二个参数是一个联合类型,所以需要 [keyof T]  拿到泛型T的所有属性类型
    2. T[Key] extends ValueType ? Key : never 泛型T的属性类型能赋值给ValueType则返回Key,否则返回never
    3. 由1可知拿到属性类型,2可知[key in keyof T]:Key 可知{ [Key in keyof T]-?: T[Key] extends ValueType ? Key : never }[keyof T] 可以拿到T的所有属性名

    9. SetDifference

    /*
     * SetDifference (same as Exclude)
     * @desc Set difference of given union types `A` and `B`
     * @example
     *   // Expect: "1"
     *   SetDifference<'1' | '2' | '3', '2' | '3' | '4'>;
     *
     *   // Expect: string | number
     *   SetDifference<string | number | (() => void), Function>;
     */
    export type SetDifference<A, B> = A extends B ? never : A;
    

    10.Omit

    /*
     * @example
     *   type Props = { name: string; age: number; visible: boolean };
     *
     *   // Expect: { name: string; visible: boolean; }
     *   type Props = Omit<Props, 'age'>;
     */
    export type Omit<T, K extends keyof any> = Pick<T, SetDifference<keyof T, K>>;
    
    1. keyof T 返回T的属性名的联合类型
    2. SetDifference<keyof T,K> 从T中找到不存在K中的属性名的联合类型C
    3. Pick<T,C> 从T中找到属性名在C中的属性

    11.Intersection

    /* @example
     *   type Props = { name: string; age: number; visible: boolean };
     *   type DefaultProps = { age: number };
     *
     *   // Expect: { age: number; }
     *   type DuplicateProps = Intersection<Props, DefaultProps>;
     */
    export type Intersection<T extends object, U extends object> = Pick<
      T,
      Extract<keyof T, keyof U> & Extract<keyof U, keyof T>
    >;
    
    1. type Extract<T, U> = T extends U ? T : never; 
    2. Extract<keyof T, keyof U> & Extract<keyof U, keyof T> 可以拿到T和U的属性的交集

    12. Diff

    /* @example
     *   type Props = { name: string; age: number; visible: boolean };
     *   type DefaultProps = { age: number };
     *
     *   // Expect: { name: string; visible: boolean; }
     *   type DiffProps = Diff<Props, DefaultProps>;
     */
    export type Diff<T extends object, U extends object> = Pick<
      T,
      SetDifference<keyof T, keyof U>
    >;
    
    1. SetDifference<keyof T,keyof U> 从T中找到不存在U中的属性的联合类型

    13.DeepPartial

    export type DeepPartial<T> = T extends Function
      ? T
      : T extends Array<infer U>
      ? _DeepPartialArray<U>
      : T extends object
      ? _DeepPartialObject<T>
      : T | undefined;
    /** @private */
    // tslint:disable-next-line:class-name
    export interface _DeepPartialArray<T> extends Array<DeepPartial<T>> {}
    /** @private */
    export type _DeepPartialObject<T> = { [P in keyof T]?: DeepPartial<T[P]> };
    
    1. T extends Function 判断T是不是可以赋值给Function,是的话返回T,
    2. T extends Array<infer u> 判断T是不是可以赋值给Array,因为不知道是什么类型的Array,所以用 infer U 作为一个类型变量代指,当T可以赋值给Array时,我们需要递归Array中的每一项,Array<DeepPartial<T>> 
    3. T extends Object 判断T是不是可以赋值给Object,是的话,我们需要递归遍历Object中的每个属性, [P in keyof T]  遍历T的属性名, DeepPartial<T[P]> 将T 的属性类型传进 DeepPartial 进行递归

    结语

    如果有错漏的地方,还请看官们指正


    起源地下载网 » TS的高级类型编程

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元