Выберите значение из объекта с динамическим ключом на основе ключа ввода в 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 у меня есть

  • Мне нужны только возможные ключи объекта ввода
  • Я хочу, чтобы Typescript возвращал ошибку, если ключ недоступен для объекта или «keyTranslations»

Обновлять Основываясь на ответе @michmich112, я использовал следующую функцию

function translate<T extends BackendTranslations>(translations: T, fallback: string, language: keyof BackendTranslations) => {
      return translations[language] || fallback;
}

translate(project.nameTranslations, project.name)

Я оставлю ответ открытым, так как мне все еще интересно, может ли это работать в Typescript.

Что не так с ответом @michmich?

captain-yossarian from Ukraine 18.12.2020 20:55

Это действительно решило мою проблему с его предложением, но не ответило на вопрос, и мне все еще интересно, может ли исходная функция быть безопасной для типов, а также следовать моим требованиям.

Richard Lindhout 18.12.2020 21:08
Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой Zod и раскрыть некоторые ее особенности, например, возможности валидации и трансформации данных, а также...
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Мне нравится библиотека Mantine Component , но заставить ее работать без проблем с Remix бывает непросто.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
TypeScript против JavaScript
TypeScript против JavaScript
TypeScript vs JavaScript - в чем различия и какой из них выбрать?
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Не все нужно хранить на стороне сервера. Иногда все, что вам нужно, это постоянное хранилище на стороне клиента для хранения уникальных для клиента...
Что такое ленивая загрузка в Angular и как ее применять
Что такое ленивая загрузка в Angular и как ее применять
Ленивая загрузка - это техника, используемая в Angular для повышения производительности приложения путем загрузки модулей только тогда, когда они...
1
2
1 176
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Во-первых, вам нужно создать интерфейс для вашего 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];
}

Я думаю, что согласен с мнением об использовании другой функции

Richard Lindhout 18.12.2020 20:21

Спасибо за ваше время и предложения!

Richard Lindhout 18.12.2020 20:28
Ответ принят как подходящий

Вот моя попытка:

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] ); } ```

Richard Lindhout 19.12.2020 11:51

@RichardLindhout Я принял ваши предложения. Теперь все в порядке

captain-yossarian from Ukraine 19.12.2020 13:11

Большое спасибо, Typescript 4 отлично справляется с литералами шаблонов, но я не понимаю, как их писать, спасибо за помощь!

Richard Lindhout 19.12.2020 13:25

Вы можете найти оригинальные PR для шаблонных строк, примеров очень много. Также взгляните на документы TS

captain-yossarian from Ukraine 19.12.2020 13:35

Другие вопросы по теме