Search code examples
typescriptgenerator

How to access IteratorResult value?


I'm very new to TypeScript and having a hard time figuring a problem.

Let's say I have a function that yields another function like below.

function sayHello() {
    return {
        name: 'mike'
    }
};
function* test() {
    yield(sayHello);
}

I'm trying to access name property in my test, but getting the following error message.

Property 'name' does not exist on type 'void | (() => { name: string; })'. Property 'name' does not exist on type 'void'.

This is my test code

const a = test();
a.next().value.name

Is there a way to indicate that value object is returned from sayHello function?


Solution

  • The issue that you are facing is the way yield works. Here is the MDN doc with a nice example.

    Bottom line, when you call next of the generator, the function pauses and returns whatever is on the yield keyword, and resumes again when we call next again, only to pause on the next yield until the function returns eventually.

    In your case,

    function* test() {
        yield(sayHello);
    }
    

    You have two stops, one when function yields at line #2 and returns the function, the next time the function's natural return which is void. So, your function may return either the function or void, hence the return type as you correctly pointed is:

    void | (() => { name: string; })
    

    Let's see the problem, with this information. When you say,

    const a = test();
    const value = a.next().value
    

    Typescript cannot guarantee whether value is the function or void, because it does not track the the number of times next() is called and which next() would result into the function and which into void. So, the responsibility is on the developer.

    This is how you do it:

    function sayHello() {
        return {
            name: 'mike'
        }
    };
    function* test() {
        yield(sayHello);
    }
    
    const a = test();
    const v = a.next().value;
    
    // We need to ensure that this v is not void, 
    // which leaves it being the function
    if(v){
      const r = v().name;
      console.log(r) // works!
    }
    
    // Now that it has yielded, this would be undefined
    console.log(a.next().value)
    

    Link to TS Playground: https://tsplay.dev/mL9VbW