Reputation: 1286
I'm trying to implement something like this but I'm not sure it's possible. I think Typescript only allows unique symbols, not global ones. Is this correct?
Is there a better way to achieve using global symbols?
// sample.d.ts
const mySymbol = Symbol.for('internal.symbol')
interface Sample{
[mySymbol]: string
a: number
b: number
}
// sample.js
class SampleClass implements Sample {
[mySymbol]: string
a: number
b: number
constructor(a: number, b: number){
this.a = a;
this.b = b;
this[mySymbol] = `${a}-${b}`
}
}
let mySample = new SampleClass(1, 2)
Is there a way to accomplish this? mySymbol
can (and ideally will) be a global symbol that will be used by other objects as well so it can be defined separately if that can be accomplished.
Upvotes: 1
Views: 1091
Reputation: 2528
... I think Typescript only allows unique symbols, not global ones. Is this correct?
All symbols are unique. That is invariant.
How to scope and access symbols is another question.
There are two ways to create symbols:
First way: Symbol(mnemonic?:string)
, e.g.
const x = Symbol('optional name')
const y = Symbol('optional name')
assert(x!==y) // pass
Every call to Symbol(...)
creates a unique symbol.
The mnemonic
is just a convenience property for debugging, etc.
Two symbols can have the same mnemonic without being the same symbol.
console.log(x.toString()) // 'Symbol(optional name)'
assert(x.toString()===y.toString()) // pass
assert(x!==y) // pass
When symbols are created in this way they only exist as long as they are referenced in user code - just like other objects, they can be garbage collected.
Second way: Symbol.for(globalKey:string)
, e.g.
In file 'x.js', with NO import
/require
statements at all
const x = Symbol.for('my.global.symbols.1')
export x
In file 'y.js', with NO import
/require
statements at all
const x = Symbol.for('my.global.symbols.1')
export y
In file 'z.js'
import {x} from './x'
import {y} from './y'
assert(x===y) // pass
const z = Symbol.for('my.global.symbols.1')
assert(x===z) // pass
In this case, a unique global symbol is created for each UNIQUE global key passed as the globalKey
parameter to Symbol.for(globalKey:string)
- from any file.
The symbol instance is stored in opaque global space, as though there were an opaque global map:
Symbol.for(globalKey:string):symbol{
if (globalSymbolMap.has(globalKey)
return globalSymbolMap.get(globalKey)
else{
const s=Symbol(globalKey)
globalSymbolMap.set(globalKey,s)
return s
}
}
(although that might not be how it is actually implemented).
Here is what MDN says about Symbol.for()
:
In contrast to Symbol(), the Symbol.for() function creates a symbol available in a global symbol registry list. Symbol.for() does also not necessarily create a new symbol on every call, but checks first if a symbol with the given key is already present in the registry. In that case, that symbol is returned. If no symbol with the given key is found, Symbol.for() will create a new global symbol.
About garbage collection for these globally managed symbols - I don't know which of the following are true:
When a globally managed symbol is no longer referenced by any user code (i.e., not including the reference from the opaque global 'virtual' map) then it may be garbage collected.
Once created, globally managed symbols remain in the opaque global 'virtual' map until end of program life.
From the perspective of user code 'logic', there would be no difference between the two - it's entirely an implementation issue. However, performance, including memory usage, would differ. My guess is that some garbage collection is enabled.
Upvotes: 0
Reputation: 1286
Here is how I was able to accomplish this.
// misc.ts
export const mySymbol = Symbol.for('internal.symbol')
// sample.d.ts
import {mySymbol} from './misc'
export as namespace Sample
export = Sample
interface Sample{
[mySymbol]: string
a: number
b: number
}
// sample.js
class SampleClass implements Sample {
[mySymbol]: string
a: number
b: number
constructor(a: number, b: number){
this.a = a;
this.b = b;
this[mySymbol] = `${a}-${b}`
}
}
let mySample = new SampleClass(1, 2)
Once mySymbol
is imported into the declaration file it turns into a module. Hence it needs to be specifically exported with the export = Sample
and export as namespace Sample
. See sample module.d.ts.
Upvotes: 1
Reputation: 20230
You can export your symbol i.e.
export const mySymbol = Symbol.for('internal.symbol')
and then import it into whatever file requires it. This way you won't pollute the global scope and you can import it only when necessary.
Upvotes: 0