首页 > 解决方案 > TypeScript 重载实现与签名不匹配

问题描述

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

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


// 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

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

您可以在 TypeScript 操场内测试示例。

标签: typescript

解决方案


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()

操场上

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

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

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

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()

在操场上

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


推荐阅读