如何在Typescript中获取下一个(或上一个)枚举值



我有一个枚举,定义如下:

enum Ed {
up,
down,
left,
right,
}
//or 
enum Es {
A = "a",
B = "b",
C = "c",
}

给定值Es.B,我想得到下一个,即Es.C

Es.B+1getNextEnum(Es.B)EnumX.of(Es).next(Es.B)这样的东西,甚至更简单?函数应该支持任何枚举,所以请不要硬编码值。

注意:最后一个元素的下一个应该是枚举的第一个元素。

感谢

这是解决问题的一种方法:

class EnumX {
static of<T extends object>(e: T) {
const values = Object.values(e)
const map = new Map(values.map((k, i) => [k, values[i + 1]]));
return {
next: <K extends keyof T>(v: T[K]) => map.get(v)
}
}
}

这是一个操场来看看。

该解决方案之所以有效,是因为枚举基本上是对象,您可以迭代密钥等。

这里是";"回环";当最后一个元素用作输入时返回第一个元素的解决方案(以及一个游乐场(:

class EnumX {
static of<T extends object>(e: T) {
const values = Object.values(e)
return {
next: <K extends keyof T>(v: T[K]) => values[(values.indexOf(v) + 1) % values.length]
}
}
}

您可以使用等效的只读对象和类型(对值和类型使用相同的名称以模拟枚举行为(,而不是在TypeScript中使用枚举功能(在迭代过程中可能会出现意外行为,请参阅TS手册中数字枚举的反向映射(。然后包装迭代是直接和可预测的:

TS游乐场

type Values<T> = T[keyof T];
type AnyEnum<Values extends string | number = string | number> =
Readonly<Record<string, Values>>;
function getOffsetValue <Enum extends AnyEnum>(
e: Enum,
current: Values<Enum>,
distance: number,
): Values<Enum> {
const values = Object.values(e) as Values<Enum>[];
// You could just do this:
// const index = (values.indexOf(current) + distance) % values.length;
let index = values.indexOf(current);
// But it's safer to validate at runtime:
if (index === -1) throw new TypeError('Value not found');
index = (index + distance) % values.length;
return values[index < 0 ? values.length + index : index]!;
}
function getNextValue <Enum extends AnyEnum>(
e: Enum,
current: Values<Enum>,
): Values<Enum> {
return getOffsetValue(e, current, 1);
}
function getPreviousValue <Enum extends AnyEnum>(
e: Enum,
current: Values<Enum>,
): Values<Enum> {
return getOffsetValue(e, current, -1);
}

// Your enums as objects created using "const assertions" —
// the result is readonly properties and literal value types:
const Ed = {
up: 0,
down: 1,
left: 2,
right: 3,
} as const;
type Ed = Values<typeof Ed>;
//^? type Ed = 0 | 1 | 2 | 3
const Es = {
A: 'a',
B: 'b',
C: 'c',
} as const;
type Es = Values<typeof Es>;
//^? type Es = "a" | "b" | "c"

// Usage:
// Numeric enum:
const left = getOffsetValue(Ed, Ed.down, 5);
//^? 0 | 1 | 2 | 3
console.log('left === Ed.left', left === Ed.left); // true
const up = getNextValue(Ed, Ed.right);
console.log('up === Ed.up', up === Ed.up); // true
const right = getPreviousValue(Ed, Ed.up);
console.log('right === Ed.right', right === Ed.right); // true

// String enum:
const b = getOffsetValue(Es, Es.A, -2);
console.log('b === Es.B', b === Es.B); // true
const a = getNextValue(Es, Es.C);
//^? "a" | "b" | "c"
console.log('a === Es.A', a === Es.A); // true
const c = getPreviousValue(Es, Es.A);
console.log('c === Es.C', c === Es.C); // true

// Full iteration example from your dataset:
for (const [name, e] of [['Ed', Ed], ['Es', Es]] as [string, AnyEnum][]) {
console.log(`${name}:`);
for (const current of Object.values(e)) {
const previous = getPreviousValue(e, current);
const next = getNextValue(e, current);
console.log(`${JSON.stringify(previous)} <- ${JSON.stringify(current)} -> ${JSON.stringify(next)}`);
}
}

// Examples of compiler safety:
getNextValue(Ed, Es.A); /*
~~~~
Argument of type '"a"' is not assignable to parameter of type 'Ed'.(2345) */
getNextValue(Es, 'c'); // ok
getNextValue(Es, 'd'); /*
~~~
Argument of type '"d"' is not assignable to parameter of type 'Es'.(2345) */

// Where runtime validation matters:
/*
This object fits the constraint criteria, but wasn't created with literal
properties — it uses an index signature. A compiler error doesn't occur
because `5` is assignable to `number` (the types are ok), but the input data
is obviously invalid (it's out of range). If there were no runtime validation
to throw an exception on invalid inputs, the behavior would be unexpected:
*/
getNextValue({a: 1, b: 2} as Record<string, number>, 5);

从操场上编译JS:

"use strict";
function getOffsetValue(e, current, distance) {
const values = Object.values(e);
// You could just do this:
// const index = (values.indexOf(current) + distance) % values.length;
let index = values.indexOf(current);
// But it's safer to validate at runtime:
if (index === -1)
throw new TypeError('Value not found');
index = (index + distance) % values.length;
return values[index < 0 ? values.length + index : index];
}
function getNextValue(e, current) {
return getOffsetValue(e, current, 1);
}
function getPreviousValue(e, current) {
return getOffsetValue(e, current, -1);
}
// Your enums as objects created using "const assertions" —
// the result is readonly properties and literal value types:
const Ed = {
up: 0,
down: 1,
left: 2,
right: 3,
};
//^? type Ed = 0 | 1 | 2 | 3
const Es = {
A: 'a',
B: 'b',
C: 'c',
};
//^? type Es = "a" | "b" | "c"
// Usage:
// Numeric enum:
const left = getOffsetValue(Ed, Ed.down, 5);
//^? 0 | 1 | 2 | 3
console.log('left === Ed.left', left === Ed.left); // true
const up = getNextValue(Ed, Ed.right);
console.log('up === Ed.up', up === Ed.up); // true
const right = getPreviousValue(Ed, Ed.up);
console.log('right === Ed.right', right === Ed.right); // true
// String enum:
const b = getOffsetValue(Es, Es.A, -2);
console.log('b === Es.B', b === Es.B); // true
const a = getNextValue(Es, Es.C);
//^? "a" | "b" | "c"
console.log('a === Es.A', a === Es.A); // true
const c = getPreviousValue(Es, Es.A);
console.log('c === Es.C', c === Es.C); // true
// Full iteration example from your dataset:
for (const [name, e] of [['Ed', Ed], ['Es', Es]]) {
console.log(`${name}:`);
for (const current of Object.values(e)) {
const previous = getPreviousValue(e, current);
const next = getNextValue(e, current);
console.log(`${JSON.stringify(previous)} <- ${JSON.stringify(current)} -> ${JSON.stringify(next)}`);
}
}

最新更新