# 源码解读utility-types

GitHub: github.com/hubvue

### SetIntersection

``````type SetIntersection<A, B> = A extends B ? A : never

``````type SetIntersectionResult = SetIntersection<'a' | 'b' | 'c', 'c' | 'b'> // 'b' | 'c'

``````'a' | 'b' | 'c' extends 'c' | 'b' ? 'a' | 'b' | 'c' : never =>
('a' extends 'c' | 'b' ? 'a' : never) |
('b' extends 'c' | 'b' ? 'b' : never) |
('c' extends 'c' | 'b' ? 'c' : never) =>
never | 'b' | 'c' => 'b' | 'c'

### SetDifference

``````type SetDifference<A, B> = A extends B ? never : A

``````type SetDifferenceResult = SetDifference<'a' | 'b' | 'c', 'b'> // 'a' | 'c'

``````'a' | 'b' | 'c' extends 'b' ? never : 'a' | 'b' | 'c' =>
('a' extends 'b' ? never : 'a') |
('b' extends 'b' ? never : 'b') |
('c' extends 'b' ? never : 'c') =>
'a' | never | 'c' => 'a' | 'c'

``````type SetComplement<A, A1 extends A> = A extends A1 ? never : A

### SymmetricDifference

`SymmetricDifference`用于获取类型 A、B 的交集在并集上的补集，多用于联合类型。

``````type SymmetricDifference<A, B> = SetDifference<A | B, SetIntersection<A, B>>

emmmm...有点绕，看个 🌰 吧

``````type SymmtricDifferenceResult = SymmetricDifference<
'1' | '2' | '3',
'2' | '3' | '4'
> // '1' | '4'

### NonUndefined

`NonUndefined`类型用于过滤掉联合类型中的 undefined 类型。

``````type NonUndefined<T> = T extends undefined ? never : T

``````type NonUndefined<T> = SetDifference<T, undefined>

``````type NonUndefinedResult = NonUndefined<string | null | undefined> // string | null

### FunctionKeys

`FunctionKeys`用于获取对象类型中值为函数的 key。

``````type FunctionKeys<T extends object> = {
[K in keyof T]-?: NonUndefined<T[K]> extends Function ? K : never
}[keyof T]

``````type MixedProps = {
name: string
setName: (name: string) => void
someKeys?: string
someFn?: (...args: any) => any
undef: undefined
unNull: null
}
type FunctionKeysResult = FunctionKeys<MixedProps> //"setName" | "someFn" | "undef"

`FunctionKeys`接受的是一个对象类型，因此可以使用索引查询操作符遍历对象类型的每一个 key 值，遍历过程中首先通过`NonUndefined`过滤掉 undefined 类型，然后 extends Function，检测可兼容 Function 类型，那么这个 key 的值类型就是一个函数类型，但是当值类型为 undefined 的时候，会被`NonUndefined`解为 never，然而 Function 类型是兼容 never 的。所以`undef`就被保留了下来。

``````type FunctionKeys<T extends object> = {
[P in keyof T]-?: SetIntersection<NonNullable<T[P]>, Function> extends never
? never
: P
}[keyof T]

### NonFunctionKeys

`NonFunctionKeys`用于获取对象类型中值不为函数的 key

``````type NonFunctionKeys<T extends Object> = {
[P in keyof T]-?: NonUndefined<T[P]> extends Function ? never : P
}[keyof T]

``````type NonFunctionKeysResult = NonFunctionKeys<MixedProps> //"name" | "someKeys" | "unNull"

### IfEquals

IfEquals 是一个辅助类型函数，用于判断两个类型是否相同。

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

``````type Same<X, Y> = X extends Y ? (Y extends X ? true : false) : false

``````type X = {
name: string
age: number
}
type Y = {
age: number
}

``````type SameResult = Same<X, Y> //true
type IfEqualsResult = IfEquals<X, Y> //never

`IfEquals`类型函数的核心就是使用了延时条件类型，在兼容性推断的时候依赖了内部类型的一致性检查。`IfEquals`内部最少依赖了两个泛型参数，`X``Y`，在传入`X``Y`泛型参数后，对类型进行推断，如果能推断出结果就返回最终的类型，否则就延时推断过程，等待确认的类型参数传进来后再进行类型推断。

`IfEquals`类型函数一样，构造一个延时条件类型很简单，只需要构建一个函数类型并且将函数的返回值构建成依赖泛型参数的条件类型就可以了。

