TypeScript 从 const 模板文字推断模板文字
TypeScript infer temlate lieral from const template literal
我正在尝试使用精美的 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//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<typeof e>
工作?结果 E == ["short", string, "path"]
或 E == ["short", unknown, "path"]
就可以了。
TypeScript 4.3 更新:
问题 microsoft/Typescript#43060 has been marked fixed by pull request microsoft/Typescript#43361, which should be released with TypeScript 4.3. At that point, your code above will work (as long as you use a 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 to tell the compiler that you'd like e
to be inferred as a template literal type and not just string
. While there is a consistency argument that, like const foo = "abc"
is inferred as "abc"
, so const bar = `abc${x}`
should be inferred as type `abc${string}`
or the like, as requested in microsoft/TypeScript#41631, this change ended up 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. But according to 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
该问题尚未归类为 bug/limitation/suggestion,但它与被视为建议的 microsoft/TypeScript#43243 相关。目前似乎还没有一种机制可以让这种推理发挥作用。我也没能找到任何行为合理的解决方法。
如果您希望看到这种情况发生,您可能想解决其中任何一个问题并给出 and/or 描述您的用例为何引人注目。
我正在尝试使用精美的 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//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<typeof e>
工作?结果 E == ["short", string, "path"]
或 E == ["short", unknown, "path"]
就可以了。
TypeScript 4.3 更新:
问题 microsoft/Typescript#43060 has been marked fixed by pull request microsoft/Typescript#43361, which should be released with TypeScript 4.3. At that point, your code above will work (as long as you use a 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 to tell the compiler that you'd like e
to be inferred as a template literal type and not just string
. While there is a consistency argument that, like const foo = "abc"
is inferred as "abc"
, so const bar = `abc${x}`
should be inferred as type `abc${string}`
or the like, as requested in microsoft/TypeScript#41631, this change ended up 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. But according to 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
该问题尚未归类为 bug/limitation/suggestion,但它与被视为建议的 microsoft/TypeScript#43243 相关。目前似乎还没有一种机制可以让这种推理发挥作用。我也没能找到任何行为合理的解决方法。
如果您希望看到这种情况发生,您可能想解决其中任何一个问题并给出 and/or 描述您的用例为何引人注目。