有没有一种方法可以在TypeScript中使用基于现有常量的计算字符串声明联合类型? [英] Is there a way to declare a union type in TypeScript with computed strings based on existing constants?

查看:61
本文介绍了有没有一种方法可以在TypeScript中使用基于现有常量的计算字符串声明联合类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我们具有以下常量:

Let's assume we have following constant:

const something = {
  foo: {
    bar: {
      num: 67,
      str: 'str',
    },
  },
  some: {
    prop: 12,
  },
  topProp: 25,
};

任务:

对以下深度属性访问功能进行类型检查

The Task:

Implement typechecking for following deep property access function


/**
 * @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. 在实现元组类型检查时偶然发现了此答案,但是在我的案例中未能以某种方式使用它.读起来很有趣,但是总的来说,它提供的解决方案可以处理现有的联合类型和键,但从不计算新的联合类型和键.
  1. Implemented function on getting array of valid paths, but it returns array of simple strings(obviously -_-), so couldn't find any way to use it to enforce types
  2. Read a bunch of articles on enum types, resulting in conslustion that enum types will not help me here, because their property values can only be computed numbers, not strings (and probably they won't help anyway, because their properties itself can not be generated on fly)
  3. Stumbled across this answer on implementing tuples typechecking, but failed to somehow make use of it in my case. It is pretty interesting read, but in general provided solution juggles with existing union types and keys but never computes new ones.

猜猜

  1. 也许它可能是一种像深局部或类似之类的递归调用自身的类型

type DeepPartial<T> = {
    [P in keyof T]?: DeepPartial<T[P]>;
};

  1. 也许可以使用通用类型从底层实现它,例如使用keyof bar,keyof foo等.

推荐答案

当TypeScript 4.1登陆时,您将能够通过模板文字类型来操作字符串文字类型,如在

When TypeScript 4.1 lands you will be able to manipulate string literal types via template literal types as implemented in microsoft/TypeScript#40336. Here's a possible implementation to convert a type to a union of slash-delimited paths that lead to non-object properties:

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 D 而只拥有

I've put explicit recursion limiters here so if you try to do Leaves<SomeTreelikeType> you can choose a max depth. If you don't care you can forget Prev and D and just have

type Leaves<T> = T extends object ?
    { [K in keyof T]-?: Join<K, Leaves<T[K]>> }[keyof T] : "";

这为您提供了您想要的工会:

This gives you the union you desire:

type ComputedUnionType = Leaves<typeof something>;
// type ComputedUnionType = "topProp" | "foo/bar/str" | "foo/bar/num" | "some/prop"

您没有问过的部分是如何使编译器将路径的类型转换为结果输出的类型.这也是可能的(使用#a002中实现的递归条件类型,TS4.1),但由于您没有要求,我不会花时间实施它.

The part you didn't ask about is how to get the compiler to convert the type of the path to the type of the resulting output. This is also possible (with recursive conditional types as implemented in #40002, also landing in TS4.1), but since you didn't ask I'm not going to spend the time implementing it.

游乐场链接到代码

这篇关于有没有一种方法可以在TypeScript中使用基于现有常量的计算字符串声明联合类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