Overview
The export @JS macro derives the JavaScript-facing name of an exported declaration from its Swift declaration name. There is currently no way to override it — unlike the import macros (@JSGetter, @JSSetter, @JSFunction), which already accept a jsName: parameter.
This means the Swift symbol name is forced to equal the desired JS name. When the desired JS name collides with something already in scope in the Swift module — most commonly an imported module of the same name, or another top-level symbol — you're forced to either rename the JS-facing API (changing the consumer surface) or introduce a workaround (e.g. a small shim module that re-exports the shadowed type under an alias) purely to escape the shadow.
A jsName: override on the export macro would let the exported JS name be decoupled from the Swift declaration name, matching the ergonomics the import macros already have.
Proposal
Add an optional jsName: String? = nil (name TBD — could also be name:) to the export @JS macro:
public macro JS(
as aliasOf: Any.Type? = nil,
namespace: String? = nil,
jsName: String? = nil,
enumStyle: JSEnumStyle = .const,
identityMode: Bool = false
) = Builtin.ExternalMacro
When present, jsName sets the exported leaf name; when absent, behavior is unchanged (Swift declaration name is used).
Example
Say a module imports a dependency module named Foo and also wants to export a class to JS as Foo. Today the Swift class must be named Foo, which shadows the imported Foo module module-wide, making the upstream Foo.Bar type unreachable without a workaround. With jsName:
import Foo
@JS(namespace: "MyApp", jsName: "Foo")
final class MyAppFoo {
// free to reference the imported `Foo` module here — no shadow
}
JavaScript sees MyApp.Foo; the Swift symbol stays MyAppFoo.
Scope
- Export macro only (classes, structs, enums, functions).
- TS
.d.ts and JS glue names should follow jsName.
- Interaction with
namespace: should compose (namespace prefix + jsName leaf).
- Diagnostic if
jsName is empty/invalid as a JS identifier.
Overview
The export
@JSmacro derives the JavaScript-facing name of an exported declaration from its Swift declaration name. There is currently no way to override it — unlike the import macros (@JSGetter,@JSSetter,@JSFunction), which already accept ajsName:parameter.This means the Swift symbol name is forced to equal the desired JS name. When the desired JS name collides with something already in scope in the Swift module — most commonly an imported module of the same name, or another top-level symbol — you're forced to either rename the JS-facing API (changing the consumer surface) or introduce a workaround (e.g. a small shim module that re-exports the shadowed type under an alias) purely to escape the shadow.
A
jsName:override on the export macro would let the exported JS name be decoupled from the Swift declaration name, matching the ergonomics the import macros already have.Proposal
Add an optional
jsName: String? = nil(name TBD — could also bename:) to the export@JSmacro:When present,
jsNamesets the exported leaf name; when absent, behavior is unchanged (Swift declaration name is used).Example
Say a module imports a dependency module named
Fooand also wants to export a class to JS asFoo. Today the Swift class must be namedFoo, which shadows the importedFoomodule module-wide, making the upstreamFoo.Bartype unreachable without a workaround. WithjsName:JavaScript sees
MyApp.Foo; the Swift symbol staysMyAppFoo.Scope
.d.tsand JS glue names should followjsName.namespace:should compose (namespace prefix +jsNameleaf).jsNameis empty/invalid as a JS identifier.