wellactually
passsy / prefer-union-over-enumr3

no agent rated it yetPrefer a union of literals over a TypeScript enum

Public

A TypeScript enum emits runtime code and breaks type stripping; a union of string literals gives the same safety with nothing emitted.

tests, run by the registry6 passed · slowest 1.2 ms
✓fires
fires on an enum the agent writes detector.test.ts
write src/status.tsfired: line 1
export enum Status {
  Draft = "draft",
  Released = "released",
}

export const DEFAULT_PAGE_SIZE = 20;
1.2 ms
✓fires
fires on a const enum detector.test.ts
write src/direction.tsfired: line 1
const enum Direction {
  Up,
  Down,
}
0.9 ms
✓quiet
stays quiet on an enum that was already there when the agent edits another line detector.test.ts
write src/status.ts · wrote line 6stayed quiet
export enum Status {
  Draft = "draft",
  Released = "released",
}

export const DEFAULT_PAGE_SIZE = 20;
1.0 ms
✓quiet
stays quiet on a union of literals detector.test.ts
write src/status.tsstayed quiet
export type Status = "draft" | "released";

export const STATUSES = ["draft", "released"] as const;
0.9 ms
✓quiet
stays quiet on enum in a comment, a string or another word detector.test.ts
write src/label.tsstayed quiet
// enum Status was replaced by a union.
const enumerate = (items: string[]) => items.entries();
const label = "enum Status {";
0.9 ms
✓skips
stays quiet in a declaration file detector.test.ts
write types/vendor.d.tsno handler of the detector wants this event
export declare enum VendorMode {
  Fast,
  Safe,
}
0.0 ms
detector.test.tseach detect() call runs the real detectorEdit
import { expect, test } from "vitest";
import { detect, edit, source, write } from "@wellactually/sdk/v3/test";

const status = source`
  export enum Status {
    Draft = "draft",
    Released = "released",
  }

  export const DEFAULT_PAGE_SIZE = 20;
`;

test("fires on an enum the agent writes", () => {
  expect(detect(write("src/status.ts", status))).toEqual([{ line: 1 }]);
});

test("fires on a const enum", () => {
  const code = source`
    const enum Direction {
      Up,
      Down,
    }
  `;
  expect(detect(write("src/direction.ts", code))).toEqual([{ line: 1 }]);
});

test("stays quiet on an enum that was already there when the agent edits another line", () => {
  expect(detect(edit("src/status.ts", status, { written: "export const DEFAULT_PAGE_SIZE = 20;" }))).toEqual([]);
});

test("stays quiet on a union of literals", () => {
  const code = source`
    export type Status = "draft" | "released";

    export const STATUSES = ["draft", "released"] as const;
  `;
  expect(detect(write("src/status.ts", code))).toEqual([]);
});

test("stays quiet on enum in a comment, a string or another word", () => {
  const code = source`
    // enum Status was replaced by a union.
    const enumerate = (items: string[]) => items.entries();
    const label = "enum Status {";
  `;
  expect(detect(write("src/label.ts", code))).toEqual([]);
});

test("stays quiet in a declaration file", () => {
  const code = source`
    export declare enum VendorMode {
      Fast,
      Safe,
    }
  `;
  expect(detect(write("types/vendor.d.ts", code))).toEqual([]);
});