有没有办法在 TypeScript 中使用基于现有常量的计算字符串来声明联合类型?

2024-04-18

假设我们有以下常量:

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>) => ...

我做了什么?

  1. 实现了获取有效路径数组的函数,但它返回简单字符串数组(显然-_-),因此找不到任何方法来使用它来强制类型
  2. 阅读了一堆关于枚举类型的文章,结果是枚举类型在这里对我没有帮助,因为它们的属性值只能是计算数字,而不是字符串(而且可能它们无论如何也没有帮助,因为它们的属性本身不能是动态生成)
  3. 偶然发现这个答案 https://stackoverflow.com/a/59774983/13104050实现元组类型检查,但在我的例子中未能以某种方式使用它。这是非常有趣的阅读,但总的来说,提供的解决方案与现有的联合类型和键混合在一起,但从不计算新的。

Guesses

  1. 也许它可能是一种递归调用自身的类型,例如深度部分或类似的东西
type DeepPartial<T> = {
    [P in keyof T]?: DeepPartial<T[P]>;
};
  1. 也许有某种方法可以通过泛型类型从底层实现它,采用 keyof bar、keyof foo 等。

当 TypeScript 4.1 登陆时,您将能够通过以下方式操作字符串文字类型模板文字类型如实施于微软/TypeScript#40336 https://github.com/microsoft/TypeScript/pull/40336。以下是一种可能的实现,用于将类型转换为斜杠分隔路径的联合,从而导致非对象属性:

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

我在这里放置了显式递归限制器,所以如果您尝试这样做Leaves<SomeTreelikeType>您可以选择最大深度。如果你不在乎你就会忘记Prev and D并且只是有

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"

你的部分didn't问的是如何让编译器将路径的类型转换为结果输出的类型。这也是可能的(与#40002 中实现的递归条件类型 https://github.com/microsoft/TypeScript/pull/40002,也登陆 TS4.1),但既然你没有问,我不会花时间实现它。

Playground 代码链接 https://www.typescriptlang.org/play?ts=4.1.0-dev.20200921#code/LAKALgngDgpgBAKQPYEsB2AeA0gGjgBQD44BeOLOGADzBjQBMBnORsAJ3QHM4AfONAK4BbAEYw2cAPyg4BSjTpMW7Lr37CxE6SFkADACQBvLAF8jAInPzaDZvilxLcAFyOA9ObOH8J3TJf8MABu4gFoweIA3KCgkLAEbMGkcADa4SFseAAMeACMeABMeADMeAAseACseABseADseAAceACceTn+uflwuUW9pb0VvdW9db2NvS297XAFOXAAdMtZKQC6azHg0PAAMjAAhiGMGAAqeAAi1orMgqKhZLlZxGQpF2vXtqnp4h+SgRkAqdPkokCIAFYwADGYCk-kMqQo6DgAGsYBAkAAzOCnNYAWkkrmQ6GweH2RxgJ1OKSwazw+ESQTea0IxBMKTRGOxuIClmiIFAbgAVHAkBJYjs4OTjmcXjiQcwwZCYXCdLI4AiaXBkZysTj8YTEKhMLgpYcZdTaay4OzddyPq4+aAhW4tqAoUg0KwWEghDAwAALVRkQz+TFIJCuUNq2QiA5sKP+dXqISuGqNJPq1gJuAAcmzuZwmZMRbVJf8jF9METMbgUDYSCgrj6pdk5bVYEbDMbrgK1VAJn5EviAGFfVABLR6ABVNAoT2nSVkaWUjBxGB6yt+wNcQj8txuODruBjoQTqez+doRfxMjmTtQbtQKx8czhpBuONsNzZl+Od+fvGbh3H+5hbjAbj1o25hbMeABCiQHCiZwKsoHBoJwlyodmu6kP4wLUDYSgGIY6CYqEABiXgXF4ZGhAA+r4qqyBRqEkXREiMUYNFGBxcCMboDg-BIzj4WxRhUdxtFoOREgAEpMf8KQUXgckOgC4j+K4KTnI45hrEO2zxAA8pO543jAZx4BQhE3GhXBYbZXw4RhyS5m4uYvBWKiuU5SgUP8wkBC53B+cwVyBREIn2b5ChfHa8qRYColqjZcVKE4-zAq4CGHMhpoXMQYWpHxKnajJoRqQ4rHFQlwLaLIplgOZOxnMpdJwAAojQbAHDCGByXgIWEJcxCuMJWkaWwsGSgAWskhhasip7njAM5zguOxrK4TUtbAa47JuVY7hh1mECYWzCse80hpmD5PuNGhRJmb4RoB36-q4IX8smr0fl+wHCOYj33GwP3qmBVaQQ2z4g5o-ImC6oBAA

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

有没有办法在 TypeScript 中使用基于现有常量的计算字符串来声明联合类型? 的相关文章

随机推荐