| |

TypeScript 11 ๐Ÿ”ท Literal Types and Const Assertions

A literal type is a type with exactly one value โ€” the string 'loading', the number 42, the boolean true. Where string means “any string,” 'loading' means “this exact string and nothing else.” Literal types are how TypeScript models closed sets: allowed values, config keys, status codes, event names. And as const is the operator that produces them โ€” it tells the compiler “don’t widen this, keep the exact literals.” Together they’re one of TypeScript’s most useful patterns, powering discriminated unions, exhaustive checks, and runtime-safe constants.

Key point: Literal types are narrower versions of string, number, and boolean. They exist at the type level only โ€” at runtime, a 'loading' value is just the string 'loading'. as const is the assertion that produces literal types from values that would otherwise widen. Every discriminated union, every config constant, every event name list is built on these two features.


What a literal type is

A literal type describes exactly one value.

let a: 'hello' = 'hello';         // only 'hello'
let b: 42 = 42;                   // only 42
let c: true = true;               // only true

a = 'world';                      // โŒ
b = 43;                           // โŒ
c = false;                        // โŒ

'hello' is not string โ€” it’s a subtype of string with a single member. 42 is not number โ€” it’s a subtype of number. true is not boolean โ€” it’s a subtype of boolean.

The three kinds of literal types:

KindExampleUnderlying type
String literal'loading'string
Numeric literal42, -1, 3.14number
Boolean literaltrue, falseboolean

Bigint and symbol literals exist too, but they’re rarer.

Why they’re subtypes:

const x: string = 'hello';       // โœ… string literal assignable to string
const y: 'hello' = 'world';       // โŒ wrong literal

A 'hello' is a string โ€” every 'hello' is a string, but not every string is 'hello'. That direction matters: literal types fit where general types are expected, not the reverse.

Why literal types matter: They let you say “these exact values” instead of “any string.” That’s the foundation of discriminated unions, exhaustive switches, config schemas, and API contracts. Without literal types, string would be the best you could do โ€” and the compiler couldn’t catch "loadng" (typo) versus "loading".


How literals arise

Literal types come from three places: const declarations, as const, and explicit annotations.

From const:

const name = 'Alice';             // type: 'Alice' (literal)
const count = 42;                 // type: 42
const active = true;              // type: true

let lname = 'Alice';              // type: string (widened)
let lcount = 42;                  // type: number
let lactive = true;               // type: boolean

const preserves the literal. let widens to the general type.

From explicit annotation:

let status: 'loading' | 'ready' = 'loading';

The annotation forces the literal union.

From as const:

const config = { mode: 'dark' } as const;
// type: { readonly mode: 'dark' }

const colors = ['red', 'green'] as const;
// type: readonly ['red', 'green']

as const widens nothing โ€” every literal stays literal, and the structure becomes readonly.

From function parameters with literal types:

function setMode(mode: 'light' | 'dark'): void { }

setMode('light');                 // โœ…
setMode('dark');                  // โœ…
setMode('blue');                  // โŒ

From return type annotations:

function getState(): 'idle' | 'active' {
  return 'idle';
}

Why const preserves literals: A const binding can’t be reassigned, so the exact value is the type. let can be reassigned to another value of the same general type, so the type is widened. This is deliberate โ€” it keeps the language ergonomic.

Why const uses literals by default: For a truly immutable binding, the value is the type. A const cannot hold anything else. TypeScript models this faithfully. That means const status = 'loading' gives you 'loading', not string โ€” a small win that compounds in literal unions.


Literal unions โ€” closed sets

The most common use of literal types is a union of literals to describe a closed set of allowed values.

type Status = 'idle' | 'loading' | 'ready' | 'error';
type Direction = 'north' | 'south' | 'east' | 'west';
type LogLevel = 'debug' | 'info' | 'warn' | 'error';
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
type Size = 'xs' | 'sm' | 'md' | 'lg' | 'xl';

A value of type Status is exactly one of those four strings.

let s: Status = 'idle';           // โœ…
s = 'loading';                    // โœ…
s = 'pending';                    // โŒ not in the union
s = 'IDLE';                       // โŒ case-sensitive

Why this is powerful: The compiler knows all valid values, so it can check every assignment, catch typos, and reason about exhaustiveness. The set is closed โ€” you can’t invent new values at runtime and expect them to type-check.

Numeric literal unions:

type DiceRoll = 1 | 2 | 3 | 4 | 5 | 6;
type Bit = 0 | 1;
type HttpStatus = 200 | 201 | 400 | 401 | 404 | 500;

Boolean literal types:

