特殊类型的特性

类型的判断要根据它的特性来

  • 比如判断联合类型就是根据它的 distributive 的特性

    IsAny

    any 类型与任何类型的交叉都是 any,就是 1 & any 结果是 any
    1. type IsAny<T> = 1 extends ('1' & T) ? true : false;

    IsEqual

    之前的实现是
    type IsEqual<A, B> = (A extends B ? true : false) & (B extends A ? true : false);
    
    但会有问题在 any 判断上,因为 any 可以是任何类型,任何类型也是 any,所以这样判断不出 any 类型。要改为
    type IsEqual<A, B> = (<T>() => T extends A ? 1 : 2) extends (<T>() => T extends B ? 1 : 2)
        ? true : false;
    

    IsUnion

    要根据遇到条件类型时会分散成单个传入做计算的特性,A 是单个类型, B是整个联合类型,根据 [B] extends [A] 是否成立来判断是联合类型:
    type IsUnion<A, B = A> = 
          A extends A
                  ? [B] extends [A]
                          ? false
                          : true
                  : never;
    

    IsNever

    never 在tuwr类型中也比较特殊,如果条件类型左边是类型参数,并且传入的是 never,那么直接返回 never。要判断 never 类型不能直接 T extends number,需要这样写:
    type IsNever<T> = [T] extends [never] ? true : false;
    

    IsTuple

    元组类型也是数组类型,但每个元素都是只读的,并且 length 是数字字面量,而数组的 length 是 number。
  1. 元组类型也是数组类型,并且每个元素都是只读
  2. 元组和数组的 length 属性值是有区别
    type len = [1,2,3]['length']; // type len = 3
    type len2 = number[]['length']; // type len2 = number
    
    根据这两个特性来判断元组类型: ```typescript type NotEqual = (() => T extends A ? 1 : 2) extends (() => T extends B ? 1 : 2) ? false : true;

type IsTuple = T extends reaonly […params: infer Eles] ? NotEqual : false;

<a name="tHWl9"></a>
## UnionToIntersection
> 类型之间是有大小关系的,比如 A 和 B 的交叉类型 A & B 就比联合类型 A | B 小。
> 如果从大的类型变为小的类型,那就是类型的缩小,叫做**逆变**。
> 如果从小的类型变为大的类型,那就是类型的放大,叫做**协变**。
> 如果大小没变化,叫做**不变**。

```typescript
type UnionToIntersection<U> = 
        (U extends U ? (x: U) => unknown: never) extends (x: infer R) => unknown
                ? R
                : never;

U 是 Union 联合类型, U extends U 触发联合类型的分布式 distributive 性质,让每个类型单独传入做计算,最后合并。
利用 U 做参数构造个函数,通过模式匹配取参数的类型。
结果就是交叉类型。

GetOptional

可选索引的值为 undefined 和值类型的联合类型。

type GetOptional<Obj extends Record<string, any>> = {
        [    
              key in keyof Obj
                      as {} extends Pick<Obj, Key> ? Key : never
      ]    : Obj[Key];
};

GetRequired

过滤所有非可选的索引构造成新的索引类型

type isRequired<Key extends keyof Obj, Obj> = 
    {} extends Pick<Obj, Key> ? never : Key;

type GetRequired<Obj extends Record<string, any>> = { 
    [Key in keyof Obj as isRequired<Key, Obj>]: Obj[Key] 
}

RemoveIndexSignature

索引是 string 类型,而可索引签名的不是

type RemoveIndexSignature<Obj extends Record<string, any>> = {
  [
      Key in keyof Obj 
          as Key extends `${infer Str}`? Str : never
  ]: Obj[Key]
}

ClassPublicProps

keyof 只能拿到 class 的 public 索引,private 和 protected 的索引会被忽略。

type ClassPublicProps<Obj extends Record<string, any>> = {
    [Key in keyof Obj]: Obj[Key]    
}

在判断或者过滤类型的时候会用到:

  • any 类型与任何类型的交叉都是 any,也就是 1 & any 结果是 any,可以用这个特性判断 any 类型。
  • 联合类型作为类型参数出现在条件类型左侧时,会分散成单个类型传入,最后合并。
  • never 作为类型参数出现在条件类型左侧时,会直接返回 never。
  • any 作为类型参数出现在条件类型左侧时,会直接返回 trueType 和 falseType 的联合类型。
  • 元组类型也是数组类型,但每个元素都是只读的,并且 length 是数字字面量,而数组的 length 是 number。可以用来判断元组类型。
  • 函数参数处会发生逆变,也就是类型缩小,可以用来实现联合类型转交叉类型。
  • 可选索引的值为 undefined 和值类型的联合类型。可以用来过滤可选索引,反过来也可以过滤非可选索引。
  • 索引类型的索引一般为 string 类型,而可索引签名不是,可以用这个特性过滤掉可索引签名。
  • keyof 只能拿到 class 的 public 的索引,可以用来过滤出 public 的属性。