diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 295b8b3c257..e4efe7ca597 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -171,6 +171,7 @@ import { EnumMember, EnumType, equateValues, + ErrorOutputContainer, escapeLeadingUnderscores, escapeString, EvaluatorResult, @@ -20608,7 +20609,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { expr: Expression | undefined, headMessage: DiagnosticMessage | undefined, containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, - errorOutputContainer: { errors?: Diagnostic[]; skipLogging?: boolean; } | undefined, + errorOutputContainer: ErrorOutputContainer | undefined, ): boolean { if (isTypeRelatedTo(source, target, relation)) return true; if (!errorNode || !elaborateError(expr, source, target, relation, headMessage, containingMessageChain, errorOutputContainer)) { @@ -20628,7 +20629,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { relation: Map, headMessage: DiagnosticMessage | undefined, containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, - errorOutputContainer: { errors?: Diagnostic[]; skipLogging?: boolean; } | undefined, + errorOutputContainer: ErrorOutputContainer | undefined, ): boolean { if (!node || isOrHasGenericConditional(target)) return false; if ( @@ -20672,7 +20673,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { relation: Map, headMessage: DiagnosticMessage | undefined, containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, - errorOutputContainer: { errors?: Diagnostic[]; skipLogging?: boolean; } | undefined, + errorOutputContainer: ErrorOutputContainer | undefined, ): boolean { const callSignatures = getSignaturesOfType(source, SignatureKind.Call); const constructSignatures = getSignaturesOfType(source, SignatureKind.Construct); @@ -20705,7 +20706,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { target: Type, relation: Map, containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, - errorOutputContainer: { errors?: Diagnostic[]; skipLogging?: boolean; } | undefined, + errorOutputContainer: ErrorOutputContainer | undefined, ): boolean { // Don't elaborate blocks if (isBlock(node.body)) { @@ -20796,7 +20797,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { target: Type, relation: Map, containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, - errorOutputContainer: { errors?: Diagnostic[]; skipLogging?: boolean; } | undefined, + errorOutputContainer: ErrorOutputContainer | undefined, ) { // Assignability failure - check each prop individually, and if that fails, fall back on the bad error span let reportedError = false; @@ -20876,7 +20877,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { target: Type, relation: Map, containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, - errorOutputContainer: { errors?: Diagnostic[]; skipLogging?: boolean; } | undefined, + errorOutputContainer: ErrorOutputContainer | undefined, ) { const tupleOrArrayLikeTargetParts = filterType(target, isArrayOrTupleLikeType); const nonTupleOrArrayLikeTargetParts = filterType(target, t => !isArrayOrTupleLikeType(t)); @@ -20978,7 +20979,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { target: Type, relation: Map, containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, - errorOutputContainer: { errors?: Diagnostic[]; skipLogging?: boolean; } | undefined, + errorOutputContainer: ErrorOutputContainer | undefined, ) { let result = elaborateElementwise(generateJsxAttributes(node), source, target, relation, containingMessageChain, errorOutputContainer); let invalidTextDiagnostic: DiagnosticMessage | undefined; @@ -21092,7 +21093,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { target: Type, relation: Map, containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, - errorOutputContainer: { errors?: Diagnostic[]; skipLogging?: boolean; } | undefined, + errorOutputContainer: ErrorOutputContainer | undefined, ) { if (target.flags & (TypeFlags.Primitive | TypeFlags.Never)) return false; if (isTupleLikeType(source)) { @@ -21139,7 +21140,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { target: Type, relation: Map, containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, - errorOutputContainer: { errors?: Diagnostic[]; skipLogging?: boolean; } | undefined, + errorOutputContainer: ErrorOutputContainer | undefined, ) { if (target.flags & (TypeFlags.Primitive | TypeFlags.Never)) return false; return elaborateElementwise(generateObjectLiteralElements(node), source, target, relation, containingMessageChain, errorOutputContainer); @@ -21637,7 +21638,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { errorNode: Node | undefined, headMessage?: DiagnosticMessage, containingMessageChain?: () => DiagnosticMessageChain | undefined, - errorOutputContainer?: { errors?: Diagnostic[]; skipLogging?: boolean; }, + errorOutputContainer?: ErrorOutputContainer, ): boolean { let errorInfo: DiagnosticMessageChain | undefined; let relatedInfo: [DiagnosticRelatedInformation, ...DiagnosticRelatedInformation[]] | undefined; @@ -35186,7 +35187,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { checkMode: CheckMode, reportErrors: boolean, containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, - errorOutputContainer: { errors?: Diagnostic[]; skipLogging?: boolean; }, + errorOutputContainer: ErrorOutputContainer, ) { // Stateless function components can have maximum of three arguments: "props", "context", and "updater". // 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 { containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, inferenceContext: InferenceContext | undefined, ): readonly Diagnostic[] | undefined { - const errorOutputContainer: { errors?: Diagnostic[]; skipLogging?: boolean; } = { errors: undefined, skipLogging: true }; + const errorOutputContainer: ErrorOutputContainer = { errors: undefined, skipLogging: true }; if (isJsxCallLike(node)) { if (!checkApplicableSignatureForJsxCallLikeElement(node, signature, relation, checkMode, reportErrors, containingMessageChain, errorOutputContainer)) { Debug.assert(!reportErrors || !!errorOutputContainer.errors, "jsx should have errors when reporting errors"); @@ -44934,7 +44935,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } if (!(type.flags & TypeFlags.Union)) { - const errorOutputContainer: { errors: Diagnostic[] | undefined; } | undefined = errorNode ? { errors: undefined } : undefined; + const errorOutputContainer: ErrorOutputContainer | undefined = errorNode ? { errors: undefined, skipLogging: true } : undefined; const iterationTypes = getIterationTypesOfIterableWorker(type, use, errorNode, errorOutputContainer); if (iterationTypes === noIterationTypes) { if (errorNode) { @@ -44959,7 +44960,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { let allIterationTypes: IterationTypes[] | undefined; for (const constituent of (type as UnionType).types) { - const errorOutputContainer: { errors: Diagnostic[] | undefined; } | undefined = errorNode ? { errors: undefined } : undefined; + const errorOutputContainer: ErrorOutputContainer | undefined = errorNode ? { errors: undefined } : undefined; const iterationTypes = getIterationTypesOfIterableWorker(constituent, use, errorNode, errorOutputContainer); if (iterationTypes === noIterationTypes) { if (errorNode) { @@ -45010,7 +45011,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { * NOTE: You probably don't want to call this directly and should be calling * `getIterationTypesOfIterable` instead. */ - function getIterationTypesOfIterableWorker(type: Type, use: IterationUse, errorNode: Node | undefined, errorOutputContainer: { errors: Diagnostic[] | undefined; } | undefined) { + function getIterationTypesOfIterableWorker(type: Type, use: IterationUse, errorNode: Node | undefined, errorOutputContainer: ErrorOutputContainer | undefined) { if (isTypeAny(type)) { return anyIterationTypes; } @@ -45152,19 +45153,23 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { * NOTE: You probably don't want to call this directly and should be calling * `getIterationTypesOfIterable` instead. */ - function getIterationTypesOfIterableSlow(type: Type, resolver: IterationTypesResolver, errorNode: Node | undefined, errorOutputContainer: { errors: Diagnostic[] | undefined; } | undefined, noCache: boolean) { + function getIterationTypesOfIterableSlow(type: Type, resolver: IterationTypesResolver, errorNode: Node | undefined, errorOutputContainer: ErrorOutputContainer | undefined, noCache: boolean) { const method = getPropertyOfType(type, getPropertyNameForKnownSymbolName(resolver.iteratorSymbolName)); const methodType = method && !(method.flags & SymbolFlags.Optional) ? getTypeOfSymbol(method) : undefined; if (isTypeAny(methodType)) { return noCache ? anyIterationTypes : setCachedIterationTypes(type, resolver.iterableCacheKey, anyIterationTypes); } - const signatures = methodType ? getSignaturesOfType(methodType, SignatureKind.Call) : undefined; - if (!some(signatures)) { + const allSignatures = methodType ? getSignaturesOfType(methodType, SignatureKind.Call) : undefined; + const validSignatures = filter(allSignatures, sig => getMinArgumentCount(sig) === 0); + if (!some(validSignatures)) { + if (errorNode && some(allSignatures)) { + checkTypeAssignableTo(type, resolver.getGlobalIterableType(/*reportErrors*/ true), errorNode, /*headMessage*/ undefined, /*containingMessageChain*/ undefined, errorOutputContainer); + } return noCache ? noIterationTypes : setCachedIterationTypes(type, resolver.iterableCacheKey, noIterationTypes); } - const iteratorType = getIntersectionType(map(signatures, getReturnTypeOfSignature)); + const iteratorType = getIntersectionType(map(validSignatures, getReturnTypeOfSignature)); const iterationTypes = getIterationTypesOfIteratorWorker(iteratorType, resolver, errorNode, errorOutputContainer, noCache) ?? noIterationTypes; return noCache ? iterationTypes : setCachedIterationTypes(type, resolver.iterableCacheKey, iterationTypes); } @@ -45193,7 +45198,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { * If we successfully found the *yield*, *return*, and *next* types, an `IterationTypes` * record is returned. Otherwise, `undefined` is returned. */ - function getIterationTypesOfIterator(type: Type, resolver: IterationTypesResolver, errorNode: Node | undefined, errorOutputContainer: { errors: Diagnostic[] | undefined; } | undefined) { + function getIterationTypesOfIterator(type: Type, resolver: IterationTypesResolver, errorNode: Node | undefined, errorOutputContainer: ErrorOutputContainer | undefined) { return getIterationTypesOfIteratorWorker(type, resolver, errorNode, errorOutputContainer, /*noCache*/ false); } @@ -45206,7 +45211,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { * NOTE: You probably don't want to call this directly and should be calling * `getIterationTypesOfIterator` instead. */ - function getIterationTypesOfIteratorWorker(type: Type, resolver: IterationTypesResolver, errorNode: Node | undefined, errorOutputContainer: { errors: Diagnostic[] | undefined; } | undefined, noCache: boolean) { + function getIterationTypesOfIteratorWorker(type: Type, resolver: IterationTypesResolver, errorNode: Node | undefined, errorOutputContainer: ErrorOutputContainer | undefined, noCache: boolean) { if (isTypeAny(type)) { return anyIterationTypes; } @@ -45348,7 +45353,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { * If we successfully found the *yield*, *return*, and *next* types, an `IterationTypes` * record is returned. Otherwise, we return `undefined`. */ - function getIterationTypesOfMethod(type: Type, resolver: IterationTypesResolver, methodName: "next" | "return" | "throw", errorNode: Node | undefined, errorOutputContainer: { errors: Diagnostic[] | undefined; } | undefined): IterationTypes | undefined { + function getIterationTypesOfMethod(type: Type, resolver: IterationTypesResolver, methodName: "next" | "return" | "throw", errorNode: Node | undefined, errorOutputContainer: ErrorOutputContainer | undefined): IterationTypes | undefined { const method = getPropertyOfType(type, methodName as __String); // Ignore 'return' or 'throw' if they are missing. @@ -45468,7 +45473,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { * NOTE: You probably don't want to call this directly and should be calling * `getIterationTypesOfIterator` instead. */ - function getIterationTypesOfIteratorSlow(type: Type, resolver: IterationTypesResolver, errorNode: Node | undefined, errorOutputContainer: { errors: Diagnostic[] | undefined; } | undefined, noCache: boolean) { + function getIterationTypesOfIteratorSlow(type: Type, resolver: IterationTypesResolver, errorNode: Node | undefined, errorOutputContainer: ErrorOutputContainer | undefined, noCache: boolean) { const iterationTypes = combineIterationTypes([ getIterationTypesOfMethod(type, resolver, "next", errorNode, errorOutputContainer), getIterationTypesOfMethod(type, resolver, "return", errorNode, errorOutputContainer), diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 374451a2aad..0a14e6ff14b 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -9981,6 +9981,12 @@ export interface TextChangeRange { newLength: number; } +/** @internal */ +export interface ErrorOutputContainer { + errors?: Diagnostic[]; + skipLogging?: boolean; +} + /** @internal */ export interface DiagnosticCollection { // Adds a diagnostic to this diagnostic collection. diff --git a/tests/baselines/reference/asyncIteratorExtraParameters.errors.txt b/tests/baselines/reference/asyncIteratorExtraParameters.errors.txt new file mode 100644 index 00000000000..9a2965d54af --- /dev/null +++ b/tests/baselines/reference/asyncIteratorExtraParameters.errors.txt @@ -0,0 +1,32 @@ +asyncIteratorExtraParameters.ts(11,27): error TS2504: Type '{ [Symbol.asyncIterator](_: number): AsyncGenerator; }' must have a '[Symbol.asyncIterator]()' method that returns an async iterator. +asyncIteratorExtraParameters.ts(13,12): error TS2504: Type '{ [Symbol.asyncIterator](_: number): AsyncGenerator; }' must have a '[Symbol.asyncIterator]()' method that returns an async iterator. + + +==== asyncIteratorExtraParameters.ts (2 errors) ==== + // https://github.com/microsoft/TypeScript/issues/57130 + const iter = { + async *[Symbol.asyncIterator](_: number) { + yield 0; + } + }; + + declare function g(...args: any): any; + + async function* f() { + for await (const _ of iter); + ~~~~ +!!! error TS2504: Type '{ [Symbol.asyncIterator](_: number): AsyncGenerator; }' must have a '[Symbol.asyncIterator]()' method that returns an async iterator. +!!! related TS2322 asyncIteratorExtraParameters.ts:11:27: Type '{ [Symbol.asyncIterator](_: number): AsyncGenerator; }' is not assignable to type 'AsyncIterable'. + Types of property '[Symbol.asyncIterator]' are incompatible. + Type '(_: number) => AsyncGenerator' is not assignable to type '() => AsyncIterator'. + Target signature provides too few arguments. Expected 1 or more, but got 0. + + yield* iter; + ~~~~ +!!! error TS2504: Type '{ [Symbol.asyncIterator](_: number): AsyncGenerator; }' must have a '[Symbol.asyncIterator]()' method that returns an async iterator. +!!! related TS2322 asyncIteratorExtraParameters.ts:13:12: Type '{ [Symbol.asyncIterator](_: number): AsyncGenerator; }' is not assignable to type 'AsyncIterable'. + Types of property '[Symbol.asyncIterator]' are incompatible. + Type '(_: number) => AsyncGenerator' is not assignable to type '() => AsyncIterator'. + Target signature provides too few arguments. Expected 1 or more, but got 0. + } + \ No newline at end of file diff --git a/tests/baselines/reference/iteratorExtraParameters.errors.txt b/tests/baselines/reference/iteratorExtraParameters.errors.txt new file mode 100644 index 00000000000..fec3eb9882b --- /dev/null +++ b/tests/baselines/reference/iteratorExtraParameters.errors.txt @@ -0,0 +1,50 @@ +iteratorExtraParameters.ts(11,21): error TS2488: Type '{ [Symbol.iterator](_: number): Generator; }' must have a '[Symbol.iterator]()' method that returns an iterator. +iteratorExtraParameters.ts(13,12): error TS2488: Type '{ [Symbol.iterator](_: number): Generator; }' must have a '[Symbol.iterator]()' method that returns an iterator. +iteratorExtraParameters.ts(15,9): error TS2488: Type '{ [Symbol.iterator](_: number): Generator; }' must have a '[Symbol.iterator]()' method that returns an iterator. +iteratorExtraParameters.ts(17,10): error TS2488: Type '{ [Symbol.iterator](_: number): Generator; }' must have a '[Symbol.iterator]()' method that returns an iterator. + + +==== iteratorExtraParameters.ts (4 errors) ==== + // https://github.com/microsoft/TypeScript/issues/57130 + const iter = { + *[Symbol.iterator](_: number) { + yield 0; + } + }; + + declare function g(...args: any): any; + + function* f() { + for (const _ of iter); + ~~~~ +!!! error TS2488: Type '{ [Symbol.iterator](_: number): Generator; }' must have a '[Symbol.iterator]()' method that returns an iterator. +!!! related TS2322 iteratorExtraParameters.ts:11:21: Type '{ [Symbol.iterator](_: number): Generator; }' is not assignable to type 'Iterable'. + Types of property '[Symbol.iterator]' are incompatible. + Type '(_: number) => Generator' is not assignable to type '() => Iterator'. + Target signature provides too few arguments. Expected 1 or more, but got 0. + + yield* iter; + ~~~~ +!!! error TS2488: Type '{ [Symbol.iterator](_: number): Generator; }' must have a '[Symbol.iterator]()' method that returns an iterator. +!!! related TS2322 iteratorExtraParameters.ts:13:12: Type '{ [Symbol.iterator](_: number): Generator; }' is not assignable to type 'Iterable'. + Types of property '[Symbol.iterator]' are incompatible. + Type '(_: number) => Generator' is not assignable to type '() => Iterator'. + Target signature provides too few arguments. Expected 1 or more, but got 0. + + [...iter] + ~~~~ +!!! error TS2488: Type '{ [Symbol.iterator](_: number): Generator; }' must have a '[Symbol.iterator]()' method that returns an iterator. +!!! related TS2322 iteratorExtraParameters.ts:15:9: Type '{ [Symbol.iterator](_: number): Generator; }' is not assignable to type 'Iterable'. + Types of property '[Symbol.iterator]' are incompatible. + Type '(_: number) => Generator' is not assignable to type '() => Iterator'. + Target signature provides too few arguments. Expected 1 or more, but got 0. + + g(...iter); + ~~~~ +!!! error TS2488: Type '{ [Symbol.iterator](_: number): Generator; }' must have a '[Symbol.iterator]()' method that returns an iterator. +!!! related TS2322 iteratorExtraParameters.ts:17:10: Type '{ [Symbol.iterator](_: number): Generator; }' is not assignable to type 'Iterable'. + Types of property '[Symbol.iterator]' are incompatible. + Type '(_: number) => Generator' is not assignable to type '() => Iterator'. + Target signature provides too few arguments. Expected 1 or more, but got 0. + } + \ No newline at end of file diff --git a/tests/cases/compiler/asyncIteratorExtraParameters.ts b/tests/cases/compiler/asyncIteratorExtraParameters.ts new file mode 100644 index 00000000000..e25a0429dc8 --- /dev/null +++ b/tests/cases/compiler/asyncIteratorExtraParameters.ts @@ -0,0 +1,18 @@ +// @target: esnext +// @noEmit: true +// @noTypesAndSymbols: true + +// https://github.com/microsoft/TypeScript/issues/57130 +const iter = { + async *[Symbol.asyncIterator](_: number) { + yield 0; + } +}; + +declare function g(...args: any): any; + +async function* f() { + for await (const _ of iter); + + yield* iter; +} diff --git a/tests/cases/compiler/iteratorExtraParameters.ts b/tests/cases/compiler/iteratorExtraParameters.ts new file mode 100644 index 00000000000..86d56387167 --- /dev/null +++ b/tests/cases/compiler/iteratorExtraParameters.ts @@ -0,0 +1,22 @@ +// @target: esnext +// @noEmit: true +// @noTypesAndSymbols: true + +// https://github.com/microsoft/TypeScript/issues/57130 +const iter = { + *[Symbol.iterator](_: number) { + yield 0; + } +}; + +declare function g(...args: any): any; + +function* f() { + for (const _ of iter); + + yield* iter; + + [...iter] + + g(...iter); +}