原文链接: ts utility-types 源码解读 Object operators
上一篇: ts utility-types 源码解读 Special operators
下一篇: ts 协变和逆变 一个不怎么有趣的问题
参考, 写的确实不错
https://juejin.im/post/6865910915011706887
FunctionKeys
提取值是函数的键
export type NonUndefined<A> = A extends undefined ? never : A;
export 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;
};
// Expect: "setName | someFn"
type Keys = FunctionKeys<MixedProps>;
对于undefined有问题
export type FunctionKeys<T extends object> = {
[K in keyof T]-?: Extract<NonNullable<T[K]>, Function> extends never
? never
: K;
}[keyof T];
NonFunctionKeys
提取值不是函数的键, 也对undefined有问题, 值是undefined的话提取不出来, 需要做同样的修改
export type NonFunctionKeys<T extends object> = {
[K in keyof T]-?: NonUndefined<T[K]> extends Function ? never : K;
}[keyof T];
export type NonFunctionKeys<T extends object> = {
[K in keyof T]-?: Extract<NonNullable<T[K]>, Function> extends never
? K
: never;
}[keyof T];
IfEquals 用于判断两个类型是否相等
对于采用双向extends的方案, 是无法判断属性修饰符是否相等的
type IfEquals<X, Y, A = X, B = never> = (<T>() => T extends X ? 1 : 2) extends <
T
>() => T extends Y ? 1 : 2
? A
: B;
type isSame<X, Y> = X extends Y ? (Y extends X ? true : false) : false;
type t1 = { name: string; age: number };
type t2 = { readonly name: string; age: number };
type r1 = isSame<t1, t2>; // true
type r2 = IfEquals<t1, t2>; // never
type r3 = IfEquals<t1, t2, true, false>; // false
type r4 = IfEquals<t1, t1, true, false>; // true
ReadonlyKeys / MutableKeys / WritableKeys 可写判断
export type MutableKeys<T extends object> = {
[P in keyof T]-?: IfEquals<
{ [Q in P]: T[P] },
{ -readonly [Q in P]: T[P] },
P
>;
}[keyof T];
export type WritableKeys<T extends object> = MutableKeys<T>;
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];
RequiredKeys / OptionalKeys
拿到必选或可选的键
export type RequiredKeys<T> = {
[K in keyof T]-?: {} extends Pick<T, K> ? never : K;
}[keyof T];
export type OptionalKeys<T> = {
[K in keyof T]-?: {} extends Pick<T, K> ? K : never;
}[keyof T];
判断键是否可选
type t0 = {
name?: string;
age: number;
};
type isRequired<T extends object> = {
[K in keyof T]-?: {} extends Pick<T, K> ? true : false;
};
/*
type k = {
name: true;
age: false;
}
*/
type k = isRequired<t0>;
PickByValue / PickByValueExact
根据值类型, 取键值
严格模式使用双向extend, 不过确实好像用ifEquals会比较好
export type PickByValue<T, ValueType> = Pick<
T,
{ [Key in keyof T]-?: T[Key] extends ValueType ? Key : never }[keyof T]
>;
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 PickByValueExact2<T, K> = Pick<
T,
{
[P in keyof T]-?: IfEquals<[K], [T[P]], P>;
}[keyof T]
>;
Omit / OmitByValue / OmitByValueExact
反向pick
可以用ifEquals优化
export type Omit<A, B extends keyof A> = Pick<A, Exclude<keyof A, B>>;
export type OmitByValue<T, ValueType> = Pick<
T,
{ [Key in keyof T]-?: T[Key] extends ValueType ? never : Key }[keyof T]
>;
export type OmitByValueExact<T, ValueType> = Pick<
T,
{
[Key in keyof T]-?: [ValueType] extends [T[Key]]
? [T[Key]] extends [ValueType]
? never
: Key
: Key;
}[keyof T]
>;
type OmitProps = {
name: string;
age: number;
visible: boolean;
sex: string | number;
};
/*
type t1 = {
age: number;
visible: boolean;
sex: string | number;
}
type t2 = {
visible: boolean;
}
type t3 = {
name: string;
age: number;
visible: boolean;
}
*/
type t1 = Omit<OmitProps, "name">;
type t2 = OmitByValue<OmitProps, string | number>;
type t3 = OmitByValueExact<OmitProps, string | number>;
Intersection / Diff / Subtract
对键求交集和差集, 然后pick两个后返回交叉类型
Subtract 是严格补集
export type Intersection<T extends object, U extends object> = Pick<
T,
Extract<keyof T, keyof U> & Extract<keyof U, keyof T>
>;
export type Diff<T extends object, U extends object> = Pick<
T,
Exclude<keyof T, keyof U>
>;
export type Subtract<T extends T1, T1 extends object> = Pick<
T,
SetComplement<keyof T, keyof T1>
>;
type Props = { name: string; age: number; visible: boolean };
type DefaultProps = { age: number };
// Expect: { age: number; }
type t1 = Intersection<Props, DefaultProps>;
// type t2 = {name: string; visible: boolean;}
type t2 = Diff<Props, DefaultProps>;
Overwrite
diff是 A-B的, intersection是 A交B的
重写A中在B中出现的键值, B中添加的不会加到A里面
export type Intersection<T extends object, U extends object> = Pick<
T,
Extract<keyof T, keyof U> & Extract<keyof U, keyof T>
>;
export type Diff<T extends object, U extends object> = Pick<
T,
Exclude<keyof T, keyof U>
>;
export type Overwrite<
T extends object,
U extends object,
I = Diff<T, U> & Intersection<U, T>
> = Pick<I, keyof I>;
type Props = { name: string; age: number; visible: boolean };
type NewProps = { age: string; other: string };
// Expect: { name: string; age: string; visible: boolean; }
type ReplacedProps = Overwrite<Props, NewProps>;
Assign
类型merge
export type Assign<
T extends object,
U extends object,
I = Diff<T, U> & Intersection<U, T> & Diff<U, T>
> = Pick<I, keyof I>;
ValuesType
接受数组或对象, 获取值的联合类型
对数组直接使用T[number] 因为数组中类型是一致的, 对象需要使用索引操作符
/**
* ValuesType
* @desc Get the union type of all the values in an object, array or array-like type `T`
* @example
* type Props = { name: string; age: number; visible: boolean };
* // Expect: string | number | boolean
* type PropsValues = ValuesType<Props>;
*
* type NumberArray = number[];
* // Expect: number
* type NumberItems = ValuesType<NumberArray>;
*
* type ReadonlySymbolArray = readonly symbol[];
* // Expect: symbol
* type SymbolItems = ValuesType<ReadonlySymbolArray>;
*
* type NumberTuple = [1, 2];
* // Expect: 1 | 2
* type NumberUnion = ValuesType<NumberTuple>;
*
* type ReadonlyNumberTuple = readonly [1, 2];
* // Expect: 1 | 2
* type AnotherNumberUnion = ValuesType<NumberTuple>;
*
* type BinaryArray = Uint8Array;
* // Expect: number
* type BinaryItems = ValuesType<BinaryArray>;
*/
export type ValuesType<
T extends ReadonlyArray<any> | ArrayLike<any> | Record<any, any>
> = T extends ReadonlyArray<any>
? T[number]
: T extends ArrayLike<any>
? T[number]
: T extends object
? T[keyof T]
: never;
AugmentedRequired
将键设为必选
export type AugmentedRequired<
T extends object,
K extends keyof T = keyof T
> = Omit<T, K> & Required<Pick<T, K>>;
TupleToUnion
元组转联合类型, 因为元组兼容数组, 所以可以使用infer推断出数组的泛型类型
export type TupleToUnion<T extends any[]> = T extends Array<infer U> ? U : never