TypeScript enums are famously problematic - they compile to IIFEs, they block tree-shaking.
This plugin rewrites export enum declarations at build time into plain named const exports in a virtual module, giving you all the ergonomics of enums with none of the baggage.
// ------------
// Before
// ------------
//#region src/enum.ts
var Fruit = /* @__PURE__ */ (function (Fruit) {
Fruit[(Fruit["Apple"] = 0)] = "Apple";
Fruit[(Fruit["Banana"] = 1)] = "Banana";
Fruit[(Fruit["Kiwi"] = 2)] = "Kiwi";
return Fruit;
})({});
//#endregion
//#region src/main.ts
var a = Fruit.Apple;
console.log(a);
console.log(Fruit.Banana);
//#endregion
// ------------
// After
// ------------
var a = 1;
console.log(a);
console.log(2);Usage stays exactly the same (except the indexing starts from 1 not 0):
import { Fruit } from "./fruit";
const pick: Fruit = Fruit.Apple; // 1// ------------
// input
// ------------
export enum Fruit {
Apple,
Banana,
Kiwi,
}
// ------------
// output generated by the plugin
// ------------
export * as Fruit from "\0enum:/abs/path/fruit.ts?Fruit";
export type Fruit = 1 | 2 | 3;
// ------------
// generated Virtual Module
// ------------
export const Apple = 1;
export const Banana = 2;
export const Kiwi = 3;| TypeScript enum | This plugin | |
|---|---|---|
| Tree-shakeable | ✗ | ✓ |
Works with isolatedModules |
✗ | ✓ |
| Serializes cleanly to JSON | ✗ | ✓ |
| Familiar import syntax | ✓ | ✓ |
| Type-safe | ✓ | ✓ |
npm install -D git+https://github.com/eye-wave/better-enums.git// vite.config.ts
import { defineConfig } from "vite";
import betterEnums from "vite-plugin-better-enums";
export default defineConfig({
plugins: [betterEnums()],
});The plugin runs with enforce: "pre" so it transforms your source before esbuild or any other plugin sees it.
Write enums exactly as you normally would - just make sure they're exported:
// status.ts
export enum Status {
Pending,
Active,
Archived,
}Import and use them identically to before:
import { Status } from "./status";
function canEdit(s: Status): boolean {
return s === Status.Active;
}Multiple enums in one file work fine:
export enum Color {
Red,
Green,
Blue,
}
export enum Size {
Small,
Medium,
Large,
}Non-exported enums are left completely untouched.
Members are assigned sequential integers in declaration order:
export enum Direction {
North, // 1
East, // 2
South, // 3
West, // 4
}Note: Initializers (
Foo = 5) are unsupported right now and will throw a build error pointing to the offending member.
Since this plugin transforms enums into plain objects at build time, there are a few architectural differences from standard TypeScript enums:
- No Reverse Lookups: You cannot do
Fruit[1]to get the string"Apple". Only forward lookups (Fruit.Apple) are supported. - Custom Initializers Unsupported: Explicit values (like
Enum = 5orEnum = "value") are currently unsupported and will throw a build error. All enums are strictly auto-incremented starting from 1. - Ambient Enums:
declare enumstatements are ignored.
MIT