根据Typescript中的输入键从具有动态键的对象中选择值
Pick value from object with dynamic key based on input key in Typescript
所以我的后端有一个像这样的JSON
name: "Hello",
nameTranslations: {nl:"Hallo", en: "Hello", fr: "Bonjour"}
description: "Hello",
descriptionTranslations: {nl:"Hallo", en: "Hello", fr: "Bonjour"}
我想构建一个函数,我将在其中放入对象和键,它会自动找到可能的最佳翻译。在 Javascript 中很容易,但我想正确使用 Typescript,这样我就不会在忘记查询翻译时处理运行时错误(我使用 GraphQL)
function getText(object, key, language) {
const best = object[`${key}Translations`][language];
if (best) {
return best;
}
return object[key];
}
我有一些 Typescript 要求
- 我只想要输入对象的可能键
- 如果密钥在对象或 'keyTranslations'
上不可用,我希望 Typescript return 出错
更新
根据@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,
key: keyof 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", en: "Hello", 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, key: K, 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。*
所以我的后端有一个像这样的JSON
name: "Hello",
nameTranslations: {nl:"Hallo", en: "Hello", fr: "Bonjour"}
description: "Hello",
descriptionTranslations: {nl:"Hallo", en: "Hello", fr: "Bonjour"}
我想构建一个函数,我将在其中放入对象和键,它会自动找到可能的最佳翻译。在 Javascript 中很容易,但我想正确使用 Typescript,这样我就不会在忘记查询翻译时处理运行时错误(我使用 GraphQL)
function getText(object, key, language) {
const best = object[`${key}Translations`][language];
if (best) {
return best;
}
return object[key];
}
我有一些 Typescript 要求
- 我只想要输入对象的可能键
- 如果密钥在对象或 'keyTranslations' 上不可用,我希望 Typescript return 出错
更新
根据@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,
key: keyof 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", en: "Hello", 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, key: K, 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。*