type Yes = true;
type No = false;
type Flag = true | false;         // same as boolean

Mixed literal unions:

type Id = 'new' | number;

A literal string or any number.

The alternative to enums: Literal unions are the modern, zero-runtime-cost alternative to TypeScript enums. They compile to nothing and integrate with the type system better.

// Enum โ€” emits runtime code
enum Status { Idle = 'idle', Ready = 'ready' }

// Literal union โ€” no runtime code
type Status = 'idle' | 'ready';

Both express the same set. Literal unions are lighter.

Why literal unions beat enums for most cases: They’re erased at compile time โ€” no runtime object, no bundle bloat. They work better with isolatedModules. They integrate cleanly with narrowing and exhaustiveness. Enums still have uses (numeric codes, iterating over values), but for most new code, literal unions are the better choice.


as const โ€” the const assertion

as const is a const assertion. It tells TypeScript to preserve literal types and make the structure readonly.

On a string:

const a = 'hello';                // 'hello'
const b = 'hello' as const;       // 'hello' โ€” same, already const

let c = 'hello';                  // string
let d = 'hello' as const;         // 'hello'

as const on a literal is a no-op at the type level for const. It matters on let and on composite values.

On a number:

const n = 42 as const;            // 42

On an array:

const colors = ['red', 'green', 'blue'] as const;
// type: readonly ['red', 'green', 'blue']

colors.push('yellow');            // โŒ readonly
colors[0] = 'purple';             // โŒ

The array becomes a readonly tuple of literal types. Each element keeps its exact string.

On an object:

const config = {
  mode: 'dark',
  retries: 3,
  features: ['auth', 'logging']
} as const;
// type: {
//   readonly mode: 'dark';
//   readonly retries: 3;
//   readonly features: readonly ['auth', 'logging'];
// }

Every property becomes readonly, and every literal stays literal. Nested objects and arrays get the same treatment recursively.

On an object with variables:

let name = 'Alice';
const obj = { name } as const;
// type: { readonly name: string }

name was string โ€” as const doesn’t turn string into a literal, because the variable’s type was already string. It only preserves what was already literal.

What as const does:

  • Makes the type narrow (literals stay literal)
  • Makes the structure readonly (properties and arrays can’t be reassigned)
  • Applies recursively to nested objects and arrays

What as const doesn’t do:

  • It doesn’t change runtime behavior โ€” the value is the same object
  • It doesn’t turn a widened variable into a literal
  • It doesn’t validate anything โ€” it’s an assertion

Why as const exists: Object and array literals widen by default. { mode: 'dark' } becomes { mode: string }, not { mode: 'dark' }. For most code, that’s fine โ€” you’ll mutate the object. For constants, you want the narrow type. as const opts into the narrow reading without changing anything at runtime.


as const on objects โ€” the config pattern

as const is standard for configuration objects, event name lists, and constant tables.

const CONFIG = {
  apiUrl: 'https://api.example.com',
  timeout: 5000,
  retries: 3,
  features: {
    auth: true,
    logging: false
  }
} as const;

Every field is readonly and literal-typed. Nothing can be reassigned, and the exact values are known.

Deriving types from as const:

const CONFIG = {
  apiUrl: 'https://api.example.com',
  timeout: 5000
} as const;

type Config = typeof CONFIG;
// { readonly apiUrl: 'https://api.example.com'; readonly timeout: 5000 }

Extracting value unions:

const COLORS = ['red', 'green', 'blue'] as const;

type Color = typeof COLORS[number];
// 'red' | 'green' | 'blue'

typeof COLORS[number] โ€” the element type of the array โ€” is the union of every literal in it.

Extracting key unions:

const CONFIG = {
  dark: 'dark',
  light: 'light'
} as const;

type ConfigKey = keyof typeof CONFIG;
// 'dark' | 'light'

type ConfigValue = typeof CONFIG[keyof typeof CONFIG];
// 'dark' | 'light'

Two common patterns:

  • keyof typeof X โ€” union of keys
  • typeof X[keyof typeof X] โ€” union of values

These extract union types from constant objects without duplicating the values.

Why this pattern is idiomatic: The single source of truth is the runtime object. The types are derived from it. Add a key to CONFIG, and the union grows automatically. No duplication โ€” one place to change.

const STATUS = {
  Idle: 'idle',
  Loading: 'loading',
  Ready: 'ready'
} as const;

type Status = typeof STATUS[keyof typeof STATUS];
// 'idle' | 'loading' | 'ready'

function setStatus(s: Status): void { }

setStatus(STATUS.Idle);           // โœ…
setStatus('idle');                // โœ…
setStatus('pending');             // โŒ

