Skip to content

Map branded primitives to their carrier type - #283

Open
paoloanzn wants to merge 1 commit into
vercel-labs:mainfrom
paoloanzn:branded-primitives
Open

Map branded primitives to their carrier type#283
paoloanzn wants to merge 1 commit into
vercel-labs:mainfrom
paoloanzn:branded-primitives

Conversation

@paoloanzn

@paoloanzn paoloanzn commented Sep 2, 2026

Copy link
Copy Markdown

A branded type is a primitive joined to a marker object, for example string & { readonly __brand: "UserId" }. The marker exists only in the type system; at runtime the value is an ordinary string. The compiler had no rule for this shape, so it failed in two different ways: brands written as an object literal were treated as records and reported SC2006, and brands written with a unique symbol key reached the unresolved-intersection fence and reported SC2008.

Map these types to the primitive they wrap. When an intersection has exactly one primitive part, and every other part is a plain object type that adds nothing at runtime, the whole type now maps to that primitive. A branded value is stored, passed and computed exactly like an unbranded one. Reading a brand member is still refused at the point of use, like any other type-only member.

Two shapes stay refused because they describe no runtime value: a primitive joined to a class instance, since nothing can be both, and an intersection of two different primitives. The test for a type-only object part already existed inline in the handle-intersection path and is now a shared helper, so both paths use one definition. The primitive lookup is shared with the scalar mapping for the same reason.

A new differential program covers string and number brands, unique symbol tags, string methods and arithmetic on branded values, branded fields in interfaces, arrays, Maps and Sets, and generic passthrough. The intersection diagnostic fixture now uses a primitive joined to a class, which is the case that still cannot compile.

Closes #282

Written with AI assistance and reviewed by a human.

@vercel

vercel Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

@paoloanzn is attempting to deploy a commit to the Vercel Labs Team on Vercel.

A member of the Team first needs to authorize it.

A branded type is a primitive joined to a marker object, for example
`string & { readonly __brand: "UserId" }`. The marker exists only in the
type system; at runtime the value is an ordinary string. The compiler had
no rule for this shape, so it failed in two different ways: brands written
as an object literal were treated as records and reported SC2006, and
brands written with a `unique symbol` key reached the unresolved-
intersection fence and reported SC2008.

Map these types to the primitive they wrap. When an intersection has
exactly one primitive part, and every other part is a plain object type
that adds nothing at runtime, the whole type now maps to that primitive.
A branded value is stored, passed and computed exactly like an unbranded
one. Reading a brand member is still refused at the point of use, like any
other type-only member.

Two shapes stay refused because they describe no runtime value: a
primitive joined to a class instance, since nothing can be both, and an
intersection of two different primitives. The test for a type-only object
part already existed inline in the handle-intersection path and is now a
shared helper, so both paths use one definition. The primitive lookup is
shared with the scalar mapping for the same reason.

A new differential program covers string and number brands, `unique
symbol` tags, string methods and arithmetic on branded values, branded
fields in interfaces, arrays, Maps and Sets, and generic passthrough. The
intersection diagnostic fixture now uses a primitive joined to a class,
which is the case that still cannot compile.

Closes vercel-labs#282

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011KE8gQ92pFxakTj5obV1oB
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Branded primitive types do not compile: object-literal brands report SC2006, unique symbol brands report SC2008

1 participant