microsoft / microsoft/TypeScript

Generators can return undefined values

Open
#59,947 1 comment 1 reaction 1 assignee View on GitHub

@rbuckton is already working on this.

Since Sep 13, 2024.

Needs Investigation
Dominant language
Go
Stars
111k
Forks
14.3k
Avg merge
2d 4h
Merged PRs (30d)
132

Description

Summary

Should the return type of yield* a be TReturn | undefined when in strict mode as opposed to TReturn (where TReturn is the value of the TReturn type argument to a's Iterable type instantiation)?

Or should the type of IteratorReturnResult.value be changed to TReturn | undefined when in strict mode (and potentially the type of Generator.return(value: TReturn) be relaxed to Generator.return(value?: TReturn))?

Have you considered these scenarios? (A github microsoft site search for "generator return undefined" yields no results).

Background

Generators will return undefined values when:

  1. next() is called after the generator has already returned its return value:
function* gen(): Generator<number, string> {
    yield 42;
    return "done";
}

const a = gen();

console.log(a.next()); // { value: 42, done: false }
console.log(a.next()); // { value: 'done', done: true }
console.log(a.next()); // { value: undefined, done: true }

2.1) the generator is interrupted via a break statement of for-of loop on the generator

const nestedIterableIterator: IterableIterator<string, string, unknown> = {
    [Symbol.iterator]() { return this; },

    next() {
        return { value: "42", done: false }
    },

    return(value: string) {
        console.log("return called with", value);
        return { value, done: true }
    }
}

function* generator() {
    yield* nestedIterableIterator;
    return "never";
}

const it = generator();

it.next();
it.return("passthrough"); // return called with passthrough

for (const x of generator()) {
    break; // return called with undefined
}

2.2) the generator is manually interrupted by calling its return method without arguments:

function* gen(): Generator<number, string> {
    yield 42;
    yield 43;
    return "done";
}

const a = gen();

console.log(a.next()); // { value: 42, done: false }
console.log(a.return(undefined as unknown as string)); // { value: undefined, done: true }
console.log(a.next()); // { value: undefined, done: true }
  1. The generator is attempted to be iterated over using yield* after it has already returned:
function* gen(): Generator<number, string> {
    return "done";
}

function* consumer() {
    const a = gen();

    console.log(yield* a); // done
    console.log(yield* a); // undefined
}

consumer().next();

Effect

The type signature of Generator.return guards against 2.2, and there is no way to get the return value from a for-of loop.

That leaves scenarios 1 and 3, of which 1 is likely uncommon, 3 is probably the only viable scenario and only in the case of user error...

⚙ Compilation target

ES2015

⚙ Library

lib.es2015.generator.d.ts

Missing / Incorrect Definition

Generator.return and IteratorReturnResult.value

Sample Code

See above.

Documentation Link

No relevant documentation.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.