Open bordoley opened 1 year ago
This would be solved by #37469.
I did develop a particularly ugly work around for myself that takes advantage of typescript's JS support( "allowJs": true
in tsconfig.json), JS type annotations and .d.ts files.
// symbol.js
const symbol = Symbol
export default symbol;
// module.symbols.js
import symbol from "./symbol.js";
/** @type {unique symbol} */
export const Symbol_keyA = symbol();
/** @type {unique symbol} */
export const Symbol_keyB = symbol();
/** @type {unique symbol} */
export const Symbol_keyC = symbol();
// module.symbols.d.ts
export declare const Symbol_keyA: unique symbol;
export declare const Symbol_keyB: unique symbol;
export declare const Symbol_keyC: unique symbol;
The .d.ts is needed for type checking, and the JS type annotations ensure that the compiler generates the correct output declarations when emitting compiled js with declarations.
It seems like terser should implement the linked suggestion. The code is semantically minifiable either way.
Obviously output size matters at some level, but this is going into gzip on the wire, right? Apart from the very first utterance of Symbol
, it's the same entropy from there on out even if you have the alias.
@RyanCavanaugh I generally agree. The reason I raised this issue was to highlight that the confluence of these two issues can result in bundle size issues today, in lieu of gzip. I suspect that terser doesn't mangle these globals due to the global scope being mutable by polyfills etc. so there may be an order of operations concern to account for, but I'm not an expert here.
That said in my own code, I don't straight re-export the Symbol constructor, but also add dev checks that removes the symbol description when bundled by terser in production mode. This allows me to avoid the use of the unsafe-symbols configuration.
const symbol = label => Symbol(__DEV__ ? label : undefined);
As a matter of semantic correctness, I think typescript should support this use case since it's valid javascript, but is it high priority, probably not given there are workarounds.
Bug Report
Symbols returned by an alias of the global Symbol constructor cannot be used as objects keys. See this playground example.
In and of itself this is not a bug per se, but it can result in increase bundle size when using a minifier such as terser. Terser does not mangle global names such as Math, Symbol, etc. (see: https://github.com/terser/terser/issues/1337), so a common trick to avoid them reappearing in minified code is to assign the global name to a local variable.
For instance, consider the following typescript code that uses the globals Symbol and Math: playground link
when compiled to javascript by TS it generates the following output:
and when minified using terser you get the following output (you can try this using https://try.terser.org):
Now consider this minor tweak to the original code, which aliases the global Math name to a local variable: playground link
when compiled to javascript by TS it generates the following output:
which terser can minify to:
Obviously in this small example there isn't a significant difference in minified size, but in a large library that includes hundreds of calls to a top level name, this reduction can be significant. Its not uncommon to re-export math functions from an internal module to enable this type of minified output when one is focused on reducing output size.
Unfortunately, for libraries written in TS that choose to use symbols for object keys, class methods, etc. the resulting output can become littered with invocations of the Symbol constructor and there is no typesafe way in typescript to work around this.
🔎 Search Terms
Symbol
🕗 Version & Regression Information
4.95
⏯ Playground Link
Symbols returned from alias to Symbol not working as keys: playground example
💻 Code
🙁 Actual behavior
Symbols returned by aliases of the global Symbol constructor are not unique and cannot be used as keys in interfaces and types.
🙂 Expected behavior
Symbols returned by aliases of the global Symbol constructor should be unique symbols usable as keys in interfaces and types.