ts utility-types ​​​​​​​ 源码解读 Object operators

08/30 15:33
阅读数 48

参考, 写的确实不错

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

 

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部