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
T implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer #35546
No errors. I need this technic to implement automatically inferred circular types. I already have a class-based implementation (you can see example here) and I basically want same with objects of course without manually defining its type. If it's not a bug, any advances on how to implement it keeping type inferred are highly appreciated.
Actual behavior:
Gives following error:
'User' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.(7022)
C:\test>node_modules\.bin\tsc -version
Version 3.7.3
C:\test>node_modules\.bin\tsc
test.ts:3:18 - error TS7022: 'b' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
3 public readonly b = new B( this );
~
Found 1 error.
It works with typescript 3.3.3.
It starts to fail with typescript 3.4.1.
The error happened when migrating our codebase from Typescript 3.2.2 to 3.7.3
The only solution I have for the moment is to change the line to
Please only log bugs when a circularity error is issued when there isn't a circularity. These examples both contain plainly-visible cycles and the error is correct.
it was working on a previous version and so need to migrate a 1.5 million LOC typescript codebase
the build randomly fails: at first, I had no build error on my code base, i had some flacky errors in my CI builds (unfortunately, unable to reproduce) (could the error depend on the order of the files are processed? > see below)
The cycle is not that obvious as type B does not depend at all of the parameters of its constructors and is not generic. Type B is straightforward, so is attribute b in Type A, so I was thinking for type A. Sorry for the misunderstanding.
Also some checks :
class A<T> { public readonly b = new B( this ); }
class B { constructor( a: A<any> ) { } }
fails, ok.
class A<T> { public readonly b = new B( this ); }
class B {
public readonly a: A<any>;
constructor( a: A<unknown> ) {
this.a = a;
}
}
fails, ok.
But, if I change the order of declarations of A and B:
class B {
public readonly a: A<any>;
constructor( a: A<unknown> ) {
this.a = a;
}
}
class A<T> { public readonly b = new B( this ); }
No build error.
class B {
public readonly a: A<any>;
constructor( a: A<any> ) {
this.a = a;
}
}
class A<T> { public readonly b = new B( this ); }
It infers Parameters: AWS.ParameterList, but gives me the same error on NextToken
'NextToken' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.ts(7022)
EDIT: I started out with the below example and that works:
import AWS from 'aws-sdk'
async function handler() {
const ssm = new AWS.SSM()
const { Parameters = [], NextToken } = await ssm
.getParametersByPath({ Path: '/prefix/' })
.promise()
}
Apparently, the circularity is in input? But it is detected only on NextToken (and not on Parameters)
// ----------
// ALL OK here, myObject1 has all types detected
const myObject1 = {
aaa: 50,
bbb: () => {
return myObject1.aaa;
}
};
// ----------
// myObject2 has compilation error: (With strict mode ON)
// T implicitly has type 'any' because it does not have a type annotation
// and is referenced directly or indirectly in its own initializer
const someFunc = async (input: () => number): Promise<string> => {
// ...
};
const myObject2 = {
aaa: 50,
bbb: someFunc(() => {
return myObject2.aaa;
})
};
Would really want to know if this is temporary limitation or rather impossible to solve limitation?
(FYI - ignore that input in someFunc is never called. It can be called in async context. This example is simplified to show the problem)
EDIT 1: In our case someFunc is async. Sync can't work, as object is not defined property by property, but all at once. That explains why this is an error. Problem is that it depends how people use it. In sync context it is error for obvious reasons, in async (when at least one tick is passed) it would work fine.
EDIT 2: Whoever is stuck with this, for us alternative approach worked OK:
This behaves pretty much same as object definition, but class is created property by property.
// ----------
// All types are correctly detected
const myObject1 = class {
public static aaa = 50;
public static bbb = someFunc(() => {
return myObject1.aaa;
});
};
Reacted by Cr., nibbleswap, scaleflake, B. Cedric Cogell, Zhi Tian and WuWen
Reacted by scaleflake, Garnet and Fernando Camargo Del Buono
Small non-circular example. Error shows up on the line that says "error here"
Removing the line 2 down (the property = records.property, then there is no more error.
exportclassResult{property?: string}exportclassMyObject{asyncget(property: string): Promise<Result>{returnnewResult();}}asyncfunctionfailingTest(queue: MyObject): Promise<void>{letproperty: string|null=null;letkeepGoing=true;while(keepGoing){if(!property){property="start";}constrecords=awaitqueue.get(property);//ERROR HEREif(records.property){property=records.property;//NO ERROR IF THIS LINE REMOVED}keepGoing=true;}}
also if I break out the await onto 2 lines, the error goes away:
I originally ran into this problem trying to use aws-sdk paginated service methods, which often have a NextToken property taking the place of a in my contrived example. The above was just as simplified as I could make it. The actual workaround I used was to declare my variable outside the loop as any.
Reacted by Robert Gardner, Arseny Smoogly, dk-teknologisk-osv, Alex, Nino Filiu, Denis Zhbankov, Zhi Tian, Kaan Barmore-Genc and Xanthus Wong
Here's another example with firebase auth and TS 4.2.3.
constadmin=require('firebase-admin');asyncfunctiontest(){constauth=admin.auth();// The following 2 assignments work fineconstresult1=awaitauth.listUsers(/** maxResult= */1000);constresult2=awaitauth.listUsers(/** maxResult= */1000,/** pageToken= */undefined);/** @type {string | undefined} */letpageToken='token';while(true){constresult3=awaitauth.listUsers(/** maxResult= */1000,pageToken);// If you comment the following line TS does not complain (but the code obviously won't work)pageToken=result3.pageToken;if(pageToken===undefined){break;}}}
We are using the same pattern with the gmail api, the only difference there is how the page token is specified. Instead of being it's own argument the gmail api received the pageToken in an options param, as in:
Actual result: 'cursor' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.(7022)
Ryan Cavanaugh (@RyanCavanaugh) is this snippet considered wrong or should I post it in a separate issue?
It would be very useful to have some explanation on why the compiler/ide/playground correctly types foo.x as number but is unable to propagate this type to the new const x binding.
iiuc this has something to do with async/await (namely, the same code snippet compiles just fine if I remove await)
That snippet has a circularity, so it's not a bug if a circularity error is issued.
The circularity is not very obvious (it looks like in all of the places where it is needed, the code already specifies an explicit type).
Additionally, it is quite unexpected that adding/removing await changes the circularity.
I don't know what to do with conversations like this. We can avoid some circularity errors by being clever, but there are limits to how much cleverness we're willing to spend. At the limit of that cleverness appears inconsistency. We could fix that by issuing more circularity errors rather than fewer, but I don't think that's what anyone's asking for.
If you want to dig into the code and send a bugfix-sized PR to address this case, that's totally fine, but we can't treat the issuance of circularity errors in the presence of circularities as a defect.
I did not mean to just ask "fix my problem" and I probably failed to explain what I considered the wrong/weird behavior, sorry.
I agree that if my code is broken just because of circularity issues it is not worth fixing as a special case, but I would expect a consistent behavior between await vs non-await.
Instead, if the compiler error is a glitch introduced by (some internal desugaring of) await it might be worth investigating.
In the following snippets I am trying to explain the issue more in detail by expanding (my mental model of) what the compiler should infer about the code.
Note that I might be very wrong: this would explain why I do not understand the compiler responses; unfortunately the tools I tried (tsc/ide/playground) did not help me much, so I would appreciate a lot the insight of someone who knows the inner details of tsc and/or can provide a different mental model so that I can fix my expectation and reasoning.
Without await everything works just as I would expect:
typeFoo={x: number};asyncfunctionbar(arg: Foo): Promise<void>{letfoo: Foo|undefined=arg;// I assume that here the compiler knows foo: Foo | undefined; arg: Foowhile(foo){// I assume that here the compiler knows foo: Foo; arg: Foo;constx=foo.x;// I assume that here the compiler knows foo: Foo; arg: Foo; x: numberfoo=makeFoo(x);// I assume that here the compiler knows foo: Foo | undefined; arg: Foo; x: number}}functionmakeFoo(x: number): Foo|undefined{returnMath.random() ? { x } : undefined;}
With await apparently my assumptions do not hold true anymore:
typeFoo={x: number};asyncfunctionbar(arg: Foo): Promise<void>{letfoo: Foo|undefined=arg;// I assume that here the compiler knows foo: Foo | undefined; arg: Foowhile(foo){// I assume that here the compiler knows foo: Foo; arg: Foo;constx=foo.x;// I assume that here the compiler knows foo: Foo; arg: Foo; x: number// in fact, the playground infers `foo.x: number`, but apparently the type is not propagated to `x`// instead the compilation fails with an errorfoo=awaitmakeFoo(x);// I assume that here the compiler knows foo: Foo | undefined; arg: Foo; x: number// but given the previous error, I am unsure if this makes sense at all}}functionmakeFoo(x: number): Foo|undefined{returnMath.random() ? { x } : undefined;}
Could you help me find out why it behaves differently with/without await?
I am not very knowledgeable of the internals of tsc, but I would definitely love to help fixing this if it considered a bug in the handling of await 😇 .
Please don't interpret any of this as impatience or offense; I'm not sure why this happens either and also find it odd!
What I'm trying to say is that circularity errors on circularities are not per se defects, and I'm not going to reallocate finite team resources away from fixing defects or implementing features toward addressing non-defects.
Could you help me find out why it behaves differently with/without await?
The investigation itself here is the resource spend that I'm trying to avoid. Figuring out why a thing happens is usually 80% of fixing it.
Reacted by Shlomi Borovitz, Alex Rattray and Ian Copp
I am not very knowledgeable of the internals of tsc, but I would definitely love to help fixing this if it considered a bug in the handling of await 😇 .
await recursively resolves promises, so it resolves the types T, Promise <T>, Promise<Promise<T>>, ... etc. all down to just T, for some T. I guess that there is a recursive type definition relating to promises (e.g. Awaited<T>) in the typescript codebase, which is where the circularity comes from, but not visible in our code examples.
await recursively resolves promises, so it resolves the types T, Promise <T>, Promise<Promise<T>>, ... etc. all down to just T, for some T. I guess that there is a recursive type definition relating to promises (e.g. Awaited<T>) in the typescript codebase, which is where the circularity comes from, but not visible in our code examples.
I tried reproducing it under this assumption and it looks the issue is not in the recursive type.
This does not compile even though the await has been replaced with a (trivial) polymorphic identity function.
Posting here because this lint can show up when using default arguments.
exportconstX={// typescript: 'X' implicitly has type 'any' because it does not have a typey: 5,// annotation and is referenced directly or indirectly in its own initializer. (7022)z(_: number=X.y){}}
This doesn't happen in a class:
exportclassX{// no errorstaticy=5z(_: number=X.y){}}
The solution is to move the default into the body of the function:
exportconstX={// no errory: 5,z(w?: number){w??=X.y// ...}}
But in JS, the default argument is evaluated at call time, so it's not like Python where X is evaluated while the function object is being created. Even though I suppose it's possible to use something like an IIFE to call the function before X is finished initializing, TypeScript has enough flow analysis to make this an error:
exportconstX={// typescript: 'X' is declared here.y: 5,z: ((w?: number)=>{w??=X.y// typescript: Block-scoped variable 'X' used before its declaration. (2448)})()// <--}
Default arguments should be handled like that as well, as evaluated only at call time.
Let me know if I should open a separate GitHub issue for this... since if all flow analysis makes this incorrect assumption, that would be a separate issue. But I think it might be just this lint, which rejects code that would otherwise be accepted.
Activity
mttcr commentedon Dec 9, 2019
Hello,
I have another small snippet that shows a similar error:
tsconfig.json:
output
It works with typescript 3.3.3.
It starts to fail with typescript 3.4.1.
The error happened when migrating our codebase from Typescript 3.2.2 to 3.7.3
The only solution I have for the moment is to change the line to
RyanCavanaugh commentedon Dec 9, 2019
Please only log bugs when a circularity error is issued when there isn't a circularity. These examples both contain plainly-visible cycles and the error is correct.
mttcr commentedon Dec 10, 2019
Hm, ok, I just commented out because
Also some checks :
fails, ok.
fails, ok.
But, if I change the order of declarations of A and B:
No build error.
Fails (replaced unknown by any)
MickVanDuijn commentedon Dec 20, 2019
This also happens to me when I try to use the
aws-sdkIt infers
Parameters: AWS.ParameterList, but gives me the same error onNextTokenEDIT: I started out with the below example and that works:
Apparently, the circularity is in
input? But it is detected only onNextToken(and not onParameters)Baldurans commentedon Apr 9, 2020
Having similar issue with very simple example
Would really want to know if this is temporary limitation or rather impossible to solve limitation?
(FYI - ignore that input in someFunc is never called. It can be called in async context. This example is simplified to show the problem)
Tested with TS 3.8.3
Playground link: https://www.typescriptlang.org/play/#code/PTAEFpK6FgCgSgIIBkWgPIGlQAsCmATvgDSgC2AnhgEYBW+AxgC4CMeAhgM6gcA2fUM0oAHfDwAm+ZkxkT4jAPYA7LswrV6s9gF5QAb3ihjvDhwBcoAKwAGEkZM0nlgBQBKUDoB8BhyZPEzACuhMoatAwsrAB0ZhwA3H6gAL7wyYlw8IjQOeBZYFQRsgBMnDxK5CIAlnwczFUqoESEioSuAOpVzLigaoRVLBSKUpgAcm75oAAqoFWVfANdfJRlQqL4oADkHMqUm6A0TBxBXBtdoBKK4qDKiuq4HABuGxxrYrzKt8x1DcqT-jsJLMeMQAGZEfDKRj4IESKrEFjLUCtWbKOEI5hIqphLo8RQAdxxyi6VX4VQAXkQFCo1L1FOR8AAxIJQzygFzYkRBZiuDzeG5BciHQhuSx9bEAc08PkMcH8gRCYQARDJaUqMul4NTVOpCloWKU9LKAWZLLZ7HLHM46QzmVCXO5pb5Lf5jArQuF9cxirEzBl-MkJnBNXAgA
EDIT 1: In our case someFunc is async. Sync can't work, as object is not defined property by property, but all at once. That explains why this is an error. Problem is that it depends how people use it. In sync context it is error for obvious reasons, in async (when at least one tick is passed) it would work fine.
EDIT 2: Whoever is stuck with this, for us alternative approach worked OK:
This behaves pretty much same as object definition, but class is created property by property.
vshlos commentedon May 1, 2020
Small non-circular example. Error shows up on the line that says "error here"
Removing the line 2 down (the
property = records.property, then there is no more error.also if I break out the await onto 2 lines, the error goes away:
... const promise = queue.get(property); const records = await promise; ...amatiasq commentedon May 31, 2020
Here's another snippet to reproduce it.
Playground
vobarian commentedon Jun 16, 2020
Simplified example:
Note:
| undefinedis removed on type ofaa = b;is removed or inlined toa = await getAString(a);getAStringis not asyncPlayground Link
I originally ran into this problem trying to use aws-sdk paginated service methods, which often have a
NextTokenproperty taking the place ofain my contrived example. The above was just as simplified as I could make it. The actual workaround I used was to declare my variable outside the loop asany.swansontec commentedon Aug 3, 2020
I am getting this in an even simpler case:
You can try it out:
However,
ashould clearly be of typenumber, based on the way Javascript destructuring works.tianzhich commentedon Aug 27, 2020
I have a similar #40279
bolddp commentedon Nov 3, 2020
I came across this when the services in my backend needed to reference each other.
Playground
rajeevnaikte commentedon Dec 30, 2020
Same issue troubling me while creating an utility for when-then chaining
Playground
dk-teknologisk-osv commentedon Jan 14, 2021
I have the same issue with
awaitkeyword:Without
awaitkeyword I getWith
awaitkeyword I getsk- commentedon Mar 30, 2021
Here's another example with firebase auth and TS 4.2.3.
We are using the same pattern with the gmail api, the only difference there is how the page token is specified. Instead of being it's own argument the gmail api received the
pageTokenin an options param, as in:dustin-rcg commentedon Feb 10, 2022
Here's another example:
Code:
Actual result:
'cursor' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.(7022)Typescript version 4.5.4
See Playground
Comment: This is a common pattern for iterating over paginated results using a cursor. The error goes away if I write
ranma42 commentedon Apr 6, 2022
Another example:
Playground
gfernandez-me commentedon Jun 23, 2022
Ryan Cavanaugh (@RyanCavanaugh)
ranma42 commentedon Jun 24, 2022
Ryan Cavanaugh (@RyanCavanaugh) is this snippet considered wrong or should I post it in a separate issue?
It would be very useful to have some explanation on why the compiler/ide/playground correctly types
foo.xasnumberbut is unable to propagate this type to the newconst xbinding.iiuc this has something to do with
async/await(namely, the same code snippet compiles just fine if I removeawait)RyanCavanaugh commentedon Jun 24, 2022
That snippet has a circularity, so it's not a bug if a circularity error is issued.
ranma42 commentedon Jun 24, 2022
The circularity is not very obvious (it looks like in all of the places where it is needed, the code already specifies an explicit type).
Additionally, it is quite unexpected that adding/removing
awaitchanges the circularity.RyanCavanaugh commentedon Jun 24, 2022
I don't know what to do with conversations like this. We can avoid some circularity errors by being clever, but there are limits to how much cleverness we're willing to spend. At the limit of that cleverness appears inconsistency. We could fix that by issuing more circularity errors rather than fewer, but I don't think that's what anyone's asking for.
If you want to dig into the code and send a bugfix-sized PR to address this case, that's totally fine, but we can't treat the issuance of circularity errors in the presence of circularities as a defect.
ranma42 commentedon Jun 24, 2022
I did not mean to just ask "fix my problem" and I probably failed to explain what I considered the wrong/weird behavior, sorry.
I agree that if my code is broken just because of circularity issues it is not worth fixing as a special case, but I would expect a consistent behavior between
awaitvs non-await.Instead, if the compiler error is a glitch introduced by (some internal desugaring of)
awaitit might be worth investigating.In the following snippets I am trying to explain the issue more in detail by expanding (my mental model of) what the compiler should infer about the code.
Note that I might be very wrong: this would explain why I do not understand the compiler responses; unfortunately the tools I tried (tsc/ide/playground) did not help me much, so I would appreciate a lot the insight of someone who knows the inner details of
tscand/or can provide a different mental model so that I can fix my expectation and reasoning.Without
awaiteverything works just as I would expect:With
awaitapparently my assumptions do not hold true anymore:Could you help me find out why it behaves differently with/without
await?I am not very knowledgeable of the internals of
tsc, but I would definitely love to help fixing this if it considered a bug in the handling ofawait😇 .RyanCavanaugh commentedon Jun 24, 2022
Please don't interpret any of this as impatience or offense; I'm not sure why this happens either and also find it odd!
What I'm trying to say is that circularity errors on circularities are not per se defects, and I'm not going to reallocate finite team resources away from fixing defects or implementing features toward addressing non-defects.
The investigation itself here is the resource spend that I'm trying to avoid. Figuring out why a thing happens is usually 80% of fixing it.
dustin-rcg commentedon Jun 24, 2022
awaitrecursively resolves promises, so it resolves the typesT,Promise <T>,Promise<Promise<T>>, ... etc. all down to justT, for someT. I guess that there is a recursive type definition relating to promises (e.g.Awaited<T>) in the typescript codebase, which is where the circularity comes from, but not visible in our code examples.ranma42 commentedon Jun 27, 2022
I tried reproducing it under this assumption and it looks the issue is not in the recursive type.
This does not compile even though the
awaithas been replaced with a (trivial) polymorphic identity function.Conversely, defining
would make the problem go away.
Another option is assigning a name to the result of
makeFoo(x); this compiles successfully:(the same code works even if
makeFooreturns aPromise).I guess that depends on how the type inference is driven (around loops and bindings?)
LoganDark commentedon May 30, 2024
Posting here because this lint can show up when using default arguments.
This doesn't happen in a class:
The solution is to move the default into the body of the function:
But in JS, the default argument is evaluated at call time, so it's not like Python where
Xis evaluated while the function object is being created. Even though I suppose it's possible to use something like an IIFE to call the function beforeXis finished initializing, TypeScript has enough flow analysis to make this an error:and this not an error:
Default arguments should be handled like that as well, as evaluated only at call time.
Let me know if I should open a separate GitHub issue for this... since if all flow analysis makes this incorrect assumption, that would be a separate issue. But I think it might be just this lint, which rejects code that would otherwise be accepted.
perhaps this is why... rangesFor() TS being weird...