0

For my unit tests I create a fake window.location object. Here's the slightly simplified code:

function getFakeLocation(getCurrentUrl: () => URL): Location {
  return {
    get ancestorOrigins(): DOMStringList {
      return {
        length: 1,
        contains: (origin: string) => {
          // …
        },
        item(index: number) {
          // …
        },
        *[Symbol.iterator]() {
          yield getCurrentUrl().origin;
        },
      };
    },
    assign: (url: string) => {
      // …
    }),
    get href() {
      // …
    },
    set href(url: string) {
      //
    },
    // …
  };
}

This worked fine until I upgraded from TypeScript 5.4.2 to 5.6.0-beta. Now, tsc complains about the ancestorOrigins definition above:

Type '() => Generator<string, void, any>' is not assignable to type '() => BuiltinIterator<string, undefined, any>'.
  Call signature return types 'Generator<string, void, any>' and 'BuiltinIterator<string, undefined, any>' are incompatible.
    The types returned by 'next(...)' are incompatible between these types.
      Type 'IteratorResult<string, void>' is not assignable to type 'IteratorResult<string, undefined>'.
        Type 'IteratorReturnResult<void>' is not assignable to type 'IteratorResult<string, undefined>'.
          Type 'IteratorReturnResult<void>' is not assignable to type 'IteratorReturnResult<undefined>'.
            Type 'void' is not assignable to type 'undefined'.

            *[Symbol.iterator]() {
             ~~~~~~~~~~~~~~~~~

I already updated @types/node to version 20.16.1 as per this question which solved another type error but the above error still remains. However, to me the latter sounds very similar to what Daniel Rosenwasser wrote in his answer in that thread.

1 Answer 1

4

The DOMStringList interface changed between 5.5.4 and 5.6.0

5.5.4:

interface DOMStringList {
    [Symbol.iterator](): IterableIterator<string>;
}

5.6.0-beta:

interface DOMStringList {
    [Symbol.iterator](): BuiltinIterator<string, BuiltinIteratorReturn>;
}

For motivation see:

You stumbled upon a problem described in Disambiguate BuiltinIterator/BuiltinIteratorReturn #59506

In #58243 we introduced the --strictBuiltinIteratorReturn flag and the BuiltinIteratorReturn type to improve type checking for iterator results produced by the iterators of built-ins like Array, Map, Set, etc. While in #58222 we separately introduced the type BuiltinIterator to identify the shape of the new Iterator.prototype used by built-ins like Array, Map, and Set, but also for Generators. Where these two features touch, you will generally see a type reference like BuiltinIterator<T, BuiltinIteratorReturn>.

Despite the overlap between these two features, there is a subtle distinction between the two mechanisms that are causing some confusion for users. In #59444, it was requested that we make BuiltinIteratorReturn the default for the TReturn type parameter of BuiltinIterator, but this is problematic since Generator now inherits from BuiltinIterator, but the default inference we make for the TReturn of a Generator is void, which is not assignable to BuiltinIteratorReturn when the strictBuiltinIteratorReturn flag is set. On its own, this is not terribly problematic since a Generator instantiation would properly instantiate BuiltinIterator with the correct return type, it does mean that the following code sample would produce an error:

function* f() { yield ""; }
const it: BuiltinIterator<string> = f(); // error: `void` is not assignable to `undefined`

The example above works in 5.6.0-beta, but this does not

function* f() { yield ""; }
const it: BuiltinIterator<string, BuiltinIteratorReturn> = f(); // error: `void` is not assignable to `undefined`

Note that this is a precise definition of iterator in DOMStringList

You have 2 options to solve:

Explicit type

*[Symbol.iterator](): BuiltinIterator<string, BuiltinIteratorReturn> {
  yield getCurrentUrl().origin;      
},

Return value

*[Symbol.iterator]() {
  yield getCurrentUrl().origin;      
  return undefined;
},
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, the option with return undefined works! As for the one involving the more explicit return type (BuiltinIterator<…>) I'm still getting the same error. Interestingly, my IDE complains that it cannot find BuiltinIterator(-Return) but I don't see that error on the command line. Do I have to add any additional types to my tsconfig's lib? devblogs.microsoft.com/typescript/announcing-typescript-5-6-rc doesn't mention anything in this regard.
BuiltinIteratorReturn should be defined in es2015.iterable.d.ts. I can see that in 5.7.0 nightly the API has more changes annd now returns ArrayIterator: interface DOMStringList { [Symbol.iterator](): ArrayIterator<string>; }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.