TypeScript 模板字面量类型推断失败,由于懒惰占位符匹配,

cbjzeqam  于 2个月前  发布在  TypeScript
关注(0)|答案(6)|浏览(43)

Bug报告

🔎 搜索词

模板字面量类型,占位符,模式,推断,懒惰/非贪婪

🕗 版本与回归信息

  • 在TS4.1引入模板字面量类型后,我在每个版本中都尝试了这种行为,并查阅了关于模板字面量类型的FAQ条目。

⏯ Playground链接

带有相关代码的Playground链接

💻 代码

declare function f<T extends "x" | "y">(a: `${string}.${T}`): T;
const x = f("abc.x") // "x"
const y = f("def.y") // "y"
const z = f("ghi.jkl.x") // "x" | "y" !!!

🙁 实际行为

在第三次调用时,T被推断为"x" | "y"

🙂 预期行为

T应该被推断为"x"。这来自this Stack Overflow question。当将其与"ghi.jkl.x"进行比较时,类型${string}.${T}"ghi"的方式懒惰地匹配${string},因此它将"jkl.x"作为T的推断候选项。这未能满足"x" | "y"的约束。所以这是一个无效的推断候选项。
我猜推断只是失败了,所以T回退到约束。果然,"ghi.jkl.x"确实匹配${string}.x` | `${string}.y,所以没有错误,但它不再作为泛型调用有用。
我只是想知道这种情况在“bug”和“故意”之间属于哪个范围。
(请注意,这种情况与紧挨着的两个占位符的情况并不完全相同,如#46124#47048#49411。)
(另外,我解决这个问题的方法是编写一个递归条件类型来实际找到最后一个分隔符,就像这里展示的那样,但这很讨厌。)

mqxuamgl

mqxuamgl1#

这纯属猜测,但我猜想发生的情况是:

  • 我们尝试从 "ghi.jkl.x"${string}.${T} 进行推断并收集一个孤立的候选者 jkl.x ,因为这是一个贪婪的操作
  • 这不符合约束条件 x | y ,所以我们回到约束条件。在这种情况下推理实际上已经失败了;在大多数情况下,这会导致调用上的错误,尽管严格来说这绝对不是一个错误条件
  • 我们评估 "ghi.jkl.x"${string}.x | ${string}.y 的成功,因为现在我们使用的是基于后锚定的方法

似乎在第一步中,我们需要将关于约束条件的信息应用到字符串匹配推理算法中

u5i3ibmn

u5i3ibmn2#

我非常喜欢这个$\mathfrak{Cursed?}$标签。

fcy6dtqo

fcy6dtqo3#

@ahejlsberg,也许你会喜欢看这个,如果你愿意的话。

5jvtdoz2

5jvtdoz24#

我相信@jfet97发现的问题本质上与这里报告的问题相同(或者至少是因为相同的实现细节而失败):

type Exactly5<T extends string> = T extends `${any}${any}${any}${any}${any}${infer Last extends ""}` ? T : never

type test01 = Exactly5<""> 
//   ^? type test01 = never
type test11 = Exactly5<"a"> 
//   ^? type test11 = never
type test21 = Exactly5<"ab"> 
//   ^? type test21 = never
type test31 = Exactly5<"abc"> 
//   ^? type test31 = never
type test41 = Exactly5<"abcd"> 
//   ^? type test41 = never
type test51 = Exactly5<"abcde">  
//   ^? type test51 = "abcde"
type test61 = Exactly5<"abcdef"> // should be never
//   ^? type test61 = "abcdef"

可以在这个TS playground中进行测试。
当我在调试这个时,我在编译器中看到的非常接近于@RyanCavanaugh在这里描述的情况。
1.推断出的候选项与约束不匹配
1.因此,它被替换为这里的约束
1.由于约束是一个空字符串,所以它满足最终的检查
似乎在第一步,我们需要将关于约束的信息应用到字符串匹配算法中
你能详细解释一下这个问题吗?我不确定我是否理解了。我猜想这应该在步骤1之后处理。

qybjjes1

qybjjes15#

你能详细解释一下这个吗?

目前,将算法推断到模板字符串的算法并不“知道”它正在尝试匹配具有约束的类型参数,因此它没有意识到它收集的推断实际上可能是垃圾。在这个调用中,我们“应该”做的是做一些与推断到 "ghi.jkl.x"${string}.x | ${string}.y 相同(但更有效)的事情,看看是否有任何成功的匹配,并将这些记录为推断候选项(在这种情况下,由于第一个成功匹配而收集了一个单独的候选项 "x")。这将产生预期的结果。

3okqufwl

3okqufwl6#

模板字符串实现的细节在许多场景中确实让我们感到困惑,也许需要时间来改进。

相关问题