You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Web API type definition issue] AbortSignal.any should expect an Iterable<AbortSignal>, not an AbortSignal[] #2574
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>
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.
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!
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?
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.
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.tsinterfaceAbortSignalStatic{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;}declarevarAbortSignal: AbortSignalStatic;
// lib.dom.iterable.tsinterfaceAbortSignalStatic{/** * 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;}
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.
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.
Activity
Cipscis commentedon Oct 21, 2026
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.jsoncor 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.
MartinFerret commentedon Nov 1, 2026
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!
Cipscis commentedon Nov 2, 2026
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.tsfile, it looks like the way to update the tests is to make changes to the type declaration files in thebaselinesdirectory?MartinFerret commentedon Nov 2, 2026
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.tsfilters them out (!m.static) when generating theIterable<T>overloads. That was added in #1839, because before that the overload was emitted asinterface AbortSignal { any(signals: Iterable<AbortSignal>): AbortSignal }indom.iterable.generated.d.ts, which putany()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.tsfiles 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 toIterable<AbortSignal>wouldn't work either, since the main lib also has to work withoutIterable(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 justAbortSignal.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 buildandnpm run baseline-acceptto regenerate them, andnpm testto check everything. For behavior, you can add a small file inunittests/files/(see formData.ts or structuredClone.ts) using// @ts-expect-errorfor calls that should fail.Feel free to ping me if you get stuck!
Cipscis commentedon Nov 2, 2026
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 AbortSignaldeclaration could be split out into a namedinterface(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:
MartinFerret commentedon Nov 2, 2026
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.
saschanaz commentedon Nov 3, 2026
I think we should just always emit Iterable for TS6+ and drop the separate signatures.
jakebailey commentedon Nov 3, 2026
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.
Cipscis commentedon Nov 4, 2026
I'd be interested to see that, if you can find it. Given
Array<T>satisfiesIterable<T>, I wouldn't have thought that would be an issue when function parameters are getting changed.jakebailey commentedon Nov 4, 2026
That run was here: microsoft/TypeScript#63193