Why derive from the object: If you wrote type Status = 'idle' | 'loading' | 'ready' and a separate object, the two could drift. One has 'loading', the other 'loadng'. Deriving from one source of truth prevents that. It also means the object provides runtime constants (for code that needs a value) and the type provides compile-time checking.


Literal types in function signatures

Literal unions are common in function parameters โ€” they document the allowed values.

function log(message: string, level: 'info' | 'warn' | 'error'): void {
  console.log(`[${level.toUpperCase()}] ${message}`);
}

log('started', 'info');           // โœ…
log('failed', 'error');           // โœ…
log('hmm', 'debug');              // โŒ

The parameter type restricts the allowed strings.

Return type as a literal:

function isReady(): true {
  return true;
}

Rare, but useful for discriminated unions and type narrowing.

Overloaded literal returns:

function parse(input: 'json'): object;
function parse(input: 'text'): string;
function parse(input: 'json' | 'text'): object | string {
  return input === 'json' ? {} : '';
}

The return type depends on the literal argument.

Literal union for configuration:

interface Config {
  mode: 'development' | 'production' | 'test';
  logLevel: 'debug' | 'info' | 'warn' | 'error';
  target: 'es2018' | 'es2020' | 'es2022';
}

const config: Config = {
  mode: 'development',
  logLevel: 'debug',
  target: 'es2022'
};

Each field is a closed set. The compiler catches typos and unlisted values.

Why use literal unions in parameters: They document the API contract directly in the type. function log(level: string) accepts any string โ€” including typos. function log(level: 'info' | 'warn' | 'error') accepts exactly three. The second is self-documenting and checked. Autocomplete shows the valid options.


Exhaustiveness with literal unions

Literal unions pair naturally with switch and a never check to enforce exhaustiveness.

type Status = 'idle' | 'loading' | 'ready' | 'error';

function assertNever(x: never): never {
  throw new Error(`Unhandled: ${x}`);
}

function message(status: Status): string {
  switch (status) {
    case 'idle': return 'Waiting';
    case 'loading': return 'Loading...';
    case 'ready': return 'Ready';
    case 'error': return 'Failed';
    default: return assertNever(status);
  }
}

In the default branch, TypeScript has narrowed status to never โ€” meaning every case is handled. If you add a new status and forget to handle it, the default branch receives a non-never type, and assertNever fails to compile.

Adding a new variant triggers the error:

type Status = 'idle' | 'loading' | 'ready' | 'error' | 'cancelled';

// Now message() fails because 'cancelled' isn't handled.
// Error: Argument of type 'string' is not assignable to 'never'.

That’s the exhaustive-check pattern. Add a value to the union, and every switch that needs to handle it refuses to compile until updated.

What never means here: never is the empty type โ€” no value belongs to it. After handling all cases of a union, TypeScript narrows the remaining type to never. That’s how it detects exhaustiveness.

Why this pattern matters: It makes adding a variant a compile-time event. You can’t forget to handle a new case โ€” the compiler tells you. For state machines, event handlers, and command dispatchers, this is essential.

type Command =
  | { kind: 'add'; item: string }
  | { kind: 'remove'; id: number }
  | { kind: 'clear' };

function execute(cmd: Command): void {
  switch (cmd.kind) {
    case 'add': return console.log('add', cmd.item);
    case 'remove': return console.log('remove', cmd.id);
    case 'clear': return console.log('clear');
    default: return assertNever(cmd);
  }
}

The default branch handles any new command added later โ€” but only by failing to compile until the case is added.

Why exhaustive checks are powerful: They turn the compiler into a checklist. Every time you extend a union, the compiler forces you to update every consumer. Without them, a missing case becomes a silent runtime bug โ€” a missing handler, a broken state. With them, the compiler catches it before you ship.


Literal types + discriminated unions

The most common production use of literal types is the discriminated union โ€” a union of object types with a shared literal field that distinguishes them.

type Success = { status: 'success'; data: string };
type Failure = { status: 'failure'; error: string };
type Loading = { status: 'loading' };

type Result = Success | Failure | Loading;

function display(r: Result): string {
  switch (r.status) {
    case 'success': return r.data;         // r narrowed to Success
    case 'failure': return r.error;        // r narrowed to Failure
    case 'loading': return 'Loading...';   // r narrowed to Loading
  }
}

The status field is the discriminant โ€” a literal type that tells which branch you have. Narrowing on it narrows the whole object.

Why literal types are required here: Without them, status: string wouldn’t narrow. TypeScript needs literal types to distinguish branches. That’s why discriminated unions are impossible without literal types.

With as const for constants:

