Enum как параметр в typescript
Невозможно ли установить тип параметра в Enum? Вот так:
private getRandomElementOfEnum(e : enum):string{
var length:number = Object.keys(e).length;
return e[Math.floor((Math.random() * length)+1)];
}
Если я это сделаю, Intellij отметит этот код как неизвестный. И предложить переименовать переменную, имеет ли это смысл?
private getRandomElementOfEnum(e : any):string{
var length:number = Object.keys(e).length;
return e[Math.floor((Math.random() * length)+1)];
}
Этот код работает нормально. но не столь изящным и понятным для кода.
Есть ли возможность или небольшое обходное решение для определения перечисления в качестве параметра?
EDIT:
Изучив эти ответы, могу ли я сделать это также с ah набором определенных enum, sth, подобным enum1 | enum2 | enum3?
Ответы
Ответ 1
Невозможно гарантировать, что параметр является перечислением, потому что перечисления в TS не наследуются от общего предка или интерфейса.
TypeScript обеспечивает статический анализ. В вашем коде используется динамическое программирование с Object.keys
и e[dynamicKey]
. Для динамических кодов удобно использовать тип any
.
Ваш код неисправен: length()
не существует, e[Math.floor((Math.random() * length)+1)]
возвращает строку или целое число, а значения перечисления могут быть установлены вручную...
Вот предложение:
function getRandomElementOfEnum<E>(e: any): E {
var keys = Object.keys(e),
index = Math.floor(Math.random() * keys.length),
k = keys[index];
if (typeof e[k] === 'number')
return <any>e[k];
return <any>parseInt(k, 10);
}
function display(a: Color) {
console.log(a);
}
enum Color { Blue, Green };
display(getRandomElementOfEnum<Color>(Color));
В идеале, тип параметра any
должен быть заменен на typeof E
, но компилятор (TS 1.5) не может понять этот синтаксис.
Ответ 2
Вы можете сделать лучше, чем any
:
enum E1 {
A, B, C
}
enum E2 {
X, Y, Z
}
function getRandomElementOfEnum(e: { [s: number]: string }): number {
/* insert working implementation here */
return undefined;
}
// OK
var x: E1 = getRandomElementOfEnum(E1);
// Error
var y: E2 = getRandomElementOfEnum(window);
// Error
var z: string = getRandomElementOfEnum(E2);
Ответ 3
Я согласен с @Tarh. Перечисления в TypeScript - это просто объекты Javascript без общего интерфейса или прототипа (и если они const enum
, то они даже не являются объектами), поэтому вы не можете ограничивать типы "любым перечислением".
Самое близкое, что я мог получить, это что-то вроде следующего:
enum E1 {
A, B, C
}
enum E2 {
X, Y, Z
}
// make up your own interface to match TypeScript enums
// as closely as possible (not perfect, though)
interface Enum {
[id: number]: string
}
function getRandomElementOfEnum(e: Enum): string {
let length = Object.keys(e).length / 2;
return e[Math.floor((Math.random() * length))];
}
Это работает для всех перечислений (без пользовательских инициализаторов), но оно также будет принимать другие массивы в качестве входных данных (а затем завершаться ошибкой, поскольку тело метода опирается на очень специфическую структуру ключей, имеющуюся в перечислениях TypeScript).
Поэтому, если у вас нет реальной необходимости в такой "универсальной" функции, создайте безопасные для типов типы для отдельных типов перечислений (или типа объединения, например E1|E2|E3
), которые вам действительно нужны.
И если у вас есть такая необходимость (и это вполне может быть проблема X-Y, которую можно решить лучше, совершенно другим способом, учитывая больший контекст), используйте any
, потому что вы все равно оставили безопасную для типов территорию.
Ответ 4
Другой возможный вариант, не упомянутый выше, это использование фактических значений. Однако это возможно только тогда, когда вы знаете все варианты. Это, на мой взгляд, определенно лучше, чем любой.
doSomething(a: string, b: 'this'|'can'|'work'): void {
//do something
}
Ответ 5
Суммируя предыдущие ответы с небольшим новым синтаксисом - универсальной типобезопасной функцией, которая работает как с числовыми, так и с строковыми перечислениями:
function getRandomElementOfEnum<T extends {[key: number]: string | number}>(e: T): T[keyof T] {
const keys = Object.keys(e);
const randomKeyIndex = Math.floor(Math.random() * keys.length);
const randomKey = keys[randomKeyIndex];
// Numeric enums members also get a reverse mapping from enum values to enum names.
// So, if a key is a number, actually it a value of a numeric enum.
// see https://www.typescriptlang.org/docs/handbook/enums.html#reverse-mappings
const randomKeyNumber = Number(randomKey);
return isNaN(randomKeyNumber)
? e[randomKey as keyof T]
: randomKeyNumber as unknown as T[keyof T];
}
Ответ 6
Решение @selinathat отлично подходит, только если у вас есть несколько типов. но что, если у нас будет больше? например:
doSomething(a: string, b: 'this'|'can'|'work'|'test1'|'test2'|'test3'): void {
//do something
}
это довольно уродливо, ха!?
я предпочитаю использовать keyof :
interface Items {
'this',
'can',
'work',
'test1',
'test2',
'test3',
}
doSomething(a: string, b: keyof Items): void {
//do something
}