Как объединить два перечисления в TypeScript
Предположим, у меня есть два перечисления, как описано ниже в Typcript, затем как их объединить
enum Mammals {
Humans,
Bats,
Dolphins
}
enum Reptiles {
Snakes,
Alligators,
Lizards
}
export default Mammals & Reptiles // For Illustration purpose, Consider both the Enums have been merged.
Теперь, когда я import
exported value
в другой файл, я должен иметь доступ к значениям из обоих перечислений.
import animalTypes from "./animalTypes"
animalTypes.Humans //valid
animalTypes.Snakes // valid
Как я могу достичь такой функциональности в Typcript?
Ответы
Ответ 1
Я не собираюсь предлагать решение для слияния с перечислениями (я не мог найти правильный способ сделать это)
Но если вы хотите, чтобы что-то велось как перечисление по сравнению с тем, как вы его потребляете, вы все равно можете использовать объединенный объект в javascript.
enum Mammals {
Humans = 'Humans',
Bats = 'Bats',
Dolphins = 'Dolphins',
}
enum Reptiles {
Snakes = 'Snakes',
Alligators = 'Alligators',
Lizards = 'Lizards',
}
const Animals = {
...Mammals,
...Reptiles,
}
type Animals = Mammals | Reptiles
Тогда вы можете использовать Animals.Snakes или Animals.Dolphins, и оба должны быть правильно напечатаны и работать как перечисление
Ответ 2
Перечисления, интерфейсы и типы - рабочее решение для объединения перечислений
Что сбивает с толку, так это типы против значений.
- Если вы определите значение (
let
, const
и т.д.), Оно будет иметь значение плюс некоторый вычисляемый, но не названный по отдельности тип.
- Если вы определите
type
или interface
, он создаст именованный тип, но он не будет ни выводиться, ни учитываться в окончательном JS. Это помогает только при написании вашего приложения.
- Если вы создаете
enum
в Typescript, он создает имя статического типа, которое вы можете использовать, плюс реальный объект, выводимый в JS, который вы можете использовать.
Из справочника TS:
Использовать перечисление очень просто: просто получите доступ к любому члену как свойству самого перечисления и объявите типы, используя имя перечисления.
Таким образом, если вы Object.assign()
два перечисления, это создаст новое объединенное значение (объект), но не новый именованный тип.
Поскольку это больше не enum
, вы теряете преимущество наличия значения и именованного типа, но вы все равно можете создать отдельное имя типа в качестве обходного пути.
К счастью, у вас может быть одинаковое имя для значения и типа, и TS будет импортировать оба, если вы их экспортируете.
// This creates a merged enum, but not a type
const Animals = Object.assign({}, Mammals, Reptiles);
// Workaround: create a named type (typeof Animals won't work here!)
type Animals = Mammals | Reptiles;
TS площадка для игр
Ответ 3
Проблемы со слиянием:
enum AA1 {
aKey, // = 0
bKey // = 1
}
enum BB1 {
cKey, // = 0
dKey // = 1
}
- ❌ Перечисления с одинаковыми ключами (=> ключи перезаписываются)
enum AA2 {
aKey = 1
}
enum BB2 {
aKey = 2
}
enum AA3 {
aKey, // = 0
bKey // = 1
}
enum BB3 {
cKey = 2,
dKey // = 3
}
enum AA4 {
aKey = 'Hello',
bKey = 0,
cKey // = 1
}
enum BB4 {
dKey = 2,
eKey = 'Hello',
fKey = 'World'
}
Примечание:
aKey = 'Hello'
и eKey = 'Hello'
работают, потому что перечисление со строковым значением не имеет этого значения в качестве ключа
// For aKey = 'Hello', key is working
type aa4aKey = AA4.aKey; // = AA4.aKey
// value is not.
type aa4aValue = AA4.Hello; // ❌ Namespace 'AA4' has no exported member 'Hello'
type aa4aValue2 = AA4['Hello']; // ❌ Property 'Hello' does not exist on type 'AA4'
console.log(AA4); // { 0: 'bKey', 1: 'cKey', aKey: 'Hello', bKey: 0, cKey: 1 }
console.log(BB4); // { 2: 'dKey', dKey: 2, eKey: 'Hello', fKey: 'World' }
Слияние
- ❌ используя типы объединений
type AABB1 = AA4 | BB4; // = AA4 | BB4
type AABB1key = AABB1['aKey']; // = never
type AABB1key2 = AABB1.aKey; // ❌ 'AABB1' only refers to a type, but is being used as a namespace here. ts(2702)
- ❌ используя типы пересечений
type AABB1 = AA4 & BB4; // = never
type AABB1key = AABB1['aKey']; // = never
- ✅ используя типы пересечений с typeof
type AABB2 = (typeof AA4) & (typeof BB4); // = typeof AA4 & typeof BB4
type AABB2key = AABB2['aKey']; // = AA4.aKey
const aabb1 = { ...AA4, ...BB4 };
const aabb2 = Object.assign({}, AA4, BB4); // also work
// aabb1 = {
// 0: 'bKey',
// 1: 'cKey',
// 2: 'dKey',
// aKey: 'Hello',
// bKey: 0,
// cKey: 1,
// dKey: 2,
// eKey: 'Hello',
// fKey: 'World' }
- Type используя typeof с копией js
const aabb = { ...AA4, ...BB4 };
type TypeofAABB = typeof aabb;
// type TypeofAABB = {
// [x: number]: string;
// dKey: BB4.dKey;
// eKey: BB4.eKey;
// fKey: BB4.fKey;
// aKey: AA4.aKey;
// bKey: AA4.bKey;
// cKey: AA4.cKey;
// };
Совет: вы можете использовать одно и то же имя для типа и значения
const merged = { ...AA4, ...BB4 };
type merged = typeof merged;
const aValue = merged.aKey;
type aType = merged['aKey'];
Ваш случай
Если вы хотите объединить 2 перечисления, у вас есть ~ 3 варианта:
1. Использование строковых перечислений
enum Mammals {
Humans = 'Humans',
Bats = 'Bats',
Dolphins = 'Dolphins'
}
enum Reptiles {
Snakes = 'Snakes',
Alligators = 'Alligators',
Lizards = 'Lizards'
}
export const Animals = { ...Mammals, ...Reptiles };
export type Animals = typeof Animals;
2. Использование уникальных номеров
enum Mammals {
Humans = 0,
Bats,
Dolphins
}
enum Reptiles {
Snakes = 2,
Alligators,
Lizards
}
export const Animals = { ...Mammals, ...Reptiles };
export type Animals = typeof Animals;
3. Использование вложенных перечислений
enum Mammals {
Humans,
Bats,
Dolphins
}
enum Reptiles {
Snakes,
Alligators,
Lizards
}
export const Animals = { Mammals, Reptiles };
export type Animals = typeof Animals;
const bats = Animals.Mammals.Bats; // = 1
const alligators = Animals.Reptiles.Alligators; // = 1
Примечание: вы также можете объединить вложенные перечисления со следующим кодом. Будьте осторожны, чтобы НЕ иметь дублированные значения, если вы это сделаете!
type Animal = {
[K in keyof Animals]: {
[K2 in keyof Animals[K]]: Animals[K][K2]
}[keyof Animals[K]]
}[keyof Animals];
const animal: Animal = 0 as any;
switch (animal) {
case Animals.Mammals.Bats:
case Animals.Mammals.Dolphins:
case Animals.Mammals.Humans:
case Animals.Reptiles.Alligators:
case Animals.Reptiles.Lizards:
case Animals.Reptiles.Snakes:
break;
default: {
const invalid: never = animal; // no error
}
}
Ответ 4
Перечисление TypeScript не только содержит определяемые вами ключи, но и числовое обратное, например:
Mammals.Humans === 0 && Mammals[0] === 'Humans'
Теперь, если вы попытаетесь объединить их, например, с назначением Object#assign
вы получите две клавиши с одинаковым числовым значением:
const AnimalTypes = Object.assign({}, Mammals, Reptiles);
console.log(AnimalTypes.Humans === AnimalTypes.Snakes) // true
И я полагаю, это не то, что вы хотите.
Один из способов предотвратить это - это вручную присвоить значения перечислению и убедиться, что они разные:
enum Mammals {
Humans = 0,
Bats = 1,
Dolphins = 2
}
enum Reptiles {
Snakes = 3,
Alligators = 4,
Lizards = 5
}
или менее явным, но в остальном эквивалентным:
enum Mammals {
Humans,
Bats,
Dolphins
}
enum Reptiles {
Snakes = 3,
Alligators,
Lizards
}
Во всяком случае, до тех пор, пока вы убедитесь, что у перечислений, которые вы объединяете, есть разные наборы ключей/значений, вы можете объединить их с назначением Object#assign
.
Демонстрация игровой площадки
Ответ 5
Я бы сказал, что правильный способ сделать это - определить новый тип:
enum Mammals {
Humans = 'Humans',
Bats = 'Bats',
Dolphins = 'Dolphins',
}
enum Reptiles {
Snakes = 'Snakes',
Alligators = 'Alligators',
Lizards = 'Lizards',
}
type Animals = Mammals | Reptiles;
Ответ 6
Попробуйте этот пример перечисления ------
Перечисления или перечисления - это новый тип данных, поддерживаемый в TypeScript
enum PrintMedia {
Newspaper = 1,
Newsletter,
Magazine,
Book
}
function getMedia(mediaName: string): PrintMedia {
if ( mediaName === 'Forbes' || mediaName === 'Outlook') {
return PrintMedia.Magazine;
}
}
let mediaType: PrintMedia = getMedia('Forbes');