const SUCCESS = { status: 'success' } as const;
// { readonly status: 'success' }

The literal type is preserved, so the constant is assignable to the discriminated union.

Why this is the killer app of literal types: Discriminated unions model alternatives โ€” success or failure, one event or another, one shape or another. TypeScript narrows on the discriminant and gives you the right branch. This pattern is everywhere in real code: Redux actions, API results, parser states, command messages.

Why discriminated unions need literals: Without a literal discriminant, there’s nothing to narrow on. status: string matches every branch โ€” no narrowing. status: 'success' matches exactly one branch, so narrowing works. That’s why literal types aren’t just convenient โ€” they’re foundational for one of TypeScript’s most important patterns.


A full example

A small state machine that combines literal unions, as const, and exhaustive checks.

// ============================================
// CONSTANT TABLE โ€” SOURCE OF TRUTH
// ============================================

const EVENTS = {
  Load: 'load',
  Success: 'success',
  Fail: 'fail',
  Reset: 'reset'
} as const;

type Event = typeof EVENTS[keyof typeof EVENTS];
// 'load' | 'success' | 'fail' | 'reset'

// ============================================
// STATE TYPE โ€” LITERAL UNION
// ============================================

type State = 'idle' | 'loading' | 'ready' | 'error';

// ============================================
// TRANSITIONS โ€” DISCRIMINATED BY EVENT
// ============================================

function transition(state: State, event: Event): State {
  switch (state) {
    case 'idle':
      if (event === EVENTS.Load) return 'loading';
      return state;

    case 'loading':
      if (event === EVENTS.Success) return 'ready';
      if (event === EVENTS.Fail) return 'error';
      return state;

    case 'ready':
      if (event === EVENTS.Reset) return 'idle';
      return state;

    case 'error':
      if (event === EVENTS.Reset) return 'idle';
      return state;

    default:
      return assertNever(state);
  }
}

function assertNever(x: never): never {
  throw new Error(`Unhandled: ${x}`);
}

// ============================================
// USAGE
// ============================================

let state: State = 'idle';

state = transition(state, EVENTS.Load);
console.log(state);               // 'loading'

state = transition(state, EVENTS.Success);
console.log(state);               // 'ready'

state = transition(state, EVENTS.Reset);
console.log(state);               // 'idle'

console.log(EVENTS.Load);         // 'load'

Every piece uses literal types:

  • EVENTS โ€” as const object producing literal types
  • Event โ€” union extracted via typeof EVENTS[keyof typeof EVENTS]
  • State โ€” literal union of four strings
  • transition โ€” switches on literals, exhaustive via assertNever
  • Runtime constants โ€” EVENTS.Load gives 'load'

The compiler catches: typos in state names, unhandled states, invalid event strings.

Why this pattern matters: It’s how you model real systems โ€” state machines, workflows, protocols. The types are the specification, and the compiler enforces them. Adding a new event or state triggers compile errors in every place that needs updating, so you can’t forget.


Complete Example Session

# ============================================
# PART 1: BASIC LITERAL TYPES
# ============================================

cat > literals.ts << 'EOF'
let a: 'hello' = 'hello';
let b: 42 = 42;
let c: true = true;

// a = 'world';  // โŒ
// b = 43;       // โŒ
// c = false;    // โŒ

const name = 'Alice';    // 'Alice'
let lname = 'Alice';     // string

type Status = 'idle' | 'loading' | 'ready' | 'error';
let s: Status = 'idle';
// s = 'pending';  // โŒ

console.log(a, b, c, name, lname, s);
EOF

npx tsc --noEmit literals.ts
# (no errors)

# ============================================
# PART 2: `as const` ON OBJECTS
# ============================================

cat > as-const.ts << 'EOF'
const config = {
  mode: 'dark',
  retries: 3,
  features: ['auth', 'logging']
} as const;

// config.mode = 'light';       // โŒ readonly
// config.features.push('x');   // โŒ readonly

console.log(config);
EOF

npx tsc --noEmit as-const.ts
# (no errors)

# ============================================
# PART 3: EXTRACTING UNIONS
# ============================================

cat > extract.ts << 'EOF'
const COLORS = ['red', 'green', 'blue'] as const;
type Color = typeof COLORS[number];
// 'red' | 'green' | 'blue'

const STATUS = {
  Idle: 'idle',
  Ready: 'ready'
} as const;

type StatusKey = keyof typeof STATUS;              // 'Idle' | 'Ready'
type StatusValue = typeof STATUS[keyof typeof STATUS];  // 'idle' | 'ready'

