Skip to content

Commit 074d19b

Browse files
committed
Report implicit any error for 'yield' result with no contextual type
1 parent e044b56 commit 074d19b

13 files changed

+490
-6
lines changed

src/compiler/checker.ts

+11-2
Original file line numberDiff line numberDiff line change
@@ -30635,8 +30635,17 @@ namespace ts {
3063530635
return getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Next, returnType, isAsync)
3063630636
|| anyType;
3063730637
}
30638-
30639-
return getContextualIterationType(IterationTypeKind.Next, func) || anyType;
30638+
let type = getContextualIterationType(IterationTypeKind.Next, func);
30639+
if (!type) {
30640+
type = anyType;
30641+
if (produceDiagnostics && noImplicitAny && !expressionResultIsUnused(node)) {
30642+
const contextualType = getContextualType(node);
30643+
if (!contextualType || isTypeAny(contextualType)) {
30644+
error(node, Diagnostics.yield_expression_implicitly_results_in_an_any_type_because_its_containing_generator_lacks_a_return_type_annotation);
30645+
}
30646+
}
30647+
}
30648+
return type;
3064030649
}
3064130650

3064230651
function checkConditionalExpression(node: ConditionalExpression, checkMode?: CheckMode): Type {

src/compiler/diagnosticMessages.json

+4
Original file line numberDiff line numberDiff line change
@@ -4948,6 +4948,10 @@
49484948
"category": "Error",
49494949
"code": 7056
49504950
},
4951+
"'yield' expression implicitly results in an 'any' type because its containing generator lacks a return-type annotation.": {
4952+
"category": "Error",
4953+
"code": 7057
4954+
},
49514955

