Skip to content

Commit be960fa

Browse files
OzairPOrta
authored and
Orta
committed
Add related diagnostic to "used before defined" if type is a function that returns a union with undefined (microsoft#33171)
* Add "use before defined" diagnostic * Make "use before defined" diagnostic as related information to TS2454 * Add baseline tests for "use before defined" * Add test for type alias union with undefined for "use before defined" diagnostic * Update baselines
1 parent 47ec514 commit be960fa

7 files changed

+369
-1
lines changed

src/compiler/checker.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20190,7 +20190,26 @@ namespace ts {
2019020190
}
2019120191
}
2019220192
else if (!assumeInitialized && !(getFalsyFlags(type) & TypeFlags.Undefined) && getFalsyFlags(flowType) & TypeFlags.Undefined) {
20193-
error(node, Diagnostics.Variable_0_is_used_before_being_assigned, symbolToString(symbol));
20193+
const diag = error(node, Diagnostics.Variable_0_is_used_before_being_assigned, symbolToString(symbol));
20194+
20195+
// See GH:32846 - if the user is using a variable whose type is () => T1 | ... | undefined
20196+
// they may have meant to specify the type as (() => T1 | ...) | undefined
20197+
// This is assumed if: the type is a FunctionType, the return type is a Union, the last constituent of
20198+
// the union is `undefined`
20199+
if (type.symbol && type.symbol.declarations.length === 1 && isFunctionTypeNode(type.symbol.declarations[0])) {
20200+
const funcTypeNode = <FunctionTypeNode>type.symbol.declarations[0];
20201+
const returnType = getReturnTypeFromAnnotation(funcTypeNode);
20202+
if (returnType && returnType.flags & TypeFlags.Union) {
20203+
const unionTypes = (<UnionTypeNode>funcTypeNode.type).types;
20204+
if (unionTypes && unionTypes[unionTypes.length - 1].kind === SyntaxKind.UndefinedKeyword) {
20205+
const parenedFuncType = getMutableClone(funcTypeNode);
20206+
// Highlight to the end of the second to last constituent of the union
20207+
parenedFuncType.end = unionTypes[unionTypes.length - 2].end;
20208+
addRelatedInfo(diag, createDiagnosticForNode(parenedFuncType, Diagnostics.Did_you_mean_to_parenthesize_this_function_type));
20209+
}
20210+
}
20211+
}
20212+
2019420213
// Return the declared type to reduce follow-on errors
2019520214
return type;
2019620215
}

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,6 +1047,10 @@
10471047
"category": "Error",
10481048
"code": 1359
10491049
},
1050+
"Did you mean to parenthesize this function type?": {
1051+
"category": "Error",
1052+
"code": 1360
1053+
},
10501054

