Description
Now that we have #15473, when a symbol
—which is guaranteed to be unique by the ES2015 spec—is assigned to a const
variable, it can be used (via computed property syntax) in place of a normal property name.
Quick contrived example:
export const FOO = Symbol('FOO');
export const BAR = Symbol('BAR');
export interface Thing {
[ FOO ]: boolean;
[ BAR ]: number;
}
const thing: Thing = undefined;
const foo = thing[ FOO ]; // ☝️ hover shows type of `foo` as `boolean`
const bar = thing[ BAR ]; // ☝️ hover shows type of `bar` as `number`
I love this, it allows a symbol
to act as a unique identifier. But it truth, a symbol
represents a unique value, I want to use that unique value to describe a type.
Another quick contrived example:
export const FOO = Symbol('FOO');
export const BAR = Symbol('BAR');
export function fooOrBar(value: FOO | BAR): string { // ❌ Cannot find name 'FOO'.
// ❌ Cannot find name 'BAR'.
if (value === FOO) return 'You gave me FOO!';
else if (value === BAR) return 'You gave me BAR!';
else throw TypeError('What was that!?');
}
Is that something anyone else would want? A lot of us will use a symbol
to define a unique constant, I think it just makes sense to be able to use that constant to... refer to that constant.
On a side note, I think I may have found a bug. So while typing up this issue, I found that I am able to achieve the desired behavior by using an intersection with a "tagged" object literal:
export type FOO = symbol & { FOO: true };
export const FOO: FOO = Symbol('FOO'); // ❓️ no error
export type BAR = symbol & { BAR: true };
export const BAR: BAR = Symbol('BAR'); // ❓ no error
While this is nice for me because it achieves what I want, I don't think it should work. Heres why:
So if you hover over the Symbol()
constructor you'll see this:
var Symbol: SymbolConstructor
(description?: string | number) => symbol
That's what I expected to see. You (optionally) give it a string | number
description and it gives you a symbol
in return. So why doesn't the compiler freakout when I assign it to something that is symbol & { FOO: true }
? If IRRC the spec says that no properties can be set on a symbol
. I can't find where it said that, but really quickly in my DevTools Console, I did this which seems to affirm my belief:
$> const foo = Symbol('foo')
undefined
$> foo
Symbol(foo)
$> foo.foo = true
true
$> foo
Symbol(foo)
$> foo.foo
undefined
Perhaps there some special assignability feature for typescript primitive symbol
that I'm overlooking? I don't know. But if you do:
export type FOO = string & { FOO: true };
export const FOO: FOO = String('FOO'); // ❌ Type 'string' is not assignable to type 'FOO'.
// ❌ Type 'string' is not assignable to type '{ FOO: true; }'.
Looking at the type info for the String()
constructor, it's nearly identical:
const String: StringConstructor
(value?: any) => string
so why does it behave differently?
Just out of curiosity, I tried:
export const FOO: boolean = Symbol('FOO');
And got no errors. Something is definitely broken because symbol
is acting like any
.
Because you will ask, I am currently running: [email protected]
, I've also tested this on 2.7.0-insiders.20171214
, but the playground correctly gives me errors.