结合 SO 上提出的各种问题的解决方案,我设法创建了一个应该正是您正在寻找的解决方案!
首先,let's grab a type that can build us a tuple!
type BuildTuple<Current extends [...T[]], T, Count extends number> =
Current["length"] extends Count
? Current
: BuildTuple<[T, ...Current], T, Count>
type Tuple<T, Count extends number> = BuildTuple<[], T, Count>
Then we'll hop on over to this SO question, which has some really useful union helper functions!
type UnionToIntersection<U> =(U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never
type UnionToOvlds<U> = UnionToIntersection<U extends any ? (f: U) => void : never>;
type PopUnion<U> = UnionToOvlds<U> extends ((a: infer A) => void) ? A : never;
然后我们将重写该 SO 答案的主要方法,因为我们的目标不是将联合转换为元组(我不建议这样做 - read this for an explanation as to why)
type UnionCount<U, L extends any[] = []> = {
0: PopUnion<U> extends infer SELF ? UnionCount<Exclude<U, SELF>, [any, ...L]> : never;
1: L['length'];
}[[U] extends [never] ? 1 : 0];
从那里开始,剩下的就相当简单了。我们将制作几个别名类型,以便于阅读
type InterfaceLength<T> = UnionCount<keyof T>;
type EnumLength<T> = UnionCount<T>;
如果我们将所有这些组合在一起,我们就可以创建我们的final枚举
type InterfaceTuple<T, L> = Tuple<T, InterfaceLength<L>>;
type EnumTuple<T, L> = Tuple<T, EnumLength<L>>;
您的代码:
enum Chest {
ID,
NAME,
CAPACITY,
OPEN_WIN_POINTS,
}
type ChestStringTuple = EnumTuple<string, Chest>
const correctTuple: ChestStringTuple = ['foo', 'bar', 'baz', 'goo'] // Valid
const incorrectTuple1: ChestStringTuple = ['foo', 'bar', 'baz'] // Not valid
const incorrectTuple2: ChestStringTuple = ['foo', 'bar', 'baz', false] // Not valid
Playground
使用风险自负。