Use private constructors in type definitions of structs without an exported constructor #4282
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Motivation
A simple empty struct was previously typed as follows:
This typing is incorrect. In JavaScript, a missing constructor is equivalent to an empty constructor.
Example:
So the above type for
Foo
is actually just a short form for this:Obviously, this is not correct. The
Foo
class is not supported to be instantiate-able using the class constructor. In debug mode, WBG even generates a constructor that always throws.Changes in this PR
This PR solves this problem by adding a
private constructor
for structs with a#[wasm_bindgen(constructor)]
. E.g. the aboveFoo
struct would get this type definitions:A private constructor in TS declares the constructor as inaccessible to anyone besides the class itself. In particular, a private constructor prevents users in TypeScript from:
new Foo()
.class Bar extends Foo {}
.Foo
in generic functions that require a constructor.See this TS playground for proof.
With those changes, the type definitions now accurately represent the correct usage of class
Foo
and correctly cause errors on incorrect usage.Is this a breaking change?
Probably not.
Since this is a type-only change, nothing will break at runtime (more than it already is). Users using the implicitly defined constructor most likely isn't intended by WBG since WBG even generates a constructor that always throws in debug mode to prevent exactly this.
So this PR will at most cause new TS compiler errors for incorrect code. From that perspective, I would argue that this PR is not a breaking change.