A shared literal field lets TypeScript narrow a union to the matching object shape.

discriminated union A discriminated union uses one field, often named `kind`, to tell each case apart.

Discriminated Unions

shapeKind
discriminated.ts
Replay: real traced execution (multi-file project)
type Circle = {
    kind: "circle";
    radius: number;
};

type Square = {
    kind: "square";
    side: number;
};

type Shape = Circle | Square;

function area(shape: Shape): number {
    if (shape.kind === "circle") {
        return Math.PI * shape.radius * shape.radius;
    }
    return shape.side * shape.side;
}

const shapeKind: "circle" | "square" = "circle";
const shape: Shape = shapeKind === "circle"
    ? { kind: "circle", radius: 3 }
    : { kind: "square", side: 4 };
const result: number = area(shape);

console.log(`${shape.kind} area=${result.toFixed(2)}`);
type Circle = {
    kind: "circle";
    radius: number;
};

type Square = {
    kind: "square";
    side: number;
};

type Shape = Circle | Square;

function area(shape: Shape): number {
    if (shape.kind === "circle") {
        return Math.PI * shape.radius * shape.radius;
    }
    return shape.side * shape.side;
}

const shapeKind: "circle" | "square" = "square";
const shape: Shape = shapeKind === "circle"
    ? { kind: "circle", radius: 3 }
    : { kind: "square", side: 4 };
const result: number = area(shape);

console.log(`${shape.kind} area=${result.toFixed(2)}`);
  1. shapeKind ← circle, shape ← [object Object]

    17    return shape.side * shape.side;18}1920const shapeKind→ circle: "circle" | "square" = "circle";  //@shapeKind="square"21const shape→ [object Object]: Shape = shapeKind→ circle === "circle"22    ? { kind: "circle", radius: 3 }23    : { kind: "square", side: 4 };24const result: number = area(shape[object Object]);
  2. function area(shape: Shape): number

    11type Shape = Circle | Square;1213function area(shape[object Object]: Shape): number {14    if (shape.kind === "circle") {
  3. if (shape.kind === "circle")

    13function area(shape: Shape): number {14    if (shape.kindcircle === "circle") {15        return Math.PI * shape.radius * shape.radius3;16    }
  4. result ← 28.274333882308138

    22    ? { kind: "circle", radius: 3 }23    : { kind: "square", side: 4 };24const result→ 28.274333882308138: number = area(shape[object Object]);2526console.log(`${shape.kindcircle} area=${result28.274333882308138.toFixed(2)}`);
    outputcircle area=28.27
  1. shapeKind ← square, shape ← [object Object]

    17    return shape.side * shape.side;18}1920const shapeKind→ square: "circle" | "square" = "square";21const shape→ [object Object]: Shape = shapeKind→ square === "circle"22    ? { kind: "circle", radius: 3 }23    : { kind: "square", side: 4 };24const result: number = area(shape[object Object]);
  2. function area(shape: Shape): number

    11type Shape = Circle | Square;1213function area(shape[object Object]: Shape): number {14    if (shape.kind === "circle") {15        return Math.PI * shape.radius * shape.radius;16    }17    return shape.side * shape.side4;18}
  3. result ← 16

    22    ? { kind: "circle", radius: 3 }23    : { kind: "square", side: 4 };24const result→ 16: number = area(shape[object Object]);2526console.log(`${shape.kindsquare} area=${result16.toFixed(2)}`);
    outputsquare area=16.00