lightsoutAlpha

discriminant-const-object

a discriminant field typed as a raw string literal instead of the `const` object's member

Deterministic checkadvisory by defaultbasecode

The argument

Discriminants Use the const Object

Discriminant fields in union families reference the const object, not raw string literals — otherwise consumers retype the literal at every narrowing site. TypeScript narrows identically.

✅ GOOD:

export interface FileAddedEvent {
	kind: typeof SyncEventKind.FileAdded;
	path: string;
}

// consumer — no raw strings, whichever way it narrows
if (event.kind === SyncEventKind.FileAdded) { /* ... */ }

switch (event.kind) {
	case SyncEventKind.FileAdded: {
		/* ... */
	}
}

❌ BAD:

export interface FileAddedEvent {
	kind: 'file-added'; // literal leaks to every consumer call site
}

What this rule is not about

A narrowing site is at fault only when there is a member to reference instead. Two cases that look the same and are not:

  • A union with no const object behind it. type Mode = 'fast' | 'slow' has no member to name — declaring one is [bare-string-union]'s ask, and this rule has nothing to say until it exists.
  • A constant the file cannot import. Narrowing against a member needs a path from this file to the object holding it. Where there is none — a package that does not depend on the one declaring the constant, a folder a build deliberately isolates — the literal is the only spelling available, and this rule has nothing to ask for.

typeof value === 'string' is not a discriminant either, whatever a const object elsewhere happens to hold: typeof types as the operator's own fixed union.

The proof

fail
export const SyncEventKind = {
	FileAdded: 'file-added',
	RecordParsed: 'record-parsed',
} as const;

export type SyncEventKind = (typeof SyncEventKind)[keyof typeof SyncEventKind];
pass
export const SyncEventKind = {
	FileAdded: 'file-added',
	RecordParsed: 'record-parsed',
} as const;

export type SyncEventKind = (typeof SyncEventKind)[keyof typeof SyncEventKind];

Turn it down

Both lines go in your lightsout.config.json.

"standards-checks": { "discriminant-const-object": "advisory" }
"standards-checks": { "discriminant-const-object": "off" }