function paint(c: Color): void { console.log(c); }
paint('red');
// paint('purple');  // โŒ

console.log(STATUS.Idle, STATUS.Ready);
EOF

npx tsc --noEmit extract.ts
# (no errors)

# ============================================
# PART 4: EXHAUSTIVE CHECKS
# ============================================

cat > exhaustive.ts << 'EOF'
type Status = 'idle' | 'loading' | 'ready' | 'error';

function assertNever(x: never): never {
  throw new Error(`Unhandled: ${x}`);
}

function message(s: Status): string {
  switch (s) {
    case 'idle': return 'Waiting';
    case 'loading': return 'Loading...';
    case 'ready': return 'Ready';
    case 'error': return 'Failed';
    default: return assertNever(s);
  }
}

console.log(message('idle'));
console.log(message('ready'));
EOF

npx tsc --noEmit exhaustive.ts
# (no errors)

# ============================================
# PART 5: DISCRIMINATED UNION
# ============================================

cat > discriminated.ts << 'EOF'
type Result =
  | { status: 'success'; data: string }
  | { status: 'failure'; error: string }
  | { status: 'loading' };

function display(r: Result): string {
  switch (r.status) {
    case 'success': return r.data;
    case 'failure': return r.error;
    case 'loading': return 'Loading...';
  }
}

console.log(display({ status: 'success', data: 'ok' }));
console.log(display({ status: 'loading' }));
EOF

npx tsc --noEmit discriminated.ts
# (no errors)

# ============================================
# PART 6: FULL EXAMPLE
# ============================================

cat > machine.ts << 'EOF'
const EVENTS = {
  Load: 'load',
  Success: 'success',
  Fail: 'fail',
  Reset: 'reset'
} as const;

type Event = typeof EVENTS[keyof typeof EVENTS];
type State = 'idle' | 'loading' | 'ready' | 'error';

function assertNever(x: never): never {
  throw new Error(`Unhandled: ${x}`);
}

function transition(state: State, event: Event): State {
  switch (state) {
    case 'idle':
      return event === EVENTS.Load ? 'loading' : state;
    case 'loading':
      if (event === EVENTS.Success) return 'ready';
      if (event === EVENTS.Fail) return 'error';
      return state;
    case 'ready':
      return event === EVENTS.Reset ? 'idle' : state;
    case 'error':
      return event === EVENTS.Reset ? 'idle' : state;
    default:
      return assertNever(state);
  }
}

let state: State = 'idle';
state = transition(state, EVENTS.Load);
console.log(state);   // loading
state = transition(state, EVENTS.Success);
console.log(state);   // ready
state = transition(state, EVENTS.Reset);
console.log(state);   // idle
EOF

npx tsc --noEmit machine.ts
# (no errors)

# ============================================
# PART 7: COMPILE AND RUN
# ============================================

npx tsc literals.ts as-const.ts extract.ts exhaustive.ts discriminated.ts machine.ts
node literals.js
# [ hello 42 true Alice Alice idle ]

node as-const.js
# [ { mode: 'dark', retries: 3, features: [ 'auth', 'logging' ] } ]

node extract.js
# [ red ]
# [ idle ready ]

node exhaustive.js
# [ Waiting ]
# [ Ready ]

node discriminated.js
# [ ok ]
# [ Loading... ]

node machine.js
# [ loading ]
# [ ready ]
# [ idle ]

Quick Reference

Literal Type Syntax

KindExample
String literal'loading'
Number literal42
Boolean literaltrue
Bigint literal10n
Union'a' | 'b'

How Literals Arise

SourceExampleResult
constconst x = 'a''a'
letlet x = 'a'string
Annotationlet x: 'a' | 'b'literal union
as const['a'] as constreadonly ['a']
Function paramf(m: 'a' | 'b')literal union

Widening

DeclarationType
let x = 'a'string
const x = 'a''a'
let x = ['a']string[]
const x = ['a']string[]
const x = ['a'] as constreadonly ['a']
const x = { a: 'b' }{ a: string }
const x = { a: 'b' } as const{ readonly a: 'b' }

as const Effects

InputOutput
'a''a'
['a', 'b']readonly ['a', 'b']
{ a: 'b' }{ readonly a: 'b' }
Nested objectsRecursively readonly + literals
string variablestring (no change)

Union Extraction

PatternResult
typeof COLORS[number]Element union
keyof typeof OBJKey union
typeof OBJ[keyof typeof OBJ]Value union

Common Literal Unions

TypeValues
Status'idle' | 'loading' | 'ready' | 'error'
Direction'north' | 'south' | 'east' | 'west'
Log level'debug' | 'info' | 'warn' | 'error'
HTTP method'GET' | 'POST' | 'PUT' | 'DELETE'
Size'xs' | 'sm' | 'md' | 'lg' | 'xl'

