Итак, мой бэкэнд имеет такой 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 у меня есть
Обновлять Основываясь на ответе @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}Переводы][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.*
Действительно хорошо <3, спасибо. Если вы измените его на приведенный ниже код, я приму ответ. Он автоматически увидит, какой входной объект помещен в функцию ``` 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] ); } ```
@RichardLindhout Я принял ваши предложения. Теперь все в порядке
Большое спасибо, Typescript 4 отлично справляется с литералами шаблонов, но я не понимаю, как их писать, спасибо за помощь!
Вы можете найти оригинальные PR для шаблонных строк, примеров очень много. Также взгляните на документы TS
Что не так с ответом @michmich?