GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — ES-conformant symbol typing by michaelfig · Pull Request #64451 · microsoft/TypeScript

Conversation

@michaelfig

@michaelfig Michael FIG (michaelfig) commented Sep 25, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

This PR is AI-generated from human-written test cases that demonstrate discrepancies between TypeScript typing and ES standard behaviour. The PR itself has also been reviewed by me before submission. Here are the main issues it addresses:

Closes: #35909

Two Symbol.for calls with the same key currently produces non-identical unique symbol types, so TypeScript rejects equality checks and assignments that succeed at runtime. This PR gives them the same registered-symbol type when their keys are the same literal.

Closes: #27524

Assigning a known symbol to another const currently widens its type to symbol, preventing the alias from serving as the same computed property key. This PR retains the symbol's narrow type through the const alias.

Closes: #55901

A const initialized with an assertion such as u as typeof u can still lose its unique symbol type, complicating uses such as isolated declaration emit. This PR preserves the asserted symbol identity in that const-like declaration.

Summary

  • Add a RegisteredSymbol<Key> intrinsic and preserve literal registry keys in the return type of Symbol.for.
  • Represent unresolved registry keys with a deferred symbol type, so separate generic calls do not receive one unsound unique-symbol identity.
  • Preserve narrow unique symbol types in const-like declarations and allow const assertions on them.
  • Emit and resolve symbol-keyed object properties using their registered and unique symbol types; serialize computed registered-symbol keys through their inferred types in declarations.
  • Add compiler cases and accepted baselines for symbol typing, property access, and deferred registry keys.

Tests

  • go -C ./tsc test -run='TestLocal/(registeredSymbolComputedNameDeclaration|registeredSymbolGenericRelations|registeredSymbolDeferredKey|registeredSymbolIntrinsic|registeredSymbolPropertyAccess|uniqueSymbolConstLikeWidening)\.ts$' ./internal/testrunner -count=1
  • go -C ./tsc test ./internal/checker ./internal/api
  • go -C ./tsc test -run='^TestLocal$' ./internal/testrunner -count=1

Related Work

Refs: #35562

This issue asks for symbols stored in an as const object to retain unique symbol types so they can be used as computed class property names. It relates to this PR's preservation of narrow symbol types in const-like contexts.

Refs: #37469

This issue proposes symbol-specific return types for functions and aliases of Symbol and Symbol.for, including a type tied to a global registry key. This PR addresses the registry-backed portion through RegisteredSymbol<Key>; it does not add a general new symbol return type.

Refs: #54778

This earlier PR proposed preventing readonly symbol values from widening, including cases involving indirect unique-symbol declaration emit. Its approach overlaps with this PR's const-like symbol preservation.

Refs: #60052

This merged PR relaxed restrictions on computed names in declarations and preserved computed names expressed as entity names during declaration emit. Those changes are relevant to representing and emitting the symbol-keyed properties handled here.

Co-authored-by: Codex [email protected]

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

snarbles2 reacted with rocket emoji
All reactions
  • 🚀 1 reaction
Michael FIG (michaelfig) and others added 4 commits September 25, 2026 11:29
@michaelfig
New RegisteredSymbol<Key> intrinsic
9853f5f
@michaelfig
Propagate a narrow unique symbol as if const
5865d6a
@michaelfig
Verify symbol-keyed object property access
99187cd
@michaelfig @codex
Place symbol compiler tests in active testdata
f42f422
Move the new test cases and accepted baselines into tsc/testdata so the compiler test runner executes them. Regenerate baselines with the repository's CRLF convention.

Co-authored-by: Codex <[email protected]>
Copilot AI balanced review requested due to automatic review settings September 25, 2026 20:12
@github-project-automation github-project-automation Bot added this to PR Backlog Sep 25, 2026
@typescript-automation

typescript-automation Bot commented Sep 25, 2026

Copy link
Copy Markdown
Contributor

This PR doesn't have any linked issues. Please open an issue that references this PR. From there we can discuss and prioritise.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@typescript-automation typescript-automation Bot added the For Uncommitted Bug PR for untriaged, rejected, closed or missing bug label Sep 25, 2026
@github-project-automation github-project-automation Bot moved this to Not started in PR Backlog Sep 25, 2026
Copilot started reviewing on behalf of Michael FIG (michaelfig) September 25, 2026 20:13
View session
Copilot AI reviewed Sep 25, 2026
View reviewed changes

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Non-literal and union registry keys currently receive unsound single unique-symbol identities.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: None

What changed in this PR

Adds registered-symbol typing and preserves symbol identities through inference, property access, and declaration emit.