``````type DeferConditionalType = <T>(value: T) => T extends string ? number : boolean

### WriteableKeys

`WriteableKeys` 用于获取对象类型中所有可写的 key。

``````export type WriteableKeys<T extends object> = {
[P in keyof T]-?: IfEquals<
{ [Q in P]: T[P] },
{ -readonly [Q in P]: T[P] },
P
>
}[keyof T]

``````type Props = { readonly foo: string; bar: number }

type WriteableKeysResult = WriteableKeys<Props> // "bar"

`ReadonlyKeys` 用于获取对象类型中所有被 readonly 修饰的 key。

``````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]

``````type Props = { readonly foo: string; bar: number }

`ReadonlyKeys` 的实现方式和 `WriteableKeys` 的实现方式基本相同，区别在于 `IfEquals` 函数的第三、四个参数。在 `WriteableKeys` 中，第三个参数是 key，第四个参数默认是 `never`，而在 `ReadonlyKeys` 中颠倒过来了，原因是，当两个类型匹配成功后，则认定这两个类型是严格相同的，那么就表示当前 key 是不被 readonly 修饰的，所以在 `WriteableKeys` 中返回 key、在 `ReadonlyKeys` 中返回 never；当两个类型匹配不成功后，则认定这两个类型是不相同的。

RequiredKeys `RequiredKeys` 用于获取对象类型中所有必选的 key。

``````export type RequiredKeys<T extends object> = {
[P in keyof T]-?: {} extends Pick<T, P> ? never : P
}[keyof T]

``````type RequiredProps = {
req: number
reqUndef: number | undefined
opt?: string
optUndef?: number | undefined
}

type RequiredKeysResult = RequiredKeys<RequiredProps> //"req" | "reqUndef"

`RequiredKeys` 中用到了 Pick，首先说下 Pick 是干嘛的

`Pick` 是 Typescript 内置的泛型函数，接受两个 T, U，第一个参数 T 是一个对象类型，第二个参数 U 是联合类型，并且 U extends keyof T。Pick 用于过滤掉泛型 T 中不能兼容 U 的 key。

``````type Props = {
req: number
reqUndef: number | undefined
opt?: string
optUndef?: number | undefined
}
type result = Pick<Props, 'req' | 'opt'> //  {req: number,opt?: string}

### OptionalKeys

`OptionalKeys` 用于获取对象类型上所有可选的 key。

``````export type OptionalKeys<T extends object> = {
[P in keyof T]-?: {} extends Pick<T, P> ? P : never
}[keyof T]

``````type RequiredProps = {
req: number
reqUndef: number | undefined
opt?: string
optUndef?: number | undefined
}
type OptionalKeysResult = OptionalKeys<RequiredProps> // "opt" | "optUndef"

`OptionalKeys` 的实现方式和 `RequiredKeys` 基本相同，区别在于条件类型的取值是相当的，具体细节可以看下 `RequiredKeys` 的实现分析。

### PickByValue

``````export type PickByValue<T, K> = Pick<
T,
{
[P in keyof T]-?: T[P] extends K ? P : never
}[keyof T]
>

``````type PickByValueProps = {
req: number
reqUndef: number | undefined
opt?: string
}

type PickByValueResult = PickByValue<PickByValueProps, number> //{req: number; reqUndef: number | undefined; }

``````type PickByValue<T, K> = Pick<T, ...>

``````type PickByValue<T, K> = Pick<T, {
[P in keyof T]: ...
}[keyof T]>

### PickByValueExact

`PickByValueExact``PickByValue` 的严格版

``````export type PickByValueExact<T, ValueType> = Pick<
T,
{
[Key in keyof T]-?: [ValueType] extends [T[Key]]
? [T[Key]] extends [ValueType]
? Key
: never
: never
}[keyof T]
>

``````export type PickByValueExact<T, K> = Pick<
T,
{
[P in keyof T]-?: IfEquals<[K], [T[P]], P>
}[keyof T]
>

``````type PickByValueProps = {
req: number
reqUndef: number | string
opt?: string
}

type PickByValueExactResult = PickByValueExact<PickByValueProps, number> //{req: number;}

### Omit

`Omit` 的作用就是反向 Pick，删除泛型 A 中可匹配泛型 B 的 key。

``````export type Omit<A, B extends keyof A> = Pick<A, Exclude<keyof A, B>>

``````type OmitProps = {
name: string
age: number
visible: boolean
sex: string | number
}

