Skip to content

Add related diagnostic to "used before defined" if type is a function that returns a union with undefined #33171

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

Merged
merged 6 commits into from
Nov 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20190,7 +20190,26 @@ namespace ts {
}
}
else if (!assumeInitialized && !(getFalsyFlags(type) & TypeFlags.Undefined) && getFalsyFlags(flowType) & TypeFlags.Undefined) {
error(node, Diagnostics.Variable_0_is_used_before_being_assigned, symbolToString(symbol));
const diag = error(node, Diagnostics.Variable_0_is_used_before_being_assigned, symbolToString(symbol));

// See GH:32846 - if the user is using a variable whose type is () => T1 | ... | undefined
// they may have meant to specify the type as (() => T1 | ...) | undefined
// This is assumed if: the type is a FunctionType, the return type is a Union, the last constituent of
// the union is `undefined`
if (type.symbol && type.symbol.declarations.length === 1 && isFunctionTypeNode(type.symbol.declarations[0])) {
const funcTypeNode = <FunctionTypeNode>type.symbol.declarations[0];
const returnType = getReturnTypeFromAnnotation(funcTypeNode);
if (returnType && returnType.flags & TypeFlags.Union) {
const unionTypes = (<UnionTypeNode>funcTypeNode.type).types;
if (unionTypes && unionTypes[unionTypes.length - 1].kind === SyntaxKind.UndefinedKeyword) {
const parenedFuncType = getMutableClone(funcTypeNode);
// Highlight to the end of the second to last constituent of the union
parenedFuncType.end = unionTypes[unionTypes.length - 2].end;
addRelatedInfo(diag, createDiagnosticForNode(parenedFuncType, Diagnostics.Did_you_mean_to_parenthesize_this_function_type));
}
}
}

// Return the declared type to reduce follow-on errors
return type;
}
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1047,6 +1047,10 @@
"category": "Error",
"code": 1359
},
"Did you mean to parenthesize this function type?": {
"category": "Error",
"code": 1360
},

"The types of '{0}' are incompatible between these types.": {
"category": "Error",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts(2,1): error TS2454: Variable 'a' is used before being assigned.
tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts(6,1): error TS2454: Variable 'c' is used before being assigned.
tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts(10,1): error TS2454: Variable 'd' is used before being assigned.
tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts(13,1): error TS2454: Variable 'e' is used before being assigned.
tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts(16,1): error TS2454: Variable 'f' is used before being assigned.
tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts(19,1): error TS2454: Variable 'g' is used before being assigned.
tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts(24,1): error TS2454: Variable 'h' is used before being assigned.
tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts(28,1): error TS2454: Variable 'i' is used before being assigned.
tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts(32,1): error TS2454: Variable 'j' is used before being assigned.
tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts(36,1): error TS2454: Variable 'j' is used before being assigned.


==== tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts (10 errors) ====
let a: () => void | undefined;
a; // Error did you mean to paren this function type
~
!!! error TS2454: Variable 'a' is used before being assigned.
!!! related TS1360 tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts:1:8: Did you mean to parenthesize this function type?

function b (): void | undefined {}
let c: typeof b;
c;
~
!!! error TS2454: Variable 'c' is used before being assigned.

type Undefined = undefined
let d: () => void | Undefined;
d;
~
!!! error TS2454: Variable 'd' is used before being assigned.

let e: () => string | void | number | object | undefined;
e; // Error did you mean to paren...
~
!!! error TS2454: Variable 'e' is used before being assigned.
!!! related TS1360 tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts:12:8: Did you mean to parenthesize this function type?

let f: () => void;
f;
~
!!! error TS2454: Variable 'f' is used before being assigned.

let g: () => undefined;
g;
~
!!! error TS2454: Variable 'g' is used before being assigned.

type T1 = undefined | string;
type T2 = undefined | void;
let h: () => T1 & T2;
h;
~
!!! error TS2454: Variable 'h' is used before being assigned.

type T3 = void | undefined;
let i: () => T3;
i;
~
!!! error TS2454: Variable 'i' is used before being assigned.

type T4 = () => void | undefined;
let j: T4;
j; // Error did you mean to paren...
~
!!! error TS2454: Variable 'j' is used before being assigned.
!!! related TS1360 tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts:30:11: Did you mean to parenthesize this function type?

type T5 = () => void
let k: T5 | undefined
j;
~
!!! error TS2454: Variable 'j' is used before being assigned.
!!! related TS1360 tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts:30:11: Did you mean to parenthesize this function type?

Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//// [functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts]
let a: () => void | undefined;
a; // Error did you mean to paren this function type

function b (): void | undefined {}
let c: typeof b;
c;

type Undefined = undefined
let d: () => void | Undefined;
d;

let e: () => string | void | number | object | undefined;
e; // Error did you mean to paren...

let f: () => void;
f;

let g: () => undefined;
g;

type T1 = undefined | string;
type T2 = undefined | void;
let h: () => T1 & T2;
h;

type T3 = void | undefined;
let i: () => T3;
i;

type T4 = () => void | undefined;
let j: T4;
j; // Error did you mean to paren...

type T5 = () => void
let k: T5 | undefined
j;


//// [functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.js]
var a;
a; // Error did you mean to paren this function type
function b() { }
var c;
c;
var d;
d;
var e;
e; // Error did you mean to paren...
var f;
f;
var g;
g;
var h;
h;
var i;
i;
var j;
j; // Error did you mean to paren...
var k;
j;
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
=== tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts ===
let a: () => void | undefined;
>a : Symbol(a, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 0, 3))