49524956
"You cannot rename this element.": {
49534957
"category": "Error",

src/compiler/utilities.ts

+38
Original file line numberDiff line numberDiff line change
@@ -6973,4 +6973,42 @@ namespace ts {
69736973
return bindParentToChildIgnoringJSDoc(child, parent) || bindJSDoc(child);
69746974
}
69756975
}
6976+
6977+
/**
6978+
* Indicates whether the result of an `Expression` will be unused.
6979+
*
6980+
* NOTE: This requires a node with a valid `parent` pointer.
6981+
*/
6982+
export function expressionResultIsUnused(node: Expression): boolean {
6983+
Debug.assertIsDefined(node.parent);
6984+
while (true) {
6985+
const parent: Node = node.parent;
6986+
// walk up parenthesized expressions, but keep a pointer to the top-most parenthesized expression
6987+
if (isParenthesizedExpression(parent)) {
6988+
node = parent;
6989+
continue;
6990+
}
6991+
// result is unused in an expression statement, `void` expression, or the initializer or incrementer of a `for` loop
6992+
if (isExpressionStatement(parent) ||
6993+
isVoidExpression(parent) ||
6994+
isForStatement(parent) && (parent.initializer === node || parent.incrementor === node)) {
6995+
return true;
6996+
}
6997+
if (isCommaListExpression(parent)) {
6998+
// left side of comma is always unused
6999+
if (node !== last(parent.elements)) return true;
7000+
// right side of comma is unused if parent is unused
7001+
node = parent;
7002+
continue;
7003+
}
7004+
if (isBinaryExpression(parent) && parent.operatorToken.kind === SyntaxKind.CommaToken) {
7005+
// left side of comma is always unused
7006+
if (node === parent.left) return true;
7007+
// right side of comma is unused if parent is unused
7008+
node = parent;
7009+
continue;
7010+
}
7011+
return false;
7012+
}
7013+
}
69767014
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
tests/cases/conformance/generators/generatorImplicitAny.ts(8,19): error TS7057: 'yield' expression implicitly results in an 'any' type because its containing generator lacks a return-type annotation.
2+
tests/cases/conformance/generators/generatorImplicitAny.ts(26,7): error TS7057: 'yield' expression implicitly results in an 'any' type because its containing generator lacks a return-type annotation.
3+
4+
5+
==== tests/cases/conformance/generators/generatorImplicitAny.ts (2 errors) ====
6+
function* g() {}
7+
8+
// https://github.com/microsoft/TypeScript/issues/35105
9+
declare function noop(): void;
10+
declare function f<T>(value: T): void;
11+
12+
function* g2() {
13+
const value = yield; // error: implicit any
14+
~~~~~
15+
!!! error TS7057: 'yield' expression implicitly results in an 'any' type because its containing generator lacks a return-type annotation.
16+
}
17+
18+
function* g3() {
19+
const value: string = yield; // ok, contextually typed by `value`.
20+
}
21+
22+
function* g4() {
23+
yield; // ok, result is unused
24+
yield, noop(); // ok, result is unused
25+
noop(), yield, noop(); // ok, result is unused
26+
(yield); // ok, result is unused
27+
(yield, noop()), noop(); // ok, result is unused
28+
for(yield; false; yield); // ok, results are unused
29+
void (yield); // ok, results are unused
30+
}
31+
32+
function* g5() {
33+
f(yield); // error: implicit any
34+
~~~~~
35+
!!! error TS7057: 'yield' expression implicitly results in an 'any' type because its containing generator lacks a return-type annotation.
36+
}
37+
38+
function* g6() {
39+
f<string>(yield); // ok, contextually typed by f<string>
40+
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,57 @@
11
//// [generatorImplicitAny.ts]
2-
function* g() {}
2+
function* g() {}
3+
4+
// https://github.com/microsoft/TypeScript/issues/35105
5+
declare function noop(): void;
6+
declare function f<T>(value: T): void;
7+
8+
function* g2() {
9+
const value = yield; // error: implicit any
10+
}
11+
12+
function* g3() {
13+
const value: string = yield; // ok, contextually typed by `value`.
14+
}
15+
16+
function* g4() {
17+
yield; // ok, result is unused
18+
yield, noop(); // ok, result is unused
19+
noop(), yield, noop(); // ok, result is unused
20+
(yield); // ok, result is unused
21+
(yield, noop()), noop(); // ok, result is unused
22+
for(yield; false; yield); // ok, results are unused
23+
void (yield); // ok, results are unused
24+
}
25+
26+
function* g5() {
27+
f(yield); // error: implicit any
28+
}
29+
30+
function* g6() {
31+
f<string>(yield); // ok, contextually typed by f<string>
32+
}
333

434
//// [generatorImplicitAny.js]
535
function* g() { }
36+
function* g2() {
37+
const value = yield; // error: implicit any
38+
}
39+
function* g3() {
40+
const value = yield; // ok, contextually typed by `value`.
41+
}
42+
function* g4() {
43+
yield; // ok, result is unused
44+
yield, noop(); // ok, result is unused
45+
noop(), yield, noop(); // ok, result is unused
46+
(yield); // ok, result is unused
47+
(yield, noop()), noop(); // ok, result is unused
48+
for (yield; false; yield)
49+
; // ok, results are unused
50+
void (yield); // ok, results are unused
51+
}
52+
function* g5() {
53+
f(yield); // error: implicit any
54+
}
55+
function* g6() {
56+
f(yield); // ok, contextually typed by f<string>
57+
}

tests/baselines/reference/generatorImplicitAny.symbols

+57
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,60 @@
22
function* g() {}
33
>g : Symbol(g, Decl(generatorImplicitAny.ts, 0, 0))
44

5+
// https://github.com/microsoft/TypeScript/issues/35105
6+
declare function noop(): void;
7+
>noop : Symbol(noop, Decl(generatorImplicitAny.ts, 0, 16))
8+
9+
declare function f<T>(value: T): void;
10+
>f : Symbol(f, Decl(generatorImplicitAny.ts, 3, 30))
11+
>T : Symbol(T, Decl(generatorImplicitAny.ts, 4, 19))
12+
>value : Symbol(value, Decl(generatorImplicitAny.ts, 4, 22))
13+
>T : Symbol(T, Decl(generatorImplicitAny.ts, 4, 19))
14+
15+
function* g2() {
16+
>g2 : Symbol(g2, Decl(generatorImplicitAny.ts, 4, 38))
17+
18+
const value = yield; // error: implicit any
19+
>value : Symbol(value, Decl(generatorImplicitAny.ts, 7, 9))
20+
}
21+
22+
function* g3() {
23+
>g3 : Symbol(g3, Decl(generatorImplicitAny.ts, 8, 1))
24+
25+
const value: string = yield; // ok, contextually typed by `value`.
26+
>value : Symbol(value, Decl(generatorImplicitAny.ts, 11, 9))
27+
}
28+
29+
function* g4() {
30+
>g4 : Symbol(g4, Decl(generatorImplicitAny.ts, 12, 1))
31+
32+
yield; // ok, result is unused
33+
yield, noop(); // ok, result is unused
34+
>noop : Symbol(noop, Decl(generatorImplicitAny.ts, 0, 16))
35+
36+
noop(), yield, noop(); // ok, result is unused
37+
>noop : Symbol(noop, Decl(generatorImplicitAny.ts, 0, 16))
38+
>noop : Symbol(noop, Decl(generatorImplicitAny.ts, 0, 16))
39+
40+
(yield); // ok, result is unused
41+
(yield, noop()), noop(); // ok, result is unused
42+
>noop : Symbol(noop, Decl(generatorImplicitAny.ts, 0, 16))
43+
>noop : Symbol(noop, Decl(generatorImplicitAny.ts, 0, 16))
44+
45+
for(yield; false; yield); // ok, results are unused
46+
void (yield); // ok, results are unused
47+
}
48+
49+
function* g5() {
50+
>g5 : Symbol(g5, Decl(generatorImplicitAny.ts, 22, 1))
51+
52+
f(yield); // error: implicit any
53+
>f : Symbol(f, Decl(generatorImplicitAny.ts, 3, 30))
54+
}
55+
56+
function* g6() {
57+
>g6 : Symbol(g6, Decl(generatorImplicitAny.ts, 26, 1))
58+
59+
f<string>(yield); // ok, contextually typed by f<string>
60+
>f : Symbol(f, Decl(generatorImplicitAny.ts, 3, 30))
61+
}

tests/baselines/reference/generatorImplicitAny.types

+87
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,90 @@
22
function* g() {}
33
>g : () => Generator<never, void, unknown>
44

5+
// https://github.com/microsoft/TypeScript/issues/35105
6+
declare function noop(): void;
7+
>noop : () => void
8+
9+
declare function f<T>(value: T): void;
10+
>f : <T>(value: T) => void
11+
>value : T
12+
13+
function* g2() {
14+
>g2 : () => Generator<undefined, void, unknown>
15+
16+
const value = yield; // error: implicit any
17+
>value : any
18+
>yield : any
19+
}
20+
21+
function* g3() {
22+
>g3 : () => Generator<undefined, void, string>
23+
24+
const value: string = yield; // ok, contextually typed by `value`.
25+
>value : string
26+
>yield : any
27+
}
28+
29+
function* g4() {
30+
>g4 : () => Generator<undefined, void, unknown>
31+
32+
yield; // ok, result is unused
33+
>yield : any
34+
35+
yield, noop(); // ok, result is unused
36+
>yield, noop() : void
37+
>yield : any
38+
>noop() : void
39+
>noop : () => void
40+
41+
noop(), yield, noop(); // ok, result is unused
42+
>noop(), yield, noop() : void
43+
>noop(), yield : any
44+
>noop() : void
45+
>noop : () => void
46+
>yield : any
47+
>noop() : void
48+
>noop : () => void
49+
50+
(yield); // ok, result is unused
51+
>(yield) : any
52+
>yield : any
53+
54+
(yield, noop()), noop(); // ok, result is unused
55+
>(yield, noop()), noop() : void
56+
>(yield, noop()) : void
57+
>yield, noop() : void
58+
>yield : any
59+
>noop() : void
60+
>noop : () => void
61+
>noop() : void
62+
>noop : () => void
63+
64+
for(yield; false; yield); // ok, results are unused
65+
>yield : any
66+
>false : false
67+
>yield : any
68+
69+
void (yield); // ok, results are unused
70+
>void (yield) : undefined
71+
>(yield) : any
72+
>yield : any
73+
}
74+
75+
function* g5() {
76+
>g5 : () => Generator<undefined, void, any>
77+
78+
f(yield); // error: implicit any
79+
>f(yield) : void
80+
>f : <T>(value: T) => void
81+
>yield : any
82+
}
83+
84+
function* g6() {
85+
>g6 : () => Generator<undefined, void, string>
86+
87+
f<string>(yield); // ok, contextually typed by f<string>
88+
>f<string>(yield) : void
89+
>f : <T>(value: T) => void
90+
>yield : any
91+
}

0 commit comments

Comments
 (0)