// {
//     name: string;
//     visible: boolean;
//     sex: string | number;
// }
type OmitResult = Omit<OmitProps, 'age'>

### OmitByValue

``````export type OmitByValue<T, U> = Pick<
T,
{
[P in keyof T]: T[P] extends U ? never : P
}
>

``````type OmitProps = {
name: string
age: number
visible: boolean
sex: string | number
}
// {
//     age: number;
//     visible: boolean;
//     sex: string | number;
// }
type OmitByValueResult = OmitByValue<OmitProps, string>

`PickByValue` 类似，只是将 extends 的结果交换了位置，就可以实现反向操作，具体思路请看 `PickByValue` 的分析。

### OmitByValueExact

``````export type OmitByValueExact<T, ValueType> = Pick<
T,
{
[Key in keyof T]-?: [ValueType] extends [T[Key]]
? [T[Key]] extends [ValueType]
? never
: Key
: Key
}[keyof T]
>

``````export type OmitByValueExact<A, B> = Pick<
A,
{
[P in keyof A]-?: IfEquals<A[P], B, never, P>
}[keyof A]
>

``````type OmitProps = {
name: string
age: number
visible: boolean
sex: string | number
}
// {
//   name: string
//   age: number
//   visible: boolean
// }
type OmitByValueExactResult = OmitByValueExact<OmitProps, string | number>

### Intersection

`Intersection` 用于获取对象类型 key 的交集。

``````export type Intersection<T extends object, U extends object> = Pick<
T,
Extract<keyof T, keyof U> & Extract<keyof U, keyof T>
>

``````type IntersectionProps = {
name: string
age: number
visible: boolean
value: number
}
type DefaultProps = { age: number; value: number }
// {
//     age: number;
//     value: number;
// }
type IntersectionResult = Intersection<IntersectionProps, DefaultProps>

`Intersection` 类型函数接受`<A,B>`两个对象类型，最终得到的是两个对象类型 key 的交集在 A 上的 Pick。 所以我们只要先解两个对象类型 key 的交集，然后再对 A 进行 Pick 就 ok 了。

### Diff

`Diff` 类型函数接受两个泛型变量 T、U，且 T、U 都是对象类型，用于获取泛型 U 在泛型 T 上的补集。

``````export type Diff<T extends object, U extends object> = Pick<
T,
Exclude<keyof T, keyof U>
>

``````type Props = {
name: string
age: number
visible: boolean
value: number
}
type Props2 = { age: number; value: number }
// {
//     name: string;
//     visible: boolean;
// }
type DiffResult = Diff<Props, Props2>

### Overwrite

`Overwrite` 接收两个泛型参数 T、U，且都为对象类型，作用是若 U 中属性在 T 中也存在，则覆盖 T 中的属性。

``````export type Overwrite<
T extends object,
U extends Object,
I = Diff<T, U> & Intersection<U, T>
> = Pick<I, keyof I>

``````type Props1 = { name: string; age: number; visible: boolean }
type Props2 = { age: string; other: string }

// {
//   name: string
//   age: string
//   visible: boolean
// }
type OverwriteResult = Overwrite<Props1, Props2>

1. 使用 Pick
``````type OverwriteResult = Overwrite<Props1, Props2>
//  =>
// {
//   name: string
//   age: string
//   visible: boolean
// }

1. 不使用 Pick
``````export type Overwrite<T extends object, U extends Object> = Diff<T, U> &
Intersection<U, T>
type OverwriteResult = Overwrite<Props1, Props2>
// => Pick<OverwriteProps, "name" | "visible"> & Pick<NewProps, "age">

### Assign

`Assign``Overwrite` 的能力更强大一些。它接收两个泛型参数 T、U，且都为对象类型，作用是若 U 中的属性在 T 中存在则覆盖，不存在则添加。

``````export type Assign<
T extends object,
U extends object,
I = Diff<T, U> & Intersection<U, T> & Diff<U, T>
> = Pick<I, keyof I>

``````type Props1 = { name: string; age: number; visible: boolean }
type Props2 = { age: string; other: string }
// {
//     name: string;
//     age: string;
//     visible: boolean;
//     other: string;
// }
type AssignResult = Assign<Props1, Props2>

`Assign` 在实现上与 `Overwrite` 区别是在处理 I 上比 `Overwrite` 多&了`Diff<U, T>``Overwrite` 的作用是覆盖已有元素，那么实现 `Assign` 只需要将在 T 上不存在的属性合并到 T 上就 ok 了，因此就可以使用`Diff<U, T>`的方式获取到在 U 上而不再 T 上的属性，最后与前面和为交叉类型。

