Как правильно набрать итератор свойства массива, как это в Typescript? - PullRequest
0 голосов
/ 13 апреля 2020

Возьмем этот пример кода:

interface Cat {
  fur: string
  food: string
}

const cats: Array<Cat> = [
  {
    fur: 'brown',
    food: 'fish'
  },
  {
    fur: 'white',
    food: 'biscuits'
  }
];

interface Cols {
  name: string
  title: string
}

const cols: Array<Cols> = [
  {
    name: 'fur',
    title: 'Fur colour'
  },
  {
    name: 'food',
    title: 'Favourite food'
  }
];

cats.map(cat => {
  cols.map(col => {
    console.log(`${col.title} - ${cat[col.name]}`); // errors here
  });
});

Это ошибки на cat[col.name] с:

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Cat'.
  No index signature with a parameter of type 'string' was found on type 'Cat'.(7053)

Я могу устранить эту ошибку, добавив any к cat, но это побеждает суть. Как правильно это напечатать?

1 Ответ

2 голосов
/ 13 апреля 2020

Это связано с тем, что TypeScript видит col.name как строку, а интерфейс Cat содержит только два возможных ключа (ни одна строка в качестве ключей). Теоретически, col.name может возвращать любую строку, которая отсутствует в качестве ключа в объекте cat.

Поскольку вы знаете, что col.name должен быть ключом Cat интерфейс, вы можете подсказывать TypeScript следующим образом:

// Tell TS that col.name must be a keyof the Cat interface
cat[col.name as keyof Cat]

Следовательно, при таком обновлении кода TypeScript будет распознавать, что col.name действительно возвращает действительный ключ в интерфейсе Cat:

cats.map(cat => {
  cols.map(col => {
    console.log(`${col.title} - ${cat[col.name as keyof Cat]}`);
  });
});

См. Пример проверки концепции на TypeScript Playground .

Pro-tip: допустим, у вас нет явного интерфейса Cat, а есть только cat объект, вы также можете сделать:

// Tell TS that col.name must be a keyof the typeof `cat` object
cat[col.name as keyof typeof cat]

Альтернативный способ - использовать enum для свойств в интерфейсе Cat, поэтому вам не придется вручную бросьте его: так как TypeScript может вывести тип как таковой. Этот метод значительно более многословен на том основании, что не нужно вручную приводить тип col.name ( см. Пример игровой площадки здесь ):

// Declare an enum for properties in `Cat` interface
enum CatProperty {
  FUR = 'fur',
  FOOD = 'food',
}

Затем при определении массива для ваш cols, вам нужно будет использовать перечисления:

const cols: Array<Cols> = [
  {
    name: CatProperty.FUR,
    title: 'Fur colour'
  }
];

Это должно привести к тому, что TypeScript сможет распознавать, что col.name должно иметь значение fur | food, которое существует в Cat интерфейс, поэтому кастинг не требуется:

cats.map(cat => {
  cols.map(col => {
    console.log(`${col.title} - ${cat[col.name]}`);
  });
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...