问题描述
所以我的后端有一个这样的 JSON
name: "Hello",nameTranslations: {nl:"Hallo",en: "Hello",fr: "Bonjour"}
description: "Hello",descriptionTranslations: {nl:"Hallo",fr: "Bonjour"}
我想构建一个函数,我将在其中放入对象和键,它会自动找到可能的最佳翻译。在 Javascript 中很容易,但我想正确使用 Typescript,这样如果我忘记查询翻译,我就不会处理运行时错误(我使用 GraphQL)
function getText(object,key,language) {
const best = object[`${key}Translations`][language];
if (best) {
return best;
}
return object[key];
}
我有一些打字稿要求
- 我只想要输入对象的可能键
- 如果键在对象或“keyTranslations”上不可用,我希望 Typescript 返回错误
更新
根据@michmich112 的回答,我确实使用了以下功能
function translate<T extends BackendTranslations>(translations: T,fallback: string,language: keyof BackendTranslations) => {
return translations[language] || fallback;
}
translate(project.nameTranslations,project.name)
我会保持答案开放,因为我仍然对这是否可以在 Typescript 中工作感兴趣
解决方法
首先,您需要为您的 JSON 创建一个接口:
interface ITranslationData {
nl: string
en: string
fr: string
}
interface ITranslation {
name: string
nameTranslations: ITranslationData
description: string
descriptionTranslations: ITranslationData
}
一旦你有了,你就可以在你的函数上使用类型,如下所示:
function getText(obj: ITranslation,key: keyof ITranslation,language: keyof ITranslationData): string {
const best = obj[key][language];
if (best) {
return best;
}
return obj[key];
}
这将要求您的密钥准确无误,这意味着您将无法进行 const best = object[
${key}Translations][language];
这也意味着您可以在一行中编写代码:
function getText(obj: ITranslation,language: keyof ITranslationData): string {
return obj[key][language] || obj[key];
}
,
这是我的尝试:
const json = {
name: "Hello",nameTranslations: { nl: "Hallo",en: "Hello",fr: "Bonjour" },description: "Hello",descriptionTranslations: { nl: "Hallo",fr: "Bonjour" }
}
type BackendTranslations = {
nl: string;
en: string;
fr: string;
}
type Raw<T> = T extends `${infer R}Translations` ? R : never
/**
* Once you will have more specific interface for json
* this overloading will help you,* for now it always returns string,because all properties/nested properties of
* JSON are strings
*/
//function getText<K extends Raw<keyof Data>>(obj: Data,key: K,language: keyof Translations): K extends string ? Data[`${K}Translations`][keyof Translations] : Data[K];
function getText<
Data extends Record<string,unknown>,K extends Raw<keyof Data>
>(obj: Data,language: keyof BackendTranslations) {
return (
(obj[`${key}Translations`] as BackendTranslations)[language] || obj[key]
);
}
const result = getText(json,'name','en') // ok
const result1 = getText(json,'nameX','en') // error
const result2 = getText(json,'nameTranslations','en') // error
const result3 = getText(json,'description','en') // ok
请记住,我的解决方案仅适用于 TypeScript 4。*