svelte
svelte copied to clipboard
The exported names of the reactive Map, Set, and others conflict with platform globals
Describe the problem
I like the functionality of svelte/reactivity
's reactive classes that extend globals like Map
and Set
, and I appreciate terseness and aesthetics of naming them the same as the globals, but shadowing globals like this is troublesome.
Consider that you can't look at code and know if you're dealing with a reactive Svelte class or platform global without looking at imports:
class A {
b = new Map(); // is this reactive? check the imports or intellisense
}
function create_c(): Set<string> {
// does this return a reactive set? check the imports or intellisense
}
The two kinds of classes differ in behavior and performance. Adding an import changes the semantics of code that otherwise uses normal JS globals, which seems surprising. ~~Code is therefore not portable across modules with different imports, which can silently cause bugs that tools like TypeScript can't help with. (as @hanszoons describes below)~~ (can probably be mitigated, see replies) This ambiguity feels like the uncanny valley Svelte is trying to avoid as discussed in the feature's main issue.
This is editor-specific, but the intelliense isn't obvious. With the global:
var Set: SetConstructor
new <string>(iterable?: Iterable<string> | null | undefined) => Set<string> (+1 overload)
When imported from svelte/reactivity
:
(alias) new Set<string>(value?: Iterable<string> | null | undefined): Set<string>
import Set
The indications this is the reactive one are subtle until you learn the difference.
A workaround to most issues is aliasing yourself. Using import {Set as ReactiveSet}
:
(alias) new ReactiveSet<string>(value?: Iterable<string> | null | undefined): ReactiveSet<string>
import ReactiveSet
But unless you create your own re-exports of svelte/reactivity
, you lose autoimports because of the alias. You also lose autoimports in some cases if you don't alias, e.g. pressing ctrl+.
on a Set
to import it automatically from svelte/reactivity
does not work, although it does include the import in the suggestion dropdown when typing out the global name.
To emphasize the point, the Svelte codebase internally prefixes them with Reactive
presumably because the code uses regular maps and sets often, as is common in JS, and the reactive collections are referenced less. By conflicting with the globals for the convenience of authoring reactive UIs, it's creating fragmentation where some modules and codebases prefer aliasing or not. For un-aliased code, the default, you don't know if a class is reactive without checking the imports, ~~and code cannot be moved around safely without carefully handling imports~~ (can probably be mitigated, see replies).
Describe the proposed solution
The easy fix (a breaking change) is removing the export aliases:
// types/index.d.ts
// from this:
export { ReactiveDate as Date, ReactiveSet as Set, ReactiveMap as Map, ReactiveURL as URL, ReactiveURLSearchParams as URLSearchParams };
// to this:
export { ReactiveDate, ReactiveSet, ReactiveMap, ReactiveURL, ReactiveURLSearchParams };
Importance
would make my life easier