GeezerVison is showing github.com — Can't evaluate equality of symbols made by Symbol.for · Issue #35909 · microsoft/TypeScript
Can't evaluate equality of symbols made by Symbol.for #35909DescriptionReacted by ExE Boss, Cobertos, Semesse, SancheZz, Michał Niedziółka, Stefan Thomas, Benjamin Laird, Sʜɪᴍᴜʀᴀ Yū, Clément Maisonhaute and lionel-rowe Activityfalsandtru commented on Dec 29, 2019 ContributorAuthor More actions Example
// npm package A@v1
namespace A1 {
// Structural hidden field across versions and packages.
const tag = Symbol.for('TagName');
export class A<T> {
constructor(arg: T) { }
[tag]: T;
}
}
// npm package A@v2
namespace A2 {
// Structural hidden field across versions and packages.
const tag = Symbol.for('TagName');
export class A<T> {
constructor(arg: T) { }
[tag]: T;
}
export declare function f(a: A<'a'>): void;
}
// npm package B
namespace B {
A2.f(new A1.A('a')); // Should work.
}
// npm package A
namespace A {
// Structural hidden field across versions and packages.
const tag = Symbol.for('TagName');
export class A<T> {
constructor(arg: T) { }
[tag]: T;
}
}
// npm package B
namespace B {
// Structural hidden field across versions and packages.
const tag = Symbol.for('TagName');
export class B<T> {
constructor(arg: T) { }
[tag]: T;
}
}
// npm package C
namespace C {
declare function f(a: A.A<'a'>): void;
f(new B.B('a')); // Should work.
}ExE-Boss commented on Feb 14, 2020 Like we have unique symbol, we should probably support:
const foo: global symbol "<name>";
This would allow declaring Node’s built-in symbols as:
declare module "util" {
namespace inspect {
// https://github.com/nodejs/node/issues/20821
const custom: global symbol "nodejs.util.inspect.custom";
}
namespace promisify {
// https://github.com/nodejs/node/issues/31647
const custom: global symbol "nodejs.util.promisify.custom";
}
}Reacted by Kenta Moriuchi and Semesse typeofweb commented on Mar 17, 2020 It's a bug I just stumbled upon and it was really surprising. ExE-Boss commented on Mar 19, 2020 It’s the intended behaviour for unique symbol types, which global symbols actually aren’t, but they’re currently using the same code path. typeofweb commented on Mar 19, 2020 Sounds like it's not expected, more like an unforeseen side effect of adding unique symbols. kurtharriger commented on Apr 14, 2020 cowboyd commented on Jan 13, 2022 Is the problem that there is no concept of a "symbol literal" in TypeScript?
It would be really nice to have a literal syntax for symbols which would type just like any other primitive literal like 'string', 5, or true.
Syntax is arbitrary, but using@ as a placeholder.
const something = @something; //=> Symbol.for("something")
const somethingElse = @"something.else"; //=> Symbol.for("something.else")
const object: { [something]: string } = { @something: 'hello' };
Seems like this would ease a lot of pain in matching types that use well known symbols as keys. But even in the absence of a literal syntax, what would it take for TS to treat Symbol.for() expressions as literals? Reacted by Simon Chan and ExE Boss nicolo-ribaudo commented on Jan 13, 2022 Charles Lowell (@cowboyd) That would risk big conflicts with decorators, but a while ago I was thinking about the symbol "foo" syntax:
declare function Symbol.for<T extends string>(name: T): symbol T;
declare function Symbol.keyFor<T extends string>(sym: symbol T): T;
declare function Symbol.keyFor(sym: symbol): string | undefined;
let a = Symbol.for("foo"); // type is `symbol "foo"`
let b: symbol "foo" = Symbol.for("bar"); // error
let c = Symbol.for("baz" as string); // symbol string
let d: symbol = Symbol.for("foo"); // ok, symbol T is a subtype of symbol
let d: symbol "foo" = Symbol(); // error
let descA: "foo" = Symbol.keyFor(a); // ok
let descB: "bar" = Symbol.keyFor(a); // err, "foo" is not assignable to "bar"
EDIT: I just noticed that this was already suggested at #35909 (comment) 😬 Reacted by Simon Chan and Clément Maisonhaute cowboyd commented on Jan 14, 2022 Nicolò Ribaudo (@nicolo-ribaudo) you're definitely right, and the @ was just the first thing that popped into my head. And in fact, there isn't any need for a new literal syntax, just a new type that accurately represents the return value of Symbol.for().
I like your suggestions. Also need to consider the difference between Symbol() and Symbol.for(). Maybe something like:
let a = Symbol("foo") // type is `unique symbol "foo"`
let b = Symbol.for("foo") // type is `symbol for "foo"`
Anyhow, as you noted ExE Boss (@ExE-Boss) already suggested this.... wonder why this thread petered out? Is there something that we can do? pjeby commented on Jun 19, 2022 Throwing in a use case here: I've got a library that exports a symbol for purposes of interop, that other code can declare methods with that symbol, without needing to depend on the library that defines the symbol. But then, in a project that uses both of those libraries, Typescript can't tell that module 2 implements the interface specified by module 1, even though it uses the same Symbol.for().
As it stands, the only way to get this kind of interop is with strings, not symbols, as there's no "global symbol" or "symbol for" syntax in Typescript. Using typeof can't work here because that would force the common dependency, and a shared type definitions file, since merely equivalent types are not sufficient for "unique symbol" to match. Reacted by ExE Boss, Fosty Fost, SancheZz, Stefan Thomas, Benjamin Laird, Joshua Sosso (Deprecated) and Yonggang Luo yuhr commented on Jan 11, 2025 Excerpting from #60944, as I believe it's worth mentioning here:
1. What do you want to use this for?
Making multiple versions of the same library compatible.
Sometimes a library generates objects with a private property at a unique symbol key and use it as some kind of marker to statically or dynamically determine if an object is really generated or already processed with the library, while allowing users to define arbitrary properties on it as much as possible.
But what if a user has to end up depending on multiple versions of the library somehow? To address this, the library should accept objects from other versions of the same library, so the library might want to start using non-unique symbols for the purpose. This does work at runtime but not at compile time due to the absence of indexed symbol types.
Using a string key instead of a non-unique symbol key invades the string key space of such an object at runtime, and could conflict with an index signature at compile time.
Reacted by ExE Boss and Grigorii Ivanov lygstate commented on Jun 4, 2026 It's a bug, can it fixed? As Symbol.for("literal") basically act like "literal" with symbol attribute michaelfig commented on Oct 19, 2026 I'd encourage people following this issue to take a look at #64451. I'm addressing review comments on it if you'd like to make any, and I'm hoping that it will eventually be merged as a comprehensive solution for these problems. Richiban commented on Oct 27, 2026 I have also keenly felt the absence of this. In languages like Erlang (with atoms) and Ruby (with symbols) and now Python (with sentinels) we have the ability to pass / return arbitrary sentinel values that can be matched against.
The problem is that Typescript doesn't have any representation of the type of Symbol.for('mySymbol'); it treats all symbols as unique symbols, which they are not.
The best thing would be for Typescript to have a type symbol<"mySymbol"> or symbol "mySymbol" or similar, so you can use it in a function signature, say:
function f(): symbol "mySymbol" {
return Symbol.for("mySymbol")
}Reacted by Charles Lowell cowboyd commented on Oct 28, 2026 Oh captain symbol, my captain symbol!
(i.e. I concur)
MetadataMetadataDevelopmentNo branches or pull requestsIssue actions
|