为什么打字稿无法弄清楚联合类型的可能实际类型与函数的可用原型相对应?解决方法?

问题描述 投票:0回答:1

这个(游乐场链接)不起作用:

type ReplaceAll2ndArgType = string | ((substring: string, ...args: unknown[]) => string)

export async function renderHTMLTemplate(
    args: Record<string, ReplaceAll2ndArgType>
): Promise<string> {
    let template = "some template text"
    for (const key in args) {
        const val = args[key]
        template = template.replaceAll(`%${key}%`, val)
    }
    return template
}

打字稿编译器给出此错误:

No overload matches this call.
  Overload 1 of 2, '(searchValue: string | RegExp, replaceValue: string): string', gave the following error.
    Argument of type 'ReplaceAll2ndArgType' is not assignable to parameter of type 'string'.
      Type '(substring: string, ...args: unknown[]) => string' is not assignable to type 'string'.
  Overload 2 of 2, '(searchValue: string | RegExp, replacer: (substring: string, ...args: any[]) => string): string', gave the following error.
    Argument of type 'ReplaceAll2ndArgType' is not assignable to parameter of type '(substring: string, ...args: any[]) => string'.
      Type 'string' is not assignable to type '(substring: string, ...args: any[]) => string'.

问题似乎是打字稿无法弄清楚联合类型 ReplaceAll2ndArgType 由一组适合 template.replaceAll 可用原型集的类型组成,这似乎有点令人惊讶。 看起来它一次只考虑一个原型。 还是我没有正确理解问题?

到目前为止我唯一的解决方法看起来像这样(playground链接):

type ReplaceAll2ndArgType = string | ((substring: string, ...args: unknown[]) => string)

export async function renderHTMLTemplate(
    args: Record<string, ReplaceAll2ndArgType>
): Promise<string> {

        let template = "some template text"

    for (const key in args) {
            const val = args[key]
            switch ( typeof val ) {
                case "string":
                    template = template.replaceAll(`%${key}%`, val)
                    break
                case "function":
                    template = template.replaceAll(`%${key}%`, val)
                    break
            }
    }
    return template
}

这看起来真的很差,特别是因为

case "function"
并没有真正做太多有用的缩小。

我缺少什么更好的方法吗?

javascript typescript types type-inference
1个回答
0
投票

您遇到了 microsoft/TypeScript#44919 中报告的问题。

无论出于何种原因,replaceAll()字符串方法

TypeScript库类型被写成一对重载:

interface String {
  replaceAll(
    searchValue: string | RegExp, 
    replaceValue: string
  ): string;
  replaceAll(
    searchValue: string | RegExp, 
    replacer: (substring: string, ...args: any[]) => string
  ): string;
}

TypeScript 目前一次只能解析对重载一个调用签名的调用。您的呼叫必须至少匹配其中一个呼叫签名。但是您正在使用

union 类型
的第二个参数来调用 replaceAll()。该联合体的每个成员都与调用签名之一匹配,但联合体本身却不匹配。 除非按照 microsoft/TypeScript#14107 中的要求,TypeScript 支持一次调用多个调用签名,否则您需要以某种方式解决它。

一种解决方法如您在问题中所示:使用控制流来区分第二个参数的类型,以便调用可以匹配其中一个重载。

另一个解决方法是采用 microsoft/TypeScript#44919 中要求的方法,并使

replaceAll()
具有适当的调用签名。您无法摆脱现有的一对调用签名,但您可以为您自己的代码库合并第三个调用签名:

declare global {
  interface String {
    replaceAll(
      searchValue: string | RegExp, 
      replacerOrValue: string | ((substring: string, ...args: any[]) => string)
    ): string;
  }
}

我所做的就是手动组合两个调用签名。一旦您这样做,您的呼叫就会开始工作,因为它与新的呼叫签名匹配:

template = template.replaceAll(`%${key}%`, val) // okay

Playground 代码链接

© www.soinside.com 2019 - 2024. All rights reserved.