TypeScript не позволяет динамически оценивать свойство объекта/карты с помощью строкового ключа

#typescript

Вопрос:

У меня есть следующий код машинописи:

 const formatters = {
  time(value: string): string {
    const serialized = moment(value, dateTimeFormat, true);
    return serialized.format('MMM Do YYYY HH:mm');
  },

  date(value: string): string {
    const serialized = moment(value, 'YYYY-MM-DD', true);
    return serialized.format('MMM Do YYYY');
  },

  stringUpperCase(value: string): string {
    return value.toUpperCase();
  },

  // ...
}
 

Затем я хочу попробовать динамически найти форматер таким образом:

 function getFormattedValue(type: string): string {
  const formatter = formatters[type] || formatters.stringUpperCase;
  // ...
}
 

Однако я получаю эту ошибку машинописи:

 Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ time(value: string): string; date(value: string): string; stringUpperCase(value: string): string; ... }'.
No index signature with a parameter of type 'string' was found on type '{ time(value: string): string; date(value: string): string; stringUpperCase(value: string): string;... }'.ts(7053)
(property) type: string
 

Как мне решить эту проблему?

Ответ №1:

formatters не может быть проиндексирован, string потому что в качестве имен свойств существуют только три определенные строки.

Поэтому вам нужно сообщить typescript, что вы ожидаете type , что это будет одно из имен свойств formatters . Ты делаешь это с keyof помощью . Любая другая строка должна быть ошибкой типа.

 function getFormattedValue(
  type: keyof typeof formatters,
  value: string
): string {
  const formatter = formatters[type];
  return formatter(value)
}
 

Который вы бы использовали вот так:

 // Works
getFormattedValue('time', 'a timestamp here')
getFormattedValue('date', 'a date here')
getFormattedValue('stringUpperCase', 'a string here')

// Error as expected
getFormattedValue('asdasd', 'a type error here')
// Argument of type '"asdasd"' is not assignable to parameter of type
//   '"time" | "date" | "stringUpperCase"'.(2345)
 

Игровая площадка для машинописи без ошибок ввода

Комментарии:

1. Я Argument of type 'string' is not assignable to parameter of type 'never'.ts(2345) к чему-то клоню formatter(value) . Есть какие-нибудь идеи?

2. Код, который я опубликовал, не отображает эту ошибку, так что вы, должно быть, делаете что-то другое. Если вы отредактируете свой вопрос с кодом, который показывает эту ошибку, я, вероятно, смогу сказать вам, что не так.

3. О! formatter(value) может принимать различные типы (число, строка, логическое значение, объекты различной структуры и т.д.). Как это тогда исправить?

4. Это значительно усложняет ситуацию. У меня может не хватить времени написать это, но это работает: tsplay.dev/wXkxDW

Ответ №2:

Во-первых, время(значение: строка) не является строкой, поэтому вы не можете получить доступ к своей функции, введя формат[«время»]. Это должно быть {time: (value: string): string => { //your function }

Во-вторых, вам нужно объявить форматеры, поскольку {[type: string]: Function} typescript знает, что строка может использоваться для индексирования «форматеров», и он знает, что тип результирующего значения будет функцией.

В целом, ваш код будет выглядеть так:

 const formatters: {[type: string]: Function} = {
  time: (value: string): string => {
    const serialized = moment(value, dateTimeFormat, true);
    return serialized.format('MMM Do YYYY HH:mm');
  },

  date: (value: string): string  => {
    const serialized = moment(value, 'YYYY-MM-DD', true);
    return serialized.format('MMM Do YYYY');
  },

  stringUpperCase: (value: string): string =>  {
    return value.toUpperCase();
  },

  // ...
}

function getFormattedValue(type: string): string {
  const formatter = formatters[type] || formatters.stringUpperCase;
  // ...
}