有没有一种方法可以使用基于现有常量的计算字符串在TypeScript中声明联合类型?
假设我们有以下常数:有没有一种方法可以使用基于现有常量的计算字符串在TypeScript中声明联合类型?,typescript,generics,types,type-inference,Typescript,Generics,Types,Type Inference,假设我们有以下常数: const something = { foo: { bar: { num: 67, str: 'str', }, }, some: { prop: 12, }, topProp: 25, }; 任务: 实现以下深度属性访问函数的类型检查 /** * @example * getByPath('foo/bar/str'); // returns 'str' * * @example * ge
const something = {
foo: {
bar: {
num: 67,
str: 'str',
},
},
some: {
prop: 12,
},
topProp: 25,
};
任务:
实现以下深度属性访问函数的类型检查
/**
* @example
* getByPath('foo/bar/str'); // returns 'str'
*
* @example
* getByPath('topProp'); // returns 25
*
* @example
* getByPath('some/prop'); // returns 12
*/
const getByPath = (path: ComputedUnionType) => {<unrelated-code-magic>};
// Where
type ComputedUnionType = 'foo/bar/num' | 'foo/bar/str' | 'some/prop' | 'topProp';
// or even better
type ComputedUnionType<typeof some> = 'foo/bar/num' | 'foo/bar/str' | 'some/prop' | 'topProp';
const getByPath = <T>(path: ComputedUnionType<T>) => ...
我做了什么?
实现了获取有效路径数组的函数,但它返回简单strings数组的方式为-
阅读了大量关于枚举类型的文章,得出结论:枚举类型在这里对我没有帮助,因为它们的属性值只能是计算出的数字,而不是字符串,而且它们可能无论如何也不会有帮助,因为它们的属性本身不能动态生成
偶然发现了实现元组类型检查,但在我的案例中未能以某种方式使用它。这是一本非常有趣的书,但通常提供的解决方案会处理现有的联合类型和键,但不会计算新的联合类型和键。
猜测
也许它可能是一种递归调用自身的类型,比如deep-partial或类似的东西
也许有一些方法可以通过泛型类型从底层实现它,比如keyofbar、keyoffoo等等。
当TypeScript 4.1登陆时,您将能够通过中实现的模板文本类型操作字符串文本类型。下面是一个可能的实现,可以将类型转换为斜杠分隔路径的并集,从而生成非对象属性:
type Join<K, P> = K extends string | number ?
P extends string | number ?
`${K}${"" extends P ? "" : "/"}${P}`
: never : never;
type Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...0[]]
type Leaves<T, D extends number = 10> = [D] extends [never] ? never : T extends object ?
{ [K in keyof T]-?: Join<K, Leaves<T[K], Prev[D]>> }[keyof T] : "";
这给了你想要的联盟:
type ComputedUnionType = Leaves<typeof something>;
// type ComputedUnionType = "topProp" | "foo/bar/str" | "foo/bar/num" | "some/prop"
您没有询问的部分是如何让编译器将路径的类型转换为结果输出的类型。这在TS4.1中也是可能的,但是既然你没有问我,我就不打算花时间来实现它
还可以添加我的解决方案,它主要相当于jcalz的未检查递归版本,但结合了Join助手。在本例中,它的行为相同,但如果您的对象具有字符串和数字以外的值,或者如果您的对象类型具有可选值,则其行为可能不同?钥匙
我猜解决方案将需要下一版本的TS4.1templating@MingweiSamuel你能把这件事联系起来吗?也许是TS路线图,或者一些关于未来TS版本的文章?也可能是非常有用和快速的答案,谢谢@我也在努力寻找答案,但jcalz和往常一样太棒了:
type Leaves<T> = T extends object ?
{ [K in keyof T]-?: Join<K, Leaves<T[K]>> }[keyof T] : "";
type ComputedUnionType = Leaves<typeof something>;
// type ComputedUnionType = "topProp" | "foo/bar/str" | "foo/bar/num" | "some/prop"
type ComputedUnionType<T, S extends string = '/'> = {
[K in keyof T]:
K extends string
? ComputedUnionType<T[K]> extends string
? `${K}${S}${ComputedUnionType<T[K]>}`
: K
: never;
}[keyof T];
type Z = ComputedUnionType<typeof something>;
// type Z = "topProp" | "foo/bar/str" | "foo/bar/num" | "some/prop"