Skip to content

Latest commit

 

History

History

1097-IsUnion-medium

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 

1097 Is Union

关键词: TypeScript, TypeChallenge

实现IsUnion类, 当传入参数是联合类型时返回 true, 否则返回 false

举个例子

type case1 = IsUnion<string>; // false
type case2 = IsUnion<string | number>; // true
type case3 = IsUnion<[string | number]>; // false

答案

type IsUnion<T, C = T> = T extends any
  ? C[] extends T[]
    ? false
    : true
  : never;

分析

利用联合类型分配的性质, 我们可以提取联合类型中的每一项, 记作t.
此后, 假如传入类型T不为联合类型, T extends t是成立的, 反之则不成立, 利用这一点我们可以判断T是否为联合类型

// 通过`C`类型保存下原始的 T
type IsUnion<T, C = T> = T extends any ? (C extends T ? false : true) : never;
// -------------------------------------------------^ 此处的T已经是原来 T 中的一项了

但是这样判断得不到要求的结果, 传入非联合类型会返回 false 没问题, 但是传入联合类型则返回boolean, 为什么呢?

原因是在C extends T的判断中, C也被分配了, 返回的结果是T中各项是否互相extends 的排列组合, 举例, 假如传入string|number:

// 化简一步
type IsUnion = string | number extends any
  ? string | number extends T
    ? false
    : true
  : never;

// 化简两步, 将 T 分配为两种情况
type IsUnion =
  | (string | number extends string ? false : true)
  | (string | number extends number ? false : true);

// 化简三步, 将 C 分配为两种情况
type IsUnion =
  | (string extends string ? false : true)
  | (number extends string ? false : true)
  | (string extends string ? false : true)
  | (number extends number ? false : true);
type IsUnion = true | false; // = boolean

所以, 我们要避免这里C被分配, 将其包装一层即可:

type IsUnion<T, C = T> = T extends any
  ? C[] extends T[] // 将 C 包装为数组类型, 就不会被分配了
    ? false
    : true
  : never;