1

I want to know why errors occur, not how to get rid of them. I understand that I can use the as syntax to eliminate the error.

Please tell me why the error occurs. My understanding is that neither variable v2 nor v3 should have any elements that would prevent type identification. Variable v2 is definitely a variable of type string of PROMISE. Variable v3 is definitely an instance of class Hoge2. Please tell me why the error occurs because I want to learn typescript!

https://www.typescriptlang.org/play?ts=4.9.4#code/IYZwngdgxgBAZgV2gFwJYHsI2QUxMgCgEoYBvAWACgYYAbHZGANwEYAuGfAJ1QgHMYAHxgQEtWjAC8IsbQDcVGgHcAFqnowCyLghwkK1GkaiZ8zAExSYwJcFSMV6PjgKsiCw0ZgmIZpgGYrCBwlGAAJJxxzV3N3RS9mFisAgDoABy50NI8aAF8qfMpQSFhEFAwsR2cCAH0Obl4BYVFxIgMaLgYELiwAIl6PQqhaUBBwyPN271NtBChkdC4CDNQmYFwYTuAAE0xaMBg64AgwNsKaZ0YMrOIpjq6emH6cmELcoA

async function test() {
  let v1: string | null = null;
  while (true) {
      const v2 = await hoge(v1);
      const v3 = new Hoge2(v2);
      v1 = v3.prop;
  }
}
async function hoge(_: string | null){
  return "";
}
class Hoge2{
  constructor(private readonly _:any){}
  get prop(){
    return "";
  }
}

'v2' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer

'v3' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.

1
  • 1
    I found an issue on typescript github. I don't understand the logic, but it was a limitation of the current typescript spec. I'm closing it now that I found the cause. github.com/microsoft/TypeScript/issues/30308 Commented Jan 21, 2023 at 17:12

5 Answers 5

2

This is definitely a very strange problem. I found that by removing the await from const v2 = await hoge(v1); (turning it into const v2 = hoge(v1);), both type errors are resolved and the implicit typing recognized by intellisense has the correct types (v2 as Promise<string> and v3 as Hoge2). I'm not sure why the await on an async method call creates this problem, especially since it creates the problem for both variables, not just for the one that is calling the async method. Sorry I don't have a complete answer, but you certainly seem to be making good progress on learning TypeScript to want the answer to this question.

Sign up to request clarification or add additional context in comments.

Comments

0

There is a lot of interesting behaviour to study in this small piece of code. I have tried to investigate myself and found my understanding of the problem evolving multiple times. Wich is why I have edited my answer.


A big part of the reason you are receiving these errors, is because you have the setting noImplicitAny configured to be true in your .tsconfig file.

When you write:

const v2 = await hoge(v1);
const v3 = new Hoge2(v2);

You do not have type annotations for the constants v2 and v3. This causes them to implicitly be of any type.

This shouldn't be a problem because the actual values are never (and can't be) of a type they are not allowed to be. However if we take a close look at the actual error message:

'v2' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer

  //We initialize v2 using v1, which is initially false
  const v2 = await hoge(v1);

  //We then initialize v2 using v3
  const v3 = new Hoge2(v2);

Both v2 and v3 are "implicitly any" but they are not referenced directly nor indirectly in their own initializer.

However, once we add v1 = v3.prop, that is no longer the case, as this is all happening inside a while loop.

  //You could look at it like this:
  v1 = v3.prop;
  v2 = hoge(v1);
  v3 = new Hoge(v2);

You can clearly see how the statement "is referenced directly or indirectly in its own initializer" is true for both v2 and v3.


I think that explains the OPs question.


However, the problem is made more complex when you look at the various ways you can make the errors go away:

If you where to typecast v2 for example, I honestly would expect this error to still be thrown for v3...

3 Comments

v2 and v3 are not implicitly of type any, their implicit typing is supposed to come from their definition. OP's problem is why these variables are not finding the correct implicit typing in this situation, and for that I have no answer.
I thought they were "implicitly any" because there is no type definition? If they were annotated like const v2 : string they would "explicitly be strings".
An implicit any is when a variable both is not type annotated AND the type cannot be inferred from usage. const v2: string = 'test' is explicitly a string, const v2 = 'test' is implicitly a string, const v2: any = 'test' is explicitly any. Implicit any is actually hard to give a short example of, but using a function defined in a JavaScript file from a TypeScript file (without a type annotation on the response) would be an implicit any because the JavaScript basically doesn't know what types are. I found that removing the await from OP's code somehow fixes the type inferences.
0

Define a type for v3 such as

type V3={
    prop: string      
}

then change this line

const v3 = new Hoge2(v2);
to
const v3:V3 = new Hoge2(v2);

1 Comment

I know how to modify the script to fix the problem. I would like to know if this error is a false positive for an error that is occurring in a technical typescript convention, or if it is a correct error caused by a javascript/typescript convention that I am not aware of.
0

When looking at await hoge(v1), on the one hand the type of v1 is null (initialization value), on the other hand, because of the loop, it's type is whatever the v3.prop expression returns.

Typescript can't seem to handle this circle (v1 => v2 => v3 => v1) if v1's type is determined in two places, although on paper there are no type conflicts.

And indeed, if you don't initialize v1 with a concrete value, the error disappears:

async function test() {
  let v1: string | null | undefined;
  while (true) {
      const v2 = await hoge(v1);
      const v3 = new Hoge2(v2);
      v1 = v3.prop;
  }
}
async function hoge(_?: string | null){
  return "";
}
class Hoge2{
  constructor(private readonly _:any){}
  get prop(){
    return "";
  }
}

Typescript playground

Comments

0

I think you must have already got your answer from above codes, i just wanted to add additional way, also you can add :any type to v2 or v3, because of indirect assigned to each other

while (true) {
  const v2: any = await hoge(v1);
  const v3 = new Hoge2(v2);
  v1 = v3.prop;
}

or

while (true) {
  const v2 = await hoge(v1);
  const v3: any = new Hoge2(v2);
  v1 = v3.prop;
}

Comments

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.