Skip to content

[Web API type definition issue] exactOptionalPropertyTypes makes working with most of the interfaces a nightmare #2119

Open

Description

@rijenkii

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

  • This API is supported in at least two major browser engines (not two Chromium-based browsers).

Have Tried The Latest Releases

  • This issue applies to the latest release of TypeScript.
  • 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.

Activity

  1. changed the title [-][Web API type definition issue] `exactOptionalPropertyTypes` makes working with most of the types a nightmare[/-] [+][Web API type definition issue] `exactOptionalPropertyTypes` makes working with most of the interfaces a nightmare[/+] on Aug 18, 2025
  2. Bashamega commented on Aug 18, 2025

    @Bashamega
    Contributor

    Hello,
    Can I work on this Jake Bailey (@jakebailey)?
    It is an easy fix

  3. jakebailey commented on Aug 18, 2025

    @jakebailey
    Member

    It's easy if it's added everywhere, but I do know if that's quite the right fix

  4. Bashamega commented on Aug 18, 2025

    @Bashamega
    Contributor

    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.

  5. rijenkii commented on Aug 18, 2025

    @rijenkii
    Author

    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.

  6. Bashamega commented on Aug 18, 2025

    @Bashamega
    Contributor

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions