Skip to content

Commit e25abe3

Browse files
authored
Error on too many parameters for iterator method (#60321)
1 parent aa411ac commit e25abe3

File tree

6 files changed

+156
-23
lines changed

6 files changed

+156
-23
lines changed

src/compiler/checker.ts

+28-23
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ import {
171171
EnumMember,
172172
EnumType,
173173
equateValues,
174+
ErrorOutputContainer,
174175
escapeLeadingUnderscores,
175176
escapeString,
176177
EvaluatorResult,
@@ -20608,7 +20609,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2060820609
expr: Expression | undefined,
2060920610
headMessage: DiagnosticMessage | undefined,
2061020611
containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined,
20611-
errorOutputContainer: { errors?: Diagnostic[]; skipLogging?: boolean; } | undefined,
20612+
errorOutputContainer: ErrorOutputContainer | undefined,
2061220613
): boolean {
2061320614
if (isTypeRelatedTo(source, target, relation)) return true;
2061420615
if (!errorNode || !elaborateError(expr, source, target, relation, headMessage, containingMessageChain, errorOutputContainer)) {
@@ -20628,7 +20629,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2062820629
relation: Map<string, RelationComparisonResult>,
2062920630
headMessage: DiagnosticMessage | undefined,
2063020631
containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined,
20631-
errorOutputContainer: { errors?: Diagnostic[]; skipLogging?: boolean; } | undefined,
20632+
errorOutputContainer: ErrorOutputContainer | undefined,
2063220633
): boolean {
2063320634
if (!node || isOrHasGenericConditional(target)) return false;
2063420635
if (
@@ -20672,7 +20673,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2067220673
relation: Map<string, RelationComparisonResult>,
2067320674
headMessage: DiagnosticMessage | undefined,
2067420675
containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined,
20675-
errorOutputContainer: { errors?: Diagnostic[]; skipLogging?: boolean; } | undefined,
20676+
errorOutputContainer: ErrorOutputContainer | undefined,
2067620677
): boolean {
2067720678
const callSignatures = getSignaturesOfType(source, SignatureKind.Call);
2067820679
const constructSignatures = getSignaturesOfType(source, SignatureKind.Construct);
@@ -20705,7 +20706,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2070520706
target: Type,
2070620707
relation: Map<string, RelationComparisonResult>,
2070720708
containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined,
20708-
errorOutputContainer: { errors?: Diagnostic[]; skipLogging?: boolean; } | undefined,
20709+
errorOutputContainer: ErrorOutputContainer | undefined,
2070920710
): boolean {
2071020711
// Don't elaborate blocks
2071120712
if (isBlock(node.body)) {
@@ -20796,7 +20797,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2079620797
target: Type,
2079720798
relation: Map<string, RelationComparisonResult>,
2079820799
containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined,
20799-
errorOutputContainer: { errors?: Diagnostic[]; skipLogging?: boolean; } | undefined,
20800+
errorOutputContainer: ErrorOutputContainer | undefined,
2080020801
) {
2080120802
// Assignability failure - check each prop individually, and if that fails, fall back on the bad error span
2080220803
let reportedError = false;
@@ -20876,7 +20877,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2087620877
target: Type,
2087720878
relation: Map<string, RelationComparisonResult>,
2087820879
containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined,
20879-
errorOutputContainer: { errors?: Diagnostic[]; skipLogging?: boolean; } | undefined,
20880+
errorOutputContainer: ErrorOutputContainer | undefined,
2088020881
) {
2088120882
const tupleOrArrayLikeTargetParts = filterType(target, isArrayOrTupleLikeType);
2088220883
const nonTupleOrArrayLikeTargetParts = filterType(target, t => !isArrayOrTupleLikeType(t));
@@ -20978,7 +20979,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2097820979
target: Type,
2097920980
relation: Map<string, RelationComparisonResult>,
2098020981
containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined,
20981-
errorOutputContainer: { errors?: Diagnostic[]; skipLogging?: boolean; } | undefined,
20982+
errorOutputContainer: ErrorOutputContainer | undefined,
2098220983
) {
2098320984
let result = elaborateElementwise(generateJsxAttributes(node), source, target, relation, containingMessageChain, errorOutputContainer);
2098420985
let invalidTextDiagnostic: DiagnosticMessage | undefined;
@@ -21092,7 +21093,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2109221093
target: Type,
2109321094
relation: Map<string, RelationComparisonResult>,
2109421095
containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined,
21095-
errorOutputContainer: { errors?: Diagnostic[]; skipLogging?: boolean; } | undefined,
21096+
errorOutputContainer: ErrorOutputContainer | undefined,
2109621097
) {
2109721098
if (target.flags & (TypeFlags.Primitive | TypeFlags.Never)) return false;
2109821099
if (isTupleLikeType(source)) {
@@ -21139,7 +21140,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2113921140
target: Type,
2114021141
relation: Map<string, RelationComparisonResult>,
2114121142
containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined,
21142-
errorOutputContainer: { errors?: Diagnostic[]; skipLogging?: boolean; } | undefined,
21143+
errorOutputContainer: ErrorOutputContainer | undefined,
2114321144
) {
2114421145
if (target.flags & (TypeFlags.Primitive | TypeFlags.Never)) return false;
2114521146
return elaborateElementwise(generateObjectLiteralElements(node), source, target, relation, containingMessageChain, errorOutputContainer);
@@ -21637,7 +21638,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2163721638
errorNode: Node | undefined,
2163821639
headMessage?: DiagnosticMessage,
2163921640
containingMessageChain?: () => DiagnosticMessageChain | undefined,
21640-
errorOutputContainer?: { errors?: Diagnostic[]; skipLogging?: boolean; },
21641+
errorOutputContainer?: ErrorOutputContainer,
2164121642
): boolean {
2164221643
let errorInfo: DiagnosticMessageChain | undefined;
2164321644
let relatedInfo: [DiagnosticRelatedInformation, ...DiagnosticRelatedInformation[]] | undefined;
@@ -35186,7 +35187,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3518635187
checkMode: CheckMode,
3518735188
reportErrors: boolean,
3518835189
containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined,
35189-
errorOutputContainer: { errors?: Diagnostic[]; skipLogging?: boolean; },
35190+
errorOutputContainer: ErrorOutputContainer,
3519035191
) {
3519135192
// Stateless function components can have maximum of three arguments: "props", "context", and "updater".
3519235193
// However "context" and "updater" are implicit and can't be specify by users. Only the first parameter, props,
@@ -35302,7 +35303,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3530235303
containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined,
3530335304
inferenceContext: InferenceContext | undefined,
3530435305
): readonly Diagnostic[] | undefined {
35305-
const errorOutputContainer: { errors?: Diagnostic[]; skipLogging?: boolean; } = { errors: undefined, skipLogging: true };
35306+
const errorOutputContainer: ErrorOutputContainer = { errors: undefined, skipLogging: true };
3530635307
if (isJsxCallLike(node)) {
3530735308
if (!checkApplicableSignatureForJsxCallLikeElement(node, signature, relation, checkMode, reportErrors, containingMessageChain, errorOutputContainer)) {
3530835309
Debug.assert(!reportErrors || !!errorOutputContainer.errors, "jsx should have errors when reporting errors");
@@ -44934,7 +44935,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4493444935
}
4493544936

4493644937
if (!(type.flags & TypeFlags.Union)) {
44937-
const errorOutputContainer: { errors: Diagnostic[] | undefined; } | undefined = errorNode ? { errors: undefined } : undefined;
44938+
const errorOutputContainer: ErrorOutputContainer | undefined = errorNode ? { errors: undefined, skipLogging: true } : undefined;
4493844939
const iterationTypes = getIterationTypesOfIterableWorker(type, use, errorNode, errorOutputContainer);
4493944940
if (iterationTypes === noIterationTypes) {
4494044941
if (errorNode) {
@@ -44959,7 +44960,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4495944960

4496044961
let allIterationTypes: IterationTypes[] | undefined;
4496144962
for (const constituent of (type as UnionType).types) {
44962-
const errorOutputContainer: { errors: Diagnostic[] | undefined; } | undefined = errorNode ? { errors: undefined } : undefined;
44963+
const errorOutputContainer: ErrorOutputContainer | undefined = errorNode ? { errors: undefined } : undefined;
4496344964
const iterationTypes = getIterationTypesOfIterableWorker(constituent, use, errorNode, errorOutputContainer);
4496444965
if (iterationTypes === noIterationTypes) {
4496544966
if (errorNode) {
@@ -45010,7 +45011,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4501045011
* NOTE: You probably don't want to call this directly and should be calling
4501145012
* `getIterationTypesOfIterable` instead.
4501245013
*/
45013-
function getIterationTypesOfIterableWorker(type: Type, use: IterationUse, errorNode: Node | undefined, errorOutputContainer: { errors: Diagnostic[] | undefined; } | undefined) {
45014+
function getIterationTypesOfIterableWorker(type: Type, use: IterationUse, errorNode: Node | undefined, errorOutputContainer: ErrorOutputContainer | undefined) {
4501445015
if (isTypeAny(type)) {
4501545016
return anyIterationTypes;
4501645017
}
@@ -45152,19 +45153,23 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4515245153
* NOTE: You probably don't want to call this directly and should be calling
4515345154
* `getIterationTypesOfIterable` instead.
4515445155
*/
45155-
function getIterationTypesOfIterableSlow(type: Type, resolver: IterationTypesResolver, errorNode: Node | undefined, errorOutputContainer: { errors: Diagnostic[] | undefined; } | undefined, noCache: boolean) {
45156+
function getIterationTypesOfIterableSlow(type: Type, resolver: IterationTypesResolver, errorNode: Node | undefined, errorOutputContainer: ErrorOutputContainer | undefined, noCache: boolean) {
4515645157
const method = getPropertyOfType(type, getPropertyNameForKnownSymbolName(resolver.iteratorSymbolName));
4515745158
const methodType = method && !(method.flags & SymbolFlags.Optional) ? getTypeOfSymbol(method) : undefined;
4515845159
if (isTypeAny(methodType)) {
4515945160
return noCache ? anyIterationTypes : setCachedIterationTypes(type, resolver.iterableCacheKey, anyIterationTypes);
4516045161
}
4516145162

45162-
const signatures = methodType ? getSignaturesOfType(methodType, SignatureKind.Call) : undefined;
45163-
if (!some(signatures)) {
45163+
const allSignatures = methodType ? getSignaturesOfType(methodType, SignatureKind.Call) : undefined;
45164+
const validSignatures = filter(allSignatures, sig => getMinArgumentCount(sig) === 0);
45165+
if (!some(validSignatures)) {
45166+
if (errorNode && some(allSignatures)) {
45167+
checkTypeAssignableTo(type, resolver.getGlobalIterableType(/*reportErrors*/ true), errorNode, /*headMessage*/ undefined, /*containingMessageChain*/ undefined, errorOutputContainer);
45168+
}
4516445169
return noCache ? noIterationTypes : setCachedIterationTypes(type, resolver.iterableCacheKey, noIterationTypes);
4516545170
}
4516645171

45167-
const iteratorType = getIntersectionType(map(signatures, getReturnTypeOfSignature));
45172+
const iteratorType = getIntersectionType(map(validSignatures, getReturnTypeOfSignature));
4516845173
const iterationTypes = getIterationTypesOfIteratorWorker(iteratorType, resolver, errorNode, errorOutputContainer, noCache) ?? noIterationTypes;
4516945174
return noCache ? iterationTypes : setCachedIterationTypes(type, resolver.iterableCacheKey, iterationTypes);
4517045175
}
@@ -45193,7 +45198,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4519345198
* If we successfully found the *yield*, *return*, and *next* types, an `IterationTypes`
4519445199
* record is returned. Otherwise, `undefined` is returned.
4519545200
*/
45196-
function getIterationTypesOfIterator(type: Type, resolver: IterationTypesResolver, errorNode: Node | undefined, errorOutputContainer: { errors: Diagnostic[] | undefined; } | undefined) {
45201+
function getIterationTypesOfIterator(type: Type, resolver: IterationTypesResolver, errorNode: Node | undefined, errorOutputContainer: ErrorOutputContainer | undefined) {
4519745202
return getIterationTypesOfIteratorWorker(type, resolver, errorNode, errorOutputContainer, /*noCache*/ false);
4519845203
}
4519945204

@@ -45206,7 +45211,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4520645211
* NOTE: You probably don't want to call this directly and should be calling
4520745212
* `getIterationTypesOfIterator` instead.
4520845213
*/
45209-
function getIterationTypesOfIteratorWorker(type: Type, resolver: IterationTypesResolver, errorNode: Node | undefined, errorOutputContainer: { errors: Diagnostic[] | undefined; } | undefined, noCache: boolean) {
45214+
function getIterationTypesOfIteratorWorker(type: Type, resolver: IterationTypesResolver, errorNode: Node | undefined, errorOutputContainer: ErrorOutputContainer | undefined, noCache: boolean) {
4521045215
if (isTypeAny(type)) {
4521145216
return anyIterationTypes;
4521245217
}
@@ -45348,7 +45353,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4534845353
* If we successfully found the *yield*, *return*, and *next* types, an `IterationTypes`
4534945354
* record is returned. Otherwise, we return `undefined`.
4535045355
*/
45351-
function getIterationTypesOfMethod(type: Type, resolver: IterationTypesResolver, methodName: "next" | "return" | "throw", errorNode: Node | undefined, errorOutputContainer: { errors: Diagnostic[] | undefined; } | undefined): IterationTypes | undefined {
45356+
function getIterationTypesOfMethod(type: Type, resolver: IterationTypesResolver, methodName: "next" | "return" | "throw", errorNode: Node | undefined, errorOutputContainer: ErrorOutputContainer | undefined): IterationTypes | undefined {
4535245357
const method = getPropertyOfType(type, methodName as __String);
4535345358

4535445359
// Ignore 'return' or 'throw' if they are missing.
@@ -45468,7 +45473,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4546845473
* NOTE: You probably don't want to call this directly and should be calling
4546945474
* `getIterationTypesOfIterator` instead.
4547045475
*/
45471-
function getIterationTypesOfIteratorSlow(type: Type, resolver: IterationTypesResolver, errorNode: Node | undefined, errorOutputContainer: { errors: Diagnostic[] | undefined; } | undefined, noCache: boolean) {
45476+
function getIterationTypesOfIteratorSlow(type: Type, resolver: IterationTypesResolver, errorNode: Node | undefined, errorOutputContainer: ErrorOutputContainer | undefined, noCache: boolean) {
4547245477
const iterationTypes = combineIterationTypes([
4547345478
getIterationTypesOfMethod(type, resolver, "next", errorNode, errorOutputContainer),
4547445479
getIterationTypesOfMethod(type, resolver, "return", errorNode, errorOutputContainer),

src/compiler/types.ts

+6
Original file line numberDiff line numberDiff line change
@@ -9981,6 +9981,12 @@ export interface TextChangeRange {
99819981
newLength: number;
99829982
}
99839983

9984+
/** @internal */
9985+
export interface ErrorOutputContainer {
9986+
errors?: Diagnostic[];
9987+
skipLogging?: boolean;
9988+
}
9989+
99849990
/** @internal */
99859991
export interface DiagnosticCollection {
99869992
// Adds a diagnostic to this diagnostic collection.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
asyncIteratorExtraParameters.ts(11,27): error TS2504: Type '{ [Symbol.asyncIterator](_: number): AsyncGenerator<number, void, unknown>; }' must have a '[Symbol.asyncIterator]()' method that returns an async iterator.
2+
asyncIteratorExtraParameters.ts(13,12): error TS2504: Type '{ [Symbol.asyncIterator](_: number): AsyncGenerator<number, void, unknown>; }' must have a '[Symbol.asyncIterator]()' method that returns an async iterator.
3+
4+
5+
==== asyncIteratorExtraParameters.ts (2 errors) ====
6+
// https://github.com/microsoft/TypeScript/issues/57130
7+
const iter = {
8+
async *[Symbol.asyncIterator](_: number) {
9+
yield 0;
10+
}
11+
};
12+
13+
declare function g(...args: any): any;
14+
15+
async function* f() {
16+
for await (const _ of iter);
17+
~~~~
18+
!!! error TS2504: Type '{ [Symbol.asyncIterator](_: number): AsyncGenerator<number, void, unknown>; }' must have a '[Symbol.asyncIterator]()' method that returns an async iterator.
19+
!!! related TS2322 asyncIteratorExtraParameters.ts:11:27: Type '{ [Symbol.asyncIterator](_: number): AsyncGenerator<number, void, unknown>; }' is not assignable to type 'AsyncIterable<T, TReturn, TNext>'.
20+
Types of property '[Symbol.asyncIterator]' are incompatible.
21+
Type '(_: number) => AsyncGenerator<number, void, unknown>' is not assignable to type '() => AsyncIterator<T, TReturn, TNext>'.
22+
Target signature provides too few arguments. Expected 1 or more, but got 0.
23+
24+
yield* iter;
25+
~~~~
26+
!!! error TS2504: Type '{ [Symbol.asyncIterator](_: number): AsyncGenerator<number, void, unknown>; }' must have a '[Symbol.asyncIterator]()' method that returns an async iterator.
27+
!!! related TS2322 asyncIteratorExtraParameters.ts:13:12: Type '{ [Symbol.asyncIterator](_: number): AsyncGenerator<number, void, unknown>; }' is not assignable to type 'AsyncIterable<T, TReturn, TNext>'.
28+
Types of property '[Symbol.asyncIterator]' are incompatible.
29+
Type '(_: number) => AsyncGenerator<number, void, unknown>' is not assignable to type '() => AsyncIterator<T, TReturn, TNext>'.
30+
Target signature provides too few arguments. Expected 1 or more, but got 0.
31+
}
32+

0 commit comments

Comments
 (0)