Exhaustive Check Pattern

StepCode
1. Helperfunction assertNever(x: never): never { throw x; }
2. Switch all casescase 'a': ... case 'b': ...
3. Defaultdefault: return assertNever(x);
4. Add new caseCompile error in every switch

Discriminated Union

PartExample
Discriminantstatus: 'success'
Branches{ status: 'success'; data: T }
Narrowingswitch (r.status) { case 'success': ... }
Exhaustivedefault: assertNever(r)

Best Practices

โœ… Do This:

// Use literal unions for closed sets
type Status = 'idle' | 'loading' | 'ready';              // โœ…

// Use `as const` for constants
const CONFIG = { mode: 'dark' } as const;                // โœ…

// Extract unions from constants
type Mode = typeof CONFIG.mode;                          // โœ…

// Use `typeof X[number]` for array element unions
const COLORS = ['red', 'green'] as const;
type Color = typeof COLORS[number];                      // โœ…

// Use `keyof typeof X` for key unions
type Key = keyof typeof CONFIG;                          // โœ…

// Use exhaustive switch with `assertNever`
default: return assertNever(x);                          // โœ…

// Use discriminated unions for variants
type Result = { status: 'ok'; value: T } | { status: 'err'; error: string };  // โœ…

// Use literal unions in function params
function log(msg: string, level: 'info' | 'warn' | 'error'): void { }         // โœ…

// Import constants for runtime + types
import { STATUS } from './constants';                    // โœ…

โŒ Don’t Do This:

// Don't use plain strings where a literal union fits
function setMode(m: string): void { }                    // โš ๏ธ  accepts typos

// Don't forget `as const` on constant objects
const CONFIG = { mode: 'dark' };  // mode: string            // โš ๏ธ  widened

// Don't duplicate the union and the object
type Status = 'idle' | 'ready';
const Status = { Idle: 'idle', Ready: 'ready' };         // โš ๏ธ  can drift

