我正在尝试构建一些类型,以便我可以定义一个通用函数
我已经成功地完成了第一个项目,但是,我无法获得任何自动完成提示。
我尝试构建一个工作类型,该类型创建所有有效路径的联合。 但这就是我卡住的地方。
这是我目前得到的
// helper types
type Length<T> = T extends { length: infer L } ? L : never;
type PopFront<T extends unknown[]> = T extends [infer U, ...infer R] ? U : never;
type Shift<T extends unknown[]> = T extends [infer U, ...infer R] ? R : never;
type Filter<T extends unknown[], U> = T extends [] ? [] : T extends [infer F, ...infer R] ? F extends U ? Filter<R, T> : [F, ...Filter<R, U>] : never
type TupleIncludes<T extends unknown[], U> = Length<Filter<T, U>> extends Length<T> ? false : true
type StringIncludes<S extends string, D extends string> = S extends `${infer T}${D}${infer U}` ? true : false;
type Includes<T extends unknown[]|string, U> = T extends unknown[] ? TupleIncludes<T, U> : T extends string ? U extends string ? StringIncludes<T, U> : never : never
type Join<T extends unknown[], D extends string> = PopFront<T> extends string ? Length<T> extends 1 ? `${PopFront<T>}` : Shift<T> extends string[] ? `${PopFront<T>}${D}${Join<Shift<T>, D>}` : never : never;
type Split<S extends string, D extends string> =
string extends S ? string[] :
S extends '' ? [] :
S extends `${infer T}${D}${infer U}` ? [T, ...Split<U, D>] : [S];
// return type deduction from path
type NestedType<T, P extends string> = (
Includes<P, '.'> extends true
? PopFront<Split<P, '.'>> extends keyof T
? NestedType<T[PopFront<Split<P, '.'>>], Join<Shift<Split<P, '.'>>, '.'>>
: never
: P extends keyof T ? T[P] : never
);
// Demo
const dictionary = {
someProp: 123,
nested: {
moreProps: 333,
deeper: {
evenDeeper: {
deepest: 'str'
}
},
alsoDeeper: {
randomProp: {
anotherProp: 'yay'
}
}
}
} as const;
type MyDict = typeof dictionary;
type Test1 = NestedType<MyDict, 'nested.alsoDeeper.randomProp.anotherProp'>; // = yay
const Fn = <T, P extends string>(dict: T, path: P): NestedType<T, P> => {
// skip impl.
return undefined as any;
};
const testFromFn = Fn(dictionary, 'nested.moreProps'); // = 333
上面的代码都很好,现在到我尝试构建的类型, 这应该给我所有有效的路径:
// If you look at type "Debug" below, this surprisingly works.
// ...apart from the `Type instantiation is excessively deep and possibly infinite.`-error
type ValidPaths<T> = keyof T extends never ? never : ({
[K in keyof T]: T[K] extends never ? never : T[K] extends Record<string|number|symbol, unknown>
? K extends string ? `${K}.${ValidPaths<T[K]>}` | K : never
: K
})[keyof T];
type Debug = ValidPaths<MyDict>;
// So I also tried with Tuples.
type ValidPathTuples<T> = keyof T extends never ? never : ({
[K in keyof T]: T[K] extends never ? never : T[K] extends Record<string|number|symbol, unknown>
? [K, ...ValidPathTuples<T[K]>] | [K]
: [K]
})[keyof T];
// Which seems fine, too!
type DebugTuples = ValidPathTuples<MyDict>;
// But now, I cannot join them to path-strings
type DebugTuples1 = Join<ValidPathTuples<MyDict>, '.'>;
我到底错过了什么,还是我走错了路?
为什么元组版本可以正常工作,而使用字符串文字模板的版本却不能?
编辑:
我刚刚想到了一些东西。
我把我的加入类型改成了这个
type Join<T extends unknown[], D extends string> = T extends string[]
? PopFront<T> extends string ? Length<T> extends 1 ? `${PopFront<T>}` : `${PopFront<T>}${D}${Join<Shift<T>, D>}` : never
: never;
现在 Join<ValidPathTuples, '.'>
产生正确的结果。
唯一要做的就是删除 Shift<T> extends string[] ? trueExpr : falseExpr;
并使用已删除的 trueExpr
我真的不明白这让¯\_(ツ)_/¯ 有什么不同