【问题标题】:TypeScript overload implementation does not match the signatureTypeScript 重载实现与签名不匹配
【发布时间】:2019-08-12 06:44:45
【问题描述】:

我想使用 TypeScript 的重载功能创建一个函数,该函数根据传入的参数返回不同类型。我设法让它工作,但编译器无法在重载函数的实现中捕获错误.

下面的例子来自the TypeScript documentation(见下文)。该函数接受两种不同类型的参数:

  • object:期望返回类型number
  • number:期望返回类型object

// With incompatible types

const suits = ["hearts", "spades", "clubs", "diamonds"];

function pickCard(x: { suit: string; card: number }[]): number;
function pickCard(x: number): { suit: string; card: number };
function pickCard(x): any {
  if (typeof x == "object") {
    let pickedCard = Math.floor(Math.random() * x.length);
    // This part does not match the overload definition. The signature
    // expect a `number` but we provide a `string`. The compiler does
    // not throw an error in that case.
    return pickedCard.toString();
  } else if (typeof x == "number") {
    let pickedSuit = Math.floor(x / 13);
    return { suit: suits[pickedSuit], card: x % 13 };
  }
}

const card = pickCard([{ suit: 'hearts', card: 5 }])

card.toFixed() // throw a runtime error: card.toFixed is not a function

// With compatible types

type Hand = { suit: string; card: number };

type HandWithScore = Hand & { score: number }

const suits = ["hearts", "spades", "clubs", "diamonds"];

function pickCard(x: Hand[]): HandWithScore;
function pickCard(x: number): Hand;
function pickCard(x): HandWithScore | Hand {
  if (typeof x == "object") {
    let pickedCard = Math.floor(Math.random() * x.length);
    // This part does not match the overload definition.
    return { suit: 'hearts', card: x % 13 };
  } else if (typeof x == "number") {
    let pickedSuit = Math.floor(x / 13);
    return { suit: suits[pickedSuit], card: x % 13 };
  }
}

const card = pickCard([{ suit: 'hearts', card: 5 }])

card.score.toFixed() // throw a runtime

实现与重载的定义不匹配,在这种情况下编译器不会警告我们,这意味着我们可能在运行时遇到问题,因为我们期望 number 但我们实际上得到了 string。是否期望编译器不会抛出?

你可以在the TypeScript playground里面测试样本。

【问题讨论】:

  • 是的,这是意料之中的。你告诉编译器返回类型是any。使用number | { suit: string; card: number; },它只允许返回它。您的 x 参数也是如此。并编写单元测试。
  • @JBNizet 是的,你是对的,但它解决了这个例子的问题,因为类型完全不兼容。想象一个不同的用例,两个类型的联合类型共享一些属性,我们又遇到了问题。
  • 编译器不可能检查你在代码中可能犯的每一个错误。编写测试。进行代码审查。
  • 是的,但这也是编译器的目的。
  • 我用不兼容的类型更新了示例。

标签: typescript


【解决方案1】:

TypeScript 编译器无法分析代码的逻辑以确保其符合合约。我很惊讶地发现它无法识别您从应该返回数字或对象的函数中返回字符串的事实,但能够做到这一点很可能在“待办事项”列表中(有 很多 要做),只是还没有到达那里。我倾向于认为它的优先级相当低,因为它无论如何都无法确保正确性,因为它无法分析逻辑流。

如果你为你的卡片定义了一个类型,你可以指定number | Card而不是any来实现:

const suits = ["hearts", "spades", "clubs", "diamonds"];
type Card = { suit: string; card: number; };

function pickCard(x: Card[]): number;
function pickCard(x: number): Card;
function pickCard(x): number | Card {
  if (typeof x == "object") {
    const cards = x as Card[];
    let pickedCard = Math.floor(Math.random() * cards.length);
    return pickedCard.toString();  // <========================== Compilation error
  } else if (typeof x == "number") {
    const n = x as number;
    let pickedSuit = Math.floor(n / 13);
    return { suit: suits[pickedSuit], card: x % 13 };
  }
}

const card = pickCard([{ suit: 'hearts', card: 5 }])

card.toFixed()

On the playground.

这对您没有正确分支的情况没有帮助,但它可以帮助您解决从应该返回数字或卡片的函数返回字符串的情况。

(也许你可以在不定义类型的情况下让它工作,但我不能立即这样做,无论如何,所有重新输入似乎都有问题。)

如果实现不重要,您甚至可以将其与我之前提出的将实现拆分为它们自己的类型安全函数的建议结合起来:

const suits = ["hearts", "spades", "clubs", "diamonds"];
type Card = { suit: string; card: number; };

function pickCard(x: Card[]): number;
function pickCard(x: number): Card;
function pickCard(x): number | Card {
  if (typeof x == "object") {
    return pickCard_Cards(x as Card[]);
  } else if (typeof x == "number") {
    return pickCard_number(x as number);
  }
}
// These wouldn't be exported
function pickCard_Cards(cards: Card[]): number {
    let pickedCard = Math.floor(Math.random() * cards.length);
    return pickedCard.toString();  // <========================== Compilation error
}
function pickCard_number(n: number): Card {
    let pickedSuit = Math.floor(n / 13);
    return { suit: suits[pickedSuit], card: x % 13 };
}

const card = pickCard([{ suit: 'hearts', card: 5 }])

card.toFixed()

On the playground

...以便单独检查每个分支的逻辑(您不会意外地从应该返回 number 的分支返回 Card[],反之亦然)。

【讨论】:

  • 感谢您的回答!问题是,如果私有函数的返回类型不正确,我们也会遇到同样的问题。我们正在将问题转移到另一个地方。
  • @SamuelVaillant - 是的,但如果实现不简单,至少它可以帮助您抓住它。但更糟糕的是,有一个更好的答案(但它与我原来的方法结合得很好)。更新。 :-)
猜你喜欢
  • 2018-02-07
  • 2016-02-02
  • 1970-01-01
  • 2023-04-04
  • 1970-01-01
  • 2019-02-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多