Skip to content

Branded type does not work anymore #323

@salemkode

Description

@salemkode

Describe the bug

When using ts-pattern with a branded primitive type (e.g., string & { __type: 'id' }), calling .exhaustive() does not trigger a TypeScript error or a runtime error, even if not all possible inputs are handled.

This gives a false sense of exhaustiveness. The pattern only matches one literal value, but ts-pattern doesn’t detect that other values (with the same brand) are possible. At runtime, unmatched values simply fall through silently, despite .exhaustive() being used.


TypeScript playground with a minimal reproduction case

👉 [Playground Link](https://www.typescriptlang.org/play/?#code/JYWwDg9gTgLgBAbziAhjAxgCwDRwApwC+cAZlBCHAOQwDOAtGGjAKZQB2VAUFzAJ5gWcAEZQU7ACYsJASQlwAvHFowowdgHM4AMkRwA+vv6CAXHABEwCeaIBuHugjsVcK4osBGAEwBmGyloRMUlpOXsSAFd2dBhgJzhWFQAKdTAImDNRcSlZCQBKRC44YrgoFhgIjmQ0LBT2NJg8opKAOgB3YBhMFIlcJIKFAD44R2cIABsWFvGIDSSqFnB+OHHgFSo8ppK4AHod13gQCJcNYAA3IRAhNnIoODbMYEm4GTgUSnYIeExslnG38b-dABFi0ZrFFosAAeP2OsQu-XshAcTloEymMzm5m8PgALP5AlkQrk8rYgA)

Code from playground:

import { match } from 'ts-pattern';

type BrandedId = string & { __type: 'id' };

const id = "123" as BrandedId;

function test(input: BrandedId) {
  return match(input)
    .with(id, () => "matched")
    .exhaustive(); // ❌ No TS error or runtime error
}

test("456" as BrandedId); // ❌ Compiles, but give me runtime error.

Expected behavior

  • At compile time: TypeScript should warn that the match is not exhaustive (e.g., missing fallback or additional cases).
  • At runtime: .exhaustive() should throw if no pattern matches.

Versions

  • TypeScript version: 5.8.3
  • ts-pattern version: 5.7.0 (latest)

I see already #178 and #167:

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions