ESC

Type to search the knowledge base.

Literal Types

String, number, and boolean literal types — model fixed variants, narrow with equality, and combine with unions for safe UI props.

beginner3 min read
  • typescript
  • literal-types

A literal type is a type that is exactly one value — not any string, but specifically 'loading'. Literal unions are how TypeScript models buttons, tabs, HTTP methods, and feature flags without full enums.

type Align = 'left' | 'center' | 'right';
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
type Dice = 1 | 2 | 3 | 4 | 5 | 6;
type Yes = true; // boolean literal

Docs: Literal Types, Narrowing.

Why literals beat bare string

function setVariant(v: string) {
  /* accepts typos */
}

function setVariantSafe(v: 'primary' | 'secondary' | 'danger') {
  /* typos fail compile */
}

UI APIs should fail at compile time for 'primry'.

Inference and widening

let status = 'idle'; // string — widened
const statusConst = 'idle'; // 'idle' — literal

let flagged = true; // boolean
const flaggedConst = true; // true

let often widens; const keeps literals for primitives. For objects:

const config = { mode: 'dark' };
// mode: string

const configLit = { mode: 'dark' } as const;
// mode: 'dark'

See readonly and const assertions.

Narrowing with equality

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

function message(s: Status) {
  if (s === 'error') {
    return 'Something failed';
  }
  return s === 'loading' ? '…' : 'Ready';
}

Discriminated unions are literal tags on objects — discriminated unions for UI state.

Template literal types (preview)

type EventName = `on${Capitalize<'click' | 'focus'>}`;
// 'onClick' | 'onFocus'

Useful for typed event maps and CSS-in-JS. Deep dive: template literal types.

React props

type ButtonProps = {
  size?: 'sm' | 'md' | 'lg';
  type?: 'button' | 'submit' | 'reset';
  children: React.ReactNode;
};

function Button({ size = 'md', type = 'button', children }: ButtonProps) {
  return (
    <button type={type} data-size={size}>
      {children}
    </button>
  );
}

Prefer literal unions for design tokens over freeform strings.

as const satisfies pattern

const routes = {
  home: '/',
  about: '/about',
} as const;

type Path = (typeof routes)[keyof typeof routes]; // '/' | '/about'

Or with satisfies for checking against a wider type while keeping literals — satisfies operator.

Numeric and boolean literals

type Port = 80 | 443 | 3000;
type Feature = { enabled: true; flag: string } | { enabled: false };

function run(f: Feature) {
  if (f.enabled) {
    console.log(f.flag);
  }
}

Boolean discriminants work; string discriminants are often clearer in logs.

Footguns

  1. Union with string — 'a' | 'b' | string collapses to string.
  2. Over-using literals for open sets — user-entered names stay string.
  3. Comparing without narrowing — still need runtime checks for external data.
  4. Enum vs union — enums vs union types.
// Collapsed
type Oops = 'GET' | 'POST' | string; // string

Interview out-loud answer

“Literal types fix a type to a specific value. Literal unions model closed variants for props and state. const and as const preserve literals; let widens. I use them for UI variants and discriminants, not for freeform user text.”

Further reading

Related guides