Comments (11)
@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):
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.
يعتمد النوع المستنبط من (بعد التعيين) على نوع .
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.
This is working as intended / a design limitation. If you search for the error message you will find many duplicates.
from typescript.
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.
The inferred type of
current
(after assignment) depends on the type ofhead
.
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 asany
, so no implicitany
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.
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.
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.
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.
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 tonumber
based on the type of the assigned expression (123
, akanumber
)?
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] = ...
.
Either you omit the type annotation, in which case the type of
head
is simplyany
(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.
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.
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)
- `tryGetPerformanceHooks` throws on Stackblitz with ts 5.5.3 HOT 3
- Avoid providing promise methods as completions at return positions in non-async functions
- Consider disallowing `in` operator use with arbitrary key operands on closed types for which the keys are known HOT 2
- Running type checker produces js out of memory error HOT 2
- Callback argument type not inferred for union of interfaces HOT 1
- Como Hackear Instagram 2024 Hackear Instagram Metodos Atualizados #221857
- [transpileDeclaration API] ts.transpileDeclaration always returns empty sourceMapText
- Incorrect Error Message: "Cannot assign to 'name' because it is a constant" Instead of "Identifier 'name' is not defined" for Undefined Identifier Assignment HOT 2
- Generic with extends yields to different type than directly specifying it HOT 5
- Instantiation expressions don't reject incompatible signatures in situations with mixed call and construct signatures HOT 1
- Parameters of overridden methods are made implicit any when using JSDoc `@override` HOT 13
- Auto complete and auto import not work (ReactJS, ChakraUI)
- 'TypeScript and JavaScript Language Features' Formatter hangs HOT 3
- Mapped type of a conditional type unexpectedly fails inference
- Returing a generic argument from a function allows returning a wrong type HOT 1
- Module resolution behaves differently when typeRoots is specified
- Ability to type check setters in interface implementations HOT 2
- ⚡ Performance: Project service spends excess time cleaning client files when called synchronously HOT 2
- ⚡ Performance: Project service doesn't cache all fs.statSync HOT 18
- jsxRuntime pragma does not work
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from typescript.