[Web API type definition issue] `exactOptionalPropertyTypes` makes working with most of the interfaces a nightmare
Summary
Optional fields in lib.dom.d.ts do not explicity have undefined in their types, which makes their construction needlessly difficult
Expected vs. Actual Behavior
Expected following code to just work:
function fetchUsers(params?: { signal?: AbortSignal | undefined }) {
return fetch("/api/v1/user/", { signal: params?.signal });
}
But instead the following error is shown:
Argument of type '{ signal: AbortSignal | undefined; }' is not assignable to parameter of type 'RequestInit' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
Types of property 'signal' are incompatible.
Type 'AbortSignal | undefined' is not assignable to type 'AbortSignal | null'.
Type 'undefined' is not assignable to type 'AbortSignal | null'. (2379)
Following is required to be done instead (let's pretend that RequestInit.signal cannot be set to null):
function fetchUsers(params?: { signal?: AbortSignal | undefined }) {
const init: RequestInit = {};
if (params?.signal) {
init.signal = params.signal;
}
return fetch("/api/v1/user/", init);
}
Playground Link
https://www.typescriptlang.org/play/?exactOptionalPropertyTypes=true#code/GYVwdgxgLglg9mABMAplCALAqgZxQJxwAoAHAQ3zIFscB+ALkQG9EcYBzMMgGwcQEEARnHxQAyhy7dEAH0TgAJimAwwKBYgC+ASmYAoRInxoQ+JKnQYiAIgD0ZEjFsA3AIy2QefLesAaZqySPIzklDS0AHRsnDxa2gDceppAA
Browser Support
- [x] This API is supported in at least two major browser engines (not two Chromium-based browsers).
Have Tried The Latest Releases
- [x] This issue applies to the latest release of TypeScript.
- [x] This issue applies to the latest release of
@types/web.
Additional Context
See https://webidl.spec.whatwg.org/#idl-dictionaries:
In the JavaScript binding, a value of undefined for the property corresponding to a dictionary member is treated the same as omitting that property.
I have not found a single | undefined on an optional interface field in the entire lib.dom.d.ts of Typescript 5.9.2.
Hello, Can I work on this @jakebailey? It is an easy fix
It's easy if it's added everywhere, but I do know if that's quite the right fix
It's easy if it's added everywhere, but I do know if that's quite the right fix
I was thinking of adding undefined here: https://github.com/microsoft/TypeScript-DOM-lib-generator/blob/fad434be5961529bee396efab03cce2a1bc333e2/src/build/emitter.ts#L497C1-L501C4 Alternatively, I can make a new kdl file which stores the properties that need to be undefined, but this would require more code, and may result in a long file.
If optional fields are supposed to be nullable, then something very wrong is going on. This is how RequestInit looks currently:
interface RequestInit {
/** A BodyInit object or null to set request's body. */
body?: BodyInit | null;
/** A string indicating how the request will interact with the browser's cache to set request's cache. */
cache?: RequestCache;
/** A string indicating whether credentials will be sent with the request always, never, or only when sent to a same-origin URL. Sets request's credentials. */
credentials?: RequestCredentials;
/** A Headers object, an object literal, or an array of two-item arrays to set request's headers. */
headers?: HeadersInit;
/** A cryptographic hash of the resource to be fetched by request. Sets request's integrity. */
integrity?: string;
/** A boolean to set request's keepalive. */
keepalive?: boolean;
/** A string to set request's method. */
method?: string;
/** A string to indicate whether the request will use CORS, or will be restricted to same-origin URLs. Sets request's mode. */
mode?: RequestMode;
priority?: RequestPriority;
/** A string indicating whether request follows redirects, results in an error upon encountering a redirect, or returns the redirect (in an opaque fashion). Sets request's redirect. */
redirect?: RequestRedirect;
/** A string whose value is a same-origin URL, "about:client", or the empty string, to set request's referrer. */
referrer?: string;
/** A referrer policy to set request's referrerPolicy. */
referrerPolicy?: ReferrerPolicy;
/** An AbortSignal to set request's signal. */
signal?: AbortSignal | null;
/** Can only be null. Used to disassociate request from any Window. */
window?: null;
}
Not all optional fields have | null, so I do not think that changing makeNullable will fix this.
I think this is the case, because the web IDL compiler doesn't add null when it is optional. So this would require a separate update.