Skip to content

[Web API type definition issue] AbortSignal.any should expect an Iterable<AbortSignal>, not an AbortSignal[] #2574

Open

Description

@Cipscis

Summary

The argument type defined for AbortSignal.any is stricter than in its specification

Expected vs. Actual Behavior

Expected:

The static method AbortSignal.any is defined as expecting an argument of type AbortSignal[]. This prevents narrower types such as readonly AbortSignal[] or Iterable<AbortSignal> from being passed.

Actual:

AbortSignal.any is able to accept Iterable<AbortSignal>, and should have its type defined as such.

Passing non-Array iterables to AbortSignal.abort works as expected in browsers (testing in Chromium and Firefox).

The AbortSignal spec WebIDL definition says this argument is of type sequence<AbortSignal>

Playground Link

https://www.typescriptlang.org/play/?ts=6.0.3#code/MYewdgzgLgBKZQE4gDYoKaIIIwLwzHQHcYsAjERKAYXCVQ0QAoBKAbgCh5o47k1MAITwFipClVoJ+jVpw4AzAK5hgUAJbgAVDADm6KOUpQAyut1gAhiggBJKJktkMrGAG8OHAJABPdehQAE15pBkwsADoIcysUTl9-IJD6AURBKJjrTgBfT25YaItrESMqMyKUCMswHyZ9QwlTTJt7R2d0VnYOAHpumAA9ABUfAAd0E2BEdRHYTGREABoYMiVYMBAYRBUNAFt0GDnKT16YWh2ydUJgwtiYIkoAawgYS2f0AA8xtXRAgC4ucAQVDoCIoEC6Jg3axVRo-LonfoKawQdAA0KpSJOYxyNFAjCg8GQ5ow4xwzgIpBKdBAA

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

From MDN:

The AbortSignal.any() static method takes an iterable of abort signals and returns an AbortSignal.

