Open nstepien opened 1 year ago
Possibly related: https://github.com/microsoft/TypeScript/issues/53276
@fatcerberus I'm not convinced this issue is related.
@nstepien Note I said "related", not "duplicate".
That said, I think the limitation here is that unique symbols explicitly have type typeof x
, which implies there must be an x
in scope that can be used with typeof
. Given an anonymous object literal containing Symbol()
calls, there's nothing for typeof
to refer back to.
Given
const obj = { x: Symbol() } as const
, would it be possible for its type to be typeof obj.x
?
Although that wouldn't work in a case like
fn({ [dynamicKey]: Symbol() } as const)
as there is no specific name to refer back to.
Maybe unique symbol
is good enough.
I wonder if TS could use the description when available, i.e. Symbol('desc')
's type could be typeof Symbol('desc')
or unique symbol 'desc'
.
could be typeof Symbol('desc') or unique symbol 'desc'.
That's expressible as declare const mySymbol: symbol & {readonly description:'desc'}
. Unfortunately, you have to give up uniqueness. (unique symbol) & {readonly description:'desc'}
silently ignores the unique
ness. And {readonly description:'desc'} & (unique symbol)
errors "'unique symbol' types are not allowed here."
Trying to fix this with an explicit annotation fails:
// ERROR: Type 'symbol' is not assignable to type 'unique symbol'.
const myEnum: {readonly a:unique symbol} = { a: Symbol() };
The best I achieve is with a cast:
const myEnum = { a: Symbol() } as { readonly a: unique symbol }
But this doesn't provide the type safety you seek because unique symbols decay too easily:
const myEnum = { a: Symbol() } as { readonly a: unique symbol }
const anotherEnum = { a: Symbol() } as { readonly a: unique symbol }
let b = myEnum.a
b = anotherEnum.a // OOPS! NO ERROR!
Wrote up some of the related issue in #56535 (namely that the type system disregards the description
passed in to the Symbol
constructor, even if it's a string literal)
+1 to this feature request.
Running into a similar issue where I would like the type generic to prefer a unique symbol.
e.g.
const doGenericThing = <T extends symbol>(val: T): { val: T } => {
return { val };
};
Doesn't use unique symbol by default:
// { val: symbol }
const notUnique = doGenericThing(Symbol('abc'));
Can assign to a const first, which works but requires a technically unnecessary variable declaration
const uniqueSym = Symbol('abc');
// { val: typeof uniqueSym }
const unique = doGenericThing(uniqueSym);
Appending as const
seems like a very reasonable approach to have it use a unique symbol.
// Not yet legal
const standaloneUnique = doGenericThing(Symbol('abc') as const);
Possibly off topic, but I wouldn't expect any of this unique functionality to apply to Symbol.for
, unless symbols were to start tracking their description
(see issue linked above).
However it appears that is not the case:
const sym = Symbol.for('abc');
const restrictToSym = (val: typeof sym) => {};
// _should_ work, but treated as a separate unique symbol
restrictToSym(Symbol.for('abc'));
Bug Report
🔎 Search Terms
symbol object "as const"
🕗 Version & Regression Information
⏯ Playground Link
Playground link with relevant code
💻 Code
🙁 Actual behavior
Symbols in
as const
-ed objects should beunique symbol
s, instead they're of typesymbol
.🙂 Expected behavior
When I write
I want
obj.a
to be aunique symbol
.What I'm trying to do is replace TS enums with symbol-based object "enums" in some scenarios, as it gives me greater typecheck-time and runtime guarantees.
I can do
and that works great, but I end up with many
const ... = Symbol()
which pollute the scope and can be misused, when I'd rather do