GithubHelp home page GithubHelp logo

Type narrowing not work about typescript HOT 11 CLOSED

nuintun avatar nuintun commented on July 25, 2024
Type narrowing not work

from typescript.

Comments (11)

rotu avatar rotu commented on July 25, 2024 1

@MartinJohns I'm not sure that's the case and I think this deserves a closer look. If you still think it's a duplicate, can you mention the particular issue it duplicates?

The type of head can be inferred. In fact, an identical loop typechecks properly in the above provided case getLeadingKeys2, the only difference being a preceding if statement which does not touch the first head variable. Why doesn't the same behavior happen in getLeadingKeys2?

Also it typechecks properly if you merely reassign head in the loop instead of declaring it there (though something different may be going on there - it might be inferring head as any and narrowing late based on CFA):

Playground Link

interface Tree {
  key: string;
  isLeaf?: boolean;
  children?: Tree[];
}

export function getLeadingKeys(roots: Tree[] = []): string[] {
  const keys: string[] = [];

  let current: Tree[] | undefined = roots;
  let head;

  while (current && current.length > 0) {
    ([head] = current);

    keys.push(head.key);

    current = head.children;
  }

  return keys;
}

from typescript.

AlQubaiisi avatar AlQubaiisi commented on July 25, 2024 1

يعتمد النوع المستنبط من (بعد التعيين) على نوع .current``head

هذا هو الجزء الذي لا أتحسبه. يحتوي على تعليق توضيحي للنوع - لا يتم الاستدلال على نوعه أبدا.current

إذا أضفت عبارة مباشرة كما هو الحال في الحلقة ، فإن ذلك يتسبب في تغيير نوع - بدلا من ذلك ، يشكو المترجم من مهمة غير صالحة.current = 42;``current

يعمل رابط الملعب الجديد الخاص بك لأنه لا يحتوي على تعليق توضيحي من النوع ويتم كتابته ك ، لذلك لا يتم تقديم أي ضمني بواسطة التهيئة.head``any``any

يبدو أنك تشير إلى أنه يتم تقديم ضمني بواسطة على الرغم من الإعلان عنه بتعليق توضيحي من النوع.any``const [head] = current``current

ولكن ربما كان المثال الخاص بي أكثر تعقيدا من كونه مفيدا. كيف ، في المثال الثاني الأصلي ، هل يؤدي إلغاء الحلقة يدويا بواسطة واحد إلى منع المشكلة؟while

export function getLeadingKeys2(roots: Tree[] = []): string[] {
  const keys: string[] = [];

  let current: Tree[] | undefined = roots;

  if (current && current.length > 0) {
    const [head] = current;

    keys.push(head.key);

    current = head.children;
  }

  while (current && current.length > 0) {
    const [head] = current;

    keys.push(head.key);

    current = head.children;
  }

  return keys;
}

interface Tree {
key: string;
isLeaf?: boolean;
children?: Tree[];
}

export function getLeadingKeys(roots: Tree[] = []): string[] {
const keys: string[] = [];

let current: Tree[] | undefined = roots;
let head;

while (current && current.length > 0) {
([head] = current);

keys.push(head.key);

current = head.children;

}

return keys;
}

from typescript.

MartinJohns avatar MartinJohns commented on July 25, 2024

This is working as intended / a design limitation. If you search for the error message you will find many duplicates.

from typescript.

MartinJohns avatar MartinJohns commented on July 25, 2024

The inferred type of head depends on current. The inferred type of current (after assignment) depends on the type of head. So head indirectly references itself in it's initializer, as the error tells you.

Your new playground link works because head has no type annotation and is typed as any, so no implicit any is introduced by the initialization.

from typescript.

rotu avatar rotu commented on July 25, 2024

The inferred type of current (after assignment) depends on the type of head.

This is the part I don't grok. current has a type annotation - its type is never inferred.

If I add a straightforward statement like current = 42; in the loop, that causes a change in the type of current - instead the compiler complains about an invalid assignment.

Your new playground link works because head has no type annotation and is typed as any, so no implicit any is introduced by the initialization.

You seem to suggest that an implicit any is introduced by const [head] = current even though current was declared with a type annotation.

