TypeScript基础:从含字符串的对象中动态调用函数
解决TypeScript中I18n工具调用函数式翻译项的报错问题
问题场景
调用i18n.t("signed_in_as")("Chris")时出现TypeScript报错,错误信息:
This expression is not callable.
Not all constituents of type '"Your bus tickets" | "Deine Bustickets" | ((user: string) => string) | ""' are callable.
Type '"Your bus tickets"' has no call signatures.ts(2349)
原代码如下:
const prompts = { bus_tickets: { en: 'Your bus tickets', de: 'Deine Bustickets', }, signed_in_as: { en: (user: string) => `Signed in as: ${user}`, de: (user: string) => `Eingeloggt als: ${user}`, }, } as const type TranslationKey = keyof typeof prompts class I18n { userLocale: 'en' | 'de' constructor(userLocale: 'en' | 'de') { this.userLocale = userLocale } t(translationKey: TranslationKey) { const translations = prompts[translationKey] if (translations) return translations[this.userLocale] return '' } } // 报错的调用 const i18n = new I18n("en") i18n.t("signed_in_as")("Chris") // ERROR!
报错原因
当前I18n.t方法的返回类型是所有翻译项的联合类型——包含字符串(如bus_tickets的翻译值)和函数(如signed_in_as的翻译函数)。TypeScript无法确定调用t("signed_in_as")返回的是可调用的函数,因此抛出错误,因为联合类型中存在不可调用的字符串类型。
解决方案
通过泛型让t方法根据传入的translationKey精确推断返回类型,修改后的代码如下:
const prompts = { bus_tickets: { en: 'Your bus tickets', de: 'Deine Bustickets', }, signed_in_as: { en: (user: string) => `Signed in as: ${user}`, de: (user: string) => `Eingeloggt als: ${user}`, }, } as const type TranslationKey = keyof typeof prompts class I18n { userLocale: 'en' | 'de' constructor(userLocale: 'en' | 'de') { this.userLocale = userLocale } // 用泛型K捕获具体的translationKey类型,精确返回对应翻译值的类型 t<K extends TranslationKey>(translationKey: K): typeof prompts[K][typeof this.userLocale] { const translations = prompts[translationKey] if (translations) return translations[this.userLocale] return '' as never // 断言为never,避免类型不匹配(实际不会执行到这里) } } // 现在可以正常调用 const i18n = new I18n("en") i18n.t("signed_in_as")("Chris") // 正常运行 console.log(i18n.t("bus_tickets")) // 输出 "Your bus tickets"
补充:不推荐的临时方案
如果不想修改t方法的类型定义,也可以用类型断言强制指定返回类型,但这种方式不安全,容易引发潜在错误:
(i18n.t("signed_in_as") as (user: string) => string)("Chris")
内容的提问来源于stack exchange,提问作者christianbauer1
相关产品推荐
相关产品推荐

