如何修复具有多个功能的接口的 "No overload matches this call"
How do I fix an "No overload matches this call" of an interface with multiple functions
这是我正在努力处理的代码:
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
仍然不起作用。
关于如何实现的任何想法?
这是 Playground。
如果将 label
的定义更改为
const label = {
key: labelKey.RATING,
options: { rating: '5' },
} as const;
key
属性 的推断类型将是 labelKey.RATING
而不是 labelKey
并且您不会得到此错误
--
编辑: 您可以根据 Label
的定义使用泛型替换重载
interface GetLabel {
<K extends labelKey>(key: K, options: Extract<Label, {key: K}>['options']): string;
(key: string): string;
}
问题的根源是你在调用getLabel
时不知道label.key
的类型。因此,前三个重载中的 none 可以匹配,因为它们都需要特定的键类型。但是你不能匹配第四个 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
这个函数 return 是哪个类型的。但是我们应该能够根据我们调用函数的 someCondition
的值知道我们将得到哪个 Label
。所以我们实际上可以在这里添加重载。注意:当你有一个明确的 return 类型时,你实际上并不需要 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:泛型
您需要将 labelKey
与其 options
匹配的信息已在 Label
联合中可用。所以你不需要在重载中再次输入它。我们可以使用通用函数而不是重载函数,并使用相同的 LabelByKey
辅助类型。
type LabelByKey<K extends labelKey> = Extract<Label, {key: K}>
const getLabel = <K extends labelKey>(
key: K,
options: LabelByKey<K>['options']
) => {
return '';
};
这是我正在努力处理的代码:
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
仍然不起作用。
关于如何实现的任何想法?
这是 Playground。
如果将 label
的定义更改为
const label = {
key: labelKey.RATING,
options: { rating: '5' },
} as const;
key
属性 的推断类型将是 labelKey.RATING
而不是 labelKey
并且您不会得到此错误
--
编辑: 您可以根据 Label
interface GetLabel {
<K extends labelKey>(key: K, options: Extract<Label, {key: K}>['options']): string;
(key: string): string;
}
问题的根源是你在调用getLabel
时不知道label.key
的类型。因此,前三个重载中的 none 可以匹配,因为它们都需要特定的键类型。但是你不能匹配第四个 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
这个函数 return 是哪个类型的。但是我们应该能够根据我们调用函数的 someCondition
的值知道我们将得到哪个 Label
。所以我们实际上可以在这里添加重载。注意:当你有一个明确的 return 类型时,你实际上并不需要 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:泛型
您需要将 labelKey
与其 options
匹配的信息已在 Label
联合中可用。所以你不需要在重载中再次输入它。我们可以使用通用函数而不是重载函数,并使用相同的 LabelByKey
辅助类型。
type LabelByKey<K extends labelKey> = Extract<Label, {key: K}>
const getLabel = <K extends labelKey>(
key: K,
options: LabelByKey<K>['options']
) => {
return '';
};