Changes:

  • Introduces RegisteredSymbol<Key> and generic Symbol.for.
  • Preserves unique-symbol types in const-like contexts.
  • Adds compiler tests and baselines for symbol-keyed properties.
File Description
tsc/​internal/​checker/​checker.go Implements registered-symbol types and unique-symbol preservation.
tsc/​internal/​checker/​nodebuilderimpl.go Emits registered symbol types and computed property names.
tsc/​internal/​bundled/​libs/​lib.es2015.symbol.d.ts Declares the new intrinsic and Symbol.for signature.
tsc/​testdata/​tests/​cases/​compiler/​registeredSymbolIntrinsic.ts Tests intrinsic inference.
tsc/​testdata/​tests/​cases/​compiler/​registeredSymbolPropertyAccess.ts Tests symbol-keyed property access and emit.
tsc/​testdata/​tests/​cases/​compiler/​uniqueSymbolConstLikeWidening.ts Tests const-like unique-symbol widening.
tsc/​testdata/​baselines/​reference/​compiler/​registeredSymbolIntrinsic.types Records inferred types.
tsc/​testdata/​baselines/​reference/​compiler/​registeredSymbolIntrinsic.symbols Records resolved symbols.
tsc/​testdata/​baselines/​reference/​compiler/​registeredSymbolPropertyAccess.types Records property-access types.
tsc/​testdata/​baselines/​reference/​compiler/​registeredSymbolPropertyAccess.symbols Records property symbols.
tsc/​testdata/​baselines/​reference/​compiler/​registeredSymbolPropertyAccess.js Records declaration emit.
tsc/​testdata/​baselines/​reference/​compiler/​uniqueSymbolConstLikeWidening.types Records widening types.
tsc/​testdata/​baselines/​reference/​compiler/​uniqueSymbolConstLikeWidening.symbols Records widening symbols.

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

snarbles2 reacted with rocket emoji
All reactions
  • 🚀 1 reaction
@michaelfig @codex
Avoid unique symbol identities for broad registry keys
9d2d9e6
Resolve broad registered-symbol keys to symbol and distribute literal union keys over their possible registered symbol identities. Add compiler coverage for both cases.

Co-authored-by: Codex <[email protected]>
@typescript-automation

typescript-automation Bot commented Sep 25, 2026

Copy link
Copy Markdown
Contributor

The TypeScript team hasn't accepted the linked issue #27524. If you can get it accepted, this PR will have a better chance of being reviewed.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@michaelfig Michael FIG (michaelfig) changed the title Add registered symbol types and preserve symbol-keyed property access ES-conformant symbol typing Sep 25, 2026
DanielRosenwasser
Daniel Rosenwasser (DanielRosenwasser) reviewed Sep 25, 2026
View reviewed changes
Comment thread tsc/internal/checker/checker.go Outdated
Comment on lines +23388 to +23392
// Keep a type parameter deferred so instantiating Symbol.for's generic
// return type can resolve the actual key supplied at the call site.
if keyType.flags&(TypeFlagsStringOrNumberLiteral|TypeFlagsTypeParameter) == 0 {
return c.esSymbolType
}

@DanielRosenwasser Daniel Rosenwasser (DanielRosenwasser) Sep 25, 2026

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this keep the type parameter deferred? Doesn't this just return symbol?

I think you not only need a new intrinsic type, you need a new kind of type (e.g. see newStringMappingType). Similar to how a type with TypeFlagsStringMapping doesn't have TypeFlagsString, it wouldn't have TypeFlagsSymbol - maybe because it could be instantiated to never? I'd have to consult with others.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@DanielRosenwasser Daniel Rosenwasser (DanielRosenwasser) Sep 25, 2026

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we just handle that in assignability separately and keeping the bitflags disjoint on this avoids other issues.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@michaelfig Michael FIG (michaelfig) Sep 26, 2026

Copy link
Copy Markdown
Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that a separate type flag is a better way to represent a registered symbol whose key is still a type parameter. I’ve updated the PR to use that representation and added a regression test where two Symbol.for calls with generic keys previously shared an unsound unique-symbol identity.

My main hesitation was the limited remaining space in the TypeFlags uint32. It seems that all the bits up to bit 31 are already in use, so this uses the former Reserved3 bit at 31 and keeps TypeFlags as uint32. That consumes the remaining reserved bit, but seems justified for a distinct type kind.

I’d welcome your assessment of that tradeoff.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@michaelfig @codex
Represent deferred registered symbols with a distinct type flag
c6d0ad8
Avoid assigning a unique symbol identity to Symbol.for calls with unresolved keys. Add a regression case for indexed access through two generic registry keys.