10511055
"The types of '{0}' are incompatible between these types.": {
10521056
"category": "Error",
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts(2,1): error TS2454: Variable 'a' is used before being assigned.
2+
tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts(6,1): error TS2454: Variable 'c' is used before being assigned.
3+
tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts(10,1): error TS2454: Variable 'd' is used before being assigned.
4+
tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts(13,1): error TS2454: Variable 'e' is used before being assigned.
5+
tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts(16,1): error TS2454: Variable 'f' is used before being assigned.
6+
tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts(19,1): error TS2454: Variable 'g' is used before being assigned.
7+
tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts(24,1): error TS2454: Variable 'h' is used before being assigned.
8+
tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts(28,1): error TS2454: Variable 'i' is used before being assigned.
9+
tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts(32,1): error TS2454: Variable 'j' is used before being assigned.
10+
tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts(36,1): error TS2454: Variable 'j' is used before being assigned.
11+
12+
13+
==== tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts (10 errors) ====
14+
let a: () => void | undefined;
15+
a; // Error did you mean to paren this function type
16+
~
17+
!!! error TS2454: Variable 'a' is used before being assigned.
18+
!!! related TS1360 tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts:1:8: Did you mean to parenthesize this function type?
19+
20+
function b (): void | undefined {}
21+
let c: typeof b;
22+
c;
23+
~
24+
!!! error TS2454: Variable 'c' is used before being assigned.
25+
26+
type Undefined = undefined
27+
let d: () => void | Undefined;
28+
d;
29+
~
30+
!!! error TS2454: Variable 'd' is used before being assigned.
31+
32+
let e: () => string | void | number | object | undefined;
33+
e; // Error did you mean to paren...
34+
~
35+
!!! error TS2454: Variable 'e' is used before being assigned.
36+
!!! related TS1360 tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts:12:8: Did you mean to parenthesize this function type?
37+
38+
let f: () => void;
39+
f;
40+
~
41+
!!! error TS2454: Variable 'f' is used before being assigned.
42+
43+
let g: () => undefined;
44+
g;
45+
~
46+
!!! error TS2454: Variable 'g' is used before being assigned.
47+
48+
type T1 = undefined | string;
49+
type T2 = undefined | void;
50+
let h: () => T1 & T2;
51+
h;
52+
~
53+
!!! error TS2454: Variable 'h' is used before being assigned.
54+
55+
type T3 = void | undefined;
56+
let i: () => T3;
57+
i;
58+
~
59+
!!! error TS2454: Variable 'i' is used before being assigned.
60+
61+
type T4 = () => void | undefined;
62+
let j: T4;
63+
j; // Error did you mean to paren...
64+
~
65+
!!! error TS2454: Variable 'j' is used before being assigned.
66+
!!! related TS1360 tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts:30:11: Did you mean to parenthesize this function type?
67+
68+
type T5 = () => void
69+
let k: T5 | undefined
70+
j;
71+
~
72+
!!! error TS2454: Variable 'j' is used before being assigned.
73+
!!! related TS1360 tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts:30:11: Did you mean to parenthesize this function type?
74+
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//// [functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts]
2+
let a: () => void | undefined;
3+
a; // Error did you mean to paren this function type
4+
5+
function b (): void | undefined {}
6+
let c: typeof b;
7+
c;
8+
9+
type Undefined = undefined
10+
let d: () => void | Undefined;
11+
d;
12+
13+
let e: () => string | void | number | object | undefined;
14+
e; // Error did you mean to paren...
15+
16+
let f: () => void;
17+
f;
18+
19+
let g: () => undefined;
20+
g;
21+
22+
type T1 = undefined | string;
23+
type T2 = undefined | void;
24+
let h: () => T1 & T2;
25+
h;
26+
27+
type T3 = void | undefined;
28+
let i: () => T3;
29+
i;
30+
31+
type T4 = () => void | undefined;
32+
let j: T4;
33+
j; // Error did you mean to paren...
34+
35+
type T5 = () => void
36+
let k: T5 | undefined
37+
j;
38+
39+
40+
//// [functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.js]
41+
var a;
42+
a; // Error did you mean to paren this function type
43+
function b() { }
44+
var c;
45+
c;
46+
var d;
47+
d;
48+
var e;
49+
e; // Error did you mean to paren...
50+
var f;
51+
f;
52+
var g;
53+
g;
54+
var h;
55+
h;
56+
var i;
57+
i;
58+
var j;
59+
j; // Error did you mean to paren...
60+
var k;
61+
j;
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
=== tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts ===
2+
let a: () => void | undefined;
3+
>a : Symbol(a, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 0, 3))
4+
5+
a; // Error did you mean to paren this function type
6+
>a : Symbol(a, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 0, 3))
7+
8+
function b (): void | undefined {}
9+
>b : Symbol(b, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 1, 2))
10+
11+
let c: typeof b;
12+
>c : Symbol(c, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 4, 3))
13+
>b : Symbol(b, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 1, 2))
14+
15+
c;
16+
>c : Symbol(c, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 4, 3))
17+
18+
type Undefined = undefined
19+
>Undefined : Symbol(Undefined, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 5, 2))
20+
21+
let d: () => void | Undefined;
22+
>d : Symbol(d, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 8, 3))
23+
>Undefined : Symbol(Undefined, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 5, 2))
24+
25+
d;
26+
>d : Symbol(d, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 8, 3))
27+
28+
let e: () => string | void | number | object | undefined;
29+
>e : Symbol(e, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 11, 3))
30+
31+
e; // Error did you mean to paren...
32+
>e : Symbol(e, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 11, 3))
33+
34+
let f: () => void;
35+
>f : Symbol(f, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 14, 3))
36+
37+
f;
38+
>f : Symbol(f, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 14, 3))
39+
40+
let g: () => undefined;
41+
>g : Symbol(g, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 17, 3))
42+
43+
g;
44+
>g : Symbol(g, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 17, 3))
45+
46+
type T1 = undefined | string;
47+
>T1 : Symbol(T1, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 18, 2))
48+
49+
type T2 = undefined | void;
50+
>T2 : Symbol(T2, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 20, 29))
51+
52+
let h: () => T1 & T2;
53+
>h : Symbol(h, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 22, 3))
54+
>T1 : Symbol(T1, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 18, 2))
55+
>T2 : Symbol(T2, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 20, 29))
56+
57+
h;
58+
>h : Symbol(h, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 22, 3))
59+
60+
type T3 = void | undefined;
61+
>T3 : Symbol(T3, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 23, 2))
62+
63+
let i: () => T3;
64+
>i : Symbol(i, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 26, 3))
65+
>T3 : Symbol(T3, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 23, 2))
66+
67+
i;
68+
>i : Symbol(i, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 26, 3))
69+
70+
type T4 = () => void | undefined;
71+
>T4 : Symbol(T4, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 27, 2))
72+
73+
let j: T4;
74+
>j : Symbol(j, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 30, 3))
75+
>T4 : Symbol(T4, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 27, 2))
76+
77+
j; // Error did you mean to paren...
78+
>j : Symbol(j, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 30, 3))
79+
80+
type T5 = () => void
81+
>T5 : Symbol(T5, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 31, 2))
82+
83+
let k: T5 | undefined
84+
>k : Symbol(k, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 34, 3))
85+
>T5 : Symbol(T5, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 31, 2))
86+
87+
j;
88+
>j : Symbol(j, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 30, 3))
89+
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
=== tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts ===
2+
let a: () => void | undefined;
3+
>a : () => void | undefined
4+
5+
a; // Error did you mean to paren this function type
6+
>a : () => void | undefined
7+
8+
function b (): void | undefined {}
9+
>b : () => void | undefined
10+
11+
let c: typeof b;
12+
>c : () => void | undefined
13+
>b : () => void | undefined
14+
15+
c;
16+
>c : () => void | undefined
17+
18+
type Undefined = undefined
19+
>Undefined : undefined
20+
21+
let d: () => void | Undefined;
22+
>d : () => void | undefined
23+
24+
d;
25+
>d : () => void | undefined
26+
27+
let e: () => string | void | number | object | undefined;
28+
>e : () => string | number | void | object | undefined
29+
30+
e; // Error did you mean to paren...
31+
>e : () => string | number | void | object | undefined
32+
33+
let f: () => void;
34+
>f : () => void
35+
36+
f;
37+
>f : () => void
38+
39+
let g: () => undefined;
40+
>g : () => undefined
41+
42+
g;
43+
>g : () => undefined
44+
45+
type T1 = undefined | string;
46+
>T1 : T1
47+
48+
type T2 = undefined | void;
49+
>T2 : void | undefined
50+
51+
let h: () => T1 & T2;
52+
>h : () => undefined
53+
54+
h;
55+
>h : () => undefined
56+
57+
type T3 = void | undefined;
58+
>T3 : void | undefined
59+
60+
let i: () => T3;
61+
>i : () => void | undefined
62+
63+
i;
64+
>i : () => void | undefined
65+
66+
type T4 = () => void | undefined;
67+
>T4 : T4
68+
69+
let j: T4;
70+
>j : T4
71+
72+
j; // Error did you mean to paren...
73+
>j : T4
74+
75+
type T5 = () => void
76+
>T5 : T5
77+
78+
let k: T5 | undefined
79+
>k : T5 | undefined
80+
81+
j;
82+
>j : T4
83+
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// @strictNullChecks: true
2+
3+
let a: () => void | undefined;
4+
a; // Error did you mean to paren this function type
5+
6+
function b (): void | undefined {}
7+
let c: typeof b;
8+
c;
9+
10+
type Undefined = undefined
11+
let d: () => void | Undefined;
12+
d;
13+
14+
let e: () => string | void | number | object | undefined;
15+
e; // Error did you mean to paren...
16+
17+
let f: () => void;
18+
f;
19+
20+
let g: () => undefined;
21+
g;
22+
23+
type T1 = undefined | string;
24+
type T2 = undefined | void;
25+
let h: () => T1 & T2;
26+
h;
27+
28+
type T3 = void | undefined;
29+
let i: () => T3;
30+
i;
31+
32+
type T4 = () => void | undefined;
33+
let j: T4;
34+
j; // Error did you mean to paren...
35+
36+
type T5 = () => void
37+
let k: T5 | undefined
38+
j;

0 commit comments

Comments
 (0)