a; // Error did you mean to paren this function type
>a : Symbol(a, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 0, 3))

function b (): void | undefined {}
>b : Symbol(b, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 1, 2))

let c: typeof b;
>c : Symbol(c, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 4, 3))
>b : Symbol(b, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 1, 2))

c;
>c : Symbol(c, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 4, 3))

type Undefined = undefined
>Undefined : Symbol(Undefined, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 5, 2))

let d: () => void | Undefined;
>d : Symbol(d, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 8, 3))
>Undefined : Symbol(Undefined, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 5, 2))

d;
>d : Symbol(d, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 8, 3))

let e: () => string | void | number | object | undefined;
>e : Symbol(e, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 11, 3))

e; // Error did you mean to paren...
>e : Symbol(e, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 11, 3))

let f: () => void;
>f : Symbol(f, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 14, 3))

f;
>f : Symbol(f, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 14, 3))

let g: () => undefined;
>g : Symbol(g, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 17, 3))

g;
>g : Symbol(g, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 17, 3))

type T1 = undefined | string;
>T1 : Symbol(T1, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 18, 2))

type T2 = undefined | void;
>T2 : Symbol(T2, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 20, 29))

let h: () => T1 & T2;
>h : Symbol(h, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 22, 3))
>T1 : Symbol(T1, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 18, 2))
>T2 : Symbol(T2, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 20, 29))

h;
>h : Symbol(h, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 22, 3))

type T3 = void | undefined;
>T3 : Symbol(T3, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 23, 2))

let i: () => T3;
>i : Symbol(i, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 26, 3))
>T3 : Symbol(T3, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 23, 2))

i;
>i : Symbol(i, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 26, 3))

type T4 = () => void | undefined;
>T4 : Symbol(T4, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 27, 2))

let j: T4;
>j : Symbol(j, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 30, 3))
>T4 : Symbol(T4, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 27, 2))

j; // Error did you mean to paren...
>j : Symbol(j, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 30, 3))

type T5 = () => void
>T5 : Symbol(T5, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 31, 2))

let k: T5 | undefined
>k : Symbol(k, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 34, 3))
>T5 : Symbol(T5, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 31, 2))

j;
>j : Symbol(j, Decl(functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts, 30, 3))

Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
=== tests/cases/compiler/functionTypeReturnsUnionWithUndefinedWithStrictNullChecks.ts ===
let a: () => void | undefined;
>a : () => void | undefined

a; // Error did you mean to paren this function type
>a : () => void | undefined

function b (): void | undefined {}
>b : () => void | undefined

let c: typeof b;
>c : () => void | undefined
>b : () => void | undefined

c;
>c : () => void | undefined

type Undefined = undefined
>Undefined : undefined

let d: () => void | Undefined;
>d : () => void | undefined

d;
>d : () => void | undefined

let e: () => string | void | number | object | undefined;
>e : () => string | number | void | object | undefined

e; // Error did you mean to paren...
>e : () => string | number | void | object | undefined

let f: () => void;
>f : () => void

f;
>f : () => void

let g: () => undefined;
>g : () => undefined

g;
>g : () => undefined

type T1 = undefined | string;
>T1 : T1

type T2 = undefined | void;
>T2 : void | undefined

let h: () => T1 & T2;
>h : () => undefined

h;
>h : () => undefined

type T3 = void | undefined;
>T3 : void | undefined

let i: () => T3;
>i : () => void | undefined

i;
>i : () => void | undefined

type T4 = () => void | undefined;
>T4 : T4

let j: T4;
>j : T4

j; // Error did you mean to paren...
>j : T4

type T5 = () => void
>T5 : T5

let k: T5 | undefined
>k : T5 | undefined

j;
>j : T4

Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// @strictNullChecks: true

let a: () => void | undefined;
a; // Error did you mean to paren this function type

function b (): void | undefined {}
let c: typeof b;
c;

type Undefined = undefined
let d: () => void | Undefined;
d;

let e: () => string | void | number | object | undefined;
e; // Error did you mean to paren...

let f: () => void;
f;

let g: () => undefined;
g;

type T1 = undefined | string;
type T2 = undefined | void;
let h: () => T1 & T2;
h;

type T3 = void | undefined;
let i: () => T3;
i;

type T4 = () => void | undefined;
let j: T4;
j; // Error did you mean to paren...

type T5 = () => void
let k: T5 | undefined
j;