Activity

  1. Cipscis commented on Oct 21, 2026

    @Cipscis
    Author

    I'd be happy to make a PR to resolve this myself, but it's my first time contributing here so I'm still trying to find my way around. I'm unsure if this is something that should be fixed in inputfiles/addedTypes.jsonc or upstream, and if the fix is best done upstream I'm not sure where to start.

    I mentioned in the issue description what the WebIDL definition says in the AbortSignal spec, but I'm not sure if this is "the upstream IDL" mentioned in this repo's readme or it's referring to some other place.

    If anyone is able to point me in the right direction, I'd appreciate it.

  2. MartinFerret commented on Nov 1, 2026

    @MartinFerret
    Contributor

    Hi Brooke Hart (@Cipscis), thanks for the detailed report! I’ve started digging into this. From what I see, the WebIDL uses sequence, which the generator emits as AbortSignal[].
    TaskSignal.any() has the same issue, so it would be nice to fix both. Normally the generator adds Iterable overloads for methods taking a sequence, so my guess is that static methods like AbortSignal.any() aren’t covered by that logic.

    I’m still verifying this in src/build/emitter.ts.

    Are you still planning to open a PR? If so, I’m happy to share what I find to help you along. If you’d rather not, I can take it on. Either way works for me!

  3. Cipscis commented on Nov 2, 2026

    @Cipscis
    Author

    I'd still be very happy to contribute, it's my first time here in this repo and I'm essentially keen to help but reluctant to get in anyone's way 😅

    If you're right and the issue is related to how any static method accepting a sequence has its type signatures generated, then its blast radius is a bit larger than just the method I'd identified this issue in.

    It looks like it should be straightforward to run the build and test scripts locally to confirm any changes I might make though. Looking at a couple of recent PRs, and the test.ts file, it looks like the way to update the tests is to make changes to the type declaration files in the baselines directory?

  4. MartinFerret commented on Nov 2, 2026

    @MartinFerret
    Contributor

    Not in your way at all, happy to have you on this one! I dug a bit further and it turned out to be more interesting than a missing case.

    Static methods are excluded on purpose: src/build/emitter.ts filters them out (!m.static) when generating the Iterable<T> overloads. That was added in #1839, because before that the overload was emitted as interface AbortSignal { any(signals: Iterable<AbortSignal>): AbortSignal } in dom.iterable.generated.d.ts, which put any() on instances rather than on the constructor.

    The underlying problem is that statics live in an anonymous type (declare var AbortSignal: { prototype: ...; new(): ...; any(...): ... }), and the *.iterable.d.ts files can only add overloads by merging into named interfaces. So there's currently nowhere for a static iterable overload to go. Simply overriding the param type to Iterable<AbortSignal> wouldn't work either, since the main lib also has to work without Iterable (ES5), which is why those overloads live in separate files.

    So a fix would mean changing how statics are emitted (for example, a named constructor interface the iterable file can merge into). That affects every static method taking a sequence, not just AbortSignal.any / TaskSignal.any, so it's probably worth checking with Kagami Sascha Rosylight (@saschanaz) which direction they'd prefer before writing code.

    On your baselines question: you don't edit them by hand. Change the generator or patches, then run npm run build and npm run baseline-accept to regenerate them, and npm test to check everything. For behavior, you can add a small file in unittests/files/ (see formData.ts or structuredClone.ts) using // @ts-expect-error for calls that should fail.

    Feel free to ping me if you get stuck!

  5. Cipscis commented on Nov 2, 2026

    @Cipscis
    Author

    Oh I see, so this stems from the iterable type definitions being split out from the main DOM type definitions.

    I know one of the breaking changes in TypeScript 6 is that these types are no longer split out. Is there a way to leverage that to make this easier to resolve by no longer needing to use declaration merging to support Iterable?

    If not, presumably the anonymous type used along with the declare var AbortSignal declaration could be split out into a named interface (e.g. AbortSignalStatic, not sure what naming convention may be best) and then that could allow for declaration merging?

    I've done a quick and rough test in one of my projects and this looks like it works:

    // lib.dom.ts
    interface AbortSignalStatic{
        prototype: AbortSignal;
        new(): AbortSignal;
        /**
         * The **`AbortSignal.abort()`** static method returns an AbortSignal that is already set as aborted (and which does not trigger an abort event).
         *
         * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/abort_static)
         */
        abort(reason?: any): AbortSignal;
        /**
         * The **`AbortSignal.any()`** static method takes an iterable of abort signals and returns an AbortSignal. The returned abort signal is aborted when any of the input iterable abort signals are aborted. The abort reason will be set to the reason of the first signal that is aborted. If any of the given abort signals are already aborted then so will be the returned AbortSignal.
         *
         * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/any_static)
         */
        any(signals: AbortSignal[]): AbortSignal;
        /**
         * The **`AbortSignal.timeout()`** static method returns an AbortSignal that will automatically abort after a specified time.
         *
         * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/timeout_static)
         */
        timeout(milliseconds: number): AbortSignal;
    }
    
    declare var AbortSignal: AbortSignalStatic;
    // lib.dom.iterable.ts
    interface AbortSignalStatic {
        /**
         * The **`AbortSignal.any()`** static method takes an iterable of abort signals and returns an AbortSignal. The returned abort signal is aborted when any of the input iterable abort signals are aborted. The abort reason will be set to the reason of the first signal that is aborted. If any of the given abort signals are already aborted then so will be the returned AbortSignal.
         *
         * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/any_static)
         */
        any(signals: Iterable<AbortSignal>): AbortSignal;
    }
  6. MartinFerret commented on Nov 2, 2026

    @MartinFerret
    Contributor

    Both ideas work, and the TS 6 one is probably the simplest. In the main TS 6+ lib, the iterable APIs are already in the same file and Iterable is always available, so static methods could take an Iterable directly, without declaration merging or new global names. The trade-off is that the older TS baselines would keep the array type.

    A named static interface works too, but it adds new global names to lib.dom and needs a naming decision, which feels like a bigger call for the maintainers.

  7. saschanaz commented on Nov 3, 2026

    @saschanaz
    Contributor

    I think we should just always emit Iterable for TS6+ and drop the separate signatures.

  8. jakebailey commented on Nov 3, 2026

    @jakebailey
    Member

    I need to go find it, but I had thought that dropping all the array based overloads caused issued in the wild and hence we didn't do it.

  9. Cipscis commented on Nov 4, 2026

    @Cipscis
    Author

    I'd be interested to see that, if you can find it. Given Array<T> satisfies Iterable<T>, I wouldn't have thought that would be an issue when function parameters are getting changed.

  10. jakebailey commented on Nov 4, 2026

    @jakebailey
    Member

    That run was here: microsoft/TypeScript#63193

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