Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

async, await, loop, implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. #36687

Closed
AnyhowStep opened this issue Feb 7, 2020 · 18 comments
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@AnyhowStep
Copy link
Contributor

AnyhowStep commented Feb 7, 2020

TypeScript Version: 3.5.1, 3.7.5, 3.8-Beta

Search Terms: async, await, loop, promise, variable assignment, implicitly has type any, referenced directly or indirectly in its own initializer.

Code

interface Foo {
    id : string,
}

declare function paginate(after: string|undefined): Promise<Foo[]>;

async function main() {
    let after: string | undefined = undefined;
    while (true) {
        const page = await paginate(after);
        for (const foo of page) {
            after = foo.id;
        }
    }   
}

Expected behavior:

Type checks successfully

Actual behavior:
image

Playground Link: Playground

Related Issues:

Similar to #14428 , an already fixed issue.

Similar to #36666 , but without destructuring; and my initial variable also has explicit type annotations (after : string|undefined)


Workaround

Just use an explicit type annotation,

const page : Foo[] = await paginate(after);

However, it is strange. It shouldn't need the explicit type annotation because it can only possibly be Foo[].

@AnyhowStep
Copy link
Contributor Author

Shorter repro

interface Foo {
    id : string,
}

declare function getFoo(after: string|undefined): Promise<Foo>;

async function main() {
    let after: string | undefined = undefined;
    while (true) {
        const foo = await getFoo(after);
        after = foo.id;
    }   
}

@AnyhowStep AnyhowStep changed the title async, await, implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. async, await, loop, implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. Feb 7, 2020
@Zemnmez
Copy link

Zemnmez commented Feb 8, 2020

OK. I think this is an issue of type narrowing. It looks to me like the first time the loop runs, after is explicitly typed to string | undefined, but after this, it's type is typeof await paginate(after)[0].foo.id, whose type is determined indirectly by the result of paginate(after).

@Zemnmez
Copy link

Zemnmez commented Feb 8, 2020

async function main() {
    let after: string | undefined = undefined;
    while (true) {
        const foo = await getFoo(after);
        after = foo.id;
    }   
}

the example here would be broken even if we didn't have this issue because id does not exist on Foo[] (the return type of getFoo())

@Zemnmez
Copy link

Zemnmez commented Feb 8, 2020

Here's a version of this code that does work (I am not sure why, maybe because not explicitly defining after as undefined prevents attempted narrowing):

interface Foo {
    id : string,
}

declare function paginate(after: string|undefined): Promise<Foo[]>;

async function main() {
    let after: string | undefined;
    while (true) {
        const page = await paginate(after);
        for (const foo of page) {
            after = foo.id;
        }
    }
}

Playground Link

smaller repro:

declare function paginate(after: string|undefined): Promise<string>;

async function main() {
    let after: string | undefined = undefined;
    while (true) {
        const page = await paginate(after);
        after = page?.[0]
    }
}

Playground Link

The following things stop the error:

  • not explicitly defining after's value
  • making the function synchronous
  • adding return 1 to the end of while(true)

Things that don't stop the error:

  • conditionally returning after some amount of loops (so it's not an issue with main() never returning)

@AnyhowStep
Copy link
Contributor Author

because id does not exist on Foo[] (the return type of getFoo())

What are you talking about?
In the second example, getFoo() returns Promise<Foo>. Not Promise<Foo[]>.

@Zemnmez
Copy link

Zemnmez commented Feb 9, 2020

because id does not exist on Foo[] (the return type of getFoo())

What are you talking about?
In the second example, getFoo() returns Promise<Foo>. Not Promise<Foo[]>.

sorry i misread 😅

@RyanCavanaugh RyanCavanaugh added the Design Limitation Constraints of the existing architecture prevent this from being fixed label Mar 2, 2020
@RyanCavanaugh
Copy link
Member

This is a legit circularity and none of our existing circularity-shortcutting mechanisms work in this case

@AnyhowStep
Copy link
Contributor Author

How is it circular, despite the explicit type annotation, and explicit initialization?

Why would removing explicit initialization (= undefined) make it work?

If it was circular before removing it, it should be circular after removing it

@AnyhowStep
Copy link
Contributor Author

Still confused here =(

@AnyhowStep
Copy link
Contributor Author

Bump to prevent auto closing

@evenfrost
Copy link

Any update on this? Just faced this very error in the while loop.

@evenfrost
Copy link

Faced this again, found this thread again, realized that I had already commented on this a year ago.

Well, as I'm back here, any update on this? Removing the async/await stuff from this throws no error, so I believe it's more like a bug than a design limitation.

@RyanVExtend
Copy link

I just ran into this issue with a very similar scenario. Was very surprised to find this as an open issue still... and originating in 2020...

I can't share the complete code here, but the short of it is essentially:

async processStream(): Promise<void> {
  let ShardIterator: string | undefined = await this.getShardIterator() // () => Promise<string>

  while (ShardIterator) {
    const res = await this.ddbStreamsClient.getRecords({ ShardIterator, Limit: 10 }).promise()
    //     ^ res has implicit any
    
    //  ... other code ...
    
    ShardIterator = res.NextShardIterator // string | undefined
  }
}

outside of the loop block this isn't an issue. This is just my first stop in my search for an answer, but have not seen anything in the responses here that makes me feel like error is by design.

@rdsedmundo
Copy link

Just got this on a do..while loop using pagination with a cursor, feels odd that this triggers a problem on TS.

@isnifer
Copy link

isnifer commented Dec 3, 2022

Any update on this? It's very annoying to use workarounds, cmon

@zachweinberg
Copy link

Update?

@martinlarka
Copy link

I think this issue might be fixed since I don't get any errors in the playground when I choose ts 5.

However, I have also run into this or a similar issue and just wanted to share what helped me. And that is to explicitly type the awaited result which would be const page in @AnyhowStep example.

@RealAlphabet
Copy link

I think this issue might be fixed since I don't get any errors in the playground when I choose ts 5.

However, I have also run into this or a similar issue and just wanted to share what helped me. And that is to explicitly type the awaited result which would be const page in @AnyhowStep example.

Still not working for me with TypeScript 5.3.3.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed
Projects
None yet
Development

No branches or pull requests

10 participants