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

原文链接: 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有问题

up-8c5614ae7d1a59533441fc8a14d09f745ac.png

up-f1e17c13f0c3ff0d0da2686b1604b31566b.png

export type FunctionKeys<T extends object> = {
  [K in keyof T]-?: Extract<NonNullable<T[K]>, Function> extends never
    ? never
    : K;
}[keyof T];

up-23d358f9050e420ba49c1f4f50b7b314131.png

up-9aab07b325cfba683ae2fdb43a292338c05.png

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

up-f07956f162b0daec3011c74b7537d10ebdb.png

IfEquals 用于判断两个类型是否相等

对于采用双向extends的方案, 是无法判断属性修饰符是否相等的

up-fbe87fab3009516022535da9b0f3c5c8e28.png

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

up-dd55c03c62e9484b9232f2319552da3ab7f.png

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
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值