首页 > 解决方案 > 如何修复具有多个功能的接口的“没有重载匹配此调用”

问题描述

这是我正在努力的代码:

const enum labelKey {
  RATING = 'asset.ratingPercentage',
  EPISODE_TITLE = 'episode.title.count.short',
  EPISODE_SERIES_TITLE = 'episode.title.series.count.short',
}

interface GetLabel {
  (key: labelKey.RATING, options: { rating: string }): string;
  (
    key: labelKey.EPISODE_TITLE,
    options: { episodeNumber: number; seasonNumber: number; episodeTitle: string },
  ): string;
  (
    key: labelKey.EPISODE_SERIES_TITLE,
    options: { episodeNumber: number; seasonNumber: number; seriesName: string },
  ): string;
  (key: string): string;
}

const getLabel: GetLabel = (key: string, options?: { [key: string]: unknown }) => {
  return '';
};

type Label = {
  key: labelKey.RATING;
  options: { rating: string };
} | {
  key: labelKey.EPISODE_TITLE;
  options: { episodeNumber: number; seasonNumber: number; episodeTitle: string };
} | {
  key: labelKey.EPISODE_SERIES_TITLE;
  options: { episodeNumber: number; seasonNumber: number; seriesName: string };
};

function createLabel(someCondition: boolean): Label {
  if (someCondition) {
    return {
      key: labelKey.RATING,
      options: { rating: '5' },
    };
  }
  else {
    return {
      key: labelKey.EPISODE_TITLE,
      options: { episodeNumber: 1, seasonNumber: 2, episodeTitle: 'Hello'  },
    };
  }
}

const label = createLabel(false);

getLabel(label.key, label.options);

我什至添加了一个labelKey枚举(我认为这不是必需的)来向 TypeScript 提供提示,但getLabel仍然不起作用。

关于如何实现这一点的任何想法?

这里是游乐场

标签: typescriptoverloading

解决方案


问题的根源是您label.key在调用getLabel. 所以前三个重载都不能匹配,因为它们都需要特定的键类型。但是你也不能匹配第四个string重载,因为这个重载(key: string): string;要求你不传递第二个参数。

解决方案1:getLabel

让我们添加一个重载,我们知道我们有一个labelKey但不知道是哪一个。所以我们应该接受options任何重载,而不要求选项类型与键类型匹配。然后作为最后的手段,您可以添加另一个接受string任何options对象的重载。

interface GetLabel {
...exisiting overloads 1-3...
  (key: Label['key'], options: Label['options']): string;
  (key: string, options: Record<string, any>): string;
}

解决方案2:createLabel

但是我们也可以从createLabel函数中解决这个问题。目前 Typescript 不知道Label这个函数返回的是哪种类型。但是我们应该能够根据我们调用函数Label的值来知道我们将得到哪个。someCondition所以我们实际上可以在这里添加重载。as const注意:当您有明确的返回类型时,您实际上并不需要。

// helper function to get a member of the `Label` union
type LabelByKey<K extends labelKey> = Extract<Label, {key: K}>

function createLabel(someCondition: true): LabelByKey<labelKey.RATING>;
function createLabel(someCondition: false): LabelByKey<labelKey.EPISODE_TITLE>;
function createLabel(someCondition: boolean): Label {
  if (someCondition) {
    return {
      key: labelKey.RATING,
      options: { rating: '5' },
    }
  }
  else {
    return {
      key: labelKey.EPISODE_TITLE,
      options: { episodeNumber: 1, seasonNumber: 2, episodeTitle: 'Hello'  },
    }
  }
}

现在,当你打电话时,createLabel(false)你知道你得到一个标签,其类型如下:

const label: {
    key: labelKey.EPISODE_TITLE;
    options: {
        episodeNumber: number;
        seasonNumber: number;
        episodeTitle: string;
    };
}

我们不需要那些额外的重载,因为现在我们可以将第二个重载与key: labelKey.EPISODE_TITLE.

打字稿游乐场链接

解决方案 3:泛型

您需要将 alabelKey与其匹配的信息options已在Label联合中可用。所以你不需要在重载中再次输入它。我们可以使用泛型函数而不是重载函数,并使用相同的LabelByKey辅助类型。

type LabelByKey<K extends labelKey> = Extract<Label, {key: K}>

const getLabel = <K extends labelKey>(
  key: K,
  options: LabelByKey<K>['options']
) => {
  return '';
};

推荐阅读