// Don't skip exhaustive checks
function message(s: Status): string {
  if (s === 'idle') return '';
  return '';  // silently handles 'ready' as ''                 // โš ๏ธ  missed case

// Don't use `as const` on mutable data
const mutable = { count: 0 } as const;
mutable.count = 1;                                        // โŒ

// Don't cast string variables to const
let x = 'hello';
const y = x as const;  // still string                    // โš ๏ธ  no effect

// Don't overuse `as const`
const data = getData() as const;                         // โš ๏ธ  probably wrong

Common Pitfalls

PitfallProblemSolution
Forgetting as const on constantsWide typesAdd as const
Using let for literal constantsWidensUse const
Duplicating union and objectDriftDerive from one source
Skipping exhaustive checkSilent missing caseassertNever default
Missing narrowing on discriminantNo branch narrowingUse a literal discriminant
as const on variablesDoesn’t narrow existing stringPreserve at declaration
Mutating as const objectRuntime errorDon’t mutate
Case-sensitivity'IDLE' โ‰  'idle'Match exact strings
Numeric literals from JSWidened to numberAnnotate or as const
Array literal wideningstring[] not literal tupleas const

Real-World Examples

1. Literal union type

type Status = 'idle' | 'loading' | 'ready' | 'error';

2. Const string

const mode = 'dark';              // 'dark'

3. Let widens

let mode = 'dark';                // string

4. as const object

const CONFIG = { mode: 'dark', retries: 3 } as const;

5. as const array

const COLORS = ['red', 'green'] as const;

6. Extract element union

type Color = typeof COLORS[number];

7. Extract key union

type Key = keyof typeof CONFIG;

8. Extract value union

type Value = typeof CONFIG[keyof typeof CONFIG];

9. Function param literal union

function log(level: 'info' | 'warn' | 'error'): void { }

10. Exhaustive switch

default: return assertNever(x);

11. assertNever helper

function assertNever(x: never): never {
  throw new Error(`Unhandled: ${x}`);
}

12. Discriminated union

type Result =
  | { status: 'ok'; value: string }
  | { status: 'err'; error: string };

13. Numeric literal union

type Dice = 1 | 2 | 3 | 4 | 5 | 6;

14. Boolean literal type

type Yes = true;

15. Constant table with values

const STATUS = {
  Idle: 'idle',
  Ready: 'ready'
} as const;

16. Runtime constant use

setStatus(STATUS.Idle);

17. Literal union from runtime object

type Status = typeof STATUS[keyof typeof STATUS];

18. Function returning literal

function ready(): 'ready' { return 'ready'; }

19. Overloaded literal return

function parse(x: 'json'): object;
function parse(x: 'text'): string;

20. State machine transitions

function transition(state: State, event: Event): State { }

Visual: Literal Types Are Subtypes

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  string                                      โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚  'loading'  'ready'  'error'           โ”‚  โ”‚
โ”‚  โ”‚                                        โ”‚  โ”‚
โ”‚  โ”‚  literal types are narrower versions   โ”‚  โ”‚
โ”‚  โ”‚                                        โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

'loading' assignable to string  โœ…
string assignable to 'loading'  โŒ

Visual: Widening Rules

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  let x = 'hello';                            โ”‚
โ”‚       โ”‚                                      โ”‚
โ”‚       โ–ผ                                      โ”‚
โ”‚  string  (widened)                           โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  const x = 'hello';                          โ”‚
โ”‚       โ”‚                                      โ”‚
โ”‚       โ–ผ                                      โ”‚
โ”‚  'hello'  (literal preserved)                โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  const obj = { a: 'x' };                     โ”‚
โ”‚       โ”‚                                      โ”‚
โ”‚       โ–ผ                                      โ”‚
โ”‚  { a: string }  (property widens)            โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  const obj = { a: 'x' } as const;            โ”‚
โ”‚       โ”‚                                      โ”‚
โ”‚       โ–ผ                                      โ”‚
โ”‚  { readonly a: 'x' }  (literal + readonly)   โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Visual: as const Transformation

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Before `as const`                           โ”‚
โ”‚                                              โ”‚
โ”‚  const CONFIG = {                            โ”‚
โ”‚    apiUrl: 'https://x',                      โ”‚
โ”‚    timeout: 5000,                            โ”‚
โ”‚    features: ['auth']                        โ”‚
โ”‚  };                                          โ”‚
โ”‚                                              โ”‚
โ”‚  { apiUrl: string; timeout: number;          โ”‚
โ”‚    features: string[] }                      โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                  โ”‚
                  โ”‚  as const
                  โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  After `as const`                            โ”‚
โ”‚                                              โ”‚
โ”‚  {                                           โ”‚
โ”‚    readonly apiUrl: 'https://x';             โ”‚
โ”‚    readonly timeout: 5000;                   โ”‚
โ”‚    readonly features: readonly ['auth'];     โ”‚
โ”‚  }                                           โ”‚
โ”‚                                              โ”‚
โ”‚  Every literal preserved, recursively        โ”‚
โ”‚  readonly                                     โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Visual: Exhaustive Check

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  type Status = 'a' | 'b' | 'c';              โ”‚
โ”‚                                              โ”‚
โ”‚  function f(s: Status) {                     โ”‚
โ”‚    switch (s) {                              โ”‚
โ”‚      case 'a': return 1;                     โ”‚
โ”‚      case 'b': return 2;                     โ”‚
โ”‚      case 'c': return 3;                     โ”‚
โ”‚      default:                                โ”‚
โ”‚        return assertNever(s);                โ”‚
โ”‚        // s is never โ€” exhaustive             โ”‚
โ”‚    }                                         โ”‚
โ”‚  }                                           โ”‚
โ”‚                                              โ”‚
โ”‚  Add 'd' to Status:                          โ”‚
โ”‚  โ†’ default branch โ€” s isn't never            โ”‚
โ”‚  โ†’ assertNever(s) fails to compile           โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Visual: Union Extraction Patterns

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  const COLORS = ['red', 'green'] as const;   โ”‚
โ”‚                                              โ”‚
โ”‚  typeof COLORS          โ†’ readonly [...]     โ”‚
โ”‚  typeof COLORS[number]  โ†’ 'red' | 'green'    โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  const STATUS = {                            โ”‚
โ”‚    Idle: 'idle',                             โ”‚
โ”‚    Ready: 'ready'                            โ”‚
โ”‚  } as const;                                 โ”‚
โ”‚                                              โ”‚
โ”‚  keyof typeof STATUS              โ†’ 'Idle' | 'Ready'  โ”‚
โ”‚  typeof STATUS[keyof typeof STATUS]โ†’ 'idle' | 'ready' โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Visual: Discriminated Union

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  type Result =                               โ”‚
โ”‚    | { status: 'success'; data: string }     โ”‚
โ”‚    | { status: 'failure'; error: string }    โ”‚
โ”‚    | { status: 'loading' };                  โ”‚
โ”‚                                              โ”‚
โ”‚  function display(r: Result) {               โ”‚
โ”‚    switch (r.status) {                       โ”‚
โ”‚      case 'success': return r.data;          โ”‚
โ”‚        // r is Success                       โ”‚
โ”‚                                              โ”‚
โ”‚      case 'failure': return r.error;         โ”‚
โ”‚        // r is Failure                       โ”‚
โ”‚                                              โ”‚
โ”‚      case 'loading': return 'Loading...';    โ”‚
โ”‚        // r is Loading                       โ”‚
โ”‚    }                                         โ”‚
โ”‚  }                                           โ”‚
โ”‚                                              โ”‚
โ”‚  Narrowing on the literal discriminant       โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Visual: Literal Union vs Enum

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Literal union                               โ”‚
โ”‚                                              โ”‚
โ”‚  type Status = 'idle' | 'ready';             โ”‚
โ”‚                                              โ”‚
โ”‚  โ€ข Zero runtime code                         โ”‚
โ”‚  โ€ข Erased at compile                         โ”‚
โ”‚  โ€ข Integrates with narrowing                 โ”‚
โ”‚  โ€ข Preferred in modern TS                    โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Enum                                        โ”‚
โ”‚                                              โ”‚
โ”‚  enum Status { Idle = 'idle', Ready = 'ready' }โ”‚
โ”‚                                              โ”‚
โ”‚  โ€ข Emits runtime object                      โ”‚
โ”‚  โ€ข Provides values                           โ”‚
โ”‚  โ€ข Has reverse mapping (numeric)             โ”‚
โ”‚  โ€ข Older pattern                             โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Best of both:                               โ”‚
โ”‚                                              โ”‚
โ”‚  const Status = {                            โ”‚
โ”‚    Idle: 'idle',                             โ”‚
โ”‚    Ready: 'ready'                            โ”‚
โ”‚  } as const;                                 โ”‚
โ”‚                                              โ”‚
โ”‚  type Status =                               โ”‚
โ”‚    typeof Status[keyof typeof Status];       โ”‚
โ”‚                                              โ”‚
โ”‚  โ€ข Named runtime constants                   โ”‚
โ”‚  โ€ข Zero runtime enum code                    โ”‚
โ”‚  โ€ข Type-safe at every use site               โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Summary

ConceptMeaning
Literal typeSingle-value type โ€” 'a', 42, true
Literal unionClosed set โ€” 'a' | 'b' | 'c'
constPreserves literal types
letWidens to general types
as constPreserves literals, makes readonly
typeof X[number]Extract array element union
keyof typeof XExtract object key union
typeof X[keyof typeof X]Extract object value union
DiscriminantLiteral field that picks a union branch
Discriminated unionUnion of objects with a literal discriminator
neverEmpty type โ€” used for exhaustiveness
assertNeverHelper that throws on unhandled values

Key takeaways:

  • Literal types describe exactly one value โ€” 'loading', 42, true
  • They’re subtypes of their general types โ€” 'loading' is a string, 42 is a number
  • const preserves literals; let widens to the general type
  • Object and array properties widen even under const โ€” use as const to preserve
  • as const makes the structure readonly and keeps every literal โ€” recursively
  • Literal unions ('a' | 'b' | 'c') model closed sets โ€” the modern alternative to enums
  • as const objects are the single source of truth for constants and their types
  • Extract unions with typeof X[number], keyof typeof X, and typeof X[keyof typeof X]
  • Discriminated unions rely on literal discriminants for narrowing
  • Exhaustive checks use assertNever to catch missing cases at compile time
  • Adding a new value to a literal union triggers compile errors in every place that needs updating
  • Literal unions have zero runtime cost; enums emit a JavaScript object

Remember: Literal types are how TypeScript models closed sets โ€” the exact values a thing can be. They’re subtypes of string, number, and boolean, and they preserve their exact value only under const or as const. Combine them with unions for state machines and config, with as const objects for runtime constants, and with discriminated unions for variant types. The compiler then checks every value against every case โ€” turning accidental typos into compile errors and missing cases into failed builds.


Stop using slow, ad-bloated tool sites! ๐Ÿคฎ

๐Ÿ”Ž Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
โœ… Finance (Mortgage, Interest, Inflation)
โœ… Tech (Base64, JSON, Dev Suite, IP)
โœ… Health (BMI, BMR, TDEE)
โœ… Productivity (Timer, Workspace, QR)

โšก๏ธ Fast & Private
๐Ÿ”’ No data leaves your device
๐Ÿ’Ž 100% Free

๐Ÿ”— Use it now: https://tools.kandz.me
๐Ÿ”– Bookmark itโ€”youโ€™ll need it later!