Open xxleyi opened 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 也可以。
以上同时使用了 PathOf 和 AllPathsOf,其实只使用 AllPathsOf 也可以。