【问题标题】:Function overload compilation error (This overload signature is not compatible) with template literal types函数重载编译错误(此重载签名不兼容)与模板文字类型
【发布时间】:2021-06-22 21:25:32
【问题描述】:

我有一个函数,它采用日期格式字符串(例如,"YYYY-MM-DD""YYYY")并返回基于该格式的模板文字类型。下面的代码是一个最小的例子:

type ISODate = `${number}-${number}-${number}`;
type Year = `${number}`;

type ISOFormat = "YYYY-MM-DD";
type YearFormat = "YYYY";

function format(f: ISOFormat): ISODate;
function format(f: YearFormat): Year;
function format(f: ISOFormat | YearFormat) {
  if (f === "YYYY-MM-DD") {
    return "1234-12-12";
  }
  return "1234";
}

但是,我收到编译错误 (Typescript playground link):

此重载签名与其实现签名不兼容。

我发现真正奇怪的是,当我将任一返回值更改为模板文字时,编译错误就消失了。例如(Typescript playground link):

type ISODate = `${number}-${number}-${number}`;
type Year = `${number}`;

type ISOFormat = "YYYY-MM-DD";
type YearFormat = "YYYY";

function format(f: ISOFormat): ISODate;
function format(f: YearFormat): Year;
function format(f: ISOFormat | YearFormat) {
  if (f === "YYYY-MM-DD") {
    return "1234-12-12";
  }
  return `${1}234`;
}

谁能帮我理解为什么前一个例子有编译错误,而后者编译得很好?

【问题讨论】:

  • ${number}${number}${number}${number} 是怎么回事?这不是你的意思; ${number} 匹配数字的任何字符串表示形式,例如 "2""1.23"。所以${number}${number}${number}${number} 的意思是“数字的四个字符串表示形式的连接”,比如,呃"3.141.2399.99-3.5"。这不是你想要的,对吧?
  • 哦,是的,这是一个非常愚蠢的错误。我的意图是一个四位数的数字,但现在我意识到这显然不会强制执行四位数。 (我想它会强制执行不少于四位数......)
  • 我倾向于关闭它;但是,我仍然对为什么会发生这种情况感兴趣。即使类型分别为`${number}-${number}-${number}``${number}`,编译错误似乎仍然发生
  • 是的,您可能希望保持打开状态,但也许将其更改为不存在此特定问题的 minimal reproducible example
  • 因为您的ISODate 类型中有一个额外的}

标签: typescript


【解决方案1】:

这里的问题似乎是编译器认为调用签名的返回类型对于实现来说太宽了。推断实现签名返回"1234-12-12" | "1234"。但是其中一个调用签名声称能够返回ISODate,这是一种更广泛的类型,不完全包含"1234-12-12" | "1234"。为什么编译器关心这个我不清楚。如果这是 TypeScript 中的错误,我已经提交了microsoft/TypeScript#44661。至少我们应该得到消息,这不是错误以及原因。

更新:根据this comment,当前行为不是错误,但目的是防止完全不正确的返回类型,例如"1234" 单独为ISODate。他们将此报告作为建议允许调用签名返回类型仅与实现返回类型重叠,而不是超类型或子类型。


无论如何,我在这里的建议是明确地将函数实现的annotate the return type 成为所有调用签名返回类型的联合。然后编译器将看到每个调用签名与其兼容,同时检查实现是否也与签名兼容:

function format(f: ISOFormat): ISODate; // okay
function format(f: YearFormat): Year;
function format(f: ISOFormat | YearFormat): ISODate | Year {
  if (f === "YYYY-MM-DD") {
    return "1234-12-12";
  }
  return "1234";
}

Playground link to code

【讨论】:

  • 很棒的分析,谢谢。显式注释返回类型的解决方案确实有效,我期待关注您提出的问题。
  • 更新了关于 GitHub 问题的信息
猜你喜欢
  • 2017-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-19
  • 1970-01-01
相关资源
最近更新 更多