Co-authored-by: Codex <[email protected]>
@michaelfig

Michael FIG (michaelfig) commented Sep 28, 2026

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@michaelfig Michael FIG (michaelfig) mentioned this pull request Oct 2, 2026
TS Symbol typing clashes with standard, idiomatic JS #64585
Closed
@michaelfig
Michael FIG (michaelfig) requested review from Daniel Rosenwasser (DanielRosenwasser) and a balanced review from Copilot October 2, 2026 04:58
Copilot AI reviewed Oct 2, 2026
View reviewed changes

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Copilot review overview

Review effort: Lite
Findings: 1 High severity · 2 Medium severity

Open (3)
  • High severity structuredTypeRelatedToWorker unconditionally casts target to RegisteredESSymbolType when… · New
  • Medium severity TypeFlagsESSymbolLike was expanded to include TypeFlagsRegisteredESSymbol, but… · New
  • Medium severity TypeFlagsESSymbolLike was expanded to include TypeFlagsRegisteredESSymbol, but… · New
Files not reviewed (1)
  • tsc/internal/api/enum_values_generated.go: Generated file

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread tsc/internal/checker/relater.go
return r.isRelatedTo(source.AsStringMappingType().target, target.AsStringMappingType().target, RecursionFlagsBoth, false /*reportErrors*/)
}
case source.flags&TypeFlagsRegisteredESSymbol != 0:
return r.isRelatedTo(source.AsRegisteredESSymbolType().target, target.AsRegisteredESSymbolType().target, RecursionFlagsBoth, false /*reportErrors*/)
Comment thread tsc/internal/checker/types.go
TypeFlagsBooleanLike = TypeFlagsBoolean | TypeFlagsBooleanLiteral
TypeFlagsEnumLike = TypeFlagsEnum | TypeFlagsEnumLiteral
TypeFlagsESSymbolLike = TypeFlagsESSymbol | TypeFlagsUniqueESSymbol
TypeFlagsESSymbolLike = TypeFlagsESSymbol | TypeFlagsUniqueESSymbol | TypeFlagsRegisteredESSymbol
Comment thread tsc/internal/checker/types.go Outdated
@@ -505,7 +505,7 @@ const (
// This *should* be every type other than null, undefined, void, and never
TypeFlagsNarrowable = TypeFlagsAny | TypeFlagsUnknown | TypeFlagsStructuredOrInstantiable | TypeFlagsStringLike | TypeFlagsNumberLike | TypeFlagsBigIntLike | TypeFlagsBooleanLike | TypeFlagsESSymbol | TypeFlagsUniqueESSymbol | TypeFlagsNonPrimitive
Copilot started reviewing on behalf of Michael FIG (michaelfig) October 2, 2026 21:05
View session
Copilot stopped reviewing on behalf of Michael FIG (michaelfig) due to an error October 2, 2026 21:06
Error performing PR review: SessionModelError: Authorization error, you may need to run /login (Request ID: 4001:C5C26:31EE07:3C3792:6AC01CB2); cause: Error: Authorization error, you may need to run /login (Request ID: 4001:C5C26:31EE07:3C3792:6AC01CB2)
Michael FIG (michaelfig) and others added 5 commits October 2, 2026 17:21
@michaelfig @codex
Relate deferred registered symbols through their keys
d91d6e0
Add regression coverage for generic key relations and symbol union and intersection reduction.

Co-authored-by: Codex <[email protected]>
@michaelfig @codex
Emit type queries for inaccessible unique symbol aliases
39605ea
Preserve const alias identities in declaration output and refresh the unique-symbol declaration baselines.

Co-authored-by: Codex <[email protected]>
@michaelfig @codex
Accept intended symbol behavior baselines
8ed748a
Refresh 19 type, symbol, and diagnostic baselines while leaving the computed registered-symbol type baseline for further investigation.

Co-authored-by: Codex <[email protected]>
@michaelfig @codex
Emit inferred registered symbol keys as mapped properties
3457127
Serialize computed expressions through their known registered-symbol types, preserving ordinary and readonly properties. Add declaration emit coverage and accept the corrected symbol-name baseline.

Co-authored-by: Codex <[email protected]>
@michaelfig @codex
Simplify registered symbol type flag masks
10d6b07
Use the existing Instantiable aggregate for base constraints and omit a redundant IncludesMask bit in both enum definitions.

Co-authored-by: Codex <[email protected]>
jakebailey
Jake Bailey (jakebailey) reviewed Oct 12, 2026
View reviewed changes
Comment thread tsc/internal/bundled/libs/lib.es2015.symbol.d.ts Outdated

declare var Symbol: SymbolConstructor;

type RegisteredSymbol<Key extends string | number> = intrinsic;

@jakebailey Jake Bailey (jakebailey) Oct 12, 2026

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Numeric keys appear to get separate identities from their string equivalents, but the registry stringifies keys. This should accept only strings or canonicalize numeric keys.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@michaelfig Michael FIG (michaelfig) Oct 16, 2026

Copy link
Copy Markdown
Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 44adab7 and baa2e42. The ECMAScript Symbol.for algorithm applies ToString(key) before looking in the registry; ToString throws a TypeError for a primitive Symbol (Symbol.for, ToString). The declaration now constrains Key to NonSymbol, rejecting symbol-typed arguments while accepting non-symbol primitives and objects. Runtime calls that bypass the type check still retain the specified throw behavior.

For statically known primitive keys, the RegisteredSymbol intrinsic emulates that string conversion before caching the symbol identity. Thus 1, 1n, and "1" share an identity, as do -0 and 0; distinct resulting strings remain distinct. Finite unions are handled member by member. Broad string, number, and bigint keys, and objects whose conversion result cannot be known statically, widen to symbol rather than claiming a particular registry identity. The new registeredSymbolPrimitiveKeys.ts regression test covers these cases, and the full compiler baseline suite passes.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@michaelfig @codex
Canonicalize registered symbol keys via ECMAScript ToString
44adab7
Accept primitive Symbol.for keys and share registered symbol identities by their stringified registry key. Cover collisions, unions, broad keys, and invalid inputs.

Co-authored-by: Codex <[email protected]>
@michaelfig @codex
Allow nonsymbol objects as Symbol.for keys
baa2e42
Use a NonSymbol constraint so object and function values are accepted while unresolved keys remain plain symbol types. Update compiler coverage and baselines.

Co-authored-by: Codex <[email protected]>
@michaelfig Michael FIG (michaelfig) mentioned this pull request Oct 19, 2026
Can't evaluate equality of symbols made by Symbol.for #35909
Open
ExE-Boss
ExE Boss (ExE-Boss) reviewed Nov 5, 2026
View reviewed changes

@ExE-Boss ExE Boss (ExE-Boss) left a comment

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’d prefer if RegisteredSymbol and Symbol.for retained the string type restriction:

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread tsc/internal/bundled/libs/lib.es2015.symbol.d.ts
* @param key key to search for.
*/
for(key: string): symbol;
for<Key extends NonSymbol>(key: Key): RegisteredSymbol<Key>;

@ExE-Boss ExE Boss (ExE-Boss) Nov 5, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for<Key extends NonSymbol>(key: Key): RegisteredSymbol<Key>;
for<Key extends string>(key: Key): RegisteredSymbol<Key>;

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread tsc/internal/bundled/libs/lib.es2015.symbol.d.ts
Comment on lines +46 to +48
type NonSymbol = string | number | bigint | boolean | null | undefined | object;

type RegisteredSymbol<Key extends NonSymbol> = intrinsic;

@ExE-Boss ExE Boss (ExE-Boss) Nov 5, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
type NonSymbol = string | number | bigint | boolean | null | undefined | object;
type RegisteredSymbol<Key extends NonSymbol> = intrinsic;
type RegisteredSymbol<Key extends string> = intrinsic;

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@michaelfig

Michael FIG (michaelfig) commented Nov 7, 2026

Copy link
Copy Markdown
Author

I’d prefer if RegisteredSymbol and Symbol.for retained the string type restriction:

I was trying to be as lax with the types as the ES standard allows, but if TS has a history of providing stricter types than ES APIs handle, I can certainly restore that behaviour.

ExE-Boss reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@jakebailey Jake Bailey (jakebailey) jakebailey left review comments
Copilot code review Copilot
Copilot review effort
Applies to this pull request for everyone.Learn more about Copilot code review.
Copilot left review comments
@DanielRosenwasser Daniel Rosenwasser (DanielRosenwasser) Awaiting requested review from DanielRosenwasser
+1 more reviewer
@ExE-Boss ExE Boss (ExE-Boss) ExE-Boss left review comments
Reviewers whose approvals may not affect merge requirements

At least 1 approving review is required to merge this pull request.

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

For Uncommitted Bug PR for untriaged, rejected, closed or missing bug

Projects

PR Backlog
Status: Not started

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

unique symbol lost on assignment to const despite type assertion Can't evaluate equality of symbols made by Symbol.for Assign a known symbol to a constant should retain type information

Uh oh!

There was an error while loading. Please reload this page.

5 participants

@michaelfig @DanielRosenwasser @ExE-Boss @jakebailey
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.