xxleyi / learning_list

聚集自己的学习笔记
11 stars 3 forks source link

GetDeepType #317

Open xxleyi opened 3 years ago

xxleyi commented 3 years ago
type GetKeyWhichValueIsRecord<T, K extends keyof T> = K extends keyof T
  ? Exclude<T[K], undefined | null> extends Record<string, unknown> | unknown[]
    ? K
    : never
  : never;

type OneLevelPathOf<T, Hint = GetKeyWhichValueIsRecord<T, keyof T> & string> = T extends unknown[]
  ? 0
  : never extends Hint
  ? keyof T & string
  : Hint;

type PathForHint<T> = OneLevelPathOf<Exclude<T, undefined | null>>;

type HintPrifix = '🤨🤨 路径有误,我猜了一些你可能想输入的路径';

type PathOf<
  MaybeObject,
  K extends string,
  P extends string = '',
  RealObject = Exclude<MaybeObject, undefined | null>
> = K extends `${infer U}.${infer V}`
  ? U extends keyof RealObject // Record
    ? PathOf<RealObject[U], V, `${P}${U}.`>
    : RealObject extends unknown[] // Array
    ? PathOf<RealObject[number], V, `${P}${0}.`>
    : HintPrifix | `${P}${PathForHint<RealObject>}` // just for hint
  : K extends keyof RealObject
  ? `${P}${K}`
  : K extends `${number}`
  ? RealObject extends unknown[]
    ? `${P}${0}`
    : HintPrifix | `${P}${PathForHint<RealObject>}` // just for hint
  : HintPrifix | `${P}${PathForHint<RealObject>}`; // just for hint

type SplitTemplateStringTypeToTuple<T> = T extends `${infer First}.${infer Rest}`
  ? First extends `${number}`
    ? [number, ...SplitTemplateStringTypeToTuple<Rest>]
    : [First, ...SplitTemplateStringTypeToTuple<Rest>]
  : T extends `${number}`
  ? [number]
  : [T];

type StringableKey<T> = T extends readonly unknown[]
  ? number extends T['length']
    ? // 针对取类型的场景,数组改为 0,获得完备的类型补全功能
      0
    : `${number}`
  : string;

// eslint-disable-next-line @typescript-eslint/ban-types
type AllPathsOf<T> = T extends object
  ? {
      [P in keyof T & StringableKey<T>]: `${P}` | `${P}.${AllPathsOf<T[P]>}`;
    }[keyof T & StringableKey<T>]
  : never;

/** 推荐使用,因为有别致的类型提示 */
type GetDeepType<
  MaybeDeepObj,
  // Path extends PathOf<MaybeDeepObj, AllPathsOf<MaybeDeepObj>>
  Path extends AllPathsOf<MaybeDeepObj>,
  RealPath = PathOf<MaybeDeepObj, Path>
> = HintPrifix extends RealPath
  ? RealPath
  : DeepPick<MaybeDeepObj, SplitTemplateStringTypeToTuple<RealPath>>;

/** 不再推荐直接使用,因为没有类型提示 */
type DeepPick<
  MaybeDeepObj,
  Path extends unknown[],
  RealDeepObj = Exclude<MaybeDeepObj, undefined | null>
> = Path extends [infer First, ...infer Rest]
  ? First extends keyof RealDeepObj
    ? DeepPick<RealDeepObj[First], Rest>
    : never
  : RealDeepObj;

/** 使用例子 */
type TestDeepObj = {
  a?: {
    b?: [string, number, { a: string; b: number }];
  };
};

type tttt = PathOf<TestDeepObj, 'a.b.0.c'>;

type TestRes = GetDeepType<TestDeepObj, ''>;

以上同时使用了 PathOf 和 AllPathsOf,其实只使用 AllPathsOf 也可以。