1、OptionalKeys
获取对象类型中的可选属性的联合类型
type a1 = OptionalKeys<{ foo: number | undefined, bar?: string, flag: boolean }> // bar
type a2 = OptionalKeys<{ foo: number, bar?: string }> // bar
type a3 = OptionalKeys<{ foo: number, flag: boolean }> // never
type a4 = OptionalKeys<{ foo?: number, flag?: boolean }> // foo|flag
type a5 = OptionalKeys<{}> // never
实现:
type Equals<X, Y> =
(<T>() => T extends X ? 1 : 2) extends
(<U>() => U extends Y ? 1 : 2) ? true : false
//遍历T的K,如果原始的属性K与加可选`?`构造对象相同,说明此属性为可选属性
type OptionalKeys<T> = {
[K in keyof T]: Equals<{ [F in K]: T[F] },
{ [F in K]?: T[F] }> extends true ? K : never
}[keyof T]
//另外一种思路:就是如果去除一个属性还能与原对象相同,说明就是可选属性
type Omit<T, K> = Pick<T, Exclude<keyof T, K>>
type OptionalKeys<T> = {
[K in keyof T]: Omit<T, K> extends T ? K : never
}[keyof T]
2、PickOptional
保留一个对象中的可选属性类型
type a1 = PickOptional<{ foo: number | undefined, bar?: string, flag: boolean }> // {bar?:string|undefined}
type a2 = PickOptional<{ foo: number, bar?: string }> // {bar?:string}
type a3 = PickOptional<{ foo: number, flag: boolean }> // {}
type a4 = PickOptional<{ foo?: number, flag?: boolean }> // {foo?:number,flag?:boolean}
type a5 = PickOptional<{}> // {}
实现:
type Equals<X, Y> =
(<T>() => T extends X ? 1 : 2) extends
(<U>() => U extends Y ? 1 : 2) ? true : false
type OptionalKeys<T> = {
[K in keyof T]: Equals<{ [F in K]: T[F] },
{ [F in K]?: T[F] }> extends true ? K : never
}[keyof T]
//关键就是实现OptionalKeys类型,看上图有两种处理方式
type PickOptional<T> = {
[K in OptionalKeys<T>]?: T[K]
}
3、RequiredKeys
保留一个对象中的必须属性
type a1 = PickRequired<{ foo: number | undefined, bar?: string, flag: boolean }> // {foo:number|undefined,flag:boolean}
type a2 = PickRequired<{ foo: number, bar?: string }> // {foo:number}
type a3 = PickRequired<{ foo: number, flag: boolean }> // {foo:number,flag:boolean}
type a4 = PickRequired<{ foo?: number, flag?: boolean }> // {}
type a5 = PickRequired<{}> // {}
实现:
type Omit<T, K> = Pick<T, Exclude<keyof T, K>>
type RequiredKeys<T> = {
[K in keyof T]: Omit<T, K> extends T ? never : K
}[keyof T]
4、PickRequired
保留一个对象中的必须属性
type a1 = PickRequired<{ foo: number | undefined, bar?: string, flag: boolean }> // {foo:number|undefined,flag:boolean}
type a2 = PickRequired<{ foo: number, bar?: string }> // {foo:number}
type a3 = PickRequired<{ foo: number, flag: boolean }> // {foo:number,flag:boolean}
type a4 = PickRequired<{ foo?: number, flag?: boolean }> // {}
type a5 = PickRequired<{}> // {}
实现:
type Omit<T, K> = Pick<T, Exclude<keyof T, K>>
type OptionalKeys<T> = {
[K in keyof T]: Omit<T, K> extends T ? K : never
}[keyof T]
//需要采用提出可选属性的方式获得必须属性,否则联合类型的值,undefined会消失(foo: number | undefined)
type PickRequired<T> = Omit<T, OptionalKeys<T>>
5、Merge
合并两个对象类型
T
以及K
,如果属性重复,则以K
中属性类型为准
type obj1 = {
el: string,
age: number
}
type obj2 = {
el: HTMLElement,
flag: boolean
}
type obj3 = Merge<obj1, obj2> // {el:HtmlElement,age:number,flag:boolean}
const a = {...{} as obj3}
console.log(a.el.scrollTop, a.age.toFixed(0), a.flag.valueOf())
// console.log(a.el.charAt(0)) // error
实现:
//优先返回U中的键值对方式
type Merge<T extends object, U extends object> = {
[key in (keyof T | keyof U)]: key extends keyof U ? U[key] : key extends keyof T ? T[key] : never
}
6、IsNever
判断是否为never类型
type A = IsNever<never> // true
type B = IsNever<string> // false
type C = IsNever<undefined> // false
type D = IsNever<any> // false
实现:
type IsNever<T> = [T, never] extends [never, T] ? true : false
7、IsEmptyType
判断是否为没有属性的对象类型
{}
type A = IsEmptyType<string> // false
type B = IsEmptyType<{ a: 3 }> // false
type C = IsEmptyType<{}> // true
type D = IsEmptyType<any> // false
type E = IsEmptyType<object> // false
type F = IsEmptyType<Object> // false
type G = IsEmptyType<unknown> // false
实现:
type IsEmptyType<T> = T extends object ? [keyof T] extends [] ? true : false : false
8、IsAny
判断是否为any类型
type A = IsAny<string> // false
type B = IsAny<any> // true
type C = IsAny<unknown> // false
type D = IsAny<never> // false
实现:
// unknown 只能赋值给 any或者unknown
// any可以赋值给string,但是unknown不可以赋值给string
type IsAny<T> = [unknown] extends [T] ? [T] extends [string] ? true : false : false
//还有一种更加简洁的写法,通过any可以被赋值任意类型,而其它不行的特性
type IsAny<T> = 0 extends (1 & T) ? true : false
9、Redux Connect
实现Connect类型,能够自动地转化Redux Module对象中的函数类型
interface Module {
count: number;
message: string;
asyncMethod<T, U>(input: Promise<T>): Promise<Action<U>>;
syncMethod<T, U>(action: Action<T>): Action<U>;
}
interface Action<T> {
payload?: T;
type: string;
}
// 这个要求的结果
type Result = {
asyncMethod<T, U>(input: T): Action<U>;
syncMethod<T, U>(action: T): Action<U>;
}
// 实现类型Connect,要求 Connect<Module> 的结果为上面的 Result
// 只要函数类型的属性;
// 如果函数是异步函数,要求自动解析出来Promise中的类型;
实现:
//获取普通属性的key
type NormalKeys<T> = {
[K in keyof T]: T[K] extends (...args: any) => any ? never : K
}[keyof T]
//剔除非函数属性
type FunctionOptional<T> = Omit<T, NormalKeys<T>>
type InferConnectFunctionParameterType<Func> =
Func extends (<T, U>(input: Promise<T>) => Promise<Action<U>>) ? (<T, U>(input: T) => Action<U>) :
Func extends (<T, U>(action: Action<T>) => Action<U>) ? (<T, U>(action: T) => Action<U>) : never
type Connect<T extends object> = {
[K in keyof FunctionOptional<T>]: InferConnectFunctionParameterType<T[K]>
}
10、UnionToBooleanProps
有且只有一个属性
// 实现一个叫做 UnionToBooleanProps 的泛型,使得以下需求成立
type MessageStringType = "info" | "success" | "warning" | "error";
type OneMessageTypes = UnionToBooleanProps<MessageStringType>
type Props = OneMessageTypes & { id: string; }
function Component(props: Props) {
return <></>
}
const a = <Component id="abc" info/> //correct
const b = <Component id="abc" success/> //correct
const c = <Component id="abc"/> //wrong
const d = <Component id="abc" info success/> //wrong
// 组件Component所接收的属性,有且只有一个 "info" | "success" | "warning" | "error" 中的值;
实现:
//联合类型extends时会分发,利用此特性生成如下结构
type UnionToBooleanProps<T extends string, U extends string = T> =
T extends any ? { [k in Exclude<U, T>]?: void } & { [k in T]: boolean } : never
/**
{
info: boolean;
success?: void;
warning?: void;
error?: void
} | {
info?: void;
success: boolean;
warning?: void;
error?: void
} | {
info?: void;
success?: void;
warning: boolean;
error?: void
} | {
info?: void;
success?: void;
warning?: void;
error: boolean
}
*/
11、UnionToIntersection
将联合类型转换为交叉类型
type A = UnionToIntersection<{a: string} | {b: string} | {c: string}>
// {a: string} & {b: string} & {c: string}
实现:
type UnionToIntersection<T> = (T extends infer R ? (x: R) => any : never) extends (x: infer V) => any ? V : never
/**
* 1. T extends infer R ? (x: R) => any : never 将生成一个函数的联合类型,就像下面这样:
* (x: {a: string}) => any | (x: {b: string}) => any | (x: {c: string}) => any
*
* 2. extends (x: infer V) => any ? V : never 将会寻找联合函数的超集,而满足条件的就是下面这样的函数
* (x: {a: string} & {b: string} & {c: string}) => any
*/
这道题解答看的github:https://github.com/type-challenges/type-challenges/issues/775
12、UnionPop
得到联合类型中的最后一个类型
type a = 1 | 2 | 3
type b = UnionPop<a>; // 3
实现:
//将联合类型转成函数类型的交叉 ((arg: "1") => void) & ((arg: "2") => void) & ((arg: "3") => void)
type UnionToIntersectionFn<T> = (T extends any ? (k: (arg: T) => void) => void : never) extends (
(k: infer I) => void) ? I : never
//函数类型的 intersection 等价于 接口的 call signature 重载
//这里有个TS特性,https://github.com/type-challenges/type-challenges/issues/737#issuecomment-791505468
//如果有必要从重载中输出一种类型,TS会选择重载中的最后一个签名
type UnionPop<U> = UnionToIntersectionFn<U> extends { (a: infer A): void; } ? A : never;
小结:
这个与11题都是利用了协变与逆变。针对于最后这三题的解答可以查看:https://zhuanlan.zhihu.com/p/58704376
13、UnionToTuple
联合类型转换为元组类型
type a = UnionToTuple<1 | 2 | 3> // [1,2,3]
type b = UnionToTuple<1 | string | boolean> // [1,string,boolean]
实现:
type UnionToIntersectionFn<T> = (T extends any ? (k: (arg: T) => void) => void : never) extends (
(k: infer I) => void) ? I : never
type UnionPop<U> = UnionToIntersectionFn<U> extends { (a: infer A): void; } ? A : never;
type UnionToTuple<T, Last = UnionPop<T>> = [T] extends [never] ? [] : [...UnionToTuple<Exclude<T, Last>>, Last]