-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Control flow based type guards #6959
Closed
Closed
Changes from all commits
Commits
Show all changes
67 commits
Select commit
Hold shift + click to select a range
42a8efd
Merge remote-tracking branch 'Microsoft/master'
ivogabe 80f42ed
Merge remote-tracking branch 'Microsoft/master'
ivogabe 57f5d5c
Merge branch 'master' of https://github.com/Microsoft/TypeScript
ivogabe 163291f
Merge remote-tracking branch 'Microsoft/master' into guards
ivogabe 441c999
Add flow markers to binder
ivogabe 522a99c
Add BranchFlow for type guards
ivogabe 78ca7b5
Add caching of identifier narrowing in binder
ivogabe 1fb2db6
Implement flow based type guards in checker
ivogabe f1c9a82
Narrow assignments to unions only
ivogabe d3dd1fa
Make isDeclarationName a weak type guard for Identifiers
ivogabe 702edac
Remove trailing space
ivogabe 8670014
Make getNarrowedTypeOfSymbol work on all nodes again
ivogabe ecbf9e2
Merge remote-tracking branch 'Microsoft/master' into guards
ivogabe c1abed2
Merge remote-tracking branch 'Microsoft/master' into guards
ivogabe 905a240
Bind flow of loops and named labels
ivogabe b8c0e2c
Fix flow based type guards in checker
ivogabe f6dc119
Merge remote-tracking branch 'Microsoft/master' into guards
ivogabe b385472
Fix errors after merge
ivogabe 6f1d84e
Remove restriction on type narrowing with assignments in loops
ivogabe ffa8db3
Fix assignment checking
ivogabe 844a8c1
Remove flowIndex, use getNodeId
ivogabe ff1b2d6
Remove PreviousOccurency interface
ivogabe 6c7c678
Fix typo
ivogabe 7d2daf9
Merge remote-tracking branch 'Microsoft/master' into guards
ivogabe 035d973
Fix binding of condition less for statement
ivogabe b2e51a3
Handle narrowing in var declarations
ivogabe 2747c98
Rename `showError` to `reportError`
ivogabe b58eb60
Merge remote-tracking branch 'Microsoft/master' into guards
ivogabe 3f0c283
Fix typos after merge
ivogabe a30fa8f
Merge master
ivogabe ac2f547
Remove old assertion
ivogabe 0a91991
Keep narrowed types in a loop
ivogabe a44de51
Merge remote-tracking branch 'Microsoft/master' into guards
ivogabe 67913d8
Fix narrowing of union type assigned to a union type
ivogabe f15291f
Merge remote-tracking branch 'Microsoft/master' into guards
ivogabe 26a4d37
Merge remote-tracking branch 'Microsoft/master' into guards
ivogabe 074a05d
Add more caching
ivogabe 2c57ee5
Use cache to speed up multiple paths to same node in flow graph
ivogabe 927c562
Don't error when assigning to narrowed variable using destructuring
ivogabe a4b91f3
Merge remote-tracking branch 'Microsoft/master' into guards
ivogabe 8673a46
Narrow after destructuring assignment
ivogabe 09e4302
Rewrite recursion to loop, reduces check time
ivogabe 1037efa
Merge remote-tracking branch 'Microsoft/master' into guards
ivogabe 8331915
Fix issues with destructuring assignments
ivogabe 8d4cea4
Merge remote-tracking branch 'Microsoft/master' into guards
ivogabe c128582
Fix narrowing of JSX tags
ivogabe 19b3f90
Use initial type when narrowing resulted in empty type
ivogabe 59b543f
Move narrowing info from node to NodeLinks
ivogabe c0061c0
Allow narrowing in nested functions
ivogabe 096c70a
Narrow after destructuring assignments with initializers
ivogabe fa77df5
Merge remote-tracking branch 'Microsoft/master' into guards
ivogabe 46786b3
Fix narrowing in for of statement
ivogabe 3a7575b
Stop narrowing with guards that contain assignments
ivogabe b9f5b79
Add control flow tests
ivogabe 21efd00
Add test for narrowing after assignment
ivogabe d5fbd4e
Modify existing tests for new type guards behavior
ivogabe f8a1827
Add more tests
ivogabe 7cceb0c
Remove redundant bindFlowMarker calls
ivogabe bea32e2
Fix tests
ivogabe a659239
Accept baseline
ivogabe 9e13d0f
Merge remote-tracking branch 'Microsoft/master' into guards
ivogabe 3490979
Update tests
ivogabe a719858
Merge remote-tracking branch 'Microsoft/master' into guards
ivogabe 90a678d
Implement flow based type guards for this expression and dotted names
ivogabe 7646af1
Fix unresolved symbols, wrong caching and empty type issues
ivogabe 4a76fe2
Accept new baselines
ivogabe 16555dd
Stop narrowing of dotted name after assignment of parent type
ivogabe File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
//// [assignmentTypeNarrowing.ts] | ||
let x: string | number | boolean | RegExp; | ||
|
||
x = ""; | ||
x; // string | ||
|
||
[x] = [true]; | ||
x; // boolean | ||
|
||
[x = ""] = [1]; | ||
x; // string | number | ||
|
||
({x} = {x: true}); | ||
x; // boolean | ||
|
||
({y: x} = {y: 1}); | ||
x; // number | ||
|
||
({x = ""} = {x: true}); | ||
x; // string | boolean | ||
|
||
({y: x = /a/} = {y: 1}); | ||
x; // number | RegExp | ||
|
||
let a: string[]; | ||
|
||
for (x of a) { | ||
x; // string | ||
} | ||
|
||
|
||
//// [assignmentTypeNarrowing.js] | ||
var x; | ||
x = ""; | ||
x; // string | ||
x = [true][0]; | ||
x; // boolean | ||
_a = [1][0], x = _a === void 0 ? "" : _a; | ||
x; // string | number | ||
(_b = { x: true }, x = _b.x, _b); | ||
x; // boolean | ||
(_c = { y: 1 }, x = _c.y, _c); | ||
x; // number | ||
(_d = { x: true }, _e = _d.x, x = _e === void 0 ? "" : _e, _d); | ||
x; // string | boolean | ||
(_f = { y: 1 }, _g = _f.y, x = _g === void 0 ? /a/ : _g, _f); | ||
x; // number | RegExp | ||
var a; | ||
for (var _i = 0, a_1 = a; _i < a_1.length; _i++) { | ||
x = a_1[_i]; | ||
x; // string | ||
} | ||
var _a, _b, _c, _d, _e, _f, _g; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
=== tests/cases/conformance/expressions/assignmentOperator/assignmentTypeNarrowing.ts === | ||
let x: string | number | boolean | RegExp; | ||
>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 0, 3)) | ||
>RegExp : Symbol(RegExp, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) | ||
|
||
x = ""; | ||
>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 0, 3)) | ||
|
||
x; // string | ||
>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 0, 3)) | ||
|
||
[x] = [true]; | ||
>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 0, 3)) | ||
|
||
x; // boolean | ||
>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 0, 3)) | ||
|
||
[x = ""] = [1]; | ||
>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 0, 3)) | ||
|
||
x; // string | number | ||
>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 0, 3)) | ||
|
||
({x} = {x: true}); | ||
>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 11, 2)) | ||
>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 11, 8)) | ||
|
||
x; // boolean | ||
>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 0, 3)) | ||
|
||
({y: x} = {y: 1}); | ||
>y : Symbol(y, Decl(assignmentTypeNarrowing.ts, 14, 2)) | ||
>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 0, 3)) | ||
>y : Symbol(y, Decl(assignmentTypeNarrowing.ts, 14, 11)) | ||
|
||
x; // number | ||
>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 0, 3)) | ||
|
||
({x = ""} = {x: true}); | ||
>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 17, 2)) | ||
>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 17, 13)) | ||
|
||
x; // string | boolean | ||
>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 0, 3)) | ||
|
||
({y: x = /a/} = {y: 1}); | ||
>y : Symbol(y, Decl(assignmentTypeNarrowing.ts, 20, 2)) | ||
>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 0, 3)) | ||
>y : Symbol(y, Decl(assignmentTypeNarrowing.ts, 20, 17)) | ||
|
||
x; // number | RegExp | ||
>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 0, 3)) | ||
|
||
let a: string[]; | ||
>a : Symbol(a, Decl(assignmentTypeNarrowing.ts, 23, 3)) | ||
|
||
for (x of a) { | ||
>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 0, 3)) | ||
>a : Symbol(a, Decl(assignmentTypeNarrowing.ts, 23, 3)) | ||
|
||
x; // string | ||
>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 0, 3)) | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
=== tests/cases/conformance/expressions/assignmentOperator/assignmentTypeNarrowing.ts === | ||
let x: string | number | boolean | RegExp; | ||
>x : string | number | boolean | RegExp | ||
>RegExp : RegExp | ||
|
||
x = ""; | ||
>x = "" : string | ||
>x : string | number | boolean | RegExp | ||
>"" : string | ||
|
||
x; // string | ||
>x : string | ||
|
||
[x] = [true]; | ||
>[x] = [true] : [boolean] | ||
>[x] : [string | number | boolean | RegExp] | ||
>x : string | number | boolean | RegExp | ||
>[true] : [boolean] | ||
>true : boolean | ||
|
||
x; // boolean | ||
>x : boolean | ||
|
||
[x = ""] = [1]; | ||
>[x = ""] = [1] : [number] | ||
>[x = ""] : [string] | ||
>x = "" : string | ||
>x : string | number | boolean | RegExp | ||
>"" : string | ||
>[1] : [number] | ||
>1 : number | ||
|
||
x; // string | number | ||
>x : string | number | ||
|
||
({x} = {x: true}); | ||
>({x} = {x: true}) : { x: boolean; } | ||
>{x} = {x: true} : { x: boolean; } | ||
>{x} : { x: string | number | boolean | RegExp; } | ||
>x : string | number | boolean | RegExp | ||
>{x: true} : { x: boolean; } | ||
>x : boolean | ||
>true : boolean | ||
|
||
x; // boolean | ||
>x : boolean | ||
|
||
({y: x} = {y: 1}); | ||
>({y: x} = {y: 1}) : { y: number; } | ||
>{y: x} = {y: 1} : { y: number; } | ||
>{y: x} : { y: string | number | boolean | RegExp; } | ||
>y : string | number | boolean | RegExp | ||
>x : string | number | boolean | RegExp | ||
>{y: 1} : { y: number; } | ||
>y : number | ||
>1 : number | ||
|
||
x; // number | ||
>x : number | ||
|
||
({x = ""} = {x: true}); | ||
>({x = ""} = {x: true}) : { x?: boolean; } | ||
>{x = ""} = {x: true} : { x?: boolean; } | ||
>{x = ""} : { x?: string | number | boolean | RegExp; } | ||
>x : string | number | boolean | RegExp | ||
>{x: true} : { x?: boolean; } | ||
>x : boolean | ||
>true : boolean | ||
|
||
x; // string | boolean | ||
>x : string | boolean | ||
|
||
({y: x = /a/} = {y: 1}); | ||
>({y: x = /a/} = {y: 1}) : { y?: number; } | ||
>{y: x = /a/} = {y: 1} : { y?: number; } | ||
>{y: x = /a/} : { y?: RegExp; } | ||
>y : RegExp | ||
>x = /a/ : RegExp | ||
>x : string | number | boolean | RegExp | ||
>/a/ : RegExp | ||
>{y: 1} : { y?: number; } | ||
>y : number | ||
>1 : number | ||
|
||
x; // number | RegExp | ||
>x : number | RegExp | ||
|
||
let a: string[]; | ||
>a : string[] | ||
|
||
for (x of a) { | ||
>x : string | number | boolean | RegExp | ||
>a : string[] | ||
|
||
x; // string | ||
>x : string | ||
} | ||
|
22 changes: 22 additions & 0 deletions
22
tests/baselines/reference/controlFlowAssignmentExpression.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
//// [controlFlowAssignmentExpression.ts] | ||
let x: string | boolean | number; | ||
let obj: any; | ||
|
||
x = ""; | ||
x = x.length; | ||
x; // number | ||
|
||
x = true; | ||
(x = "", obj).foo = (x = x.length); | ||
x; // number | ||
|
||
|
||
//// [controlFlowAssignmentExpression.js] | ||
var x; | ||
var obj; | ||
x = ""; | ||
x = x.length; | ||
x; // number | ||
x = true; | ||
(x = "", obj).foo = (x = x.length); | ||
x; // number |
33 changes: 33 additions & 0 deletions
33
tests/baselines/reference/controlFlowAssignmentExpression.symbols
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
=== tests/cases/conformance/controlFlow/controlFlowAssignmentExpression.ts === | ||
let x: string | boolean | number; | ||
>x : Symbol(x, Decl(controlFlowAssignmentExpression.ts, 0, 3)) | ||
|
||
let obj: any; | ||
>obj : Symbol(obj, Decl(controlFlowAssignmentExpression.ts, 1, 3)) | ||
|
||
x = ""; | ||
>x : Symbol(x, Decl(controlFlowAssignmentExpression.ts, 0, 3)) | ||
|
||
x = x.length; | ||
>x : Symbol(x, Decl(controlFlowAssignmentExpression.ts, 0, 3)) | ||
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --)) | ||
>x : Symbol(x, Decl(controlFlowAssignmentExpression.ts, 0, 3)) | ||
>length : Symbol(String.length, Decl(lib.d.ts, --, --)) | ||
|
||
x; // number | ||
>x : Symbol(x, Decl(controlFlowAssignmentExpression.ts, 0, 3)) | ||
|
||
x = true; | ||
>x : Symbol(x, Decl(controlFlowAssignmentExpression.ts, 0, 3)) | ||
|
||
(x = "", obj).foo = (x = x.length); | ||
>x : Symbol(x, Decl(controlFlowAssignmentExpression.ts, 0, 3)) | ||
>obj : Symbol(obj, Decl(controlFlowAssignmentExpression.ts, 1, 3)) | ||
>x : Symbol(x, Decl(controlFlowAssignmentExpression.ts, 0, 3)) | ||
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --)) | ||
>x : Symbol(x, Decl(controlFlowAssignmentExpression.ts, 0, 3)) | ||
>length : Symbol(String.length, Decl(lib.d.ts, --, --)) | ||
|
||
x; // number | ||
>x : Symbol(x, Decl(controlFlowAssignmentExpression.ts, 0, 3)) | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see this brand applied anywhere - is there a reason for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If a type guard returns false, it is expected that the argument is not of the specified type. However,
isDeclarationName
can return false whenname
is an identifier. This wasn't an issue, asisDeclarationName
was never used in an if statement with an else. However, with control flow based type guards, this wasn't working anymore. The then-block of an if statement contained a return, thus the statements after the if were the else branch of the if. It might not be the best way to solve this, but it's working..