But perhaps my example was more complicated than helpful. How, in the original second example, does manually unrolling the while loop by one prevent the issue?

export function getLeadingKeys2(roots: Tree[] = []): string[] {
  const keys: string[] = [];

  let current: Tree[] | undefined = roots;

  if (current && current.length > 0) {
    const [head] = current;

    keys.push(head.key);

    current = head.children;
  }

  while (current && current.length > 0) {
    const [head] = current;

    keys.push(head.key);

    current = head.children;
  }

  return keys;
}

from typescript.

MartinJohns avatar MartinJohns commented on July 25, 2024

This is the part I don't grok. current has a type annotation - its type is never inferred.

Variables are narrowed upon assignment. The type depends on the expression. In your example the type depends on head.

from typescript.

rotu avatar rotu commented on July 25, 2024

Variables are narrowed upon assignment. The type depends on the expression. In your example the type depends on head.

Okay. I'm not 100% on what you're saying, but I think you're saying that inside the loop body, current has a different type. And this different type is circular. This seems supported by the fact that, replacing the annotation on current with one not narrowed by the loop condition (Tree[] instead of Tree[] | undefined) causes the code again to pass. Playground Link.

This still doesn't explain why the declaration of head matters. When declared outside the loop, the post-assignment type of head is just as circular as when head is both declared and assigned inside the loop.

from typescript.

MartinJohns avatar MartinJohns commented on July 25, 2024

I'm not 100% on what you're saying, but I think you're saying that inside the loop body, current has a different type.

Consider this example:

let v: number | undefined;
v = 123;

Right after the assignment, is the type of v number | undefined like the type annotation, or has it been narrowed to number based on the type of the assigned expression (123, aka number)?

When your type is not a union anymore this narrowing does not happen, as it's always the same type.

This still doesn't explain why the declaration of head matters.

I'm not sure what you mean. Either you omit the type annotation, in which case the type of head is simply any (as mentioned before), or you have an explicit type annotation , in which case the error does not apply anymore (it mentions adding a type annotation).

from typescript.

rotu avatar rotu commented on July 25, 2024

Consider this example:

let v: number | undefined;
v = 123;

Right after the assignment, is the type of v number | undefined like the type annotation, or has it been narrowed to number based on the type of the assigned expression (123, aka number)?

After the assignment, v has type number. But v still "remembers" its number|undefined annotation, as shown when you assign any to it.

let v: number | undefined;
v = 123;
void v
//   ^? let v: number
v = (null as any)
void v
//   ^? let v: number | undefined

When your type is not a union anymore this narrowing does not happen, as it's always the same type.

This still doesn't explain why the declaration of head matters.

I'm not sure what you mean.

What I mean is that let head; ...; const [head] = ... declares an identifier without declaring its type and yet the compiler infers the type (more specific than the implicit any) and provides contextual help. This is the same developer experience I'd expect with const [head] = ....

image

Either you omit the type annotation, in which case the type of head is simply any (as mentioned before), or you have an explicit type annotation

That does not match my experience.

With no type annotation, (let head;), then after the assignment, intellisense shows let head: Tree. Is this considered something other than "type narrowing" (since the type of head is not a union type)?

With an "explicit any" (let head: any;), then after assignment, intellisense shows let head: any.

in which case the error does not apply anymore (it mentions adding a type annotation).

Unfortunately, AFAICT, adding a type annotation to const [head] = ... is not straightforward (const [head:Tree] = current). I'm guessing there's some syntactic issue here, but const head:Tree = current[0] does work.

Narrowing does seem to happen if I declare head with the union type let head: Tree | undefined;. I would expect to see an error again, but I don't!

from typescript.

fatcerberus avatar fatcerberus commented on July 25, 2024

Unfortunately, AFAICT, adding a type annotation to const [head] = ... is not straightforward (const [head:Tree] = current).

For future reference the way to add a type annotation to a destructuring assignment is

const [head]: [Tree] = current;
const { foo, bar }: { foo: string, bar: string } = whatever;

from typescript.

typescript-bot avatar typescript-bot commented on July 25, 2024

This issue has been marked as "Duplicate" and has seen no recent activity. It has been automatically closed for house-keeping purposes.

from typescript.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.