Skip to content

Commit 8fd2016

Browse files
authored
Drop the Null arm when flattening Nullable<T> into argument variants (#20)
A Nullable<T> argument was fanning out into [T, Null], producing a `_with_null(val: &Null)` overload everywhere a parameter or setter accepted `T | null`. The Null overload is API noise: callers either have a value to pass (use the T arm) or they don't (use an optional-truncation overload). Forcing them to construct a Null value just to clear a field offers nothing. Now Nullable<T> in argument position flattens to [T] only. Return-position handling (Option<T>, JsOption<T>) is unchanged — that lives in to_syn_type, independent of this fan-out.
1 parent f75e49c commit 8fd2016

7 files changed

Lines changed: 25 additions & 597 deletions

File tree

CONVENTIONS.md

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,15 +60,18 @@ borrowed by reference; return-position container types are owned.
6060

6161
## Optional and nullable types
6262

63-
* `T | null``Option<T>` in return position, `Option<T>` in argument
64-
position. (`null`-only is rare; treated like `undefined`.)
65-
* `T | undefined` and `T | null | undefined` → also `Option<T>`. We coalesce
66-
at parse time; the rendered union has no separate `null`/`undefined`
67-
arm.
63+
* `T | null``Option<T>` in return position. In argument position the
64+
`null` arm is dropped — the parameter takes `T` directly. A
65+
`_with_null(val: &Null)` overload would force callers to construct a
66+
`Null` value with no real upside, and the omission case for
67+
truly-optional params is already covered by the optional-truncation
68+
rule below.
69+
* `T | undefined` and `T | null | undefined` follow the same rules as
70+
`T | null` — coalesced at parse time; the rendered union has no
71+
separate `null`/`undefined` arm.
6872
* In inner type positions, `T | null``JsOption<T>` unless `T` already
6973
erases to `JsValue`; `JsOption<JsValue>` simplifies to `JsValue`.
70-
* `T?` on a property → `Option<T>`. The setter takes `Option<T>` too, so
71-
callers can clear the property by passing `None`.
74+
* `T?` on a property → getter returns `Option<T>`; setter takes `T`.
7275
* `f(x?: T)` (optional parameter) → produces an overload pair, *not* an
7376
`Option<T>` parameter. See [Signature flattening](#signature-flattening).
7477

src/codegen/signatures.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -600,15 +600,18 @@ fn flatten_type(ty: &TypeRef, cgctx: Option<&CodegenContext<'_>>, scope: ScopeId
600600
vec![ty.clone()]
601601
}
602602

603-
// Nullable: flatten inner types unwrapped, then add a Null variant.
604-
// This expands `T | null` into separate overload variants for each T
605-
// plus an explicit `_with_null` variant, rather than wrapping every
606-
// alternative in `Option<T>`.
607-
TypeRef::Nullable(inner) => {
608-
let mut alts = flatten_type(inner, cgctx, scope);
609-
alts.push(TypeRef::Null);
610-
alts
611-
}
603+
// Nullable: flatten the inner type, dropping the Null arm.
604+
//
605+
// Argument-position `Nullable<T>` carries no information that helps
606+
// the caller — a `_with_null(val: &Null)` overload is always
607+
// useless: callers either have a value to pass (use the `T` arm)
608+
// or they don't (use an optional-truncation overload, or the
609+
// setter just isn't called). Forcing them to construct a `Null`
610+
// value just to clear a field is API noise.
611+
//
612+
// Return-position handling (`Option<T>` / `JsOption<T>`) is
613+
// independent of this fan-out — it lives in `to_syn_type`.
614+
TypeRef::Nullable(inner) => flatten_type(inner, cgctx, scope),
612615

613616
// Generic containers are not distributive: `Array<A | B>` and
614617
// `Record<K, A | B>` are single parameter shapes, not overloads.

tests/snapshots/basic.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,6 @@ extern "C" {
6565
#[wasm_bindgen(constructor, catch, js_name = "Response")]
6666
pub fn new_with_form_data(body: &FormData) -> Result<Response, JsValue>;
6767
#[wasm_bindgen(constructor, catch, js_name = "Response")]
68-
pub fn new_with_null(body: &Null) -> Result<Response, JsValue>;
69-
#[wasm_bindgen(constructor, catch, js_name = "Response")]
7068
pub fn new_with_readable_stream_and_init(
7169
body: &ReadableStream,
7270
init: &ResponseInit,
@@ -90,8 +88,6 @@ extern "C" {
9088
body: &FormData,
9189
init: &ResponseInit,
9290
) -> Result<Response, JsValue>;
93-
#[wasm_bindgen(constructor, catch, js_name = "Response")]
94-
pub fn new_with_null_and_init(body: &Null, init: &ResponseInit) -> Result<Response, JsValue>;
9591
#[doc = " Returns a new Response with a network error."]
9692
# [wasm_bindgen (static_method_of = Response)]
9793
pub fn error() -> Response;

tests/snapshots/cloudflare-worker.rs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,6 @@ extern "C" {
147147
pub fn set_body_with_url_search_params(this: &RequestInit, val: &URLSearchParams);
148148
#[wasm_bindgen(method, setter, js_name = "body")]
149149
pub fn set_body_with_form_data(this: &RequestInit, val: &FormData);
150-
#[wasm_bindgen(method, setter, js_name = "body")]
151-
pub fn set_body_with_null(this: &RequestInit, val: &Null);
152150
#[wasm_bindgen(method, getter)]
153151
pub fn redirect(this: &RequestInit) -> Option<String>;
154152
#[wasm_bindgen(method, setter)]
@@ -212,10 +210,6 @@ impl RequestInitBuilder {
212210
self.inner.set_body_with_form_data(val);
213211
self
214212
}
215-
pub fn body_with_null(self, val: &Null) -> Self {
216-
self.inner.set_body_with_null(val);
217-
self
218-
}
219213
pub fn redirect(self, val: &str) -> Self {
220214
self.inner.set_redirect(val);
221215
self
@@ -250,8 +244,6 @@ extern "C" {
250244
#[wasm_bindgen(constructor, catch, js_name = "Response")]
251245
pub fn new_with_form_data(body: &FormData) -> Result<Response, JsValue>;
252246
#[wasm_bindgen(constructor, catch, js_name = "Response")]
253-
pub fn new_with_null(body: &Null) -> Result<Response, JsValue>;
254-
#[wasm_bindgen(constructor, catch, js_name = "Response")]
255247
pub fn new_with_readable_stream_and_init(
256248
body: &ReadableStream,
257249
init: &ResponseInit,
@@ -275,8 +267,6 @@ extern "C" {
275267
body: &FormData,
276268
init: &ResponseInit,
277269
) -> Result<Response, JsValue>;
278-
#[wasm_bindgen(constructor, catch, js_name = "Response")]
279-
pub fn new_with_null_and_init(body: &Null, init: &ResponseInit) -> Result<Response, JsValue>;
280270
# [wasm_bindgen (static_method_of = Response)]
281271
pub fn redirect(url: &str) -> Response;
282272
# [wasm_bindgen (static_method_of = Response , catch , js_name = "redirect")]

tests/snapshots/coverage.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -212,8 +212,6 @@ extern "C" {
212212
pub fn next(this: &LinkedList) -> Option<LinkedList>;
213213
#[wasm_bindgen(method, setter)]
214214
pub fn set_next(this: &LinkedList, val: &LinkedList);
215-
#[wasm_bindgen(method, setter, js_name = "next")]
216-
pub fn set_next_with_null(this: &LinkedList, val: &Null);
217215
}
218216
impl LinkedList {
219217
pub fn new(data: &JsValue, next: Option<&LinkedList>) -> LinkedList {
@@ -408,8 +406,6 @@ extern "C" {
408406
pub fn set_body(this: &FetchOptions, val: &str);
409407
#[wasm_bindgen(method, setter, js_name = "body")]
410408
pub fn set_body_with_array_buffer(this: &FetchOptions, val: &ArrayBuffer);
411-
#[wasm_bindgen(method, setter, js_name = "body")]
412-
pub fn set_body_with_null(this: &FetchOptions, val: &Null);
413409
#[wasm_bindgen(method, getter)]
414410
pub fn redirect(this: &FetchOptions) -> Option<String>;
415411
#[wasm_bindgen(method, setter)]
@@ -453,10 +449,6 @@ impl FetchOptionsBuilder {
453449
self.inner.set_body_with_array_buffer(val);
454450
self
455451
}
456-
pub fn body_with_null(self, val: &Null) -> Self {
457-
self.inner.set_body_with_null(val);
458-
self
459-
}
460452
pub fn redirect(self, val: &str) -> Self {
461453
self.inner.set_redirect(val);
462454
self

tests/snapshots/node-console.rs

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,12 @@ pub mod console {
1919
pub fn stderr(this: &ConsoleOptions) -> Option<Object>;
2020
#[wasm_bindgen(method, setter)]
2121
pub fn set_stderr(this: &ConsoleOptions, val: &Object);
22-
#[wasm_bindgen(method, setter, js_name = "stderr")]
23-
pub fn set_stderr_with_null(this: &ConsoleOptions, val: &Null);
2422
#[doc = " Ignore errors when writing to the underlying streams."]
2523
#[doc = " @default true"]
2624
#[wasm_bindgen(method, getter, js_name = "ignoreErrors")]
2725
pub fn ignore_errors(this: &ConsoleOptions) -> Option<bool>;
2826
#[wasm_bindgen(method, setter, js_name = "ignoreErrors")]
2927
pub fn set_ignore_errors(this: &ConsoleOptions, val: bool);
30-
#[wasm_bindgen(method, setter, js_name = "ignoreErrors")]
31-
pub fn set_ignore_errors_with_null(this: &ConsoleOptions, val: &Null);
3228
#[doc = " Set color support for this `Console` instance."]
3329
#[doc = " @default 'auto'"]
3430
#[doc = ""]
@@ -39,16 +35,12 @@ pub mod console {
3935
pub fn set_color_mode(this: &ConsoleOptions, val: bool);
4036
#[wasm_bindgen(method, setter, js_name = "colorMode")]
4137
pub fn set_color_mode_with_str(this: &ConsoleOptions, val: &str);
42-
#[wasm_bindgen(method, setter, js_name = "colorMode")]
43-
pub fn set_color_mode_with_null(this: &ConsoleOptions, val: &Null);
4438
#[doc = " Set group indentation."]
4539
#[doc = " @default 2"]
4640
#[wasm_bindgen(method, getter, js_name = "groupIndentation")]
4741
pub fn group_indentation(this: &ConsoleOptions) -> Option<f64>;
4842
#[wasm_bindgen(method, setter, js_name = "groupIndentation")]
4943
pub fn set_group_indentation(this: &ConsoleOptions, val: f64);
50-
#[wasm_bindgen(method, setter, js_name = "groupIndentation")]
51-
pub fn set_group_indentation_with_null(this: &ConsoleOptions, val: &Null);
5244
}
5345
impl ConsoleOptions {
5446
pub fn new(stdout: &Object) -> ConsoleOptions {
@@ -68,18 +60,10 @@ pub mod console {
6860
self.inner.set_stderr(val);
6961
self
7062
}
71-
pub fn stderr_with_null(self, val: &Null) -> Self {
72-
self.inner.set_stderr_with_null(val);
73-
self
74-
}
7563
pub fn ignore_errors(self, val: bool) -> Self {
7664
self.inner.set_ignore_errors(val);
7765
self
7866
}
79-
pub fn ignore_errors_with_null(self, val: &Null) -> Self {
80-
self.inner.set_ignore_errors_with_null(val);
81-
self
82-
}
8367
pub fn color_mode(self, val: bool) -> Self {
8468
self.inner.set_color_mode(val);
8569
self
@@ -88,18 +72,10 @@ pub mod console {
8872
self.inner.set_color_mode_with_str(val);
8973
self
9074
}
91-
pub fn color_mode_with_null(self, val: &Null) -> Self {
92-
self.inner.set_color_mode_with_null(val);
93-
self
94-
}
9575
pub fn group_indentation(self, val: f64) -> Self {
9676
self.inner.set_group_indentation(val);
9777
self
9878
}
99-
pub fn group_indentation_with_null(self, val: &Null) -> Self {
100-
self.inner.set_group_indentation_with_null(val);
101-
self
102-
}
10379
pub fn build(self) -> ConsoleOptions {
10480
self.inner
10581
}

0 commit comments

Comments
 (0)