Исчерпывающий перебор всех значений в объединении строк?

#typescript

#typescript

Вопрос:

У меня есть объединение строк, подобное этому:

export type Intervals = 'total' | 'weekly' | 'biweekly' | 'monthly' | 'annually';

Я хочу отобразить их пользователю, перебрав массив значений объединения:

 const intervals = ['total', 'weekly', 'biweekly', 'monthly', 'annually'];
intervals.forEach(...);
  

Как я могу ввести intervals массив таким образом, чтобы он гарантированно содержал все значения Intervals объединения?

Ответ №1:

Вместо того, чтобы выводить массив из типа, выводите тип из массива.

 export const INTERVALS = ['total', 'weekly', 'biweekly', 'monthly', 'annually'] as const;

const temp = [...INTERVALS];
export type Interval = typeof temp[0];
  

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

1. еще проще: export type Interval = typeof INTERVALS[number];

2. что означает «число» в этой строке?

3. Я этого не понимаю. temp это просто мелкая копия исходного массива и temp[0] является первым элементом этого массива, который является 'total' и typeof temp[0] должен быть справедливым string . Чего мне здесь не хватает?

Ответ №2:

Достаточно просто убедиться, что intervals это должно быть назначено Array<Intervals> :

 const intervalsMisspelled: Array<Intervals> = 
  ['weekly', 'biweekly', 'annually', 'monthly', 'totul']; // error, "totul"
  

Но это не мешает вам пропускать вещи:

 const intervalsMissing: Array<Intervals> = 
  ['weekly', 'biweekly', 'annually', 'monthly']; // oops, no error but missing "total"
  

Чтобы исправить это, вы можете вызвать вспомогательную функцию ensureArray() , которая принимает параметр типа T (который будет Intervals для вас), а затем возвращает новую функцию, которая принимает список параметров типа T и выводит тип массива A для этого списка. Если элементы A array ( A[number] ) имеют более узкий тип, чем T , то вы, должно быть, что-то пропустили, и вы должны получить сообщение об ошибке. Вот способ сделать это:

 const ensureArray = <T>() => <A extends T[]>(
  ...a: A amp; ([T] extends [A[number]] ? A : never)
): A => a;

const ensureIntervalsArray = ensureArray<Intervals>();

const intervals = ensureIntervalsArray(
  'annually', 'biweekly', 'monthly', 'total', 'weekly'); // okay

const intervalsMisspelled = ensureIntervalsArray(
  'annually', 'biweekly', 'monthly', 'totul', 'weekly'); // error, "totul"

const intervalsMissing = ensureIntervalsArray(
  'annually', 'biweekly', 'monthly', 'weekly'); // error,
// [string, string, string, string] is not assignable to never
  

Это работает, хотя ошибка, которую вы получаете intervalsMissing , довольно загадочна, говоря, что что-то не может быть назначено never , не сообщая вам, в чем проблема на самом деле. Поскольку TypeScript в настоящее время не позволяет нам создавать ошибки пользовательского типа, мы можем только попытаться обойти это.

Следующее дает еще более странное сообщение об ошибке, но оно дает разработчику подсказку:

 const ensureArray = <T>() => <A extends T[]>(
  ...a: A amp; ([T] extends [A[number]] ? A :
    { errorMessage: [Error, "You are missing", Exclude<T, A[number]>] })
): A => a;

const ensureIntervalsArray = ensureArray<Intervals>();

const intervalsMissing = ensureIntervalsArray(
  'annually', 'biweekly', 'monthly', 'weekly'); // error,
// Property 'errorMessage' is missing in type 
// '["annually", "biweekly", "monthly", "weekly"]' 
// but required in type '{ errorMessage: [Error, "You are missing", "total"]; }'
  

Надеюсь, одного из них достаточно для ваших нужд. Удачи!

Ответ №3:

Как я могу ввести массив интервалов таким образом, чтобы он гарантированно содержал все значения объединения интервалов

Нелегко. Вы можете объявить его как объединение кортежей. Скажем, у вас есть только 'total' | 'weekly' , вы могли бы сделать:

 const intervals: 
| ['total', 'weekly']
| ['weekly', 'total']
  

То, что у вас здесь, — это перестановка nPn .