Typescript: получить тип объединения из массива объектов - PullRequest
8 голосов
/ 02 марта 2020

Я хотел бы объявить массив элементов с принудительным типом и получить из него тип объединения. Этот шаблон работает, если вы явно не указываете тип элементов в массиве. Я не уверен, как это лучше всего объяснить, поэтому вот пример:

ПРИМЕР 1

type Pair = {
  key: string;
  value: number;
};

const pairs: ReadonlyArray<Pair> = [
  { key: 'foo', value: 1 },
  { key: 'bar', value: 2 },
] as const;

type Keys = typeof pairs[number]['key']

ПРИМЕР 2

type Data = {
  name: string;
  age: number;
};

const DataRecord: Record<string, Data> = {
  foo: { name: 'Mark', age: 35 },
  bar: { name: 'Jeff', age: 56 },
} as const;

type Keys = keyof typeof DataRecord;

Вот пример получения ключей при использовании as const. Мне нужно такое же поведение, но с явно заданным массивом.

const pairs = [
  { key: 'foo', value: 1 },
  { key: 'bar', value: 2 },
] as const;

type Keys = typeof pairs[number]['key']; // "foo" | "bar"

желаемое значение ключей: "foo"|"bar"

фактическое значение ключей: string

Ответы [ 2 ]

3 голосов
/ 03 марта 2020

Для переменной вы можете либо позволить компилятору выводить тип из инициализации, либо записать его явно. Если вы пишете это явно, как и вы, тогда значение инициализации проверяется по аннотации, но фактический тип инициализатора не влияет на тип переменной (поэтому вы теряете информацию о типе, которую вы хотите). Если вы позволите компилятору сделать вывод, вы больше не сможете ограничить тип так, чтобы он соответствовал определенному c интерфейсу (как вам кажется)

Решением для этого является использование обобщенного c Функция как для ограничения значения, так и для вывода его фактического типа:

type Pair = {
  key: string;
  value: number;
};
function craetePairsArray<T extends readonly Pair[] & Array<{key: V}>, V extends string>(...args: T) {
    return args
}

const pairs = craetePairsArray(
  { key: 'foo', value: 1 },
  { key: 'bar', value: 2 },
)

type Keys1 = typeof pairs[number]['key']

type Data = {
  name: string;
  age: number;
};

function craeteDataObject<T extends Record<string, Data>>(arg: T) {
    return arg;
}
const DataRecord = craeteDataObject({
  foo: { name: 'Mark', age: 35 },
  bar: { name: 'Jeff', age: 56 },
})

type Keys2 = keyof typeof DataRecord;

Playground Link

Примечание. В случае массива нам необходимо сильное включение компилятора бит в выводимые строковые литералы для key, следовательно, целое & Array<{key: V}>, где V - параметр типа, расширяющий string

2 голосов
/ 03 марта 2020

Обычные подходы:

  • пусть TS выводит тип pairs, опуская явный тип ReadonlyArray<Pair> (см. ответ )
  • введите key в Pair тип "foo"|"bar"

Если вы не не хотите этого делать, то единственный способ определить ваши ключи и ограничение типа pairs - использование вспомогательной функции. Тип Pair также будет сделан обобщенным c для сохранения заданных типов строковых литералов key. Вы можете использовать IIFE, чтобы сделать назначение компактным:

type Pair<K = string> = {
    key: K;
    value: number;
};

const pairs = (<T>(p: readonly Pair<T>[]) => p)([
    { key: 'foo', value: 1 },
    { key: 'bar', value: 2 },
] as const) // readonly Pair<"foo" | "bar">[]

type Keys = typeof pairs[number]['key'] // "foo" | "bar"

Детская площадка

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...