Массив TypeScript для строкового литерала

В настоящее время у меня есть как массив строк, так и строковый литерал, содержащий те же строки:

const furniture = ['chair', 'table', 'lamp'];
type Furniture = 'chair' | 'table' | 'lamp';

Мне нужно как в моем приложении, но я пытаюсь сохранить код DRY. Так есть ли способ сделать вывод один из другого?

Я в основном хочу сказать что-то вроде type Furniture = [any string in furniture array], поэтому нет повторяющихся строк.

Ответ 1

Обновление для TypeScript 3.0:

С использованием универсальных параметров отдыха есть способ правильно вывести string[] как тип литерального кортежа, а затем получить тип объединения литералов.

Это выглядит так:

const tuple = <T extends string[]>(...args: T) => args;
const furniture = tuple('chair', 'table', 'lamp');
type Furniture = typeof furniture[number];

Подробнее об общих параметрах отдыха

Обновление для TypeScript 3.4:

В TypeScript версии 3.4 введены так называемые const contexts, которые позволяют объявлять тип кортежа как неизменяемый и получать узкий литеральный тип напрямую (без необходимости вызывать функцию, подобную показанной выше).

С этим новым синтаксисом мы получаем это хорошее краткое решение:

const furniture = <const> ['chair', 'table', 'lamp'];
type Furniture = typeof furniture[number];

Подробнее о новых контекстах const можно найти в этом PR, а также в заметках о выпуске.

Ответ 2

Лучший доступный обходной путь:

const furnitureObj = { chair: 1, table: 1, lamp: 1 };
type Furniture = keyof typeof furnitureObj;
const furniture = Object.keys(furnitureObj) as Furniture[];

В идеале мы могли бы сделать это:

const furniture = ['chair', 'table', 'lamp'];
type Furniture = typeof furniture[number];

К сожалению, сегодня furniture выводится как string[], что означает, что Furniture теперь также является string.

Мы можем ввести типизацию как литерал с ручной аннотацией, но это возвращает дублирование:

const furniture = ["chair", "table", "lamp"] as ["chair", "table", "lamp"];
type Furniture = typeof furniture[number];

Выпуск TypeScript # 10195 отслеживает возможность подсказки TypeScript, что список должен выводиться как статический кортеж, а не как string[], поэтому, возможно, в будущем это будет возможно.

Ответ 3

Единственная корректировка, которую я хотел бы предложить, - сделать const гарантированной совместимой с типом, например:

type Furniture = 'chair' | 'table' | 'lamp';

const furniture: Furniture[] = ['chair', 'table', 'lamp'];

Это даст вам предупреждение, если вы сделаете орфографическую ошибку в массиве или добавите неизвестный элемент:

// Warning: Type 'unknown' is not assignable to furniture
const furniture: Furniture[] = ['chair', 'table', 'lamp', 'unknown'];

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

Ответ 4

оказывается, есть более простой способ сделать это, и он работает с обычными старыми объектами.

Таким образом, вы могли бы это сделать.

const furnitureObj = { chair: 'chair', table: {$:1.00}, lamp: 2 };

type Furnitures = keyof typeof furnitureObj;

Или вы можете это сделать

import { furnitureObj } from './your-furniture-path';

type Furnitures = keyof typeof furnitureObj;