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];
答案#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数组的元素(A[number])的类型比T更窄,那么你一定遗漏了什么,你应该会收到一个错误。以下是实现方法:
const ensureArray = <T>() => <A extends T[]>(
...a: A & ([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 & ([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"]; }'
希望其中一个能够满足你的需求。祝你好运!
