# 白话typescript中的【extends】和【infer】

## extends

typescript 2.8引入了条件类型关键字: extends，长这个样子:

``````T extends U ? X : Y

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

// 如果泛型参数 T 为 null 或 undefined，那么取 never，否则直接返回T。
let demo1: NonNullable<number>; // => number
let demo2: NonNullable<string>; // => string
let demo3: NonNullable<undefined | null>; // => never

### 分配式extends

``````T extends U ? X : Y

(a + b) * c => ac + bc

``````type Diff<T, U> = T extends U ? never : T; // 找出T的差集
type Filter<T, U> = T extends U ? T : never; // 找出交集

type T30 = Diff<"a" | "b" | "c" | "d", "a" | "c" | "f">;  // => "b" | "d"
// <"a" | "b" | "c" | "d", "a" | "c" | "f">
// 相当于
// <'a', "a" | "c" | "f"> |
// <'b', "a" | "c" | "f"> |
// <'c', "a" | "c" | "f"> |
// <'d', "a" | "c" | "f">
type T31 = Filter<"a" | "b" | "c" | "d", "a" | "c" | "f">;  // => "a" | "c"
// <"a" | "b" | "c" | "d", "a" | "c" | "f"> 同上

let demo1: Diff<number, string>; // => number

## infer

``````// 内置 ReturnType
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;

infer X 就相当于声明了一个变量，这个变量随后可以使用，是不是有点像for循环里面的声明语句？

``````for (let i = 0, len = arr.length; i < len; i++) {
// do something
}

### 例子一

``````// 解读: 如果泛型变量T是 () => infer R的`子集`，那么返回 通过infer获取到的函数返回值，否则返回boolean类型
type Func<T> = T extends () => infer R ? R : boolean;

let func1: Func<number>; // => boolean
let func2: Func<''>; // => boolean
let func3: Func<() => Promise<number>>; // => Promise<number>

### 例子二

``````// 同上，但当a、b为不同类型的时候，返回不同类型的联合类型
type Obj<T> = T extends {a: infer VType, b: infer VType} ? VType : number;

let obj1: Obj<string>; // => number
let obj2: Obj<true>; // => number
let obj3: Obj<{a: number, b: number}>; // => number
let obj4: Obj<{a: number, b: () => void}>; // => number | () => void

### 例子三(Vue3中的UnwrapRef)

``````// 如果泛型变量T是ComputedRef的'子集'，那么使用UnwrapRefSimple处理infer指代的ComputedRef泛型参数V
// 否则进一步判断是否为Ref的'子集'，进一步UnwrapRefSimple
export type UnwrapRef<T> = T extends ComputedRef<infer V>
? UnwrapRefSimple<V>
: T extends Ref<infer V> ? UnwrapRefSimple<V> : UnwrapRefSimple<T>

// 我是分割线

// 如果T为Function | CollectionTypes | BaseTypes | Ref之一的'子集'，直接返回。
// 否则判断是否为数组的'子集'，不是的话视为object，调用UnwrappedObject
type UnwrapRefSimple<T> = T extends Function | CollectionTypes | BaseTypes | Ref
? T
: T extends Array<any> ? T : T extends object ? UnwrappedObject<T> : T

// 我是分割线
// 调用UnwrapRef，产生递归效果，解决了ts类型递归
type UnwrappedObject<T> = { [P in keyof T]: UnwrapRef<T[P]> } & SymbolExtract<T>

// 我是分割线

// 泛型Ref
export interface Ref<T = any> {
[Symbol()]: true
value: T
}

// 我是分割线

export interface ComputedRef<T = any> extends WritableComputedRef<T> {
}

// 我是分割线

export interface WritableComputedRef<T> extends Ref<T> {
}

## 总结

ts提供的extends和infer大大增加了类型判断的灵活性和复用性，虽然用与不用都可以，但能熟练地使用高级特性将大大提升ts推断的效率和代码类型的可读性。