See the working example on TypeScript Playground. (Quick & Dirty)
While there might be other solutions more suitable for your case, it is indeed possible to solve this your way, but it requires advanced TypeScript. Whether it's worth the effort is up to you.
Solve eventual name conflicts
First you need to rename Element to ElementType as it could conflict with the built-in DOM element (e.g. in a browser application). I wanted to execute it on the TypeScript playground, and had to rename it there:
enum ElementType {
Air,
Fire,
Earth,
Water,
}
Map Enum Keys by Adding a Suffix
And at this point it is getting a little bit complicated. You need a so called utility function to map enum keys with a suffix and add additional keys like in your case. We create first a type for this mapped enum:
type MappedEnumType<
T extends Record<string, string | number>,
Suffix extends string,
> = {
[key in keyof T as `${string & key}${Suffix}`]: number;
} & Record<string, number>;
Implement the Utility Function
Now we can begin with our utility function:
function createSkillsEnum<
T extends Record<string, string | number>,
Suffix extends string,
>(enumObj: T, suffix: string): MappedEnumType<T, Suffix> {
// Second Enum keys
const result: Record<string, number> = {
Unarmed: 100,
LightArmor: 101,
};
// Firs Enum Keys incl. Suffix
for (const key in enumObj) {
if (!isNaN(Number(key))) {
// Filter numeric keys
const value = enumObj[key];
if (typeof value === 'string') {
result[`${value}${suffix}`] = Number(key);
}
}
}
return result as MappedEnumType<T, Suffix>;
}
Create the new Enum
The rest is easy now, just create the new SkillsEnum with MappedEnumType Keys
const Skills = createSkillsEnum(ElementType, "Magic");
Usage
console.log(Skills);
// Output: { Unarmed: 100, LightArmor: 101, AirMagic: 0, FireMagic: 1, EarthMagic: 2, WaterMagic: 3 }
console.log(Skills.AirMagic); // Output: 0
console.log(Skills.FireMagic); // Output: 1
console.log(Skills.Unarmed); // Output: 100
console.log(Skills.LightArmor); // Output: 101
Handling TypeScript Warnings/Errors
If you get (due to your tsconfig.json settings) an error or a warning line Property 'AirMagic' comes from an index signature, so it must be accessed with ['AirMagic'].ts(4111), and you have to do additional checks:
type SecondEnumType = {
Unarmed: number;
LightArmor: number;
};
type MappedEnumType<
T extends Record<string, string | number>,
Suffix extends string,
> = {
[key in keyof T as `${string & key}${Suffix}`]: number;
} & SecondEnumType;
And change the usage a little bit:
// Usage
console.log(Skills);
// Output: { Unarmed: 100, LightArmor: 101, AirMagic: 0, FireMagic: 1, EarthMagic: 2, WaterMagic: 3 }
console.log(Skills['AirMagic']); // Output: 0
console.log(Skills['FireMagic']); // Output: 1
console.log(Skills.Unarmed); // Output: 100
console.log(Skills.LightArmor); // Output: 101
Skills.AirMagichave?Skills.AirMagicwould be equal to its integer position in the skills enum at the location the declaration was provided. So if the declarative value was provided afterUnarmedandLightArmorthe position ofAirMagicwould be '2' andFireMagicwould be '3', etc. . .