【问题标题】:TypeScript infer temlate lieral from const template literalTypeScript 从 const 模板文字推断模板文字
【发布时间】:2021-03-29 22:51:10
【问题描述】:

我尝试使用花哨的 TypeScript 模板文字键入 REST API。我有一个适用于 const 字符串类型和 const Javascript 模板文字类型的系统,但不适用于具有未知值的 Javascript 模板文字。

考虑以下递归路径解析器。

type PathVariable = string;
type ExtractPathVariable<T extends string> = T extends `:${string}`
    ? PathVariable
    : T;
type PathParts<Path extends string> = Path extends `/${infer Rest}`
    ? PathParts<Rest>
    : Path extends `${infer Start}/${infer Rest}`
    ? [ExtractPathVariable<Start>, ...PathParts<`${Rest}`>]
    : Path extends `${infer Item}`
    ? [ExtractPathVariable<Item>]
    : never;

它在某些情况下给出了预期的结果

// A == ["short", "url", "path"]
type A = PathParts<"/short/url/path">;
const b = `/short/url/path` as const;
// B == ["short", "url", "path"]
type B = PathParts<typeof b>;
// C == ["short", "${y}", "path"]
type C = PathParts<"/short/${y}/path">;
const d = `/short/${45}/path` as const;
// D == ["short", "45", "path"]
type D = PathParts<typeof d>;

但是,我最感兴趣的案例(因为这就是我调用 API 的方式),它不起作用。

let y: unknown;
const e = `/short/${y}/path`;
// E == never
type E = PathParts<typeof e>;

有没有办法让PathParts&lt;typeof e&gt; 工作? E == ["short", string, "path"]E == ["short", unknown, "path"] 的结果会很好。

【问题讨论】:

  • 查看 TS4.3 的更新答案

标签: typescript template-literals


【解决方案1】:

TypeScript 4.3 更新:

问题microsoft/Typescript#43060 已被拉取请求microsoft/Typescript#43361 标记为已修复,该问题应与TypeScript 4.3 一起发布。到那时,你上面的代码就可以工作了(只要你使用const assertion,正如我在下面提到的):

let y: unknown;
const e = `/short/${y}/path` as const;
// const e: `/short/${string}/path`
type E = PathParts<typeof e>;
// type E = ["short", string, "path"]

你可以在这里看到它的实际效果:Playground link to code


TS 4.2 的先前答案:

抱歉,我认为从 TypeScript 4.2 开始,这目前是不可能的。

首先,要接近这种行为,您需要使用const assertion 告诉编译器您希望将e 推断为模板文字类型,而不仅仅是string。虽然有一个一致性论点,如const foo = "abc" 被推断为"abc",所以const bar = `abc${x}` 应该被推断为`abc${string}` 或类似类型,如microsoft/TypeScript#41631 中所要求的那样,此更改最终为breaking too much real world code。所以你需要as const:

const e = `/short/${y}/path` as const
// const e: `/short/${string}/path`

但这就是我们所能做到的。您正在尝试将具有多个 infer 位置的模板文字类型与具有“模式”文字的另一个模板文字类型进行匹配,例如 `${number}`(其中模式文字在 microsoft/TypeScript#40598 中实现。但根据 microsoft/TypeScript#43060 这是不可能(它提到了`${number}` 而不是`${string},但所有模式文字都会出现同样的情况):

type Simple<T> = T extends `${infer F}/${infer L}` ? [F, L] : never
type Works = Simple<`foo/bar`> // ["foo", "bar"];
type Broken = Simple<`foo/${string}`> // never
type AlsoBroken = Simple<`${string}/bar`> // never

该问题尚未归类为错误/限制/建议,但它与被视为建议的microsoft/TypeScript#43243 有关。目前,似乎还没有一种机制来允许这种推断起作用。我也找不到任何行为合理的解决方法。

如果您关心看到这种情况发生,您可能想要解决其中任何一个问题并给予 ? 和/或描述您的用例为何令人信服。

Playground link to code

【讨论】:

    猜你喜欢
    • 2022-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多