### Unionize

`Unionize` 接收一个泛型参数，且为对象类型，作用是将对象类型转为单独 key 对象的联合类型。

``````export type Unionize<T extends object> = {
[P in keyof T]: { [Q in P]: T[P] }
}[keyof T]

``````type Props = { name: string; age: number; visible: boolean }
// {
//     name: string;
// } | {
//     age: number;
// } | {
//     visible: boolean;
// }
type UnionizeResult = Unionize<Props>

### PromiseType

PromiseType 用于获取 Promise 的泛型类型。

``````export type PromiseType<T extends Promise<unknown>> = T extends Promise<infer V>
? V
: never

``````// string
type PromiseTypeResult = PromiseType<Promise<string>>

`PromiseType` 中用到了 infer，infer 的作用是在条件类型中做延时推断，infer 用到绝佳可以实现强大的功能。

`PromiseType` 将泛型 T extends Promise，并在 Promise 泛型类型使用 infer 推断其类型，若 T 为 Promise 类型，则 V 就是 Promise 的泛型类型，否则为 never。

`utility-types``DeepX`递归类型基本上相同，`X`的逻辑在上面已经分析过了，主要分析是 `Deep` 逻辑。

``````export type DeepReadonly<T> = T extends ((...args: any[]) => any) | Primitive
? T
: T
}

``````type Props = {
first?: {
second?: {
name?: string
}
}
}

### Optional

`Optional` 接收两个泛型参数 T、K，且 T 为对象类型，K 为 T 所有 key 联合类型的子集，作用是 T 中可兼容 K 的属性转换为可选的，默认是全部。

``````export type Optional<
T extends object,
K extends keyof T = keyof T,
I = Omit<T, K> & Partial<Pick<T, K>>
> = Pick<I, keyof I>

``````type Props = {
first: string
second: number
third: boolean
}
// {
//   first?: string
//   second?: number
//   third: boolean
// }
type OptionsalResult = Optional<Props, 'first' | 'second'>

### ValuesType

`ValuesType` 接收一个泛型参数，可以是数组或对象，用于获取值的联合类型。数组在这里较多的指元组，因为普通数组所有元素的类型相同，就没必要联合了。

``````export type ValuesType<
T extends Array<any> | ReadonlyArray<any> | ArrayLike<any> | object
> = T extends Array<any> | ReadonlyArray<any> | ArrayLike<any>
? T[number]
: T extends object
? T[keyof T]
: never

``````type Props = {
first: string
second: number
third: boolean
}
// string | number | boolean
type ValuesTypeResult = ValuesType<Props>

`ValuesType` 处理参数主要分为两部分：对数组的处理和对对象的处理。对数组的处理使用`T[number]`非常优雅，并且是元组类型转联合类型最简单的方式；对对象的处理用的就比较多了，使用索引操作符就可以了。

### ArgumentsRequired

`ArgumentsRequired``Optional` 类似，用于将对象的某些属性变成必选的

``````export type ArgumentsRequired<
T extends object,
K extends keyof T = keyof T,
I = Omit<T, K> & Required<Pick<T, K>>
> = Pick<I, keyof I>

``````type Props = {
name?: string
age?: number
visible?: boolean
}
// {
//   name: string
//   age: number
//   visible: boolean
// }
type ArgumentsRequiredResult = ArgumentsRequired<Props>

### TupleToUnion

`ValuesType` 中已经提到一个特别简单的方式。还有一种方式也值得学习一下。

``````// 'true'
type ret = [number, string] extends Array<any> ? 'true' : 'false'

``````export type TupleToUnion<T extends any[]> = T extends Array<infer U> ? U : never

``````// string | number
type TupleToUnionResult = TupleToUnion<[string, number]>

### UnionToIntersection

UnionToIntersection 用于将联合类型转为交叉类型

``````export type UnionToIntersection<T> = (T extends any
? (arg: T) => void
: never) extends (arg: infer V) => void
? V
: never

``````type UnionToIntersectionResult = UnionToIntersection<
{ name: string } | { age: number } | { visible: boolean }
>

UnionToIntersection 这个泛型函数还是要好好理解的，这里用到了 TypeScript 类型系统中的概念，`同一类型变量的多个候选类型将会被推断为交叉类型`，这是 TS 类型系统函数参数位置逆变的知识。逆变与协变这篇文章说的很清晰，可以深入了解一下。