diff --git a/Gulpfile.js b/Gulpfile.js index a8f5748271d..42987e4fd8d 100644 --- a/Gulpfile.js +++ b/Gulpfile.js @@ -415,6 +415,8 @@ task("runtests").flags = { " --no-lint": "Disables lint", " --timeout=": "Overrides the default test timeout.", " --built": "Compile using the built version of the compiler.", + " --shards": "Total number of shards running tests (default: 1)", + " --shardId": "1-based ID of this shard (default: 1)", } const runTestsParallel = () => runConsoleTests("built/local/run.js", "min", /*runInParallel*/ true, /*watchMode*/ false); @@ -430,6 +432,8 @@ task("runtests-parallel").flags = { " --timeout=": "Overrides the default test timeout.", " --built": "Compile using the built version of the compiler.", " --skipPercent=": "Skip expensive tests with chance to miss an edit. Default 5%.", + " --shards": "Total number of shards running tests (default: 1)", + " --shardId": "1-based ID of this shard (default: 1)", }; task("diff", () => exec(getDiffTool(), [refBaseline, localBaseline], { ignoreExitCode: true })); diff --git a/scripts/build/options.js b/scripts/build/options.js index c93a265ff88..fecac01dd05 100644 --- a/scripts/build/options.js +++ b/scripts/build/options.js @@ -5,7 +5,7 @@ const os = require("os"); /** @type {CommandLineOptions} */ module.exports = minimist(process.argv.slice(2), { boolean: ["debug", "dirty", "inspect", "light", "colors", "lint", "lkg", "soft", "fix", "failed", "keepFailed", "force", "built"], - string: ["browser", "tests", "host", "reporter", "stackTraceLimit", "timeout"], + string: ["browser", "tests", "host", "reporter", "stackTraceLimit", "timeout", "shards", "shardId"], alias: { "b": "browser", "d": "debug", "debug-brk": "debug", diff --git a/scripts/build/tests.js b/scripts/build/tests.js index a15eb17093d..20d2a4c7c2a 100644 --- a/scripts/build/tests.js +++ b/scripts/build/tests.js @@ -36,6 +36,8 @@ async function runConsoleTests(runJs, defaultReporter, runInParallel, watchMode, const testConfigFile = "test.config"; const failed = cmdLineOptions.failed; const keepFailed = cmdLineOptions.keepFailed; + const shards = +cmdLineOptions.shards || undefined; + const shardId = +cmdLineOptions.shardId || undefined; if (!cmdLineOptions.dirty) { await cleanTestDirs(); cancelToken.throwIfCancellationRequested(); @@ -63,8 +65,8 @@ async function runConsoleTests(runJs, defaultReporter, runInParallel, watchMode, testTimeout = 400000; } - if (tests || runners || light || testTimeout || taskConfigsFolder || keepFailed || skipPercent !== undefined) { - writeTestConfigFile(tests, runners, light, skipPercent, taskConfigsFolder, workerCount, stackTraceLimit, testTimeout, keepFailed); + if (tests || runners || light || testTimeout || taskConfigsFolder || keepFailed || skipPercent !== undefined || shards || shardId) { + writeTestConfigFile(tests, runners, light, skipPercent, taskConfigsFolder, workerCount, stackTraceLimit, testTimeout, keepFailed, shards, shardId); } const colors = cmdLineOptions.colors; @@ -165,8 +167,10 @@ exports.cleanTestDirs = cleanTestDirs; * @param {string} [stackTraceLimit] * @param {string | number} [timeout] * @param {boolean} [keepFailed] + * @param {number | undefined} [shards] + * @param {number | undefined} [shardId] */ -function writeTestConfigFile(tests, runners, light, skipPercent, taskConfigsFolder, workerCount, stackTraceLimit, timeout, keepFailed) { +function writeTestConfigFile(tests, runners, light, skipPercent, taskConfigsFolder, workerCount, stackTraceLimit, timeout, keepFailed, shards, shardId) { const testConfigContents = JSON.stringify({ test: tests ? [tests] : undefined, runners: runners ? runners.split(",") : undefined, @@ -177,7 +181,9 @@ function writeTestConfigFile(tests, runners, light, skipPercent, taskConfigsFold taskConfigsFolder, noColor: !cmdLineOptions.colors, timeout, - keepFailed + keepFailed, + shards, + shardId }); log.info("Running tests with config: " + testConfigContents); fs.writeFileSync("test.config", testConfigContents); diff --git a/src/compiler/builderState.ts b/src/compiler/builderState.ts index 58b189d9e81..953334321c2 100644 --- a/src/compiler/builderState.ts +++ b/src/compiler/builderState.ts @@ -427,8 +427,8 @@ namespace ts.BuilderState { const references = state.referencedMap.get(path); if (references) { const iterator = references.keys(); - for (let { value, done } = iterator.next(); !done; { value, done } = iterator.next()) { - queue.push(value as Path); + for (let iterResult = iterator.next(); !iterResult.done; iterResult = iterator.next()) { + queue.push(iterResult.value as Path); } } } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 40040302123..70c69063696 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7,6 +7,56 @@ namespace ts { let nextMergeId = 1; let nextFlowId = 1; + const enum IterationUse { + AllowsSyncIterablesFlag = 1 << 0, + AllowsAsyncIterablesFlag = 1 << 1, + AllowsStringInputFlag = 1 << 2, + ForOfFlag = 1 << 3, + YieldStarFlag = 1 << 4, + SpreadFlag = 1 << 5, + DestructuringFlag = 1 << 6, + + // Spread, Destructuring, Array element assignment + Element = AllowsSyncIterablesFlag, + Spread = AllowsSyncIterablesFlag | SpreadFlag, + Destructuring = AllowsSyncIterablesFlag | DestructuringFlag, + + ForOf = AllowsSyncIterablesFlag | AllowsStringInputFlag | ForOfFlag, + ForAwaitOf = AllowsSyncIterablesFlag | AllowsAsyncIterablesFlag | AllowsStringInputFlag | ForOfFlag, + + YieldStar = AllowsSyncIterablesFlag | YieldStarFlag, + AsyncYieldStar = AllowsSyncIterablesFlag | AllowsAsyncIterablesFlag | YieldStarFlag, + + GeneratorReturnType = AllowsSyncIterablesFlag, + AsyncGeneratorReturnType = AllowsAsyncIterablesFlag, + + } + + const enum IterationTypeKind { + Yield, + Return, + Next, + } + + interface IterationTypesResolver { + iterableCacheKey: "iterationTypesOfAsyncIterable" | "iterationTypesOfIterable"; + iteratorCacheKey: "iterationTypesOfAsyncIterator" | "iterationTypesOfIterator"; + iteratorSymbolName: "asyncIterator" | "iterator"; + getGlobalIteratorType: (reportErrors: boolean) => Type; + getGlobalIterableType: (reportErrors: boolean) => Type; + getGlobalIterableIteratorType: (reportErrors: boolean) => Type; + getGlobalGeneratorType: (reportErrors: boolean) => Type; + resolveIterationType: (type: Type, errorNode: Node | undefined) => Type | undefined; + mustHaveANextMethodDiagnostic: DiagnosticMessage; + mustBeAMethodDiagnostic: DiagnosticMessage; + mustHaveAValueDiagnostic: DiagnosticMessage; + } + + const enum WideningKind { + Normal, + GeneratorYield + } + export function getNodeId(node: Node): number { if (!node.id) { node.id = nextNodeId; @@ -65,6 +115,7 @@ namespace ts { let typeCount = 0; let symbolCount = 0; let enumCount = 0; + let instantiationCount = 0; let instantiationDepth = 0; let constraintDepth = 0; let currentNode: Node | undefined; @@ -480,6 +531,44 @@ namespace ts { const enumNumberIndexInfo = createIndexInfo(stringType, /*isReadonly*/ true); + const iterationTypesCache = createMap(); // cache for common IterationTypes instances + const noIterationTypes: IterationTypes = { + get yieldType(): Type { throw new Error("Not supported"); }, + get returnType(): Type { throw new Error("Not supported"); }, + get nextType(): Type { throw new Error("Not supported"); }, + }; + const anyIterationTypes = createIterationTypes(anyType, anyType, anyType); + const anyIterationTypesExceptNext = createIterationTypes(anyType, anyType, unknownType); + const defaultIterationTypes = createIterationTypes(neverType, anyType, undefinedType); // default iteration types for `Iterator`. + + const asyncIterationTypesResolver: IterationTypesResolver = { + iterableCacheKey: "iterationTypesOfAsyncIterable", + iteratorCacheKey: "iterationTypesOfAsyncIterator", + iteratorSymbolName: "asyncIterator", + getGlobalIteratorType: getGlobalAsyncIteratorType, + getGlobalIterableType: getGlobalAsyncIterableType, + getGlobalIterableIteratorType: getGlobalAsyncIterableIteratorType, + getGlobalGeneratorType: getGlobalAsyncGeneratorType, + resolveIterationType: getAwaitedType, + mustHaveANextMethodDiagnostic: Diagnostics.An_async_iterator_must_have_a_next_method, + mustBeAMethodDiagnostic: Diagnostics.The_0_property_of_an_async_iterator_must_be_a_method, + mustHaveAValueDiagnostic: Diagnostics.The_type_returned_by_the_0_method_of_an_async_iterator_must_be_a_promise_for_a_type_with_a_value_property, + }; + + const syncIterationTypesResolver: IterationTypesResolver = { + iterableCacheKey: "iterationTypesOfIterable", + iteratorCacheKey: "iterationTypesOfIterator", + iteratorSymbolName: "iterator", + getGlobalIteratorType, + getGlobalIterableType, + getGlobalIterableIteratorType, + getGlobalGeneratorType, + resolveIterationType: (type, _errorNode) => type, + mustHaveANextMethodDiagnostic: Diagnostics.An_iterator_must_have_a_next_method, + mustBeAMethodDiagnostic: Diagnostics.The_0_property_of_an_iterator_must_be_a_method, + mustHaveAValueDiagnostic: Diagnostics.The_type_returned_by_the_0_method_of_an_iterator_must_have_a_value_property, + }; + interface DuplicateInfoForSymbol { readonly firstFileLocations: Node[]; readonly secondFileLocations: Node[]; @@ -532,9 +621,13 @@ namespace ts { let deferredGlobalIterableType: GenericType; let deferredGlobalIteratorType: GenericType; let deferredGlobalIterableIteratorType: GenericType; + let deferredGlobalGeneratorType: GenericType; + let deferredGlobalIteratorYieldResultType: GenericType; + let deferredGlobalIteratorReturnResultType: GenericType; let deferredGlobalAsyncIterableType: GenericType; let deferredGlobalAsyncIteratorType: GenericType; let deferredGlobalAsyncIterableIteratorType: GenericType; + let deferredGlobalAsyncGeneratorType: GenericType; let deferredGlobalTemplateStringsArrayType: ObjectType; let deferredGlobalImportMetaType: ObjectType; let deferredGlobalExtractSymbol: Symbol; @@ -722,6 +815,7 @@ namespace ts { NoIndexSignatures = 1 << 0, Writing = 1 << 1, CacheSymbol = 1 << 2, + NoTupleBoundsCheck = 1 << 3, } const enum CallbackCheck { @@ -838,6 +932,19 @@ namespace ts { addErrorOrSuggestion(isError, "message" in message ? createDiagnosticForNode(location, message, arg0, arg1, arg2, arg3) : createDiagnosticForNodeFromMessageChain(location, message)); } + function errorAndMaybeSuggestAwait( + location: Node, + maybeMissingAwait: boolean, + message: DiagnosticMessage, + arg0?: string | number | undefined, arg1?: string | number | undefined, arg2?: string | number | undefined, arg3?: string | number | undefined): Diagnostic { + const diagnostic = error(location, message, arg0, arg1, arg2, arg3); + if (maybeMissingAwait) { + const related = createDiagnosticForNode(location, Diagnostics.Did_you_forget_to_use_await); + addRelatedInfo(diagnostic, related); + } + return diagnostic; + } + function createSymbol(flags: SymbolFlags, name: __String, checkFlags?: CheckFlags) { symbolCount++; const symbol = (new Symbol(flags | SymbolFlags.Transient, name)); @@ -3592,14 +3699,10 @@ namespace ts { return createInferTypeNode(typeParameterToDeclarationWithConstraint(type as TypeParameter, context, /*constraintNode*/ undefined)); } if (context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams && - type.flags & TypeFlags.TypeParameter && - length(type.symbol.declarations) && - isTypeParameterDeclaration(type.symbol.declarations[0]) && - typeParameterShadowsNameInScope(type, context) && !isTypeSymbolAccessible(type.symbol, context.enclosingDeclaration)) { - const name = (type.symbol.declarations[0] as TypeParameterDeclaration).name; + const name = typeParameterToName(type, context); context.approximateLength += idText(name).length; - return createTypeReferenceNode(getGeneratedNameForNode(name, GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.ReservedInNestedScopes), /*typeArguments*/ undefined); + return createTypeReferenceNode(createIdentifier(idText(name)), /*typeArguments*/ undefined); } // Ignore constraint/default when creating a usage (as opposed to declaration) of a type parameter. return type.symbol @@ -3986,7 +4089,7 @@ namespace ts { context.enclosingDeclaration = saveEnclosingDeclaration; const optionalToken = propertySymbol.flags & SymbolFlags.Optional ? createToken(SyntaxKind.QuestionToken) : undefined; if (propertySymbol.flags & (SymbolFlags.Function | SymbolFlags.Method) && !getPropertiesOfObjectType(propertyType).length && !isReadonlySymbol(propertySymbol)) { - const signatures = getSignaturesOfType(propertyType, SignatureKind.Call); + const signatures = getSignaturesOfType(filterType(propertyType, t => !(t.flags & TypeFlags.Undefined)), SignatureKind.Call); for (const signature of signatures) { const methodDeclaration = signatureToSignatureDeclarationHelper(signature, SyntaxKind.MethodSignature, context); methodDeclaration.name = propertyName; @@ -4130,21 +4233,10 @@ namespace ts { return createSignatureDeclaration(kind, typeParameters, parameters, returnTypeNode, typeArguments); } - function typeParameterShadowsNameInScope(type: TypeParameter, context: NodeBuilderContext) { - return !!resolveName(context.enclosingDeclaration, type.symbol.escapedName, SymbolFlags.Type, /*nameNotFoundArg*/ undefined, type.symbol.escapedName, /*isUse*/ false); - } - function typeParameterToDeclarationWithConstraint(type: TypeParameter, context: NodeBuilderContext, constraintNode: TypeNode | undefined): TypeParameterDeclaration { const savedContextFlags = context.flags; context.flags &= ~NodeBuilderFlags.WriteTypeParametersInQualifiedName; // Avoids potential infinite loop when building for a claimspace with a generic - const shouldUseGeneratedName = - context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams && - type.symbol.declarations && type.symbol.declarations[0] && - isTypeParameterDeclaration(type.symbol.declarations[0]) && - typeParameterShadowsNameInScope(type, context); - const name = shouldUseGeneratedName - ? getGeneratedNameForNode((type.symbol.declarations[0] as TypeParameterDeclaration).name, GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.ReservedInNestedScopes) - : symbolToName(type.symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ true); + const name = typeParameterToName(type, context); const defaultParameter = getDefaultFromTypeParameter(type); const defaultParameterNode = defaultParameter && typeToTypeNodeHelper(defaultParameter, context); context.flags = savedContextFlags; @@ -4477,6 +4569,35 @@ namespace ts { } } + function typeParameterShadowsNameInScope(escapedName: __String, context: NodeBuilderContext) { + return !!resolveName(context.enclosingDeclaration, escapedName, SymbolFlags.Type, /*nameNotFoundArg*/ undefined, escapedName, /*isUse*/ false); + } + + function typeParameterToName(type: TypeParameter, context: NodeBuilderContext) { + if (context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams && context.typeParameterNames) { + const cached = context.typeParameterNames.get("" + getTypeId(type)); + if (cached) { + return cached; + } + } + let result = symbolToName(type.symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ true); + if (context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams) { + const rawtext = result.escapedText as string; + let i = 0; + let text = rawtext; + while ((context.typeParameterNamesByText && context.typeParameterNamesByText.get(text)) || typeParameterShadowsNameInScope(text as __String, context)) { + i++; + text = `${rawtext}_${i}`; + } + if (text !== rawtext) { + result = createIdentifier(text, result.typeArguments); + } + (context.typeParameterNames || (context.typeParameterNames = createMap())).set("" + getTypeId(type), result); + (context.typeParameterNamesByText || (context.typeParameterNamesByText = createMap())).set(result.escapedText as string, true); + } + return result; + } + function symbolToName(symbol: Symbol, context: NodeBuilderContext, meaning: SymbolFlags, expectsIdentifier: true): Identifier; function symbolToName(symbol: Symbol, context: NodeBuilderContext, meaning: SymbolFlags, expectsIdentifier: false): EntityName; function symbolToName(symbol: Symbol, context: NodeBuilderContext, meaning: SymbolFlags, expectsIdentifier: boolean): EntityName { @@ -4638,6 +4759,8 @@ namespace ts { approximateLength: number; truncating?: boolean; typeParameterSymbolList?: Map; + typeParameterNames?: Map; + typeParameterNamesByText?: Map; } function isDefaultBindingContext(location: Node) { @@ -5106,7 +5229,7 @@ namespace ts { // This elementType will be used if the specific property corresponding to this index is not // present (aka the tuple element property). This call also checks that the parentType is in // fact an iterable or array (depending on target language). - const elementType = checkIteratedTypeOrElementType(parentType, pattern, /*allowStringInput*/ false, /*allowAsyncIterables*/ false); + const elementType = checkIteratedTypeOrElementType(IterationUse.Destructuring, parentType, undefinedType, pattern); const index = pattern.elements.indexOf(declaration); if (declaration.dotDotDotToken) { // If the parent is a tuple type, the rest element has a tuple type of the @@ -5118,21 +5241,25 @@ namespace ts { } else if (isArrayLikeType(parentType)) { const indexType = getLiteralType(index); - const declaredType = getConstraintForLocation(getIndexedAccessType(parentType, indexType, declaration.name), declaration.name); + const accessFlags = hasDefaultValue(declaration) ? AccessFlags.NoTupleBoundsCheck : 0; + const declaredType = getConstraintForLocation(getIndexedAccessTypeOrUndefined(parentType, indexType, declaration.name, accessFlags) || errorType, declaration.name); type = getFlowTypeOfDestructuring(declaration, declaredType); } else { type = elementType; } } - // In strict null checking mode, if a default value of a non-undefined type is specified, remove - // undefined from the final type. - if (strictNullChecks && declaration.initializer && !(getFalsyFlags(checkDeclarationInitializer(declaration)) & TypeFlags.Undefined)) { - type = getTypeWithFacts(type, TypeFacts.NEUndefined); + if (!declaration.initializer) { + return type; } - return declaration.initializer && !getEffectiveTypeAnnotationNode(walkUpBindingElementsAndPatterns(declaration)) ? - getUnionType([type, checkDeclarationInitializer(declaration)], UnionReduction.Subtype) : - type; + if (getEffectiveTypeAnnotationNode(walkUpBindingElementsAndPatterns(declaration))) { + // In strict null checking mode, if a default value of a non-undefined type is specified, remove + // undefined from the final type. + return strictNullChecks && !(getFalsyFlags(checkDeclarationInitializer(declaration)) & TypeFlags.Undefined) ? + getTypeWithFacts(type, TypeFacts.NEUndefined) : + type; + } + return getUnionType([getTypeWithFacts(type, TypeFacts.NEUndefined), checkDeclarationInitializer(declaration)], UnionReduction.Subtype); } function getTypeForDeclarationFromJSDocComment(declaration: Node) { @@ -9334,25 +9461,41 @@ namespace ts { } function getGlobalAsyncIteratorType(reportErrors: boolean) { - return deferredGlobalAsyncIteratorType || (deferredGlobalAsyncIteratorType = getGlobalType("AsyncIterator" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType; + return deferredGlobalAsyncIteratorType || (deferredGlobalAsyncIteratorType = getGlobalType("AsyncIterator" as __String, /*arity*/ 3, reportErrors)) || emptyGenericType; } function getGlobalAsyncIterableIteratorType(reportErrors: boolean) { return deferredGlobalAsyncIterableIteratorType || (deferredGlobalAsyncIterableIteratorType = getGlobalType("AsyncIterableIterator" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType; } + function getGlobalAsyncGeneratorType(reportErrors: boolean) { + return deferredGlobalAsyncGeneratorType || (deferredGlobalAsyncGeneratorType = getGlobalType("AsyncGenerator" as __String, /*arity*/ 3, reportErrors)) || emptyGenericType; + } + function getGlobalIterableType(reportErrors: boolean) { return deferredGlobalIterableType || (deferredGlobalIterableType = getGlobalType("Iterable" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType; } function getGlobalIteratorType(reportErrors: boolean) { - return deferredGlobalIteratorType || (deferredGlobalIteratorType = getGlobalType("Iterator" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType; + return deferredGlobalIteratorType || (deferredGlobalIteratorType = getGlobalType("Iterator" as __String, /*arity*/ 3, reportErrors)) || emptyGenericType; } function getGlobalIterableIteratorType(reportErrors: boolean) { return deferredGlobalIterableIteratorType || (deferredGlobalIterableIteratorType = getGlobalType("IterableIterator" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType; } + function getGlobalGeneratorType(reportErrors: boolean) { + return deferredGlobalGeneratorType || (deferredGlobalGeneratorType = getGlobalType("Generator" as __String, /*arity*/ 3, reportErrors)) || emptyGenericType; + } + + function getGlobalIteratorYieldResultType(reportErrors: boolean) { + return deferredGlobalIteratorYieldResultType || (deferredGlobalIteratorYieldResultType = getGlobalType("IteratorYieldResult" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType; + } + + function getGlobalIteratorReturnResultType(reportErrors: boolean) { + return deferredGlobalIteratorReturnResultType || (deferredGlobalIteratorReturnResultType = getGlobalType("IteratorReturnResult" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType; + } + function getGlobalTypeOrUndefined(name: __String, arity = 0): ObjectType | undefined { const symbol = getGlobalSymbol(name, SymbolFlags.Type, /*diagnostic*/ undefined); return symbol && getTypeOfGlobalSymbol(symbol, arity); @@ -9381,20 +9524,22 @@ namespace ts { return createTypeFromGenericGlobalType(getGlobalTypedPropertyDescriptorType(), [propertyType]); } - function createAsyncIterableType(iteratedType: Type): Type { - return createTypeFromGenericGlobalType(getGlobalAsyncIterableType(/*reportErrors*/ true), [iteratedType]); - } - - function createAsyncIterableIteratorType(iteratedType: Type): Type { - return createTypeFromGenericGlobalType(getGlobalAsyncIterableIteratorType(/*reportErrors*/ true), [iteratedType]); + function createAsyncGeneratorType(yieldType: Type, returnType: Type, nextType: Type) { + const globalAsyncGeneratorType = getGlobalAsyncGeneratorType(/*reportErrors*/ true); + if (globalAsyncGeneratorType !== emptyGenericType) { + yieldType = getAwaitedType(yieldType) || unknownType; + returnType = getAwaitedType(returnType) || unknownType; + nextType = getAwaitedType(nextType) || unknownType; + } + return createTypeFromGenericGlobalType(globalAsyncGeneratorType, [yieldType, returnType, nextType]); } function createIterableType(iteratedType: Type): Type { return createTypeFromGenericGlobalType(getGlobalIterableType(/*reportErrors*/ true), [iteratedType]); } - function createIterableIteratorType(iteratedType: Type): Type { - return createTypeFromGenericGlobalType(getGlobalIterableIteratorType(/*reportErrors*/ true), [iteratedType]); + function createGeneratorType(yieldType: Type, returnType: Type, nextType: Type) { + return createTypeFromGenericGlobalType(getGlobalGeneratorType(/*reportErrors*/ true), [yieldType, returnType, nextType]); } function createArrayType(elementType: Type, readonly?: boolean): ObjectType { @@ -10143,7 +10288,7 @@ namespace ts { propType; } if (everyType(objectType, isTupleType) && isNumericLiteralName(propName) && +propName >= 0) { - if (accessNode && everyType(objectType, t => !(t).target.hasRestElement)) { + if (accessNode && everyType(objectType, t => !(t).target.hasRestElement) && !(accessFlags & AccessFlags.NoTupleBoundsCheck)) { const indexNode = getIndexNodeForAccessExpression(accessNode); if (isTupleType(objectType)) { error(indexNode, Diagnostics.Tuple_type_0_of_length_1_has_no_element_at_index_2, @@ -11425,13 +11570,14 @@ namespace ts { if (!type || !mapper || mapper === identityMapper) { return type; } - if (instantiationDepth === 50) { + if (instantiationDepth === 50 || instantiationCount >= 5000000) { // We have reached 50 recursive type instantiations and there is a very high likelyhood we're dealing // with a combination of infinite generic types that perpetually generate new type identities. We stop // the recursion here by yielding the error type. error(currentNode, Diagnostics.Type_instantiation_is_excessively_deep_and_possibly_infinite); return errorType; } + instantiationCount++; instantiationDepth++; const result = instantiateTypeWorker(type, mapper); instantiationDepth--; @@ -11676,7 +11822,7 @@ namespace ts { return isTypeComparableTo(type1, type2) || isTypeComparableTo(type2, type1); } - function checkTypeAssignableTo(source: Type, target: Type, errorNode: Node | undefined, headMessage?: DiagnosticMessage, containingMessageChain?: () => DiagnosticMessageChain | undefined, errorOutputObject?: { error?: Diagnostic }): boolean { + function checkTypeAssignableTo(source: Type, target: Type, errorNode: Node | undefined, headMessage?: DiagnosticMessage, containingMessageChain?: () => DiagnosticMessageChain | undefined, errorOutputObject?: { errors?: Diagnostic[] }): boolean { return checkTypeRelatedTo(source, target, assignableRelation, errorNode, headMessage, containingMessageChain, errorOutputObject); } @@ -11685,13 +11831,22 @@ namespace ts { * attempt to issue more specific errors on, for example, specific object literal properties or tuple members. */ function checkTypeAssignableToAndOptionallyElaborate(source: Type, target: Type, errorNode: Node | undefined, expr: Expression | undefined, headMessage?: DiagnosticMessage, containingMessageChain?: () => DiagnosticMessageChain | undefined): boolean { - return checkTypeRelatedToAndOptionallyElaborate(source, target, assignableRelation, errorNode, expr, headMessage, containingMessageChain); + return checkTypeRelatedToAndOptionallyElaborate(source, target, assignableRelation, errorNode, expr, headMessage, containingMessageChain, /*errorOutputContainer*/ undefined); } - function checkTypeRelatedToAndOptionallyElaborate(source: Type, target: Type, relation: Map, errorNode: Node | undefined, expr: Expression | undefined, headMessage?: DiagnosticMessage, containingMessageChain?: () => DiagnosticMessageChain | undefined): boolean { + function checkTypeRelatedToAndOptionallyElaborate( + source: Type, + target: Type, + relation: Map, + errorNode: Node | undefined, + expr: Expression | undefined, + headMessage: DiagnosticMessage | undefined, + containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, + errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } | undefined + ): boolean { if (isTypeRelatedTo(source, target, relation)) return true; - if (!errorNode || !elaborateError(expr, source, target, relation, headMessage)) { - return checkTypeRelatedTo(source, target, relation, errorNode, headMessage, containingMessageChain); + if (!errorNode || !elaborateError(expr, source, target, relation, headMessage, containingMessageChain, errorOutputContainer)) { + return checkTypeRelatedTo(source, target, relation, errorNode, headMessage, containingMessageChain, errorOutputContainer); } return false; } @@ -11700,35 +11855,52 @@ namespace ts { return !!(type.flags & TypeFlags.Conditional || (type.flags & TypeFlags.Intersection && some((type as IntersectionType).types, isOrHasGenericConditional))); } - function elaborateError(node: Expression | undefined, source: Type, target: Type, relation: Map, headMessage: DiagnosticMessage | undefined): boolean { + function elaborateError( + node: Expression | undefined, + source: Type, + target: Type, + relation: Map, + headMessage: DiagnosticMessage | undefined, + containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, + errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } | undefined + ): boolean { if (!node || isOrHasGenericConditional(target)) return false; - if (!checkTypeRelatedTo(source, target, relation, /*errorNode*/ undefined) && elaborateDidYouMeanToCallOrConstruct(node, source, target, relation, headMessage)) { + if (!checkTypeRelatedTo(source, target, relation, /*errorNode*/ undefined) + && elaborateDidYouMeanToCallOrConstruct(node, source, target, relation, headMessage, containingMessageChain, errorOutputContainer)) { return true; } switch (node.kind) { case SyntaxKind.JsxExpression: case SyntaxKind.ParenthesizedExpression: - return elaborateError((node as ParenthesizedExpression | JsxExpression).expression, source, target, relation, headMessage); + return elaborateError((node as ParenthesizedExpression | JsxExpression).expression, source, target, relation, headMessage, containingMessageChain, errorOutputContainer); case SyntaxKind.BinaryExpression: switch ((node as BinaryExpression).operatorToken.kind) { case SyntaxKind.EqualsToken: case SyntaxKind.CommaToken: - return elaborateError((node as BinaryExpression).right, source, target, relation, headMessage); + return elaborateError((node as BinaryExpression).right, source, target, relation, headMessage, containingMessageChain, errorOutputContainer); } break; case SyntaxKind.ObjectLiteralExpression: - return elaborateObjectLiteral(node as ObjectLiteralExpression, source, target, relation); + return elaborateObjectLiteral(node as ObjectLiteralExpression, source, target, relation, containingMessageChain, errorOutputContainer); case SyntaxKind.ArrayLiteralExpression: - return elaborateArrayLiteral(node as ArrayLiteralExpression, source, target, relation); + return elaborateArrayLiteral(node as ArrayLiteralExpression, source, target, relation, containingMessageChain, errorOutputContainer); case SyntaxKind.JsxAttributes: - return elaborateJsxComponents(node as JsxAttributes, source, target, relation); + return elaborateJsxComponents(node as JsxAttributes, source, target, relation, containingMessageChain, errorOutputContainer); case SyntaxKind.ArrowFunction: - return elaborateArrowFunction(node as ArrowFunction, source, target, relation); + return elaborateArrowFunction(node as ArrowFunction, source, target, relation, containingMessageChain, errorOutputContainer); } return false; } - function elaborateDidYouMeanToCallOrConstruct(node: Expression, source: Type, target: Type, relation: Map, headMessage: DiagnosticMessage | undefined): boolean { + function elaborateDidYouMeanToCallOrConstruct( + node: Expression, + source: Type, + target: Type, + relation: Map, + headMessage: DiagnosticMessage | undefined, + containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, + errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } | undefined + ): boolean { const callSignatures = getSignaturesOfType(source, SignatureKind.Call); const constructSignatures = getSignaturesOfType(source, SignatureKind.Construct); for (const signatures of [constructSignatures, callSignatures]) { @@ -11736,9 +11908,9 @@ namespace ts { const returnType = getReturnTypeOfSignature(s); return !(returnType.flags & (TypeFlags.Any | TypeFlags.Never)) && checkTypeRelatedTo(returnType, target, relation, /*errorNode*/ undefined); })) { - const resultObj: { error?: Diagnostic } = {}; - checkTypeAssignableTo(source, target, node, headMessage, /*containingChain*/ undefined, resultObj); - const diagnostic = resultObj.error!; + const resultObj: { errors?: Diagnostic[] } = errorOutputContainer || {}; + checkTypeAssignableTo(source, target, node, headMessage, containingMessageChain, resultObj); + const diagnostic = resultObj.errors![resultObj.errors!.length - 1]; addRelatedInfo(diagnostic, createDiagnosticForNode( node, signatures === constructSignatures ? Diagnostics.Did_you_mean_to_use_new_with_this_expression : Diagnostics.Did_you_mean_to_call_this_expression @@ -11749,7 +11921,14 @@ namespace ts { return false; } - function elaborateArrowFunction(node: ArrowFunction, source: Type, target: Type, relation: Map): boolean { + function elaborateArrowFunction( + node: ArrowFunction, + source: Type, + target: Type, + relation: Map, + containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, + errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } | undefined + ): boolean { // Don't elaborate blocks if (isBlock(node.body)) { return false; @@ -11770,15 +11949,15 @@ namespace ts { const sourceReturn = getReturnTypeOfSignature(sourceSig); const targetReturn = getUnionType(map(targetSignatures, getReturnTypeOfSignature)); if (!checkTypeRelatedTo(sourceReturn, targetReturn, relation, /*errorNode*/ undefined)) { - const elaborated = returnExpression && elaborateError(returnExpression, sourceReturn, targetReturn, relation, /*headMessage*/ undefined); + const elaborated = returnExpression && elaborateError(returnExpression, sourceReturn, targetReturn, relation, /*headMessage*/ undefined, containingMessageChain, errorOutputContainer); if (elaborated) { return elaborated; } - const resultObj: { error?: Diagnostic } = {}; - checkTypeRelatedTo(sourceReturn, targetReturn, relation, returnExpression, /*message*/ undefined, /*chain*/ undefined, resultObj); - if (resultObj.error) { + const resultObj: { errors?: Diagnostic[] } = errorOutputContainer || {}; + checkTypeRelatedTo(sourceReturn, targetReturn, relation, returnExpression, /*message*/ undefined, containingMessageChain, resultObj); + if (resultObj.errors) { if (target.symbol && length(target.symbol.declarations)) { - addRelatedInfo(resultObj.error, createDiagnosticForNode( + addRelatedInfo(resultObj.errors[resultObj.errors.length - 1], createDiagnosticForNode( target.symbol.declarations[0], Diagnostics.The_expected_type_comes_from_the_return_type_of_this_signature, )); @@ -11795,7 +11974,14 @@ namespace ts { * If that element would issue an error, we first attempt to dive into that element's inner expression and issue a more specific error by recuring into `elaborateError` * Otherwise, we issue an error on _every_ element which fail the assignability check */ - function elaborateElementwise(iterator: ElaborationIterator, source: Type, target: Type, relation: Map) { + function elaborateElementwise( + iterator: ElaborationIterator, + source: Type, + target: Type, + relation: Map, + containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, + errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } | undefined + ) { // Assignability failure - check each prop individually, and if that fails, fall back on the bad error span let reportedError = false; for (let status = iterator.next(); !status.done; status = iterator.next()) { @@ -11804,22 +11990,22 @@ namespace ts { if (!targetPropType || targetPropType.flags & TypeFlags.IndexedAccess) continue; // Don't elaborate on indexes on generic variables const sourcePropType = getIndexedAccessTypeOrUndefined(source, nameType); if (sourcePropType && !checkTypeRelatedTo(sourcePropType, targetPropType, relation, /*errorNode*/ undefined)) { - const elaborated = next && elaborateError(next, sourcePropType, targetPropType, relation, /*headMessage*/ undefined); + const elaborated = next && elaborateError(next, sourcePropType, targetPropType, relation, /*headMessage*/ undefined, containingMessageChain, errorOutputContainer); if (elaborated) { reportedError = true; } else { // Issue error on the prop itself, since the prop couldn't elaborate the error - const resultObj: { error?: Diagnostic } = {}; + const resultObj: { errors?: Diagnostic[] } = errorOutputContainer || {}; // Use the expression type, if available const specificSource = next ? checkExpressionForMutableLocation(next, CheckMode.Normal, sourcePropType) : sourcePropType; - const result = checkTypeRelatedTo(specificSource, targetPropType, relation, prop, errorMessage, /*containingChain*/ undefined, resultObj); + const result = checkTypeRelatedTo(specificSource, targetPropType, relation, prop, errorMessage, containingMessageChain, resultObj); if (result && specificSource !== sourcePropType) { // If for whatever reason the expression type doesn't yield an error, make sure we still issue an error on the sourcePropType - checkTypeRelatedTo(sourcePropType, targetPropType, relation, prop, errorMessage, /*containingChain*/ undefined, resultObj); + checkTypeRelatedTo(sourcePropType, targetPropType, relation, prop, errorMessage, containingMessageChain, resultObj); } - if (resultObj.error) { - const reportedDiag = resultObj.error; + if (resultObj.errors) { + const reportedDiag = resultObj.errors[resultObj.errors.length - 1]; const propertyName = isTypeUsableAsPropertyName(nameType) ? getPropertyNameFromType(nameType) : undefined; const targetProp = propertyName !== undefined ? getPropertyOfType(target, propertyName) : undefined; @@ -11902,8 +12088,15 @@ namespace ts { return filter(children, i => !isJsxText(i) || !i.containsOnlyTriviaWhiteSpaces); } - function elaborateJsxComponents(node: JsxAttributes, source: Type, target: Type, relation: Map) { - let result = elaborateElementwise(generateJsxAttributes(node), source, target, relation); + function elaborateJsxComponents( + node: JsxAttributes, + source: Type, + target: Type, + relation: Map, + containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, + errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } | undefined + ) { + let result = elaborateElementwise(generateJsxAttributes(node), source, target, relation, containingMessageChain, errorOutputContainer); let invalidTextDiagnostic: DiagnosticMessage | undefined; if (isJsxOpeningElement(node.parent) && isJsxElement(node.parent.parent)) { const containingElement = node.parent.parent; @@ -11921,17 +12114,21 @@ namespace ts { if (moreThanOneRealChildren) { if (arrayLikeTargetParts !== neverType) { const realSource = createTupleType(checkJsxChildren(containingElement, CheckMode.Normal)); - result = elaborateElementwise(generateJsxChildren(containingElement, getInvalidTextualChildDiagnostic), realSource, arrayLikeTargetParts, relation) || result; + const children = generateJsxChildren(containingElement, getInvalidTextualChildDiagnostic); + result = elaborateElementwise(children, realSource, arrayLikeTargetParts, relation, containingMessageChain, errorOutputContainer) || result; } else if (!isTypeRelatedTo(getIndexedAccessType(source, childrenNameType), childrenTargetType, relation)) { // arity mismatch result = true; - error( + const diag = error( containingElement.openingElement.tagName, Diagnostics.This_JSX_tag_s_0_prop_expects_a_single_child_of_type_1_but_multiple_children_were_provided, childrenPropName, typeToString(childrenTargetType) ); + if (errorOutputContainer && errorOutputContainer.skipLogging) { + (errorOutputContainer.errors || (errorOutputContainer.errors = [])).push(diag); + } } } else { @@ -11943,19 +12140,24 @@ namespace ts { (function*() { yield elem; })(), source, target, - relation + relation, + containingMessageChain, + errorOutputContainer ) || result; } } else if (!isTypeRelatedTo(getIndexedAccessType(source, childrenNameType), childrenTargetType, relation)) { // arity mismatch result = true; - error( + const diag = error( containingElement.openingElement.tagName, Diagnostics.This_JSX_tag_s_0_prop_expects_type_1_which_requires_multiple_children_but_only_a_single_child_was_provided, childrenPropName, typeToString(childrenTargetType) ); + if (errorOutputContainer && errorOutputContainer.skipLogging) { + (errorOutputContainer.errors || (errorOutputContainer.errors = [])).push(diag); + } } } } @@ -11987,15 +12189,22 @@ namespace ts { } } - function elaborateArrayLiteral(node: ArrayLiteralExpression, source: Type, target: Type, relation: Map) { + function elaborateArrayLiteral( + node: ArrayLiteralExpression, + source: Type, + target: Type, + relation: Map, + containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, + errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } | undefined + ) { if (target.flags & TypeFlags.Primitive) return false; if (isTupleLikeType(source)) { - return elaborateElementwise(generateLimitedTupleElements(node, target), source, target, relation); + return elaborateElementwise(generateLimitedTupleElements(node, target), source, target, relation, containingMessageChain, errorOutputContainer); } // recreate a tuple from the elements, if possible const tupleizedType = checkArrayLiteral(node, CheckMode.Contextual, /*forceTuple*/ true); if (isTupleLikeType(tupleizedType)) { - return elaborateElementwise(generateLimitedTupleElements(node, target), tupleizedType, target, relation); + return elaborateElementwise(generateLimitedTupleElements(node, target), tupleizedType, target, relation, containingMessageChain, errorOutputContainer); } return false; } @@ -12024,9 +12233,16 @@ namespace ts { } } - function elaborateObjectLiteral(node: ObjectLiteralExpression, source: Type, target: Type, relation: Map) { + function elaborateObjectLiteral( + node: ObjectLiteralExpression, + source: Type, + target: Type, + relation: Map, + containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, + errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } | undefined + ) { if (target.flags & TypeFlags.Primitive) return false; - return elaborateElementwise(generateObjectLiteralElements(node), source, target, relation); + return elaborateElementwise(generateObjectLiteralElements(node), source, target, relation, containingMessageChain, errorOutputContainer); } /** @@ -12367,6 +12583,7 @@ namespace ts { * @param errorNode The suggested node upon which all errors will be reported, if defined. This may or may not be the actual node used. * @param headMessage If the error chain should be prepended by a head message, then headMessage will be used. * @param containingMessageChain A chain of errors to prepend any new errors found. + * @param errorOutputContainer Return the diagnostic. Do not log if 'skipLogging' is truthy. */ function checkTypeRelatedTo( source: Type, @@ -12375,9 +12592,8 @@ namespace ts { errorNode: Node | undefined, headMessage?: DiagnosticMessage, containingMessageChain?: () => DiagnosticMessageChain | undefined, - errorOutputContainer?: { error?: Diagnostic } + errorOutputContainer?: { errors?: Diagnostic[], skipLogging?: boolean }, ): boolean { - let errorInfo: DiagnosticMessageChain | undefined; let relatedInfo: [DiagnosticRelatedInformation, ...DiagnosticRelatedInformation[]] | undefined; let maybeKeys: string[]; @@ -12393,13 +12609,17 @@ namespace ts { const result = isRelatedTo(source, target, /*reportErrors*/ !!errorNode, headMessage); if (overflow) { - error(errorNode, Diagnostics.Excessive_stack_depth_comparing_types_0_and_1, typeToString(source), typeToString(target)); + const diag = error(errorNode, Diagnostics.Excessive_stack_depth_comparing_types_0_and_1, typeToString(source), typeToString(target)); + if (errorOutputContainer) { + (errorOutputContainer.errors || (errorOutputContainer.errors = [])).push(diag); + } } else if (errorInfo) { if (containingMessageChain) { const chain = containingMessageChain(); if (chain) { - errorInfo = concatenateDiagnosticMessageChains(chain, errorInfo); + concatenateDiagnosticMessageChains(chain, errorInfo); + errorInfo = chain; } } @@ -12416,15 +12636,19 @@ namespace ts { } } } - const diag = createDiagnosticForNodeFromMessageChain(errorNode!, errorInfo, relatedInformation); if (relatedInfo) { addRelatedInfo(diag, ...relatedInfo); } if (errorOutputContainer) { - errorOutputContainer.error = diag; + (errorOutputContainer.errors || (errorOutputContainer.errors = [])).push(diag); } - diagnostics.add(diag); // TODO: GH#18217 + if (!errorOutputContainer || !errorOutputContainer.skipLogging) { + diagnostics.add(diag); + } + } + if (errorNode && errorOutputContainer && errorOutputContainer.skipLogging && result === Ternary.False) { + Debug.assert(!!errorOutputContainer.errors, "missed opportunity to interact with error."); } return result !== Ternary.False; @@ -13212,14 +13436,14 @@ namespace ts { if (!isGenericMappedType(source)) { const targetConstraint = getConstraintTypeFromMappedType(target); const sourceKeys = getIndexType(source, /*stringsOnly*/ undefined, /*noIndexSignatures*/ true); - const hasOptionalUnionKeys = modifiers & MappedTypeModifiers.IncludeOptional && targetConstraint.flags & TypeFlags.Union; - const filteredByApplicability = hasOptionalUnionKeys ? filterType(targetConstraint, t => !!isRelatedTo(t, sourceKeys)) : undefined; + const includeOptional = modifiers & MappedTypeModifiers.IncludeOptional; + const filteredByApplicability = includeOptional ? intersectTypes(targetConstraint, sourceKeys) : undefined; // A source type T is related to a target type { [P in Q]: X } if Q is related to keyof T and T[Q] is related to X. // A source type T is related to a target type { [P in Q]?: X } if some constituent Q' of Q is related to keyof T and T[Q'] is related to X. - if (hasOptionalUnionKeys + if (includeOptional ? !(filteredByApplicability!.flags & TypeFlags.Never) : isRelatedTo(targetConstraint, sourceKeys)) { - const indexingType = hasOptionalUnionKeys ? filteredByApplicability! : getTypeParameterFromMappedType(target); + const indexingType = filteredByApplicability || getTypeParameterFromMappedType(target); const indexedAccessType = getIndexedAccessType(source, indexingType); const templateType = getTemplateTypeFromMappedType(target); if (result = isRelatedTo(indexedAccessType, templateType, reportErrors)) { @@ -14521,6 +14745,25 @@ namespace ts { return type; } + function getWidenedLiteralLikeTypeForContextualReturnTypeIfNeeded(type: Type | undefined, contextualSignatureReturnType: Type | undefined, isAsync: boolean) { + if (type && isUnitType(type)) { + const contextualType = !contextualSignatureReturnType ? undefined : + isAsync ? getPromisedTypeOfPromise(contextualSignatureReturnType) : + contextualSignatureReturnType; + type = getWidenedLiteralLikeTypeForContextualType(type, contextualType); + } + return type; + } + + function getWidenedLiteralLikeTypeForContextualIterationTypeIfNeeded(type: Type | undefined, contextualSignatureReturnType: Type | undefined, kind: IterationTypeKind, isAsyncGenerator: boolean) { + if (type && isUnitType(type)) { + const contextualType = !contextualSignatureReturnType ? undefined : + getIterationTypeOfGeneratorFunctionReturnType(kind, contextualSignatureReturnType, isAsyncGenerator); + type = getWidenedLiteralLikeTypeForContextualType(type, contextualType); + } + return type; + } + /** * Check if a Type was written as a tuple type literal. * Prefer using isTupleLikeType() unless the use of `elementTypes` is required. @@ -14877,7 +15120,7 @@ namespace ts { return errorReported; } - function reportImplicitAny(declaration: Declaration, type: Type) { + function reportImplicitAny(declaration: Declaration, type: Type, wideningKind?: WideningKind) { const typeAsString = typeToString(getWidenedType(type)); if (isInJSFile(declaration) && !isCheckJsEnabledForFile(getSourceFileOfNode(declaration), compilerOptions)) { // Only report implicit any errors/suggestions in TS and ts-check JS files @@ -14923,10 +15166,17 @@ namespace ts { case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: if (noImplicitAny && !(declaration as NamedDeclaration).name) { - error(declaration, Diagnostics.Function_expression_which_lacks_return_type_annotation_implicitly_has_an_0_return_type, typeAsString); + if (wideningKind === WideningKind.GeneratorYield) { + error(declaration, Diagnostics.Generator_implicitly_has_yield_type_0_because_it_does_not_yield_any_values_Consider_supplying_a_return_type_annotation, typeAsString); + } + else { + error(declaration, Diagnostics.Function_expression_which_lacks_return_type_annotation_implicitly_has_an_0_return_type, typeAsString); + } return; } - diagnostic = noImplicitAny ? Diagnostics._0_which_lacks_return_type_annotation_implicitly_has_an_1_return_type : Diagnostics._0_implicitly_has_an_1_return_type_but_a_better_type_may_be_inferred_from_usage; + diagnostic = !noImplicitAny ? Diagnostics._0_implicitly_has_an_1_return_type_but_a_better_type_may_be_inferred_from_usage : + wideningKind === WideningKind.GeneratorYield ? Diagnostics._0_which_lacks_return_type_annotation_implicitly_has_an_1_yield_type : + Diagnostics._0_which_lacks_return_type_annotation_implicitly_has_an_1_return_type; break; case SyntaxKind.MappedType: if (noImplicitAny) { @@ -14939,11 +15189,11 @@ namespace ts { errorOrSuggestion(noImplicitAny, declaration, diagnostic, declarationNameToString(getNameOfDeclaration(declaration)), typeAsString); } - function reportErrorsFromWidening(declaration: Declaration, type: Type) { + function reportErrorsFromWidening(declaration: Declaration, type: Type, wideningKind?: WideningKind) { if (produceDiagnostics && noImplicitAny && getObjectFlags(type) & ObjectFlags.ContainsWideningType) { // Report implicit any error within type if possible, otherwise report error on declaration if (!reportWideningErrorsInType(type)) { - reportImplicitAny(declaration, type); + reportImplicitAny(declaration, type, wideningKind); } } } @@ -15065,7 +15315,11 @@ namespace ts { } function isTypeParameterAtTopLevel(type: Type, typeParameter: TypeParameter): boolean { - return type === typeParameter || !!(type.flags & TypeFlags.UnionOrIntersection) && some((type).types, t => isTypeParameterAtTopLevel(t, typeParameter)); + return !!(type === typeParameter || + type.flags & TypeFlags.UnionOrIntersection && some((type).types, t => isTypeParameterAtTopLevel(t, typeParameter)) || + type.flags & TypeFlags.Conditional && ( + isTypeParameterAtTopLevel(getTrueTypeFromConditionalType(type), typeParameter) || + isTypeParameterAtTopLevel(getFalseTypeFromConditionalType(type), typeParameter))); } /** Create an object with properties named in the string literal type. Every property has type `any` */ @@ -15152,7 +15406,7 @@ namespace ts { return getTypeFromInference(inference) || unknownType; } - function* getUnmatchedProperties(source: Type, target: Type, requireOptionalProperties: boolean, matchDiscriminantProperties: boolean) { + function* getUnmatchedProperties(source: Type, target: Type, requireOptionalProperties: boolean, matchDiscriminantProperties: boolean): IterableIterator { const properties = target.flags & TypeFlags.Union ? getPossiblePropertiesOfUnionType(target as UnionType) : getPropertiesOfType(target); for (const targetProp of properties) { if (requireOptionalProperties || !(targetProp.flags & SymbolFlags.Optional || getCheckFlags(targetProp) & CheckFlags.Partial)) { @@ -15174,7 +15428,8 @@ namespace ts { } function getUnmatchedProperty(source: Type, target: Type, requireOptionalProperties: boolean, matchDiscriminantProperties: boolean): Symbol | undefined { - return getUnmatchedProperties(source, target, requireOptionalProperties, matchDiscriminantProperties).next().value; + const result = getUnmatchedProperties(source, target, requireOptionalProperties, matchDiscriminantProperties).next(); + if (!result.done) return result.value; } function tupleTypesDefinitelyUnrelated(source: TupleTypeReference, target: TupleTypeReference) { @@ -15354,36 +15609,11 @@ namespace ts { inferFromTypes(getFalseTypeFromConditionalType(source), getFalseTypeFromConditionalType(target)); } else if (target.flags & TypeFlags.Conditional && !contravariant) { - inferFromTypes(source, getTrueTypeFromConditionalType(target)); - inferFromTypes(source, getFalseTypeFromConditionalType(target)); + const targetTypes = [getTrueTypeFromConditionalType(target), getFalseTypeFromConditionalType(target)]; + inferToMultipleTypes(source, targetTypes, /*isIntersection*/ false); } else if (target.flags & TypeFlags.UnionOrIntersection) { - // We infer from types that are not naked type variables first so that inferences we - // make from nested naked type variables and given slightly higher priority by virtue - // of being first in the candidates array. - let typeVariableCount = 0; - for (const t of (target).types) { - if (getInferenceInfoForType(t)) { - typeVariableCount++; - } - else { - inferFromTypes(source, t); - } - } - // Inferences directly to naked type variables are given lower priority as they are - // less specific. For example, when inferring from Promise to T | Promise, - // we want to infer string for T, not Promise | string. For intersection types - // we only infer to single naked type variables. - if (target.flags & TypeFlags.Union ? typeVariableCount !== 0 : typeVariableCount === 1) { - const savePriority = priority; - priority |= InferencePriority.NakedTypeVariable; - for (const t of (target).types) { - if (getInferenceInfoForType(t)) { - inferFromTypes(source, t); - } - } - priority = savePriority; - } + inferToMultipleTypes(source, (target).types, !!(target.flags & TypeFlags.Intersection)); } else if (source.flags & TypeFlags.Union) { // Source is a union or intersection type, infer from each constituent type @@ -15481,6 +15711,35 @@ namespace ts { return undefined; } + function inferToMultipleTypes(source: Type, targets: Type[], isIntersection: boolean) { + // We infer from types that are not naked type variables first so that inferences we + // make from nested naked type variables and given slightly higher priority by virtue + // of being first in the candidates array. + let typeVariableCount = 0; + for (const t of targets) { + if (getInferenceInfoForType(t)) { + typeVariableCount++; + } + else { + inferFromTypes(source, t); + } + } + // Inferences directly to naked type variables are given lower priority as they are + // less specific. For example, when inferring from Promise to T | Promise, + // we want to infer string for T, not Promise | string. For intersection types + // we only infer to single naked type variables. + if (isIntersection ? typeVariableCount === 1 : typeVariableCount !== 0) { + const savePriority = priority; + priority |= InferencePriority.NakedTypeVariable; + for (const t of targets) { + if (getInferenceInfoForType(t)) { + inferFromTypes(source, t); + } + } + priority = savePriority; + } + } + function inferToMappedType(source: Type, target: MappedType, constraintType: Type): boolean { if (constraintType.flags & TypeFlags.Union) { let result = false; @@ -16149,12 +16408,12 @@ namespace ts { function getTypeOfDestructuredArrayElement(type: Type, index: number) { return everyType(type, isTupleLikeType) && getTupleElementType(type, index) || - checkIteratedTypeOrElementType(type, /*errorNode*/ undefined, /*allowStringInput*/ false, /*allowAsyncIterables*/ false) || + checkIteratedTypeOrElementType(IterationUse.Destructuring, type, undefinedType, /*errorNode*/ undefined) || errorType; } function getTypeOfDestructuredSpreadExpression(type: Type) { - return createArrayType(checkIteratedTypeOrElementType(type, /*errorNode*/ undefined, /*allowStringInput*/ false, /*allowAsyncIterables*/ false) || errorType); + return createArrayType(checkIteratedTypeOrElementType(IterationUse.Destructuring, type, undefinedType, /*errorNode*/ undefined) || errorType); } function getAssignedTypeOfBinaryExpression(node: BinaryExpression): Type { @@ -18277,7 +18536,7 @@ namespace ts { // There was no contextual ThisType for the containing object literal, so the contextual type // for 'this' is the non-null form of the contextual type for the containing object literal or // the type of the object literal itself. - return contextualType ? getNonNullableType(contextualType) : checkExpressionCached(containingLiteral); + return getWidenedType(contextualType ? getNonNullableType(contextualType) : checkExpressionCached(containingLiteral)); } // In an assignment of the form 'obj.xxx = function(...)' or 'obj[xxx] = function(...)', the // contextual type for 'this' is 'obj'. @@ -18294,7 +18553,7 @@ namespace ts { } } - return checkExpressionCached(expression); + return getWidenedType(checkExpressionCached(expression)); } } } @@ -18431,7 +18690,7 @@ namespace ts { if (contextualReturnType) { return node.asteriskToken ? contextualReturnType - : getIteratedTypeOfGenerator(contextualReturnType, (functionFlags & FunctionFlags.Async) !== 0); + : getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Yield, contextualReturnType, (functionFlags & FunctionFlags.Async) !== 0); } } @@ -18664,7 +18923,7 @@ namespace ts { function getContextualTypeForElementExpression(arrayContextualType: Type | undefined, index: number): Type | undefined { return arrayContextualType && ( getTypeOfPropertyOfContextualType(arrayContextualType, "" + index as __String) - || getIteratedTypeOrElementType(arrayContextualType, /*errorNode*/ undefined, /*allowStringInput*/ false, /*allowAsyncIterables*/ false, /*checkAssignability*/ false)); + || getIteratedTypeOrElementType(IterationUse.Element, arrayContextualType, undefinedType, /*errorNode*/ undefined, /*checkAssignability*/ false)); } // In a contextually typed conditional expression, the true/false expressions are contextually typed by the same type. @@ -18862,6 +19121,10 @@ namespace ts { case SyntaxKind.AwaitExpression: return getContextualTypeForAwaitOperand(parent); case SyntaxKind.CallExpression: + if ((parent).expression.kind === SyntaxKind.ImportKeyword) { + return stringType; + } + /* falls through */ case SyntaxKind.NewExpression: return getContextualTypeForArgument(parent, node); case SyntaxKind.TypeAssertionExpression: @@ -19118,7 +19381,9 @@ namespace ts { } } // Result is union of signatures collected (return type is union of return types of this signature set) - return signatureList && createUnionSignature(signatureList[0], signatureList); + if (signatureList) { + return signatureList.length === 1 ? signatureList[0] : createUnionSignature(signatureList[0], signatureList); + } } function checkSpreadExpression(node: SpreadElement, checkMode?: CheckMode): Type { @@ -19127,7 +19392,7 @@ namespace ts { } const arrayOrIterableType = checkExpression(node.expression, checkMode); - return checkIteratedTypeOrElementType(arrayOrIterableType, node.expression, /*allowStringInput*/ false, /*allowAsyncIterables*/ false); + return checkIteratedTypeOrElementType(IterationUse.Spread, arrayOrIterableType, undefinedType, node.expression); } function hasDefaultValue(node: BindingElement | Expression): boolean { @@ -19160,7 +19425,7 @@ namespace ts { // if there is no index type / iterated type. const restArrayType = checkExpression((e).expression, checkMode, forceTuple); const restElementType = getIndexTypeOfType(restArrayType, IndexKind.Number) || - getIteratedTypeOrElementType(restArrayType, /*errorNode*/ undefined, /*allowStringInput*/ false, /*allowAsyncIterables*/ false, /*checkAssignability*/ false); + getIteratedTypeOrElementType(IterationUse.Destructuring, restArrayType, undefinedType, /*errorNode*/ undefined, /*checkAssignability*/ false); if (restElementType) { elementTypes.push(restElementType); } @@ -19212,26 +19477,7 @@ namespace ts { function getArrayLiteralTupleTypeIfApplicable(elementTypes: Type[], contextualType: Type | undefined, hasRestElement: boolean, elementCount = elementTypes.length, readonly = false) { // Infer a tuple type when the contextual type is or contains a tuple-like type if (readonly || (contextualType && forEachType(contextualType, isTupleLikeType))) { - const minLength = elementCount - (hasRestElement ? 1 : 0); - const pattern = contextualType && contextualType.pattern; - // If array literal is contextually typed by a binding pattern or an assignment pattern, pad the resulting - // tuple type with the corresponding binding or assignment element types to make the lengths equal. - if (!hasRestElement && pattern && (pattern.kind === SyntaxKind.ArrayBindingPattern || pattern.kind === SyntaxKind.ArrayLiteralExpression)) { - const patternElements = (pattern).elements; - for (let i = elementCount; i < patternElements.length; i++) { - const e = patternElements[i]; - if (hasDefaultValue(e)) { - elementTypes.push((contextualType).typeArguments![i]); - } - else if (i < patternElements.length - 1 || !(e.kind === SyntaxKind.BindingElement && (e).dotDotDotToken || e.kind === SyntaxKind.SpreadElement)) { - if (e.kind !== SyntaxKind.OmittedExpression) { - error(e, Diagnostics.Initializer_provides_no_value_for_this_binding_element_and_the_binding_element_has_no_default_value); - } - elementTypes.push(strictNullChecks ? implicitNeverType : undefinedWideningType); - } - } - } - return createTupleType(elementTypes, minLength, hasRestElement, readonly); + return createTupleType(elementTypes, elementCount - (hasRestElement ? 1 : 0), hasRestElement, readonly); } } @@ -21138,24 +21384,48 @@ namespace ts { * @param signature a candidate signature we are trying whether it is a call signature * @param relation a relationship to check parameter and argument type */ - function checkApplicableSignatureForJsxOpeningLikeElement(node: JsxOpeningLikeElement, signature: Signature, relation: Map, checkMode: CheckMode, reportErrors: boolean) { + function checkApplicableSignatureForJsxOpeningLikeElement( + node: JsxOpeningLikeElement, + signature: Signature, + relation: Map, + checkMode: CheckMode, + reportErrors: boolean, + containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, + errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } + ) { // 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, // can be specified by users through attributes property. const paramType = getEffectiveFirstArgumentForJsxSignature(signature, node); const attributesType = checkExpressionWithContextualType(node.attributes, paramType, /*inferenceContext*/ undefined, checkMode); - return checkTypeRelatedToAndOptionallyElaborate(attributesType, paramType, relation, reportErrors ? node.tagName : undefined, node.attributes); + return checkTypeRelatedToAndOptionallyElaborate( + attributesType, + paramType, + relation, + reportErrors ? node.tagName : undefined, + node.attributes, + /*headMessage*/ undefined, + containingMessageChain, + errorOutputContainer); } - function checkApplicableSignature( + function getSignatureApplicabilityError( node: CallLikeExpression, args: ReadonlyArray, signature: Signature, relation: Map, checkMode: CheckMode, - reportErrors: boolean) { + reportErrors: boolean, + containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, + ) { + + const errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } = { errors: undefined, skipLogging: true }; if (isJsxOpeningLikeElement(node)) { - return checkApplicableSignatureForJsxOpeningLikeElement(node, signature, relation, checkMode, reportErrors); + if (!checkApplicableSignatureForJsxOpeningLikeElement(node, signature, relation, checkMode, reportErrors, containingMessageChain, errorOutputContainer)) { + Debug.assert(!reportErrors || !!errorOutputContainer.errors, "jsx should have errors when reporting errors"); + return errorOutputContainer.errors || []; + } + return undefined; } const thisType = getThisTypeOfSignature(signature); if (thisType && thisType !== voidType && node.kind !== SyntaxKind.NewExpression) { @@ -21166,8 +21436,9 @@ namespace ts { const thisArgumentType = thisArgumentNode ? checkExpression(thisArgumentNode) : voidType; const errorNode = reportErrors ? (thisArgumentNode || node) : undefined; const headMessage = Diagnostics.The_this_context_of_type_0_is_not_assignable_to_method_s_this_of_type_1; - if (!checkTypeRelatedTo(thisArgumentType, thisType, relation, errorNode, headMessage)) { - return false; + if (!checkTypeRelatedTo(thisArgumentType, thisType, relation, errorNode, headMessage, containingMessageChain, errorOutputContainer)) { + Debug.assert(!reportErrors || !!errorOutputContainer.errors, "this parameter should have errors when reporting errors"); + return errorOutputContainer.errors || []; } } const headMessage = Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1; @@ -21182,17 +21453,36 @@ namespace ts { // we obtain the regular type of any object literal arguments because we may not have inferred complete // parameter types yet and therefore excess property checks may yield false positives (see #17041). const checkArgType = checkMode & CheckMode.SkipContextSensitive ? getRegularTypeOfObjectLiteral(argType) : argType; - if (!checkTypeRelatedToAndOptionallyElaborate(checkArgType, paramType, relation, reportErrors ? arg : undefined, arg, headMessage)) { - return false; + if (!checkTypeRelatedToAndOptionallyElaborate(checkArgType, paramType, relation, reportErrors ? arg : undefined, arg, headMessage, containingMessageChain, errorOutputContainer)) { + Debug.assert(!reportErrors || !!errorOutputContainer.errors, "parameter should have errors when reporting errors"); + maybeAddMissingAwaitInfo(arg, checkArgType, paramType); + return errorOutputContainer.errors || []; } } } if (restType) { const spreadType = getSpreadArgumentType(args, argCount, args.length, restType, /*context*/ undefined); const errorNode = reportErrors ? argCount < args.length ? args[argCount] : node : undefined; - return checkTypeRelatedTo(spreadType, restType, relation, errorNode, headMessage); + if (!checkTypeRelatedTo(spreadType, restType, relation, errorNode, headMessage, /*containingMessageChain*/ undefined, errorOutputContainer)) { + Debug.assert(!reportErrors || !!errorOutputContainer.errors, "rest parameter should have errors when reporting errors"); + maybeAddMissingAwaitInfo(errorNode, spreadType, restType); + return errorOutputContainer.errors || []; + } + } + return undefined; + + function maybeAddMissingAwaitInfo(errorNode: Node | undefined, source: Type, target: Type) { + if (errorNode && reportErrors && errorOutputContainer.errors && errorOutputContainer.errors.length) { + // Bail if target is Promise-like---something else is wrong + if (getAwaitedTypeOfPromise(target)) { + return; + } + const awaitedTypeOfSource = getAwaitedTypeOfPromise(source); + if (awaitedTypeOfSource && isTypeRelatedTo(awaitedTypeOfSource, target, relation)) { + addRelatedInfo(errorOutputContainer.errors[0], createDiagnosticForNode(errorNode, Diagnostics.Did_you_forget_to_use_await)); + } + } } - return true; } /** @@ -21510,7 +21800,7 @@ namespace ts { // function foo(): void; // foo(0); // - let candidateForArgumentError: Signature | undefined; + let candidatesForArgumentError: Signature[] | undefined; let candidateForArgumentArityError: Signature | undefined; let candidateForTypeArgumentError: Signature | undefined; let result: Signature | undefined; @@ -21545,9 +21835,64 @@ namespace ts { // If candidate is undefined, it means that no candidates had a suitable arity. In that case, // skip the checkApplicableSignature check. if (reportErrors) { + if (candidatesForArgumentError) { + if (candidatesForArgumentError.length === 1 || candidatesForArgumentError.length > 3) { + const last = candidatesForArgumentError[candidatesForArgumentError.length - 1]; + let chain: DiagnosticMessageChain | undefined; + if (candidatesForArgumentError.length > 3) { + chain = chainDiagnosticMessages(chain, Diagnostics.The_last_overload_gave_the_following_error); + chain = chainDiagnosticMessages(chain, Diagnostics.No_overload_matches_this_call); + } + const diags = getSignatureApplicabilityError(node, args, last, assignableRelation, CheckMode.Normal, /*reportErrors*/ true, () => chain); + if (diags) { + for (const d of diags) { + if (last.declaration && candidatesForArgumentError.length > 3) { + addRelatedInfo(d, createDiagnosticForNode(last.declaration, Diagnostics.The_last_overload_is_declared_here)); + } + diagnostics.add(d); + } + } + else { + Debug.fail("No error for last overload signature"); + } + } + else { + const allDiagnostics: DiagnosticRelatedInformation[][] = []; + let max = 0; + let min = Number.MAX_VALUE; + let minIndex = 0; + let i = 0; + for (const c of candidatesForArgumentError) { + const chain = () => chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Overload_0_of_1_2_gave_the_following_error, i + 1, candidates.length, signatureToString(c)); + const diags = getSignatureApplicabilityError(node, args, c, assignableRelation, CheckMode.Normal, /*reportErrors*/ true, chain); + if (diags) { + if (diags.length <= min) { + min = diags.length; + minIndex = i; + } + max = Math.max(max, diags.length); + allDiagnostics.push(diags); + } + else { + Debug.fail("No error for 3 or fewer overload signatures"); + } + i++; + } - if (candidateForArgumentError) { - checkApplicableSignature(node, args, candidateForArgumentError, assignableRelation, CheckMode.Normal, /*reportErrors*/ true); + const diags = max > 1 ? allDiagnostics[minIndex] : flatten(allDiagnostics); + Debug.assert(diags.length > 0, "No errors reported for 3 or fewer overload signatures"); + const chain = chainDiagnosticMessages( + map(diags, d => typeof d.messageText === "string" ? (d as DiagnosticMessageChain) : d.messageText), + Diagnostics.No_overload_matches_this_call); + const related = flatMap(diags, d => (d as Diagnostic).relatedInformation) as DiagnosticRelatedInformation[]; + if (every(diags, d => d.start === diags[0].start && d.length === diags[0].length && d.file === diags[0].file)) { + const { file, start, length } = diags[0]; + diagnostics.add({ file, start, length, code: chain.code, category: chain.category, messageText: chain, relatedInformation: related }); + } + else { + diagnostics.add(createDiagnosticForNodeFromMessageChain(node, chain, related)); + } + } } else if (candidateForArgumentArityError) { diagnostics.add(getArgumentArityError(node, [candidateForArgumentArityError], args)); @@ -21572,7 +21917,7 @@ namespace ts { return produceDiagnostics || !args ? resolveErrorCall(node) : getCandidateForOverloadFailure(node, candidates, args, !!candidatesOutArray); function chooseOverload(candidates: Signature[], relation: Map, signatureHelpTrailingComma = false) { - candidateForArgumentError = undefined; + candidatesForArgumentError = undefined; candidateForArgumentArityError = undefined; candidateForTypeArgumentError = undefined; @@ -21581,8 +21926,8 @@ namespace ts { if (typeArguments || !hasCorrectArity(node, args, candidate, signatureHelpTrailingComma)) { return undefined; } - if (!checkApplicableSignature(node, args, candidate, relation, CheckMode.Normal, /*reportErrors*/ false)) { - candidateForArgumentError = candidate; + if (getSignatureApplicabilityError(node, args, candidate, relation, CheckMode.Normal, /*reportErrors*/ false, /*containingMessageChain*/ undefined)) { + candidatesForArgumentError = [candidate]; return undefined; } return candidate; @@ -21622,11 +21967,9 @@ namespace ts { else { checkCandidate = candidate; } - if (!checkApplicableSignature(node, args, checkCandidate, relation, argCheckMode, /*reportErrors*/ false)) { + if (getSignatureApplicabilityError(node, args, checkCandidate, relation, argCheckMode, /*reportErrors*/ false, /*containingMessageChain*/ undefined)) { // Give preference to error candidates that have no rest parameters (as they are more specific) - if (!candidateForArgumentError || getEffectiveRestType(candidateForArgumentError) || !getEffectiveRestType(checkCandidate)) { - candidateForArgumentError = checkCandidate; - } + (candidatesForArgumentError || (candidatesForArgumentError = [])).push(checkCandidate); continue; } if (argCheckMode) { @@ -21644,11 +21987,9 @@ namespace ts { continue; } } - if (!checkApplicableSignature(node, args, checkCandidate, relation, argCheckMode, /*reportErrors*/ false)) { + if (getSignatureApplicabilityError(node, args, checkCandidate, relation, argCheckMode, /*reportErrors*/ false, /*containingMessageChain*/ undefined)) { // Give preference to error candidates that have no rest parameters (as they are more specific) - if (!candidateForArgumentError || getEffectiveRestType(candidateForArgumentError) || !getEffectiveRestType(checkCandidate)) { - candidateForArgumentError = checkCandidate; - } + (candidatesForArgumentError || (candidatesForArgumentError = [])).push(checkCandidate); continue; } } @@ -22043,9 +22384,11 @@ namespace ts { return true; } - function invocationErrorDetails(apparentType: Type, kind: SignatureKind): DiagnosticMessageChain { + function invocationErrorDetails(apparentType: Type, kind: SignatureKind): { messageChain: DiagnosticMessageChain, relatedMessage: DiagnosticMessage | undefined } { let errorInfo: DiagnosticMessageChain | undefined; const isCall = kind === SignatureKind.Call; + const awaitedType = getAwaitedType(apparentType); + const maybeMissingAwait = awaitedType && getSignaturesOfType(awaitedType, kind).length > 0; if (apparentType.flags & TypeFlags.Union) { const types = (apparentType as UnionType).types; let hasSignatures = false; @@ -22110,15 +22453,20 @@ namespace ts { typeToString(apparentType) ); } - return chainDiagnosticMessages( - errorInfo, - isCall ? - Diagnostics.This_expression_is_not_callable : - Diagnostics.This_expression_is_not_constructable - ); + return { + messageChain: chainDiagnosticMessages( + errorInfo, + isCall ? Diagnostics.This_expression_is_not_callable : Diagnostics.This_expression_is_not_constructable + ), + relatedMessage: maybeMissingAwait ? Diagnostics.Did_you_forget_to_use_await : undefined, + }; } function invocationError(errorTarget: Node, apparentType: Type, kind: SignatureKind, relatedInformation?: DiagnosticRelatedInformation) { - const diagnostic = createDiagnosticForNodeFromMessageChain(errorTarget, invocationErrorDetails(apparentType, kind)); + const { messageChain, relatedMessage: relatedInfo } = invocationErrorDetails(apparentType, kind); + const diagnostic = createDiagnosticForNodeFromMessageChain(errorTarget, messageChain); + if (relatedInfo) { + addRelatedInfo(diagnostic, createDiagnosticForNode(errorTarget, relatedInfo)); + } if (isCallExpression(errorTarget.parent)) { const { start, length } = getDiagnosticSpanForCallNode(errorTarget.parent, /* doNotIncludeArguments */ true); diagnostic.start = start; @@ -22218,9 +22566,12 @@ namespace ts { const headMessage = getDiagnosticHeadMessageForDecoratorResolution(node); if (!callSignatures.length) { - let errorInfo = invocationErrorDetails(apparentType, SignatureKind.Call); - errorInfo = chainDiagnosticMessages(errorInfo, headMessage); - const diag = createDiagnosticForNodeFromMessageChain(node.expression, errorInfo); + const errorDetails = invocationErrorDetails(apparentType, SignatureKind.Call); + const messageChain = chainDiagnosticMessages(errorDetails.messageChain, headMessage); + const diag = createDiagnosticForNodeFromMessageChain(node.expression, messageChain); + if (errorDetails.relatedMessage) { + addRelatedInfo(diag, createDiagnosticForNode(node.expression, errorDetails.relatedMessage)); + } diagnostics.add(diag); invocationErrorRecovery(apparentType, SignatureKind.Call, diag); return resolveErrorCall(node); @@ -22943,106 +23294,128 @@ namespace ts { } const functionFlags = getFunctionFlags(func); - let type: Type; - if (func.body.kind !== SyntaxKind.Block) { - type = checkExpressionCached(func.body, checkMode && checkMode & ~CheckMode.SkipGenericFunctions); - if (functionFlags & FunctionFlags.Async) { + const isAsync = (functionFlags & FunctionFlags.Async) !== 0; + const isGenerator = (functionFlags & FunctionFlags.Generator) !== 0; + + let returnType: Type | undefined; + let yieldType: Type | undefined; + let nextType: Type | undefined; + let fallbackReturnType: Type = voidType; + if (func.body.kind !== SyntaxKind.Block) { // Async or normal arrow function + returnType = checkExpressionCached(func.body, checkMode && checkMode & ~CheckMode.SkipGenericFunctions); + if (isAsync) { // From within an async function you can return either a non-promise value or a promise. Any // Promise/A+ compatible implementation will always assimilate any foreign promise, so the // return type of the body should be unwrapped to its awaited type, which we will wrap in // the native Promise type later in this function. - type = checkAwaitedType(type, /*errorNode*/ func, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member); + returnType = checkAwaitedType(returnType, /*errorNode*/ func, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member); } } - else { - let types = checkAndAggregateReturnExpressionTypes(func, checkMode); - if (functionFlags & FunctionFlags.Generator) { // Generator or AsyncGenerator function - types = concatenate(checkAndAggregateYieldOperandTypes(func, checkMode), types); - if (!types || types.length === 0) { - const iterableIteratorAny = functionFlags & FunctionFlags.Async - ? createAsyncIterableIteratorType(anyType) // AsyncGenerator function - : createIterableIteratorType(anyType); // Generator function - if (noImplicitAny) { - error(func.asteriskToken, - Diagnostics.Generator_implicitly_has_type_0_because_it_does_not_yield_any_values_Consider_supplying_a_return_type, typeToString(iterableIteratorAny)); - } - return iterableIteratorAny; - } + else if (isGenerator) { // Generator or AsyncGenerator function + const returnTypes = checkAndAggregateReturnExpressionTypes(func, checkMode); + if (!returnTypes) { + fallbackReturnType = neverType; } - else { - if (!types) { - // For an async function, the return type will not be never, but rather a Promise for never. - return functionFlags & FunctionFlags.Async - ? createPromiseReturnType(func, neverType) // Async function - : neverType; // Normal function - } - if (types.length === 0) { - // For an async function, the return type will not be void, but rather a Promise for void. - return functionFlags & FunctionFlags.Async - ? createPromiseReturnType(func, voidType) // Async function - : voidType; // Normal function - } + else if (returnTypes.length > 0) { + returnType = getUnionType(returnTypes, UnionReduction.Subtype); + } + const { yieldTypes, nextTypes } = checkAndAggregateYieldOperandTypes(func, checkMode); + yieldType = some(yieldTypes) ? getUnionType(yieldTypes, UnionReduction.Subtype) : undefined; + nextType = some(nextTypes) ? getIntersectionType(nextTypes) : undefined; + } + else { // Async or normal function + const types = checkAndAggregateReturnExpressionTypes(func, checkMode); + if (!types) { + // For an async function, the return type will not be never, but rather a Promise for never. + return functionFlags & FunctionFlags.Async + ? createPromiseReturnType(func, neverType) // Async function + : neverType; // Normal function + } + if (types.length === 0) { + // For an async function, the return type will not be void, but rather a Promise for void. + return functionFlags & FunctionFlags.Async + ? createPromiseReturnType(func, voidType) // Async function + : voidType; // Normal function } // Return a union of the return expression types. - type = getUnionType(types, UnionReduction.Subtype); + returnType = getUnionType(types, UnionReduction.Subtype); } - const contextualSignature = getContextualSignatureForFunctionLikeDeclaration(func); - if (!contextualSignature) { - reportErrorsFromWidening(func, type); - } - - if (isUnitType(type)) { - let contextualType = !contextualSignature ? undefined : - contextualSignature === getSignatureFromDeclaration(func) ? type : - getReturnTypeOfSignature(contextualSignature); - if (contextualType) { - switch (functionFlags & FunctionFlags.AsyncGenerator) { - case FunctionFlags.AsyncGenerator: - contextualType = getIteratedTypeOfGenerator(contextualType, /*isAsyncGenerator*/ true); - break; - case FunctionFlags.Generator: - contextualType = getIteratedTypeOfGenerator(contextualType, /*isAsyncGenerator*/ false); - break; - case FunctionFlags.Async: - contextualType = getPromisedTypeOfPromise(contextualType); - break; + if (returnType || yieldType || nextType) { + const contextualSignature = getContextualSignatureForFunctionLikeDeclaration(func); + if (!contextualSignature) { + if (yieldType) reportErrorsFromWidening(func, yieldType, WideningKind.GeneratorYield); + if (returnType) reportErrorsFromWidening(func, returnType); + if (nextType) reportErrorsFromWidening(func, nextType); + } + if (returnType && isUnitType(returnType) || + yieldType && isUnitType(yieldType) || + nextType && isUnitType(nextType)) { + const contextualType = !contextualSignature ? undefined : + contextualSignature === getSignatureFromDeclaration(func) ? isGenerator ? undefined : returnType : + getReturnTypeOfSignature(contextualSignature); + if (isGenerator) { + yieldType = getWidenedLiteralLikeTypeForContextualIterationTypeIfNeeded(yieldType, contextualType, IterationTypeKind.Yield, isAsync); + returnType = getWidenedLiteralLikeTypeForContextualIterationTypeIfNeeded(returnType, contextualType, IterationTypeKind.Return, isAsync); + nextType = getWidenedLiteralLikeTypeForContextualIterationTypeIfNeeded(nextType, contextualType, IterationTypeKind.Next, isAsync); + } + else { + returnType = getWidenedLiteralLikeTypeForContextualReturnTypeIfNeeded(returnType, contextualType, isAsync); } } - type = getWidenedLiteralLikeTypeForContextualType(type, contextualType); + + if (yieldType) yieldType = getWidenedType(yieldType); + if (returnType) returnType = getWidenedType(returnType); + if (nextType) nextType = getWidenedType(nextType); } - const widenedType = getWidenedType(type); - switch (functionFlags & FunctionFlags.AsyncGenerator) { - case FunctionFlags.AsyncGenerator: - return createAsyncIterableIteratorType(widenedType); - case FunctionFlags.Generator: - return createIterableIteratorType(widenedType); - case FunctionFlags.Async: - // From within an async function you can return either a non-promise value or a promise. Any - // Promise/A+ compatible implementation will always assimilate any foreign promise, so the - // return type of the body is awaited type of the body, wrapped in a native Promise type. - return createPromiseType(widenedType); - default: - return widenedType; + if (isGenerator) { + return createGeneratorReturnType(yieldType || neverType, returnType || fallbackReturnType, nextType || unknownType, isAsync); + } + else { + // From within an async function you can return either a non-promise value or a promise. Any + // Promise/A+ compatible implementation will always assimilate any foreign promise, so the + // return type of the body is awaited type of the body, wrapped in a native Promise type. + return isAsync + ? createPromiseType(returnType || fallbackReturnType) + : returnType || fallbackReturnType; } } - function checkAndAggregateYieldOperandTypes(func: FunctionLikeDeclaration, checkMode: CheckMode | undefined): Type[] { - const aggregatedTypes: Type[] = []; + function createGeneratorReturnType(yieldType: Type, returnType: Type, nextType: Type, isAsyncGenerator: boolean) { + return isAsyncGenerator + ? createAsyncGeneratorType(yieldType, returnType, nextType) + : createGeneratorType(yieldType, returnType, nextType); + } + + function checkAndAggregateYieldOperandTypes(func: FunctionLikeDeclaration, checkMode: CheckMode | undefined) { + const yieldTypes: Type[] = []; + const nextTypes: Type[] = []; const isAsync = (getFunctionFlags(func) & FunctionFlags.Async) !== 0; forEachYieldExpression(func.body, yieldExpression => { - pushIfUnique(aggregatedTypes, getYieldedTypeOfYieldExpression(yieldExpression, isAsync, checkMode)); + const yieldExpressionType = yieldExpression.expression ? checkExpression(yieldExpression.expression, checkMode) : undefinedWideningType; + pushIfUnique(yieldTypes, getYieldedTypeOfYieldExpression(yieldExpression, yieldExpressionType, anyType, isAsync)); + let nextType: Type | undefined; + if (yieldExpression.asteriskToken) { + const iterationTypes = getIterationTypesOfIterable( + yieldExpressionType, + isAsync ? IterationUse.AsyncYieldStar : IterationUse.YieldStar, + yieldExpression.expression); + nextType = iterationTypes && iterationTypes.nextType; + } + else { + nextType = getContextualType(yieldExpression); + } + if (nextType) pushIfUnique(nextTypes, nextType); }); - return aggregatedTypes; + return { yieldTypes, nextTypes }; } - function getYieldedTypeOfYieldExpression(node: YieldExpression, isAsync: boolean, checkMode?: CheckMode): Type | undefined { + function getYieldedTypeOfYieldExpression(node: YieldExpression, expressionType: Type, sentType: Type, isAsync: boolean): Type | undefined { const errorNode = node.expression || node; - const expressionType = node.expression ? checkExpression(node.expression, checkMode) : undefinedWideningType; // A `yield*` expression effectively yields everything that its operand yields - const yieldedType = node.asteriskToken ? checkIteratedTypeOrElementType(expressionType, errorNode, /*allowStringInput*/ false, isAsync) : expressionType; + const yieldedType = node.asteriskToken ? checkIteratedTypeOrElementType(isAsync ? IterationUse.AsyncYieldStar : IterationUse.YieldStar, expressionType, sentType, errorNode) : expressionType; return !isAsync ? yieldedType : getAwaitedType(yieldedType, errorNode, node.asteriskToken ? Diagnostics.Type_of_iterated_elements_of_a_yield_Asterisk_operand_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member : Diagnostics.Type_of_yield_operand_in_an_async_generator_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member); @@ -23188,8 +23561,11 @@ namespace ts { return; } + const functionFlags = getFunctionFlags(func); + const type = returnType && getReturnOrPromisedType(returnType, functionFlags); + // Functions with with an explicitly specified 'void' or 'any' return type don't need any return expressions. - if (returnType && maybeTypeOfKind(returnType, TypeFlags.Any | TypeFlags.Void)) { + if (type && maybeTypeOfKind(type, TypeFlags.Any | TypeFlags.Void)) { return; } @@ -23201,20 +23577,20 @@ namespace ts { const hasExplicitReturn = func.flags & NodeFlags.HasExplicitReturn; - if (returnType && returnType.flags & TypeFlags.Never) { + if (type && type.flags & TypeFlags.Never) { error(getEffectiveReturnTypeNode(func), Diagnostics.A_function_returning_never_cannot_have_a_reachable_end_point); } - else if (returnType && !hasExplicitReturn) { + else if (type && !hasExplicitReturn) { // minimal check: function has syntactic return type annotation and no explicit return statements in the body // this function does not conform to the specification. // NOTE: having returnType !== undefined is a precondition for entering this branch so func.type will always be present error(getEffectiveReturnTypeNode(func), Diagnostics.A_function_whose_declared_type_is_neither_void_nor_any_must_return_a_value); } - else if (returnType && strictNullChecks && !isTypeAssignableTo(undefinedType, returnType)) { + else if (type && strictNullChecks && !isTypeAssignableTo(undefinedType, type)) { error(getEffectiveReturnTypeNode(func), Diagnostics.Function_lacks_ending_return_statement_and_return_type_does_not_include_undefined); } else if (compilerOptions.noImplicitReturns) { - if (!returnType) { + if (!type) { // If return type annotation is omitted check if function has any explicit return statements. // If it does not have any - its inferred return type is void - don't do any checks. // Otherwise get inferred return type from function body and report error only if it is not void / anytype @@ -23304,22 +23680,20 @@ namespace ts { } } - function getReturnOrPromisedType(node: FunctionLikeDeclaration | MethodSignature, functionFlags: FunctionFlags) { - const type = getReturnTypeFromAnnotation(node); - return type && ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Async) ? - getAwaitedType(type) || errorType : type; + function getReturnOrPromisedType(type: Type | undefined, functionFlags: FunctionFlags) { + const isGenerator = !!(functionFlags & FunctionFlags.Generator); + const isAsync = !!(functionFlags & FunctionFlags.Async); + return type && isGenerator ? getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Return, type, isAsync) || errorType : + type && isAsync ? getAwaitedType(type) || errorType : + type; } function checkFunctionExpressionOrObjectLiteralMethodDeferred(node: ArrowFunction | FunctionExpression | MethodDeclaration) { Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node)); const functionFlags = getFunctionFlags(node); - const returnOrPromisedType = getReturnOrPromisedType(node, functionFlags); - - if ((functionFlags & FunctionFlags.Generator) === 0) { // Async function or normal function - // return is not necessary in the body of generators - checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, returnOrPromisedType); - } + const returnType = getReturnTypeFromAnnotation(node); + checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, returnType); if (node.body) { if (!getEffectiveReturnTypeNode(node)) { @@ -23341,6 +23715,7 @@ namespace ts { // check assignability of the awaited type of the expression body against the promised type of // its return type annotation. const exprType = checkExpression(node.body); + const returnOrPromisedType = getReturnOrPromisedType(returnType, functionFlags); if (returnOrPromisedType) { if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Async) { // Async function const awaitedType = checkAwaitedType(exprType, node.body, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member); @@ -23354,9 +23729,13 @@ namespace ts { } } - function checkArithmeticOperandType(operand: Node, type: Type, diagnostic: DiagnosticMessage): boolean { + function checkArithmeticOperandType(operand: Node, type: Type, diagnostic: DiagnosticMessage, isAwaitValid = false): boolean { if (!isTypeAssignableTo(type, numberOrBigIntType)) { - error(operand, diagnostic); + const awaitedType = isAwaitValid && getAwaitedTypeOfPromise(type); + errorAndMaybeSuggestAwait( + operand, + !!awaitedType && isTypeAssignableTo(awaitedType, numberOrBigIntType), + diagnostic); return false; } return true; @@ -23743,7 +24122,7 @@ namespace ts { // This elementType will be used if the specific property corresponding to this index is not // present (aka the tuple element property). This call also checks that the parentType is in // fact an iterable or array (depending on target language). - const elementType = checkIteratedTypeOrElementType(sourceType, node, /*allowStringInput*/ false, /*allowAsyncIterables*/ false) || errorType; + const elementType = checkIteratedTypeOrElementType(IterationUse.Destructuring, sourceType, undefinedType, node) || errorType; for (let i = 0; i < elements.length; i++) { checkArrayLiteralDestructuringElementAssignment(node, sourceType, i, elementType, checkMode); } @@ -23760,8 +24139,10 @@ namespace ts { if (isArrayLikeType(sourceType)) { // We create a synthetic expression so that getIndexedAccessType doesn't get confused // when the element is a SyntaxKind.ElementAccessExpression. - const elementType = getIndexedAccessType(sourceType, indexType, createSyntheticExpression(element, indexType)); - const type = getFlowTypeOfDestructuring(element, elementType); + const accessFlags = hasDefaultValue(element) ? AccessFlags.NoTupleBoundsCheck : 0; + const elementType = getIndexedAccessTypeOrUndefined(sourceType, indexType, createSyntheticExpression(element, indexType), accessFlags) || errorType; + const assignedType = hasDefaultValue(element) ? getTypeWithFacts(elementType, TypeFacts.NEUndefined) : elementType; + const type = getFlowTypeOfDestructuring(element, assignedType); return checkDestructuringAssignment(element, type, checkMode); } return checkDestructuringAssignment(element, elementType, checkMode); @@ -23962,8 +24343,8 @@ namespace ts { } else { // otherwise just check each operand separately and report errors as normal - const leftOk = checkArithmeticOperandType(left, leftType, Diagnostics.The_left_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_bigint_or_an_enum_type); - const rightOk = checkArithmeticOperandType(right, rightType, Diagnostics.The_right_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_bigint_or_an_enum_type); + const leftOk = checkArithmeticOperandType(left, leftType, Diagnostics.The_left_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_bigint_or_an_enum_type, /*isAwaitValid*/ true); + const rightOk = checkArithmeticOperandType(right, rightType, Diagnostics.The_right_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_bigint_or_an_enum_type, /*isAwaitValid*/ true); let resultType: Type; // If both are any or unknown, allow operation; assume it will resolve to number if ((isTypeAssignableToKind(leftType, TypeFlags.AnyOrUnknown) && isTypeAssignableToKind(rightType, TypeFlags.AnyOrUnknown)) || @@ -23972,7 +24353,7 @@ namespace ts { ) { resultType = numberType; } - // At least one is assignable to bigint, so both should be only assignable to bigint + // At least one is assignable to bigint, so check that both are else if (isTypeAssignableToKind(leftType, TypeFlags.BigIntLike) && isTypeAssignableToKind(rightType, TypeFlags.BigIntLike)) { switch (operator) { case SyntaxKind.GreaterThanGreaterThanGreaterThanToken: @@ -23981,8 +24362,9 @@ namespace ts { } resultType = bigintType; } + // Exactly one of leftType/rightType is assignable to bigint else { - reportOperatorError(); + reportOperatorError((awaitedLeft, awaitedRight) => isTypeAssignableToKind(awaitedLeft, TypeFlags.BigIntLike) && isTypeAssignableToKind(awaitedRight, TypeFlags.BigIntLike)); resultType = errorType; } if (leftOk && rightOk) { @@ -24027,7 +24409,14 @@ namespace ts { } if (!resultType) { - reportOperatorError(); + // Types that have a reasonably good chance of being a valid operand type. + // If both types have an awaited type of one of these, we’ll assume the user + // might be missing an await without doing an exhaustive check that inserting + // await(s) will actually be a completely valid binary expression. + const closeEnoughKind = TypeFlags.NumberLike | TypeFlags.BigIntLike | TypeFlags.StringLike | TypeFlags.AnyOrUnknown; + reportOperatorError((awaitedLeft, awaitedRight) => + isTypeAssignableToKind(awaitedLeft, closeEnoughKind) && + isTypeAssignableToKind(awaitedRight, closeEnoughKind)); return anyType; } @@ -24042,21 +24431,18 @@ namespace ts { if (checkForDisallowedESSymbolOperand(operator)) { leftType = getBaseTypeOfLiteralType(checkNonNullType(leftType, left)); rightType = getBaseTypeOfLiteralType(checkNonNullType(rightType, right)); - if (!(isTypeComparableTo(leftType, rightType) || isTypeComparableTo(rightType, leftType) || - (isTypeAssignableTo(leftType, numberOrBigIntType) && isTypeAssignableTo(rightType, numberOrBigIntType)) - )) { - reportOperatorError(); - } + reportOperatorErrorUnless((left, right) => + isTypeComparableTo(left, right) || isTypeComparableTo(right, left) || ( + isTypeAssignableTo(left, numberOrBigIntType) && isTypeAssignableTo(right, numberOrBigIntType))); } return booleanType; case SyntaxKind.EqualsEqualsToken: case SyntaxKind.ExclamationEqualsToken: case SyntaxKind.EqualsEqualsEqualsToken: case SyntaxKind.ExclamationEqualsEqualsToken: - if (!isTypeEqualityComparableTo(leftType, rightType) && !isTypeEqualityComparableTo(rightType, leftType)) { - reportOperatorError(); - } + reportOperatorErrorUnless((left, right) => isTypeEqualityComparableTo(left, right) || isTypeEqualityComparableTo(right, left)); return booleanType; + case SyntaxKind.InstanceOfKeyword: return checkInstanceOfExpression(left, right, leftType, rightType); case SyntaxKind.InKeyword: @@ -24183,12 +24569,33 @@ namespace ts { } } - function reportOperatorError() { - const [leftStr, rightStr] = getTypeNamesForErrorDisplay(leftType, rightType); + /** + * Returns true if an error is reported + */ + function reportOperatorErrorUnless(typesAreCompatible: (left: Type, right: Type) => boolean): boolean { + if (!typesAreCompatible(leftType, rightType)) { + reportOperatorError(typesAreCompatible); + return true; + } + return false; + } + + function reportOperatorError(awaitedTypesAreCompatible?: (left: Type, right: Type) => boolean) { + let wouldWorkWithAwait = false; const errNode = errorNode || operatorToken; - if (!tryGiveBetterPrimaryError(errNode, leftStr, rightStr)) { - error( + const [leftStr, rightStr] = getTypeNamesForErrorDisplay(leftType, rightType); + if (awaitedTypesAreCompatible) { + const awaitedLeftType = getAwaitedType(leftType); + const awaitedRightType = getAwaitedType(rightType); + wouldWorkWithAwait = !(awaitedLeftType === leftType && awaitedRightType === rightType) + && !!(awaitedLeftType && awaitedRightType) + && awaitedTypesAreCompatible(awaitedLeftType, awaitedRightType); + } + + if (!tryGiveBetterPrimaryError(errNode, wouldWorkWithAwait, leftStr, rightStr)) { + errorAndMaybeSuggestAwait( errNode, + wouldWorkWithAwait, Diagnostics.Operator_0_cannot_be_applied_to_types_1_and_2, tokenToString(operatorToken.kind), leftStr, @@ -24197,15 +24604,26 @@ namespace ts { } } - function tryGiveBetterPrimaryError(errNode: Node, leftStr: string, rightStr: string) { + function tryGiveBetterPrimaryError(errNode: Node, maybeMissingAwait: boolean, leftStr: string, rightStr: string) { + let typeName: string | undefined; switch (operatorToken.kind) { case SyntaxKind.EqualsEqualsEqualsToken: case SyntaxKind.EqualsEqualsToken: - return error(errNode, Diagnostics.This_condition_will_always_return_0_since_the_types_1_and_2_have_no_overlap, "false", leftStr, rightStr); + typeName = "false"; + break; case SyntaxKind.ExclamationEqualsEqualsToken: case SyntaxKind.ExclamationEqualsToken: - return error(errNode, Diagnostics.This_condition_will_always_return_0_since_the_types_1_and_2_have_no_overlap, "true", leftStr, rightStr); - } + typeName = "true"; + } + + if (typeName) { + return errorAndMaybeSuggestAwait( + errNode, + maybeMissingAwait, + Diagnostics.This_condition_will_always_return_0_since_the_types_1_and_2_have_no_overlap, + typeName, leftStr, rightStr); + } + return undefined; } } @@ -24249,33 +24667,44 @@ namespace ts { return anyType; } + const isAsync = (functionFlags & FunctionFlags.Async) !== 0; if (node.asteriskToken) { // Async generator functions prior to ESNext require the __await, __asyncDelegator, // and __asyncValues helpers - if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.AsyncGenerator && - languageVersion < ScriptTarget.ESNext) { + if (isAsync && languageVersion < ScriptTarget.ESNext) { checkExternalEmitHelpers(node, ExternalEmitHelpers.AsyncDelegatorIncludes); } // Generator functions prior to ES2015 require the __values helper - if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Generator && - languageVersion < ScriptTarget.ES2015 && compilerOptions.downlevelIteration) { + if (!isAsync && languageVersion < ScriptTarget.ES2015 && compilerOptions.downlevelIteration) { checkExternalEmitHelpers(node, ExternalEmitHelpers.Values); } } - const isAsync = (functionFlags & FunctionFlags.Async) !== 0; - const yieldedType = getYieldedTypeOfYieldExpression(node, isAsync)!; // TODO: GH#18217 // There is no point in doing an assignability check if the function // has no explicit return type because the return type is directly computed // from the yield expressions. const returnType = getReturnTypeFromAnnotation(func); - if (returnType) { - const signatureElementType = getIteratedTypeOfGenerator(returnType, isAsync) || anyType; - checkTypeAssignableToAndOptionallyElaborate(yieldedType, signatureElementType, node.expression || node, node.expression); + const iterationTypes = returnType && getIterationTypesOfGeneratorFunctionReturnType(returnType, isAsync); + const signatureYieldType = iterationTypes && iterationTypes.yieldType || anyType; + const signatureNextType = iterationTypes && iterationTypes.nextType || anyType; + const resolvedSignatureNextType = isAsync ? getAwaitedType(signatureNextType) || anyType : signatureNextType; + const yieldExpressionType = node.expression ? checkExpression(node.expression) : undefinedWideningType; + const yieldedType = getYieldedTypeOfYieldExpression(node, yieldExpressionType, resolvedSignatureNextType, isAsync); + if (returnType && yieldedType) { + checkTypeAssignableToAndOptionallyElaborate(yieldedType, signatureYieldType, node.expression || node, node.expression); + } + + if (node.asteriskToken) { + const use = isAsync ? IterationUse.AsyncYieldStar : IterationUse.YieldStar; + return getIterationTypeOfIterable(use, IterationTypeKind.Return, yieldExpressionType, node.expression) + || anyType; + } + else if (returnType) { + return getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Next, returnType, isAsync) + || anyType; } - // Both yield and yield* expressions have type 'any' return anyType; } @@ -24350,10 +24779,13 @@ namespace ts { function checkDeclarationInitializer(declaration: HasExpressionInitializer) { const initializer = getEffectiveInitializer(declaration)!; const type = getTypeOfExpression(initializer, /*cache*/ true); + const padded = isParameter(declaration) && declaration.name.kind === SyntaxKind.ArrayBindingPattern && + isTupleType(type) && !type.target.hasRestElement && getTypeReferenceArity(type) < declaration.name.elements.length ? + padTupleType(type, declaration.name) : type; const widened = getCombinedNodeFlags(declaration) & NodeFlags.Const || isDeclarationReadonly(declaration) || isTypeAssertion(initializer) || - isLiteralOfContextualType(type, getContextualType(initializer)) ? type : getWidenedLiteralType(type); + isLiteralOfContextualType(padded, getContextualType(initializer)) ? padded : getWidenedLiteralType(padded); if (isInJSFile(declaration)) { if (widened.flags & TypeFlags.Nullable) { reportImplicitAny(declaration, anyType); @@ -24367,6 +24799,22 @@ namespace ts { return widened; } + function padTupleType(type: TupleTypeReference, pattern: ArrayBindingPattern) { + const patternElements = pattern.elements; + const arity = getTypeReferenceArity(type); + const elementTypes = arity ? type.typeArguments!.slice() : []; + for (let i = arity; i < patternElements.length; i++) { + const e = patternElements[i]; + if (i < patternElements.length - 1 || !(e.kind === SyntaxKind.BindingElement && e.dotDotDotToken)) { + elementTypes.push(!isOmittedExpression(e) && hasDefaultValue(e) ? getTypeFromBindingElement(e, /*includePatternInType*/ false, /*reportErrors*/ false) : anyType); + if (!isOmittedExpression(e) && !hasDefaultValue(e)) { + reportImplicitAny(e, anyType); + } + } + } + return createTupleType(elementTypes, type.target.minLength, /*hasRestElement*/ false, type.target.readonly); + } + function isLiteralOfContextualType(candidateType: Type, contextualType: Type | undefined): boolean { if (contextualType) { if (contextualType.flags & TypeFlags.UnionOrIntersection) { @@ -24615,6 +25063,7 @@ namespace ts { function checkExpression(node: Expression | QualifiedName, checkMode?: CheckMode, forceTuple?: boolean): Type { const saveCurrentNode = currentNode; currentNode = node; + instantiationCount = 0; const uninstantiatedType = checkExpressionWorker(node, checkMode, forceTuple); const type = instantiateTypeWithSingleGenericCallSignature(node, uninstantiatedType, checkMode); if (isConstEnumObjectType(type)) { @@ -24973,18 +25422,17 @@ namespace ts { error(returnTypeNode, Diagnostics.A_generator_cannot_have_a_void_type_annotation); } else { - const generatorElementType = getIteratedTypeOfGenerator(returnType, (functionFlags & FunctionFlags.Async) !== 0) || anyType; - const iterableIteratorInstantiation = functionFlags & FunctionFlags.Async - ? createAsyncIterableIteratorType(generatorElementType) // AsyncGenerator function - : createIterableIteratorType(generatorElementType); // Generator function - - // Naively, one could check that IterableIterator is assignable to the return type annotation. + // Naively, one could check that Generator is assignable to the return type annotation. // However, that would not catch the error in the following case. // // interface BadGenerator extends Iterable, Iterator { } // function* g(): BadGenerator { } // Iterable and Iterator have different types! // - checkTypeAssignableTo(iterableIteratorInstantiation, returnType, returnTypeNode); + const generatorYieldType = getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Yield, returnType, (functionFlags & FunctionFlags.Async) !== 0) || anyType; + const generatorReturnType = getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Return, returnType, (functionFlags & FunctionFlags.Async) !== 0) || generatorYieldType; + const generatorNextType = getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Next, returnType, (functionFlags & FunctionFlags.Async) !== 0) || unknownType; + const generatorInstantiation = createGeneratorReturnType(generatorYieldType, generatorReturnType, generatorNextType, !!(functionFlags & FunctionFlags.Async)); + checkTypeAssignableTo(generatorInstantiation, returnType, returnTypeNode); } } else if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Async) { @@ -25875,9 +26323,9 @@ namespace ts { } } - function getAwaitedTypeOfPromise(type: Type, errorNode?: Node, diagnosticMessage?: DiagnosticMessage): Type | undefined { + function getAwaitedTypeOfPromise(type: Type, errorNode?: Node, diagnosticMessage?: DiagnosticMessage, arg0?: string | number): Type | undefined { const promisedType = getPromisedTypeOfPromise(type, errorNode); - return promisedType && getAwaitedType(promisedType, errorNode, diagnosticMessage); + return promisedType && getAwaitedType(promisedType, errorNode, diagnosticMessage, arg0); } /** @@ -25945,11 +26393,11 @@ namespace ts { * Promise-like type; otherwise, it is the type of the expression. This is used to reflect * The runtime behavior of the `await` keyword. */ - function checkAwaitedType(type: Type, errorNode: Node, diagnosticMessage: DiagnosticMessage): Type { - return getAwaitedType(type, errorNode, diagnosticMessage) || errorType; + function checkAwaitedType(type: Type, errorNode: Node, diagnosticMessage: DiagnosticMessage, arg0?: string | number): Type { + return getAwaitedType(type, errorNode, diagnosticMessage, arg0) || errorType; } - function getAwaitedType(type: Type, errorNode?: Node, diagnosticMessage?: DiagnosticMessage): Type | undefined { + function getAwaitedType(type: Type, errorNode?: Node, diagnosticMessage?: DiagnosticMessage, arg0?: string | number): Type | undefined { const typeAsAwaitable = type; if (typeAsAwaitable.awaitedTypeOfType) { return typeAsAwaitable.awaitedTypeOfType; @@ -25962,7 +26410,7 @@ namespace ts { if (type.flags & TypeFlags.Union) { let types: Type[] | undefined; for (const constituentType of (type).types) { - types = append(types, getAwaitedType(constituentType, errorNode, diagnosticMessage)); + types = append(types, getAwaitedType(constituentType, errorNode, diagnosticMessage, arg0)); } if (!types) { @@ -26016,7 +26464,7 @@ namespace ts { // Keep track of the type we're about to unwrap to avoid bad recursive promise types. // See the comments above for more information. awaitedTypeStack.push(type.id); - const awaitedType = getAwaitedType(promisedType, errorNode, diagnosticMessage); + const awaitedType = getAwaitedType(promisedType, errorNode, diagnosticMessage, arg0); awaitedTypeStack.pop(); if (!awaitedType) { @@ -26045,7 +26493,7 @@ namespace ts { if (thenFunction && getSignaturesOfType(thenFunction, SignatureKind.Call).length > 0) { if (errorNode) { if (!diagnosticMessage) return Debug.fail(); - error(errorNode, diagnosticMessage); + error(errorNode, diagnosticMessage, arg0); } return undefined; } @@ -26529,11 +26977,7 @@ namespace ts { const body = node.kind === SyntaxKind.MethodSignature ? undefined : node.body; checkSourceElement(body); - - if ((functionFlags & FunctionFlags.Generator) === 0) { // Async function or normal function - const returnOrPromisedType = getReturnOrPromisedType(node, functionFlags); - checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, returnOrPromisedType); - } + checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, getReturnTypeFromAnnotation(node)); if (produceDiagnostics && !getEffectiveReturnTypeNode(node)) { // Report an implicit any error if there is no body, no explicit return type, and node is not a private method @@ -27135,7 +27579,7 @@ namespace ts { // check the binding pattern with empty elements if (needCheckWidenedType) { if (isArrayBindingPattern(node.name)) { - checkIteratedTypeOrElementType(widenedType, node, /* allowStringInput */ false, /* allowAsyncIterables */ false); + checkIteratedTypeOrElementType(IterationUse.Destructuring, widenedType, undefinedType, node); } else if (strictNullChecks) { checkNonNullNonVoidType(widenedType, node); @@ -27433,15 +27877,16 @@ namespace ts { function checkRightHandSideOfForOf(rhsExpression: Expression, awaitModifier: AwaitKeywordToken | undefined): Type { const expressionType = checkNonNullExpression(rhsExpression); - return checkIteratedTypeOrElementType(expressionType, rhsExpression, /*allowStringInput*/ true, awaitModifier !== undefined); + const use = awaitModifier ? IterationUse.ForAwaitOf : IterationUse.ForOf; + return checkIteratedTypeOrElementType(use, expressionType, undefinedType, rhsExpression); } - function checkIteratedTypeOrElementType(inputType: Type, errorNode: Node | undefined, allowStringInput: boolean, allowAsyncIterables: boolean): Type { + function checkIteratedTypeOrElementType(use: IterationUse, inputType: Type, sentType: Type, errorNode: Node | undefined): Type { if (isTypeAny(inputType)) { return inputType; } - return getIteratedTypeOrElementType(inputType, errorNode, allowStringInput, allowAsyncIterables, /*checkAssignability*/ true) || anyType; + return getIteratedTypeOrElementType(use, inputType, sentType, errorNode, /*checkAssignability*/ true) || anyType; } /** @@ -27449,7 +27894,8 @@ namespace ts { * we want to get the iterated type of an iterable for ES2015 or later, or the iterated type * of a iterable (if defined globally) or element type of an array like for ES2015 or earlier. */ - function getIteratedTypeOrElementType(inputType: Type, errorNode: Node | undefined, allowStringInput: boolean, allowAsyncIterables: boolean, checkAssignability: boolean): Type | undefined { + function getIteratedTypeOrElementType(use: IterationUse, inputType: Type, sentType: Type, errorNode: Node | undefined, checkAssignability: boolean): Type | undefined { + const allowAsyncIterables = (use & IterationUse.AllowsAsyncIterablesFlag) !== 0; if (inputType === neverType) { reportTypeNotIterableError(errorNode!, inputType, allowAsyncIterables); // TODO: GH#18217 return undefined; @@ -27463,9 +27909,22 @@ namespace ts { // downlevelIteration is requested. if (uplevelIteration || downlevelIteration || allowAsyncIterables) { // We only report errors for an invalid iterable type in ES2015 or higher. - const iteratedType = getIteratedTypeOfIterable(inputType, uplevelIteration ? errorNode : undefined, allowAsyncIterables, /*allowSyncIterables*/ true, checkAssignability); - if (iteratedType || uplevelIteration) { - return iteratedType; + const iterationTypes = getIterationTypesOfIterable(inputType, use, uplevelIteration ? errorNode : undefined); + if (checkAssignability) { + if (iterationTypes) { + const diagnostic = + use & IterationUse.ForOfFlag ? Diagnostics.Cannot_iterate_value_because_the_next_method_of_its_iterator_expects_type_1_but_for_of_will_always_send_0 : + use & IterationUse.SpreadFlag ? Diagnostics.Cannot_iterate_value_because_the_next_method_of_its_iterator_expects_type_1_but_array_spread_will_always_send_0 : + use & IterationUse.DestructuringFlag ? Diagnostics.Cannot_iterate_value_because_the_next_method_of_its_iterator_expects_type_1_but_array_destructuring_will_always_send_0 : + use & IterationUse.YieldStarFlag ? Diagnostics.Cannot_delegate_iteration_to_value_because_the_next_method_of_its_iterator_expects_type_1_but_the_containing_generator_will_always_send_0 : + undefined; + if (diagnostic) { + checkTypeAssignableTo(sentType, iterationTypes.nextType, errorNode, diagnostic); + } + } + } + if (iterationTypes || uplevelIteration) { + return iterationTypes && iterationTypes.yieldType; } } @@ -27476,7 +27935,7 @@ namespace ts { // If strings are permitted, remove any string-like constituents from the array type. // This allows us to find other non-string element types from an array unioned with // a string. - if (allowStringInput) { + if (use & IterationUse.AllowsStringInputFlag) { if (arrayType.flags & TypeFlags.Union) { // After we remove all types that are StringLike, we will know if there was a string constituent // based on whether the result of filter is a new array. @@ -27514,19 +27973,23 @@ namespace ts { // want to say that number is not an array type. But if the input was just // number and string input is allowed, we want to say that number is not an // array type or a string type. - const isIterable = !!getIteratedTypeOfIterable(inputType, /* errorNode */ undefined, allowAsyncIterables, /*allowSyncIterables*/ true, checkAssignability); - const diagnostic = !allowStringInput || hasStringConstituent + const yieldType = getIterationTypeOfIterable(use, IterationTypeKind.Yield, inputType, /*errorNode*/ undefined); + const [defaultDiagnostic, maybeMissingAwait]: [DiagnosticMessage, boolean] = !(use & IterationUse.AllowsStringInputFlag) || hasStringConstituent ? downlevelIteration - ? Diagnostics.Type_0_is_not_an_array_type_or_does_not_have_a_Symbol_iterator_method_that_returns_an_iterator - : isIterable - ? Diagnostics.Type_0_is_not_an_array_type_or_a_string_type_Use_compiler_option_downlevelIteration_to_allow_iterating_of_iterators - : Diagnostics.Type_0_is_not_an_array_type + ? [Diagnostics.Type_0_is_not_an_array_type_or_does_not_have_a_Symbol_iterator_method_that_returns_an_iterator, true] + : yieldType + ? [Diagnostics.Type_0_is_not_an_array_type_or_a_string_type_Use_compiler_option_downlevelIteration_to_allow_iterating_of_iterators, false] + : [Diagnostics.Type_0_is_not_an_array_type, true] : downlevelIteration - ? Diagnostics.Type_0_is_not_an_array_type_or_a_string_type_or_does_not_have_a_Symbol_iterator_method_that_returns_an_iterator - : isIterable - ? Diagnostics.Type_0_is_not_an_array_type_or_a_string_type_Use_compiler_option_downlevelIteration_to_allow_iterating_of_iterators - : Diagnostics.Type_0_is_not_an_array_type_or_a_string_type; - error(errorNode, diagnostic, typeToString(arrayType)); + ? [Diagnostics.Type_0_is_not_an_array_type_or_a_string_type_or_does_not_have_a_Symbol_iterator_method_that_returns_an_iterator, true] + : yieldType + ? [Diagnostics.Type_0_is_not_an_array_type_or_a_string_type_Use_compiler_option_downlevelIteration_to_allow_iterating_of_iterators, false] + : [Diagnostics.Type_0_is_not_an_array_type_or_a_string_type, true]; + errorAndMaybeSuggestAwait( + errorNode, + maybeMissingAwait && !!getAwaitedTypeOfPromise(arrayType), + defaultDiagnostic, + typeToString(arrayType)); } return hasStringConstituent ? stringType : undefined; } @@ -27545,33 +28008,82 @@ namespace ts { } /** - * We want to treat type as an iterable, and get the type it is an iterable of. The iterable - * must have the following structure (annotated with the names of the variables below): + * Gets the requested "iteration type" from an `Iterable`-like or `AsyncIterable`-like type. + */ + function getIterationTypeOfIterable(use: IterationUse, typeKind: IterationTypeKind, inputType: Type, errorNode: Node | undefined): Type | undefined { + if (isTypeAny(inputType)) { + return undefined; + } + + const iterationTypes = getIterationTypesOfIterable(inputType, use, errorNode); + return iterationTypes && iterationTypes[getIterationTypesKeyFromIterationTypeKind(typeKind)]; + } + + function createIterationTypes(yieldType: Type = neverType, returnType: Type = neverType, nextType: Type = unknownType): IterationTypes { + // `yieldType` and `returnType` are defaulted to `neverType` they each will be combined + // via `getUnionType` when merging iteration types. `nextType` is defined as `unknownType` + // as it is combined via `getIntersectionType` when merging iteration types. + + // Use the cache only for intrinsic types to keep it small as they are likely to be + // more frequently created (i.e. `Iterator`). Iteration types + // are also cached on the type they are requested for, so we shouldn't need to maintain + // the cache for less-frequently used types. + if (yieldType.flags & TypeFlags.Intrinsic && + returnType.flags & (TypeFlags.Any | TypeFlags.Never | TypeFlags.Unknown | TypeFlags.Void | TypeFlags.Undefined) && + nextType.flags & (TypeFlags.Any | TypeFlags.Never | TypeFlags.Unknown | TypeFlags.Void | TypeFlags.Undefined)) { + const id = getTypeListId([yieldType, returnType, nextType]); + let iterationTypes = iterationTypesCache.get(id); + if (!iterationTypes) { + iterationTypes = { yieldType, returnType, nextType }; + iterationTypesCache.set(id, iterationTypes); + } + return iterationTypes; + } + return { yieldType, returnType, nextType }; + } + + /** + * Combines multiple `IterationTypes` records. * - * { // iterable - * [Symbol.iterator]: { // iteratorMethod - * (): Iterator - * } - * } + * If `array` is empty or all elements are missing or are references to `noIterationTypes`, + * then `noIterationTypes` is returned. Otherwise, an `IterationTypes` record is returned + * for the combined iteration types. + */ + function combineIterationTypes(array: (IterationTypes | undefined)[]) { + let yieldTypes: Type[] | undefined; + let returnTypes: Type[] | undefined; + let nextTypes: Type[] | undefined; + for (const iterationTypes of array) { + if (iterationTypes === undefined || iterationTypes === noIterationTypes) { + continue; + } + if (iterationTypes === anyIterationTypes) { + return anyIterationTypes; + } + yieldTypes = append(yieldTypes, iterationTypes.yieldType); + returnTypes = append(returnTypes, iterationTypes.returnType); + nextTypes = append(nextTypes, iterationTypes.nextType); + } + if (yieldTypes || returnTypes || nextTypes) { + return createIterationTypes( + yieldTypes && getUnionType(yieldTypes), + returnTypes && getUnionType(returnTypes), + nextTypes && getIntersectionType(nextTypes)); + } + return noIterationTypes; + } + + /** + * Gets the *yield*, *return*, and *next* types from an `Iterable`-like or `AsyncIterable`-like type. * - * For an async iterable, we expect the following structure: - * - * { // iterable - * [Symbol.asyncIterator]: { // iteratorMethod - * (): AsyncIterator - * } - * } - * - * T is the type we are after. At every level that involves analyzing return types - * of signatures, we union the return types of all the signatures. + * At every level that involves analyzing return types of signatures, we union the return types of all the signatures. * * Another thing to note is that at any step of this process, we could run into a dead end, * meaning either the property is missing, or we run into the anyType. If either of these things - * happens, we return undefined to signal that we could not find the iterated type. If a property - * is missing, and the previous step did not result in 'any', then we also give an error if the + * happens, we return `undefined` to signal that we could not find the iteration type. If a property + * is missing, and the previous step did not result in `any`, then we also give an error if the * caller requested it. Then the caller can decide what to do in the case where there is no iterated - * type. This is different from returning anyType, because that would signify that we have matched the - * whole pattern and that T (above) is 'any'. + * type. * * For a **for-of** statement, `yield*` (in a normal generator), spread, array * destructuring, or normal generator we will only ever look for a `[Symbol.iterator]()` @@ -27582,185 +28094,458 @@ namespace ts { * For a **for-await-of** statement or a `yield*` in an async generator we will look for * the `[Symbol.asyncIterator]()` method first, and then the `[Symbol.iterator]()` method. */ - function getIteratedTypeOfIterable(type: Type, errorNode: Node | undefined, allowAsyncIterables: boolean, allowSyncIterables: boolean, checkAssignability: boolean): Type | undefined { + function getIterationTypesOfIterable(type: Type, use: IterationUse, errorNode: Node | undefined) { if (isTypeAny(type)) { - return undefined; + return anyIterationTypes; } - return mapType(type, getIteratedType); - - function getIteratedType(type: Type) { - const typeAsIterable = type; - if (allowAsyncIterables) { - if (typeAsIterable.iteratedTypeOfAsyncIterable) { - return typeAsIterable.iteratedTypeOfAsyncIterable; + if (!(type.flags & TypeFlags.Union)) { + const iterationTypes = getIterationTypesOfIterableWorker(type, use, errorNode); + if (iterationTypes === noIterationTypes) { + if (errorNode) { + reportTypeNotIterableError(errorNode, type, !!(use & IterationUse.AllowsAsyncIterablesFlag)); } - - // As an optimization, if the type is an instantiation of the global `AsyncIterable` - // or the global `AsyncIterableIterator` then just grab its type argument. - if (isReferenceToType(type, getGlobalAsyncIterableType(/*reportErrors*/ false)) || - isReferenceToType(type, getGlobalAsyncIterableIteratorType(/*reportErrors*/ false))) { - return typeAsIterable.iteratedTypeOfAsyncIterable = (type).typeArguments![0]; - } - } - - if (allowSyncIterables) { - if (typeAsIterable.iteratedTypeOfIterable) { - return allowAsyncIterables - ? typeAsIterable.iteratedTypeOfAsyncIterable = getAwaitedType(typeAsIterable.iteratedTypeOfIterable) - : typeAsIterable.iteratedTypeOfIterable; - } - - // As an optimization, if the type is an instantiation of the global `Iterable` or - // `IterableIterator` then just grab its type argument. - if (isReferenceToType(type, getGlobalIterableType(/*reportErrors*/ false)) || - isReferenceToType(type, getGlobalIterableIteratorType(/*reportErrors*/ false))) { - return allowAsyncIterables - ? typeAsIterable.iteratedTypeOfAsyncIterable = getAwaitedType((type).typeArguments![0]) - : typeAsIterable.iteratedTypeOfIterable = (type).typeArguments![0]; - } - } - - const asyncMethodType = allowAsyncIterables && getTypeOfPropertyOfType(type, getPropertyNameForKnownSymbolName("asyncIterator")); - const methodType = asyncMethodType || (allowSyncIterables ? getTypeOfPropertyOfType(type, getPropertyNameForKnownSymbolName("iterator")) : undefined); - if (isTypeAny(methodType)) { return undefined; } + return iterationTypes; + } - const signatures = methodType ? getSignaturesOfType(methodType, SignatureKind.Call) : undefined; - if (!some(signatures)) { + const cacheKey = use & IterationUse.AllowsAsyncIterablesFlag ? "iterationTypesOfAsyncIterable" : "iterationTypesOfIterable"; + const cachedTypes = (type as IterableOrIteratorType)[cacheKey]; + if (cachedTypes) return cachedTypes === noIterationTypes ? undefined : cachedTypes; + + let allIterationTypes: IterationTypes[] | undefined; + for (const constituent of (type as UnionType).types) { + const iterationTypes = getIterationTypesOfIterableWorker(constituent, use, errorNode); + if (iterationTypes === noIterationTypes) { if (errorNode) { - // only report on the first error - reportTypeNotIterableError(errorNode, type, allowAsyncIterables); + reportTypeNotIterableError(errorNode, type, !!(use & IterationUse.AllowsAsyncIterablesFlag)); errorNode = undefined; } - return undefined; } - - const returnType = getUnionType(map(signatures, getReturnTypeOfSignature), UnionReduction.Subtype); - const iteratedType = getIteratedTypeOfIterator(returnType, errorNode, /*isAsyncIterator*/ !!asyncMethodType); - if (checkAssignability && errorNode && iteratedType) { - // If `checkAssignability` was specified, we were called from - // `checkIteratedTypeOrElementType`. As such, we need to validate that - // the type passed in is actually an Iterable. - checkTypeAssignableTo(type, asyncMethodType - ? createAsyncIterableType(iteratedType) - : createIterableType(iteratedType), errorNode); - } - - if (iteratedType) { - return allowAsyncIterables - ? typeAsIterable.iteratedTypeOfAsyncIterable = asyncMethodType ? iteratedType : getAwaitedType(iteratedType) - : typeAsIterable.iteratedTypeOfIterable = iteratedType; + else { + allIterationTypes = append(allIterationTypes, iterationTypes); } } + + const iterationTypes = allIterationTypes ? combineIterationTypes(allIterationTypes) : noIterationTypes; + (type as IterableOrIteratorType)[cacheKey] = iterationTypes; + return iterationTypes === noIterationTypes ? undefined : iterationTypes; + } + + function getAsyncFromSyncIterationTypes(iterationTypes: IterationTypes, errorNode: Node | undefined) { + if (iterationTypes === noIterationTypes) return noIterationTypes; + if (iterationTypes === anyIterationTypes) return anyIterationTypes; + const { yieldType, returnType, nextType } = iterationTypes; + return createIterationTypes( + getAwaitedType(yieldType, errorNode) || anyType, + getAwaitedType(returnType, errorNode) || anyType, + nextType); + } + + /** + * Gets the *yield*, *return*, and *next* types from a non-union type. + * + * If we are unable to find the *yield*, *return*, and *next* types, `noIterationTypes` is + * returned to indicate to the caller that it should report an error. Otherwise, an + * `IterationTypes` record is returned. + * + * 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) { + if (isTypeAny(type)) { + return anyIterationTypes; + } + + if (use & IterationUse.AllowsAsyncIterablesFlag) { + const iterationTypes = + getIterationTypesOfIterableCached(type, asyncIterationTypesResolver) || + getIterationTypesOfIterableFast(type, asyncIterationTypesResolver); + if (iterationTypes) { + return iterationTypes; + } + } + + if (use & IterationUse.AllowsSyncIterablesFlag) { + const iterationTypes = + getIterationTypesOfIterableCached(type, syncIterationTypesResolver) || + getIterationTypesOfIterableFast(type, syncIterationTypesResolver); + if (iterationTypes) { + if (use & IterationUse.AllowsAsyncIterablesFlag) { + // for a sync iterable in an async context, only use the cached types if they are valid. + if (iterationTypes !== noIterationTypes) { + return (type as IterableOrIteratorType).iterationTypesOfAsyncIterable = getAsyncFromSyncIterationTypes(iterationTypes, errorNode); + } + } + else { + return iterationTypes; + } + } + } + + if (use & IterationUse.AllowsAsyncIterablesFlag) { + const iterationTypes = getIterationTypesOfIterableSlow(type, asyncIterationTypesResolver, errorNode); + if (iterationTypes !== noIterationTypes) { + return iterationTypes; + } + } + + if (use & IterationUse.AllowsSyncIterablesFlag) { + const iterationTypes = getIterationTypesOfIterableSlow(type, syncIterationTypesResolver, errorNode); + if (iterationTypes !== noIterationTypes) { + if (use & IterationUse.AllowsAsyncIterablesFlag) { + return (type as IterableOrIteratorType).iterationTypesOfAsyncIterable = iterationTypes + ? getAsyncFromSyncIterationTypes(iterationTypes, errorNode) + : noIterationTypes; + } + else { + return iterationTypes; + } + } + } + + return noIterationTypes; + } + + /** + * Gets the *yield*, *return*, and *next* types of an `Iterable`-like or + * `AsyncIterable`-like type from the cache. + * + * NOTE: You probably don't want to call this directly and should be calling + * `getIterationTypesOfIterable` instead. + */ + function getIterationTypesOfIterableCached(type: Type, resolver: IterationTypesResolver) { + return (type as IterableOrIteratorType)[resolver.iterableCacheKey]; + } + + /** + * Gets the *yield*, *return*, and *next* types of an `Iterable`-like or `AsyncIterable`-like + * type from from common heuristics. + * + * If we previously analyzed this type and found no iteration types, `noIterationTypes` is + * returned. If we found iteration types, an `IterationTypes` record is returned. + * Otherwise, we return `undefined` to indicate to the caller it should perform a more + * exhaustive analysis. + * + * NOTE: You probably don't want to call this directly and should be calling + * `getIterationTypesOfIterable` instead. + */ + function getIterationTypesOfIterableFast(type: Type, resolver: IterationTypesResolver) { + // As an optimization, if the type is an instantiation of one of the following global types, then + // just grab its related type argument: + // - `Iterable` or `AsyncIterable` + // - `IterableIterator` or `AsyncIterableIterator` + let globalType: Type; + if (isReferenceToType(type, globalType = resolver.getGlobalIterableType(/*reportErrors*/ false)) || + isReferenceToType(type, globalType = resolver.getGlobalIterableIteratorType(/*reportErrors*/ false))) { + const [yieldType] = (type as GenericType).typeArguments!; + // The "return" and "next" types of `Iterable` and `IterableIterator` are defined by the + // iteration types of their `[Symbol.iterator]()` method. The same is true for their async cousins. + // While we define these as `any` and `undefined` in our libs by default, a custom lib *could* use + // different definitions. + const globalIterationTypes = + getIterationTypesOfIterableCached(globalType, resolver) || + getIterationTypesOfIterableSlow(globalType, resolver, /*errorNode*/ undefined); + const { returnType, nextType } = globalIterationTypes === noIterationTypes ? defaultIterationTypes : globalIterationTypes; + return (type as IterableOrIteratorType)[resolver.iterableCacheKey] = createIterationTypes(yieldType, returnType, nextType); + } + + // As an optimization, if the type is an instantiation of the following global type, then + // just grab its related type arguments: + // - `Generator` or `AsyncGenerator` + if (isReferenceToType(type, resolver.getGlobalGeneratorType(/*reportErrors*/ false))) { + const [yieldType, returnType, nextType] = (type as GenericType).typeArguments!; + return (type as IterableOrIteratorType)[resolver.iterableCacheKey] = createIterationTypes(yieldType, returnType, nextType); + } + } + + /** + * Gets the *yield*, *return*, and *next* types of an `Iterable`-like or `AsyncIterable`-like + * type from its members. + * + * If we successfully found the *yield*, *return*, and *next* types, an `IterationTypes` + * record is returned. Otherwise, `noIterationTypes` is returned. + * + * 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) { + const method = getPropertyOfType(type, getPropertyNameForKnownSymbolName(resolver.iteratorSymbolName)); + const methodType = method && !(method.flags & SymbolFlags.Optional) ? getTypeOfSymbol(method) : undefined; + if (isTypeAny(methodType)) { + return (type as IterableOrIteratorType)[resolver.iterableCacheKey] = anyIterationTypes; + } + + const signatures = methodType ? getSignaturesOfType(methodType, SignatureKind.Call) : undefined; + if (!some(signatures)) { + return (type as IterableOrIteratorType)[resolver.iterableCacheKey] = noIterationTypes; + } + + const iteratorType = getUnionType(map(signatures, getReturnTypeOfSignature), UnionReduction.Subtype); + const iterationTypes = getIterationTypesOfIterator(iteratorType, resolver, errorNode) || noIterationTypes; + return (type as IterableOrIteratorType)[resolver.iterableCacheKey] = iterationTypes; } function reportTypeNotIterableError(errorNode: Node, type: Type, allowAsyncIterables: boolean): void { - error(errorNode, allowAsyncIterables + const message = allowAsyncIterables ? Diagnostics.Type_0_must_have_a_Symbol_asyncIterator_method_that_returns_an_async_iterator - : Diagnostics.Type_0_must_have_a_Symbol_iterator_method_that_returns_an_iterator, typeToString(type)); + : Diagnostics.Type_0_must_have_a_Symbol_iterator_method_that_returns_an_iterator; + errorAndMaybeSuggestAwait(errorNode, !!getAwaitedTypeOfPromise(type), message, typeToString(type)); } /** - * This function has very similar logic as getIteratedTypeOfIterable, except that it operates on - * Iterators instead of Iterables. Here is the structure: + * Gets the *yield*, *return*, and *next* types from an `Iterator`-like or `AsyncIterator`-like type. * - * { // iterator - * next: { // nextMethod - * (): { // nextResult - * value: T // nextValue - * } - * } - * } - * - * For an async iterator, we expect the following structure: - * - * { // iterator - * next: { // nextMethod - * (): PromiseLike<{ // nextResult - * value: T // nextValue - * }> - * } - * } + * If we successfully found the *yield*, *return*, and *next* types, an `IterationTypes` + * record is returned. Otherwise, `undefined` is returned. */ - function getIteratedTypeOfIterator(type: Type, errorNode: Node | undefined, isAsyncIterator: boolean): Type | undefined { + function getIterationTypesOfIterator(type: Type, resolver: IterationTypesResolver, errorNode: Node | undefined) { if (isTypeAny(type)) { - return undefined; + return anyIterationTypes; } - const typeAsIterator = type; - if (isAsyncIterator ? typeAsIterator.iteratedTypeOfAsyncIterator : typeAsIterator.iteratedTypeOfIterator) { - return isAsyncIterator ? typeAsIterator.iteratedTypeOfAsyncIterator : typeAsIterator.iteratedTypeOfIterator; - } - - // As an optimization, if the type is an instantiation of the global `Iterator` (for - // a non-async iterator) or the global `AsyncIterator` (for an async-iterator) then - // just grab its type argument. - const getIteratorType = isAsyncIterator ? getGlobalAsyncIteratorType : getGlobalIteratorType; - if (isReferenceToType(type, getIteratorType(/*reportErrors*/ false))) { - return isAsyncIterator - ? typeAsIterator.iteratedTypeOfAsyncIterator = (type).typeArguments![0] - : typeAsIterator.iteratedTypeOfIterator = (type).typeArguments![0]; - } - - // Both async and non-async iterators must have a `next` method. - const nextMethod = getTypeOfPropertyOfType(type, "next" as __String); - if (isTypeAny(nextMethod)) { - return undefined; - } - - const nextMethodSignatures = nextMethod ? getSignaturesOfType(nextMethod, SignatureKind.Call) : emptyArray; - if (nextMethodSignatures.length === 0) { - if (errorNode) { - error(errorNode, isAsyncIterator - ? Diagnostics.An_async_iterator_must_have_a_next_method - : Diagnostics.An_iterator_must_have_a_next_method); - } - return undefined; - } - - let nextResult: Type | undefined = getUnionType(map(nextMethodSignatures, getReturnTypeOfSignature), UnionReduction.Subtype); - if (isTypeAny(nextResult)) { - return undefined; - } - - // For an async iterator, we must get the awaited type of the return type. - if (isAsyncIterator) { - nextResult = getAwaitedTypeOfPromise(nextResult, errorNode, Diagnostics.The_type_returned_by_the_next_method_of_an_async_iterator_must_be_a_promise_for_a_type_with_a_value_property); - if (isTypeAny(nextResult)) { - return undefined; - } - } - - const nextValue = nextResult && getTypeOfPropertyOfType(nextResult, "value" as __String); - if (!nextValue) { - if (errorNode) { - error(errorNode, isAsyncIterator - ? Diagnostics.The_type_returned_by_the_next_method_of_an_async_iterator_must_be_a_promise_for_a_type_with_a_value_property - : Diagnostics.The_type_returned_by_the_next_method_of_an_iterator_must_have_a_value_property); - } - return undefined; - } - - return isAsyncIterator - ? typeAsIterator.iteratedTypeOfAsyncIterator = nextValue - : typeAsIterator.iteratedTypeOfIterator = nextValue; + const iterationTypes = + getIterationTypesOfIteratorCached(type, resolver) || + getIterationTypesOfIteratorFast(type, resolver) || + getIterationTypesOfIteratorSlow(type, resolver, errorNode); + return iterationTypes === noIterationTypes ? undefined : iterationTypes; } /** - * A generator may have a return type of `Iterator`, `Iterable`, or - * `IterableIterator`. An async generator may have a return type of `AsyncIterator`, - * `AsyncIterable`, or `AsyncIterableIterator`. This function can be used to extract - * the iterated type from this return type for contextual typing and verifying signatures. + * Gets the iteration types of an `Iterator`-like or `AsyncIterator`-like type from the + * cache. + * + * NOTE: You probably don't want to call this directly and should be calling + * `getIterationTypesOfIterator` instead. */ - function getIteratedTypeOfGenerator(returnType: Type, isAsyncGenerator: boolean): Type | undefined { + function getIterationTypesOfIteratorCached(type: Type, resolver: IterationTypesResolver) { + return (type as IterableOrIteratorType)[resolver.iteratorCacheKey]; + } + + /** + * Gets the iteration types of an `Iterator`-like or `AsyncIterator`-like type from the + * cache or from common heuristics. + * + * If we previously analyzed this type and found no iteration types, `noIterationTypes` is + * returned. If we found iteration types, an `IterationTypes` record is returned. + * Otherwise, we return `undefined` to indicate to the caller it should perform a more + * exhaustive analysis. + * + * NOTE: You probably don't want to call this directly and should be calling + * `getIterationTypesOfIterator` instead. + */ + function getIterationTypesOfIteratorFast(type: Type, resolver: IterationTypesResolver) { + // As an optimization, if the type is an instantiation of one of the following global types, + // then just grab its related type argument: + // - `IterableIterator` or `AsyncIterableIterator` + // - `Iterator` or `AsyncIterator` + // - `Generator` or `AsyncGenerator` + const globalType = resolver.getGlobalIterableIteratorType(/*reportErrors*/ false); + if (isReferenceToType(type, globalType)) { + const [yieldType] = (type as GenericType).typeArguments!; + // The "return" and "next" types of `IterableIterator` and `AsyncIterableIterator` are defined by the + // iteration types of their `next`, `return`, and `throw` methods. While we define these as `any` + // and `undefined` in our libs by default, a custom lib *could* use different definitions. + const globalIterationTypes = + getIterationTypesOfIteratorCached(globalType, resolver) || + getIterationTypesOfIteratorSlow(globalType, resolver, /*errorNode*/ undefined); + const { returnType, nextType } = globalIterationTypes === noIterationTypes ? defaultIterationTypes : globalIterationTypes; + return (type as IterableOrIteratorType)[resolver.iteratorCacheKey] = createIterationTypes(yieldType, returnType, nextType); + } + if (isReferenceToType(type, resolver.getGlobalIteratorType(/*reportErrors*/ false)) || + isReferenceToType(type, resolver.getGlobalGeneratorType(/*reportErrors*/ false))) { + const [yieldType, returnType, nextType] = (type as GenericType).typeArguments!; + return (type as IterableOrIteratorType)[resolver.iteratorCacheKey] = createIterationTypes(yieldType, returnType, nextType); + } + } + + function isIteratorResult(type: Type, kind: IterationTypeKind.Yield | IterationTypeKind.Return) { + // From https://tc39.github.io/ecma262/#sec-iteratorresult-interface: + // > [done] is the result status of an iterator `next` method call. If the end of the iterator was reached `done` is `true`. + // > If the end was not reached `done` is `false` and a value is available. + // > If a `done` property (either own or inherited) does not exist, it is consider to have the value `false`. + const doneType = getTypeOfPropertyOfType(type, "done" as __String) || falseType; + return isTypeAssignableTo(kind === IterationTypeKind.Yield ? falseType : trueType, doneType); + } + + function isYieldIteratorResult(type: Type) { + return isIteratorResult(type, IterationTypeKind.Yield); + } + + function isReturnIteratorResult(type: Type) { + return isIteratorResult(type, IterationTypeKind.Return); + } + + /** + * Gets the *yield* and *return* types of an `IteratorResult`-like type. + * + * If we are unable to determine a *yield* or a *return* type, `noIterationTypes` is + * returned to indicate to the caller that it should handle the error. Otherwise, an + * `IterationTypes` record is returned. + */ + function getIterationTypesOfIteratorResult(type: Type) { + if (isTypeAny(type)) { + return anyIterationTypes; + } + + const cachedTypes = (type as IterableOrIteratorType).iterationTypesOfIteratorResult; + if (cachedTypes) { + return cachedTypes; + } + + // As an optimization, if the type is an instantiation of one of the global `IteratorYieldResult` + // or `IteratorReturnResult` types, then just grab its type argument. + if (isReferenceToType(type, getGlobalIteratorYieldResultType(/*reportErrors*/ false))) { + const yieldType = (type as GenericType).typeArguments![0]; + return (type as IterableOrIteratorType).iterationTypesOfIteratorResult = createIterationTypes(yieldType, /*returnType*/ undefined, /*nextType*/ undefined); + } + if (isReferenceToType(type, getGlobalIteratorReturnResultType(/*reportErrors*/ false))) { + const returnType = (type as GenericType).typeArguments![0]; + return (type as IterableOrIteratorType).iterationTypesOfIteratorResult = createIterationTypes(/*yieldType*/ undefined, returnType, /*nextType*/ undefined); + } + + // Choose any constituents that can produce the requested iteration type. + const yieldIteratorResult = filterType(type, isYieldIteratorResult); + const yieldType = yieldIteratorResult !== neverType ? getTypeOfPropertyOfType(yieldIteratorResult, "value" as __String) : undefined; + + const returnIteratorResult = filterType(type, isReturnIteratorResult); + const returnType = returnIteratorResult !== neverType ? getTypeOfPropertyOfType(returnIteratorResult, "value" as __String) : undefined; + + if (!yieldType && !returnType) { + return (type as IterableOrIteratorType).iterationTypesOfIteratorResult = noIterationTypes; + } + + // From https://tc39.github.io/ecma262/#sec-iteratorresult-interface + // > ... If the iterator does not have a return value, `value` is `undefined`. In that case, the + // > `value` property may be absent from the conforming object if it does not inherit an explicit + // > `value` property. + return (type as IterableOrIteratorType).iterationTypesOfIteratorResult = createIterationTypes(yieldType, returnType || voidType, /*nextType*/ undefined); + } + + /** + * Gets the *yield*, *return*, and *next* types of a the `next()`, `return()`, or + * `throw()` method of an `Iterator`-like or `AsyncIterator`-like type. + * + * 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): IterationTypes | undefined { + const method = getPropertyOfType(type, methodName as __String); + + // Ignore 'return' or 'throw' if they are missing. + if (!method && methodName !== "next") { + return undefined; + } + + const methodType = method && !(methodName === "next" && (method.flags & SymbolFlags.Optional)) + ? methodName === "next" ? getTypeOfSymbol(method) : getTypeWithFacts(getTypeOfSymbol(method), TypeFacts.NEUndefinedOrNull) + : undefined; + + if (isTypeAny(methodType)) { + // `return()` and `throw()` don't provide a *next* type. + return methodName === "next" ? anyIterationTypes : anyIterationTypesExceptNext; + } + + // Both async and non-async iterators *must* have a `next` method. + const methodSignatures = methodType ? getSignaturesOfType(methodType, SignatureKind.Call) : emptyArray; + if (methodSignatures.length === 0) { + if (errorNode) { + const diagnostic = methodName === "next" + ? resolver.mustHaveANextMethodDiagnostic + : resolver.mustBeAMethodDiagnostic; + error(errorNode, diagnostic, methodName); + } + return methodName === "next" ? anyIterationTypes : undefined; + } + + // Extract the first parameter and return type of each signature. + let methodParameterTypes: Type[] | undefined; + let methodReturnTypes: Type[] | undefined; + for (const signature of methodSignatures) { + if (methodName !== "throw" && some(signature.parameters)) { + methodParameterTypes = append(methodParameterTypes, getTypeAtPosition(signature, 0)); + } + methodReturnTypes = append(methodReturnTypes, getReturnTypeOfSignature(signature)); + } + + // Resolve the *next* or *return* type from the first parameter of a `next()` or + // `return()` method, respectively. + let returnTypes: Type[] | undefined; + let nextType: Type | undefined; + if (methodName !== "throw") { + const methodParameterType = methodParameterTypes ? getUnionType(methodParameterTypes) : unknownType; + const resolvedMethodParameterType = resolver.resolveIterationType(methodParameterType, errorNode) || anyType; + if (methodName === "next") { + nextType = resolvedMethodParameterType; + } + else if (methodName === "return") { + returnTypes = append(returnTypes, resolvedMethodParameterType); + } + } + + // Resolve the *yield* and *return* types from the return type of the method (i.e. `IteratorResult`) + let yieldType: Type; + const methodReturnType = methodReturnTypes ? getUnionType(methodReturnTypes, UnionReduction.Subtype) : neverType; + const resolvedMethodReturnType = resolver.resolveIterationType(methodReturnType, errorNode) || anyType; + const iterationTypes = getIterationTypesOfIteratorResult(resolvedMethodReturnType); + if (iterationTypes === noIterationTypes) { + if (errorNode) { + error(errorNode, resolver.mustHaveAValueDiagnostic, methodName); + } + yieldType = anyType; + returnTypes = append(returnTypes, anyType); + } + else { + yieldType = iterationTypes.yieldType; + returnTypes = append(returnTypes, iterationTypes.returnType); + } + + return createIterationTypes(yieldType, getUnionType(returnTypes), nextType); + } + + /** + * Gets the *yield*, *return*, and *next* types of an `Iterator`-like or `AsyncIterator`-like + * type from its members. + * + * If we successfully found the *yield*, *return*, and *next* types, an `IterationTypes` + * record is returned. Otherwise, `noIterationTypes` is returned. + * + * 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) { + const iterationTypes = combineIterationTypes([ + getIterationTypesOfMethod(type, resolver, "next", errorNode), + getIterationTypesOfMethod(type, resolver, "return", errorNode), + getIterationTypesOfMethod(type, resolver, "throw", errorNode), + ]); + return (type as IterableOrIteratorType)[resolver.iteratorCacheKey] = iterationTypes; + } + + /** + * Gets the requested "iteration type" from a type that is either `Iterable`-like, `Iterator`-like, + * `IterableIterator`-like, or `Generator`-like (for a non-async generator); or `AsyncIterable`-like, + * `AsyncIterator`-like, `AsyncIterableIterator`-like, or `AsyncGenerator`-like (for an async generator). + */ + function getIterationTypeOfGeneratorFunctionReturnType(kind: IterationTypeKind, returnType: Type, isAsyncGenerator: boolean): Type | undefined { if (isTypeAny(returnType)) { return undefined; } - return getIteratedTypeOfIterable(returnType, /*errorNode*/ undefined, /*allowAsyncIterables*/ isAsyncGenerator, /*allowSyncIterables*/ !isAsyncGenerator, /*checkAssignability*/ false) - || getIteratedTypeOfIterator(returnType, /*errorNode*/ undefined, isAsyncGenerator); + const iterationTypes = getIterationTypesOfGeneratorFunctionReturnType(returnType, isAsyncGenerator); + return iterationTypes && iterationTypes[getIterationTypesKeyFromIterationTypeKind(kind)]; + } + + function getIterationTypesOfGeneratorFunctionReturnType(type: Type, isAsyncGenerator: boolean) { + if (isTypeAny(type)) { + return anyIterationTypes; + } + + const use = isAsyncGenerator ? IterationUse.AsyncGeneratorReturnType : IterationUse.GeneratorReturnType; + const resolver = isAsyncGenerator ? asyncIterationTypesResolver : syncIterationTypesResolver; + return getIterationTypesOfIterable(type, use, /*errorNode*/ undefined) || + getIterationTypesOfIterator(type, resolver, /*errorNode*/ undefined); } function checkBreakOrContinueStatement(node: BreakOrContinueStatement) { @@ -27770,10 +28555,16 @@ namespace ts { // TODO: Check that target label is valid } + function unwrapReturnType(returnType: Type, functionFlags: FunctionFlags) { + const isGenerator = !!(functionFlags & FunctionFlags.Generator); + const isAsync = !!(functionFlags & FunctionFlags.Async); + return isGenerator ? getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Return, returnType, isAsync) || errorType : + isAsync ? getPromisedTypeOfPromise(returnType) || errorType : + returnType; + } + function isUnwrappedReturnTypeVoidOrAny(func: SignatureDeclaration, returnType: Type): boolean { - const unwrappedReturnType = (getFunctionFlags(func) & FunctionFlags.AsyncGenerator) === FunctionFlags.Async - ? getPromisedTypeOfPromise(returnType) // Async function - : returnType; // AsyncGenerator function, Generator function, or normal function + const unwrappedReturnType = unwrapReturnType(returnType, getFunctionFlags(func)); return !!unwrappedReturnType && maybeTypeOfKind(unwrappedReturnType, TypeFlags.Void | TypeFlags.AnyOrUnknown); } @@ -27792,17 +28583,9 @@ namespace ts { const signature = getSignatureFromDeclaration(func); const returnType = getReturnTypeOfSignature(signature); const functionFlags = getFunctionFlags(func); - const isGenerator = functionFlags & FunctionFlags.Generator; if (strictNullChecks || node.expression || returnType.flags & TypeFlags.Never) { const exprType = node.expression ? checkExpressionCached(node.expression) : undefinedType; - if (isGenerator) { // AsyncGenerator function or Generator function - // A generator does not need its return expressions checked against its return type. - // Instead, the yield expressions are checked against the element type. - // TODO: Check return types of generators when return type tracking is added - // for generators. - return; - } - else if (func.kind === SyntaxKind.SetAccessor) { + if (func.kind === SyntaxKind.SetAccessor) { if (node.expression) { error(node, Diagnostics.Setters_cannot_return_a_value); } @@ -27813,22 +28596,19 @@ namespace ts { } } else if (getReturnTypeFromAnnotation(func)) { - if (functionFlags & FunctionFlags.Async) { // Async function - const promisedType = getPromisedTypeOfPromise(returnType); - const awaitedType = checkAwaitedType(exprType, node, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member); - if (promisedType) { - // If the function has a return type, but promisedType is - // undefined, an error will be reported in checkAsyncFunctionReturnType - // so we don't need to report one here. - checkTypeAssignableToAndOptionallyElaborate(awaitedType, promisedType, node, node.expression); - } - } - else { - checkTypeAssignableToAndOptionallyElaborate(exprType, returnType, node, node.expression); + const unwrappedReturnType = unwrapReturnType(returnType, functionFlags); + const unwrappedExprType = functionFlags & FunctionFlags.Async + ? checkAwaitedType(exprType, node, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member) + : exprType; + if (unwrappedReturnType) { + // If the function has a return type, but promisedType is + // undefined, an error will be reported in checkAsyncFunctionReturnType + // so we don't need to report one here. + checkTypeAssignableToAndOptionallyElaborate(unwrappedExprType, unwrappedReturnType, node, node.expression); } } } - else if (func.kind !== SyntaxKind.Constructor && compilerOptions.noImplicitReturns && !isUnwrappedReturnTypeVoidOrAny(func, returnType) && !isGenerator) { + else if (func.kind !== SyntaxKind.Constructor && compilerOptions.noImplicitReturns && !isUnwrappedReturnTypeVoidOrAny(func, returnType)) { // The function has a return type, but the return statement doesn't have an expression. error(node, Diagnostics.Not_all_code_paths_return_a_value); } @@ -29336,6 +30116,7 @@ namespace ts { if (node) { const saveCurrentNode = currentNode; currentNode = node; + instantiationCount = 0; checkSourceElementWorker(node); currentNode = saveCurrentNode; } @@ -29607,6 +30388,7 @@ namespace ts { function checkDeferredNode(node: Node) { const saveCurrentNode = currentNode; currentNode = node; + instantiationCount = 0; switch (node.kind) { case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: @@ -30307,7 +31089,7 @@ namespace ts { const node = cast(expr.parent, isArrayLiteralExpression); // [{ property1: p1, property2 }] = elems; const typeOfArrayLiteral = getTypeOfAssignmentPattern(node) || errorType; - const elementType = checkIteratedTypeOrElementType(typeOfArrayLiteral, expr.parent, /*allowStringInput*/ false, /*allowAsyncIterables*/ false) || errorType; + const elementType = checkIteratedTypeOrElementType(IterationUse.Destructuring, typeOfArrayLiteral, undefinedType, expr.parent) || errorType; return checkArrayLiteralDestructuringElementAssignment(node, typeOfArrayLiteral, node.elements.indexOf(expr), elementType); } @@ -32773,4 +33555,12 @@ namespace ts { export const LibraryManagedAttributes = "LibraryManagedAttributes" as __String; // tslint:enable variable-name } + + function getIterationTypesKeyFromIterationTypeKind(typeKind: IterationTypeKind) { + switch (typeKind) { + case IterationTypeKind.Yield: return "yieldType"; + case IterationTypeKind.Return: return "returnType"; + case IterationTypeKind.Next: return "nextType"; + } + } } diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 7f67b9c5097..8d8e16dba5d 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -40,6 +40,7 @@ namespace ts { ["es2017.string", "lib.es2017.string.d.ts"], ["es2017.intl", "lib.es2017.intl.d.ts"], ["es2017.typedarrays", "lib.es2017.typedarrays.d.ts"], + ["es2018.asyncgenerator", "lib.es2018.asyncgenerator.d.ts"], ["es2018.asynciterable", "lib.es2018.asynciterable.d.ts"], ["es2018.intl", "lib.es2018.intl.d.ts"], ["es2018.promise", "lib.es2018.promise.d.ts"], @@ -1905,7 +1906,9 @@ namespace ts { case "object": return {}; default: - return option.type.keys().next().value; + const iterResult = option.type.keys().next(); + if (!iterResult.done) return iterResult.value; + return Debug.fail("Expected 'option.type' to have entries."); } } diff --git a/src/compiler/core.ts b/src/compiler/core.ts index f1511b79c12..e0e70c72943 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -24,7 +24,6 @@ namespace ts { " __sortedArrayBrand": any; } - /** ES6 Map interface, only read methods included. */ export interface ReadonlyMap { get(key: string): T | undefined; @@ -45,7 +44,7 @@ namespace ts { /** ES6 Iterator type. */ export interface Iterator { - next(): { value: T, done: false } | { value: never, done: true }; + next(): { value: T, done?: false } | { value: never, done: true }; } /** Array that is only intended to be pushed to, never read. */ @@ -297,12 +296,13 @@ namespace ts { forEach(action: (value: T, key: string) => void): void { const iterator = this.entries(); while (true) { - const { value: entry, done } = iterator.next(); - if (done) { + const iterResult = iterator.next(); + if (iterResult.done) { break; } - action(entry[1], entry[0]); + const [key, value] = iterResult.value; + action(value, key); } } }; @@ -346,11 +346,11 @@ namespace ts { export function firstDefinedIterator(iter: Iterator, callback: (element: T) => U | undefined): U | undefined { while (true) { - const { value, done } = iter.next(); - if (done) { + const iterResult = iter.next(); + if (iterResult.done) { return undefined; } - const result = callback(value); + const result = callback(iterResult.value); if (result !== undefined) { return result; } @@ -375,7 +375,7 @@ namespace ts { return { value: undefined as never, done: true }; } i++; - return { value: [arrayA[i - 1], arrayB[i - 1]], done: false }; + return { value: [arrayA[i - 1], arrayB[i - 1]] as [T, U], done: false }; } }; } @@ -567,7 +567,7 @@ namespace ts { return { next() { const iterRes = iter.next(); - return iterRes.done ? iterRes : { value: mapFn(iterRes.value), done: false }; + return iterRes.done ? iterRes as { done: true, value: never } : { value: mapFn(iterRes.value), done: false }; } }; } @@ -600,24 +600,18 @@ namespace ts { * * @param array The array to flatten. */ - export function flatten(array: ReadonlyArray | undefined>): T[]; - export function flatten(array: ReadonlyArray | undefined> | undefined): T[] | undefined; - export function flatten(array: ReadonlyArray | undefined> | undefined): T[] | undefined { - let result: T[] | undefined; - if (array) { - result = []; - for (const v of array) { - if (v) { - if (isArray(v)) { - addRange(result, v); - } - else { - result.push(v); - } + export function flatten(array: T[][] | ReadonlyArray | undefined>): T[] { + const result = []; + for (const v of array) { + if (v) { + if (isArray(v)) { + addRange(result, v); + } + else { + result.push(v); } } } - return result; } @@ -678,7 +672,7 @@ namespace ts { } const iterRes = iter.next(); if (iterRes.done) { - return iterRes; + return iterRes as { done: true, value: never }; } currentIter = getIterator(iterRes.value); } @@ -753,7 +747,7 @@ namespace ts { while (true) { const res = iter.next(); if (res.done) { - return res; + return res as { done: true, value: never }; } const value = mapFn(res.value); if (value !== undefined) { @@ -1078,6 +1072,7 @@ namespace ts { * @param value The value to append to the array. If `value` is `undefined`, nothing is * appended. */ + export function append[number] | undefined>(to: TArray, value: TValue): [undefined, undefined] extends [TArray, TValue] ? TArray : NonNullable[number][]; export function append(to: T[], value: T | undefined): T[]; export function append(to: T[] | undefined, value: T): T[]; export function append(to: T[] | undefined, value: T | undefined): T[] | undefined; @@ -1402,8 +1397,8 @@ namespace ts { export function arrayFrom(iterator: Iterator | IterableIterator): T[]; export function arrayFrom(iterator: Iterator | IterableIterator, map?: (t: T) => U): (T | U)[] { const result: (T | U)[] = []; - for (let { value, done } = iterator.next(); !done; { value, done } = iterator.next()) { - result.push(map ? map(value) : value); + for (let iterResult = iterator.next(); !iterResult.done; iterResult = iterator.next()) { + result.push(map ? map(iterResult.value) : iterResult.value); } return result; } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index edc3903b38e..10c3fcb6095 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1764,7 +1764,7 @@ "category": "Error", "code": 2489 }, - "The type returned by the 'next()' method of an iterator must have a 'value' property.": { + "The type returned by the '{0}()' method of an iterator must have a 'value' property.": { "category": "Error", "code": 2490 }, @@ -1992,7 +1992,7 @@ "category": "Error", "code": 2546 }, - "The type returned by the 'next()' method of an async iterator must be a promise for a type with a 'value' property.": { + "The type returned by the '{0}()' method of an async iterator must be a promise for a type with a 'value' property.": { "category": "Error", "code": 2547 }, @@ -2653,6 +2653,50 @@ "category": "Error", "code": 2762 }, + "Cannot iterate value because the 'next' method of its iterator expects type '{1}', but for-of will always send '{0}'.": { + "category": "Error", + "code": 2763 + }, + "Cannot iterate value because the 'next' method of its iterator expects type '{1}', but array spread will always send '{0}'.": { + "category": "Error", + "code": 2764 + }, + "Cannot iterate value because the 'next' method of its iterator expects type '{1}', but array destructuring will always send '{0}'.": { + "category": "Error", + "code": 2765 + }, + "Cannot delegate iteration to value because the 'next' method of its iterator expects type '{1}', but the containing generator will always send '{0}'.": { + "category": "Error", + "code": 2766 + }, + "The '{0}' property of an iterator must be a method.": { + "category": "Error", + "code": 2767 + }, + "The '{0}' property of an async iterator must be a method.": { + "category": "Error", + "code": 2768 + }, + "No overload matches this call.": { + "category": "Error", + "code": 2769 + }, + "The last overload gave the following error.": { + "category": "Error", + "code": 2770 + }, + "The last overload is declared here.": { + "category": "Error", + "code": 2771 + }, + "Overload {0} of {1}, '{2}', gave the following error.": { + "category": "Error", + "code": 2772 + }, + "Did you forget to use 'await'?": { + "category": "Error", + "code": 2773 + }, "Import declaration '{0}' is using private name '{1}'.": { "category": "Error", @@ -4218,7 +4262,7 @@ "category": "Error", "code": 7024 }, - "Generator implicitly has type '{0}' because it does not yield any values. Consider supplying a return type.": { + "Generator implicitly has yield type '{0}' because it does not yield any values. Consider supplying a return type annotation.": { "category": "Error", "code": 7025 }, @@ -4340,6 +4384,11 @@ "category": "Error", "code": 7054 }, + "'{0}', which lacks return-type annotation, implicitly has an '{1}' yield type.": { + "category": "Error", + "code": 7055 + }, + "You cannot rename this element.": { "category": "Error", "code": 8000 @@ -5034,6 +5083,14 @@ "category": "Message", "code": 95080 }, + "Add 'const' to unresolved variable": { + "category": "Message", + "code": 95081 + }, + "Add 'const' to all unresolved variables": { + "category": "Message", + "code": 95082 + }, "No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": { "category": "Error", "code": 18004 diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index f67ec39b0a4..f88950f531a 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -1544,8 +1544,8 @@ namespace ts { export function createIf(expression: Expression, thenStatement: Statement, elseStatement?: Statement) { const node = createSynthesizedNode(SyntaxKind.IfStatement); node.expression = expression; - node.thenStatement = thenStatement; - node.elseStatement = elseStatement; + node.thenStatement = asEmbeddedStatement(thenStatement); + node.elseStatement = asEmbeddedStatement(elseStatement); return node; } @@ -1559,7 +1559,7 @@ namespace ts { export function createDo(statement: Statement, expression: Expression) { const node = createSynthesizedNode(SyntaxKind.DoStatement); - node.statement = statement; + node.statement = asEmbeddedStatement(statement); node.expression = expression; return node; } @@ -1574,7 +1574,7 @@ namespace ts { export function createWhile(expression: Expression, statement: Statement) { const node = createSynthesizedNode(SyntaxKind.WhileStatement); node.expression = expression; - node.statement = statement; + node.statement = asEmbeddedStatement(statement); return node; } @@ -1590,7 +1590,7 @@ namespace ts { node.initializer = initializer; node.condition = condition; node.incrementor = incrementor; - node.statement = statement; + node.statement = asEmbeddedStatement(statement); return node; } @@ -1607,7 +1607,7 @@ namespace ts { const node = createSynthesizedNode(SyntaxKind.ForInStatement); node.initializer = initializer; node.expression = expression; - node.statement = statement; + node.statement = asEmbeddedStatement(statement); return node; } @@ -1624,7 +1624,7 @@ namespace ts { node.awaitModifier = awaitModifier; node.initializer = initializer; node.expression = expression; - node.statement = statement; + node.statement = asEmbeddedStatement(statement); return node; } @@ -1676,7 +1676,7 @@ namespace ts { export function createWith(expression: Expression, statement: Statement) { const node = createSynthesizedNode(SyntaxKind.WithStatement); node.expression = expression; - node.statement = statement; + node.statement = asEmbeddedStatement(statement); return node; } @@ -1704,7 +1704,7 @@ namespace ts { export function createLabel(label: string | Identifier, statement: Statement) { const node = createSynthesizedNode(SyntaxKind.LabeledStatement); node.label = asName(label); - node.statement = statement; + node.statement = asEmbeddedStatement(statement); return node; } @@ -3080,6 +3080,12 @@ namespace ts { return typeof value === "number" ? createToken(value) : value; } + function asEmbeddedStatement(statement: T): T | EmptyStatement; + function asEmbeddedStatement(statement: T | undefined): T | EmptyStatement | undefined; + function asEmbeddedStatement(statement: T | undefined): T | EmptyStatement | undefined { + return statement && isNotEmittedStatement(statement) ? setTextRange(setOriginalNode(createEmptyStatement(), statement), statement) : statement; + } + /** * Clears any EmitNode entries from parse-tree nodes. * @param sourceFile A source file. diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index d9d2f761595..f115d66951a 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -468,6 +468,8 @@ namespace ts { visitNode(cbNode, (node).typeExpression) : visitNode(cbNode, (node).typeExpression) || visitNode(cbNode, (node).name)); + case SyntaxKind.JSDocAuthorTag: + return visitNode(cbNode, (node as JSDocTag).tagName); case SyntaxKind.JSDocAugmentsTag: return visitNode(cbNode, (node as JSDocTag).tagName) || visitNode(cbNode, (node).class); @@ -6621,6 +6623,9 @@ namespace ts { let tag: JSDocTag | undefined; switch (tagName.escapedText) { + case "author": + tag = parseAuthorTag(start, tagName, margin); + break; case "augments": case "extends": tag = parseAugmentsTag(start, tagName); @@ -6900,6 +6905,69 @@ namespace ts { return finishNode(result); } + function parseAuthorTag(start: number, tagName: Identifier, indent: number): JSDocAuthorTag { + const result = createNode(SyntaxKind.JSDocAuthorTag, start); + result.tagName = tagName; + + const authorInfoWithEmail = tryParse(() => tryParseAuthorNameAndEmail()); + if (!authorInfoWithEmail) { + return finishNode(result); + } + + result.comment = authorInfoWithEmail; + + if (lookAhead(() => nextToken() !== SyntaxKind.NewLineTrivia)) { + const comment = parseTagComments(indent); + if (comment) { + result.comment += comment; + } + } + + return finishNode(result); + } + + function tryParseAuthorNameAndEmail(): string | undefined { + const comments: string[] = []; + let seenLessThan = false; + let seenGreaterThan = false; + let token = scanner.getToken(); + + loop: while (true) { + switch (token) { + case SyntaxKind.Identifier: + case SyntaxKind.WhitespaceTrivia: + case SyntaxKind.DotToken: + case SyntaxKind.AtToken: + comments.push(scanner.getTokenText()); + break; + case SyntaxKind.LessThanToken: + if (seenLessThan || seenGreaterThan) { + return; + } + seenLessThan = true; + comments.push(scanner.getTokenText()); + break; + case SyntaxKind.GreaterThanToken: + if (!seenLessThan || seenGreaterThan) { + return; + } + seenGreaterThan = true; + comments.push(scanner.getTokenText()); + scanner.setTextPos(scanner.getTokenPos() + 1); + break loop; + case SyntaxKind.NewLineTrivia: + case SyntaxKind.EndOfFileToken: + break loop; + } + + token = nextTokenJSDoc(); + } + + if (seenLessThan && seenGreaterThan) { + return comments.length === 0 ? undefined : comments.join(""); + } + } + function parseAugmentsTag(start: number, tagName: Identifier): JSDocAugmentsTag { const result = createNode(SyntaxKind.JSDocAugmentsTag, start); result.tagName = tagName; diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 63f58066174..19530d2a358 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -502,30 +502,29 @@ namespace ts { return output; } - export function flattenDiagnosticMessageText(messageText: string | DiagnosticMessageChain | undefined, newLine: string): string { - if (isString(messageText)) { - return messageText; + export function flattenDiagnosticMessageText(diag: string | DiagnosticMessageChain | undefined, newLine: string, indent = 0): string { + if (isString(diag)) { + return diag; } - else { - let diagnosticChain = messageText; - let result = ""; + else if (diag === undefined) { + return ""; + } + let result = ""; + if (indent) { + result += newLine; - let indent = 0; - while (diagnosticChain) { - if (indent) { - result += newLine; - - for (let i = 0; i < indent; i++) { - result += " "; - } - } - result += diagnosticChain.messageText; - indent++; - diagnosticChain = diagnosticChain.next; + for (let i = 0; i < indent; i++) { + result += " "; } - - return result; } + result += diag.messageText; + indent++; + if (diag.next) { + for (const kid of diag.next) { + result += flattenDiagnosticMessageText(kid, newLine, indent); + } + } + return result; } /* @internal */ diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index 36ac8adfa90..256e41629a1 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -2086,6 +2086,8 @@ namespace ts { return token = SyntaxKind.CloseBracketToken; case CharacterCodes.lessThan: return token = SyntaxKind.LessThanToken; + case CharacterCodes.greaterThan: + return token = SyntaxKind.GreaterThanToken; case CharacterCodes.equals: return token = SyntaxKind.EqualsToken; case CharacterCodes.comma: diff --git a/src/compiler/sourcemap.ts b/src/compiler/sourcemap.ts index b33536bab2a..c17e47724e4 100644 --- a/src/compiler/sourcemap.ts +++ b/src/compiler/sourcemap.ts @@ -148,7 +148,8 @@ namespace ts { const sourceIndexToNewSourceIndexMap: number[] = []; let nameIndexToNewNameIndexMap: number[] | undefined; const mappingIterator = decodeMappings(map.mappings); - for (let { value: raw, done } = mappingIterator.next(); !done; { value: raw, done } = mappingIterator.next()) { + for (let iterResult = mappingIterator.next(); !iterResult.done; iterResult = mappingIterator.next()) { + const raw = iterResult.value; if (end && ( raw.generatedLine > end.line || (raw.generatedLine === end.line && raw.generatedCharacter > end.character))) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 69ac55e6e1b..ce6d5764bf7 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -17,6 +17,7 @@ namespace ts { | SyntaxKind.OpenBraceToken | SyntaxKind.CloseBraceToken | SyntaxKind.LessThanToken + | SyntaxKind.GreaterThanToken | SyntaxKind.OpenBracketToken | SyntaxKind.CloseBracketToken | SyntaxKind.EqualsToken @@ -459,6 +460,7 @@ namespace ts { JSDocSignature, JSDocTag, JSDocAugmentsTag, + JSDocAuthorTag, JSDocClassTag, JSDocCallbackTag, JSDocEnumTag, @@ -2455,6 +2457,10 @@ namespace ts { class: ExpressionWithTypeArguments & { expression: Identifier | PropertyAccessEntityNameExpression }; } + export interface JSDocAuthorTag extends JSDocTag { + kind: SyntaxKind.JSDocAuthorTag; + } + export interface JSDocClassTag extends JSDocTag { kind: SyntaxKind.JSDocClassTag; } @@ -4269,13 +4275,23 @@ namespace ts { regularType: ResolvedType; // Regular version of fresh type } + /* @internal */ + export interface IterationTypes { + readonly yieldType: Type; + readonly returnType: Type; + readonly nextType: Type; + } + // Just a place to cache element types of iterables and iterators /* @internal */ export interface IterableOrIteratorType extends ObjectType, UnionType { - iteratedTypeOfIterable?: Type; - iteratedTypeOfIterator?: Type; - iteratedTypeOfAsyncIterable?: Type; - iteratedTypeOfAsyncIterator?: Type; + iterationTypesOfGeneratorReturnType?: IterationTypes; + iterationTypesOfAsyncGeneratorReturnType?: IterationTypes; + iterationTypesOfIterable?: IterationTypes; + iterationTypesOfIterator?: IterationTypes; + iterationTypesOfAsyncIterable?: IterationTypes; + iterationTypesOfAsyncIterator?: IterationTypes; + iterationTypesOfIteratorResult?: IterationTypes; } /* @internal */ @@ -4563,7 +4579,7 @@ namespace ts { messageText: string; category: DiagnosticCategory; code: number; - next?: DiagnosticMessageChain; + next?: DiagnosticMessageChain[]; } export interface Diagnostic extends DiagnosticRelatedInformation { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index ab6c0bfd83d..9a86b0a31d7 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -150,8 +150,8 @@ namespace ts { export function forEachEntry(map: ReadonlyMap, callback: (value: T, key: string) => U | undefined): U | undefined; export function forEachEntry(map: ReadonlyUnderscoreEscapedMap | ReadonlyMap, callback: (value: T, key: (string & __String)) => U | undefined): U | undefined { const iterator = map.entries(); - for (let { value: pair, done } = iterator.next(); !done; { value: pair, done } = iterator.next()) { - const [key, value] = pair; + for (let iterResult = iterator.next(); !iterResult.done; iterResult = iterator.next()) { + const [key, value] = iterResult.value; const result = callback(value, key as (string & __String)); if (result) { return result; @@ -165,8 +165,8 @@ namespace ts { export function forEachKey(map: ReadonlyMap<{}>, callback: (key: string) => T | undefined): T | undefined; export function forEachKey(map: ReadonlyUnderscoreEscapedMap<{}> | ReadonlyMap<{}>, callback: (key: string & __String) => T | undefined): T | undefined { const iterator = map.keys(); - for (let { value: key, done } = iterator.next(); !done; { value: key, done } = iterator.next()) { - const result = callback(key as string & __String); + for (let iterResult = iterator.next(); !iterResult.done; iterResult = iterator.next()) { + const result = callback(iterResult.value as string & __String); if (result) { return result; } @@ -6071,6 +6071,10 @@ namespace ts { return node.kind === SyntaxKind.JSDocComment; } + export function isJSDocAuthorTag(node: Node): node is JSDocAuthorTag { + return node.kind === SyntaxKind.JSDocAuthorTag; + } + export function isJSDocAugmentsTag(node: Node): node is JSDocAugmentsTag { return node.kind === SyntaxKind.JSDocAugmentsTag; } @@ -7121,8 +7125,8 @@ namespace ts { }; } - export function chainDiagnosticMessages(details: DiagnosticMessageChain | undefined, message: DiagnosticMessage, ...args: (string | number | undefined)[]): DiagnosticMessageChain; - export function chainDiagnosticMessages(details: DiagnosticMessageChain | undefined, message: DiagnosticMessage): DiagnosticMessageChain { + export function chainDiagnosticMessages(details: DiagnosticMessageChain | DiagnosticMessageChain[] | undefined, message: DiagnosticMessage, ...args: (string | number | undefined)[]): DiagnosticMessageChain; + export function chainDiagnosticMessages(details: DiagnosticMessageChain | DiagnosticMessageChain[] | undefined, message: DiagnosticMessage): DiagnosticMessageChain { let text = getLocaleSpecificMessage(message); if (arguments.length > 2) { @@ -7134,18 +7138,17 @@ namespace ts { category: message.category, code: message.code, - next: details + next: details === undefined || Array.isArray(details) ? details : [details] }; } - export function concatenateDiagnosticMessageChains(headChain: DiagnosticMessageChain, tailChain: DiagnosticMessageChain): DiagnosticMessageChain { + export function concatenateDiagnosticMessageChains(headChain: DiagnosticMessageChain, tailChain: DiagnosticMessageChain): void { let lastChain = headChain; while (lastChain.next) { - lastChain = lastChain.next; + lastChain = lastChain.next[0]; } - lastChain.next = tailChain; - return headChain; + lastChain.next = [tailChain]; } function getDiagnosticFilePath(diagnostic: Diagnostic): string | undefined { @@ -7181,29 +7184,42 @@ namespace ts { } function compareMessageText(t1: string | DiagnosticMessageChain, t2: string | DiagnosticMessageChain): Comparison { - let text1: string | DiagnosticMessageChain | undefined = t1; - let text2: string | DiagnosticMessageChain | undefined = t2; - while (text1 && text2) { - // We still have both chains. - const string1 = isString(text1) ? text1 : text1.messageText; - const string2 = isString(text2) ? text2 : text2.messageText; - - const res = compareStringsCaseSensitive(string1, string2); + if (typeof t1 === "string" && typeof t2 === "string") { + return compareStringsCaseSensitive(t1, t2); + } + else if (typeof t1 === "string") { + return Comparison.LessThan; + } + else if (typeof t2 === "string") { + return Comparison.GreaterThan; + } + let res = compareStringsCaseSensitive(t1.messageText, t2.messageText); + if (res) { + return res; + } + if (!t1.next && !t2.next) { + return Comparison.EqualTo; + } + if (!t1.next) { + return Comparison.LessThan; + } + if (!t2.next) { + return Comparison.GreaterThan; + } + const len = Math.min(t1.next.length, t2.next.length); + for (let i = 0; i < len; i++) { + res = compareMessageText(t1.next[i], t2.next[i]); if (res) { return res; } - - text1 = isString(text1) ? undefined : text1.next; - text2 = isString(text2) ? undefined : text2.next; } - - if (!text1 && !text2) { - // if the chains are done, then these messages are the same. - return Comparison.EqualTo; + if (t1.next.length < t2.next.length) { + return Comparison.LessThan; } - - // We still have one chain remaining. The shorter chain should come first. - return text1 ? Comparison.GreaterThan : Comparison.LessThan; + else if (t1.next.length > t2.next.length) { + return Comparison.GreaterThan; + } + return Comparison.EqualTo; } export function getEmitScriptTarget(compilerOptions: CompilerOptions) { @@ -8101,7 +8117,7 @@ namespace ts { visitDirectory(basePath, combinePaths(currentDirectory, basePath), depth); } - return flatten(results); + return flatten(results); function visitDirectory(path: string, absolutePath: string, depth: number | undefined) { const canonicalPath = toCanonical(realpath(absolutePath)); diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index c26b059ced8..ffc0759e92e 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -1497,13 +1497,7 @@ Actual: ${stringify(fullActual)}`); const diagnostics = ts.getPreEmitDiagnostics(this.languageService.getProgram()!); // TODO: GH#18217 for (const diagnostic of diagnostics) { if (!ts.isString(diagnostic.messageText)) { - let chainedMessage: ts.DiagnosticMessageChain | undefined = diagnostic.messageText; - let indentation = " "; - while (chainedMessage) { - resultString += indentation + chainedMessage.messageText + Harness.IO.newLine(); - chainedMessage = chainedMessage.next; - indentation = indentation + " "; - } + resultString += this.flattenChainedMessage(diagnostic.messageText); } else { resultString += " " + diagnostic.messageText + Harness.IO.newLine(); @@ -1521,6 +1515,17 @@ Actual: ${stringify(fullActual)}`); Harness.Baseline.runBaseline(ts.Debug.assertDefined(this.testData.globalOptions[MetadataOptionNames.baselineFile]), resultString); } + private flattenChainedMessage(diag: ts.DiagnosticMessageChain, indent = " ") { + let result = ""; + result += indent + diag.messageText + Harness.IO.newLine(); + if (diag.next) { + for (const kid of diag.next) { + result += this.flattenChainedMessage(kid, indent + " "); + } + } + return result; + } + public baselineQuickInfo() { const baselineFile = this.getBaselineFileName(); Harness.Baseline.runBaseline( diff --git a/src/harness/harness.ts b/src/harness/harness.ts index f03103d0239..fe5ec344322 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -501,7 +501,7 @@ namespace Harness { } function enumerateTestFiles(runner: RunnerBase) { - return runner.enumerateTestFiles(); + return runner.getTestFiles(); } function listFiles(path: string, spec: RegExp, options: { recursive?: boolean } = {}) { @@ -1311,6 +1311,13 @@ namespace Harness { if (jsCode.length && jsCode.charCodeAt(jsCode.length - 1) !== ts.CharacterCodes.lineFeed) { jsCode += "\r\n"; } + if (!result.diagnostics.length && !ts.endsWith(file.file, ts.Extension.Json)) { + const fileParseResult = ts.createSourceFile(file.file, file.text, options.target || ts.ScriptTarget.ES3, /*parentNodes*/ false, ts.endsWith(file.file, "x") ? ts.ScriptKind.JSX : ts.ScriptKind.JS); + if (ts.length(fileParseResult.parseDiagnostics)) { + jsCode += getErrorBaseline([file.asTestFile()], fileParseResult.parseDiagnostics); + return; + } + } jsCode += fileOutput(file, harnessSettings); }); diff --git a/src/harness/runnerbase.ts b/src/harness/runnerbase.ts index ce56772927e..70045c54c80 100644 --- a/src/harness/runnerbase.ts +++ b/src/harness/runnerbase.ts @@ -2,6 +2,9 @@ type TestRunnerKind = CompilerTestKind | FourslashTestKind | "project" | "rwc" | type CompilerTestKind = "conformance" | "compiler"; type FourslashTestKind = "fourslash" | "fourslash-shims" | "fourslash-shims-pp" | "fourslash-server"; +let shards = 1; +let shardId = 1; + abstract class RunnerBase { // contains the tests to run public tests: (string | Harness.FileBasedTest)[] = []; @@ -19,6 +22,14 @@ abstract class RunnerBase { abstract enumerateTestFiles(): (string | Harness.FileBasedTest)[]; + getTestFiles(): ReturnType { + const all = this.enumerateTestFiles(); + if (shards === 1) { + return all as ReturnType; + } + return all.filter((_val, idx) => idx % shards === (shardId - 1)) as ReturnType; + } + /** The working directory where tests are found. Needed for batch testing where the input path will differ from the output path inside baselines */ public workingDirectory = ""; diff --git a/src/harness/sourceMapRecorder.ts b/src/harness/sourceMapRecorder.ts index 893b17ff106..829e047eade 100644 --- a/src/harness/sourceMapRecorder.ts +++ b/src/harness/sourceMapRecorder.ts @@ -301,7 +301,8 @@ namespace Harness.SourceMapRecorder { SourceMapSpanWriter.initializeSourceMapSpanWriter(sourceMapRecorder, sourceMapData.sourceMap, currentFile); const mapper = ts.decodeMappings(sourceMapData.sourceMap.mappings); - for (let { value: decodedSourceMapping, done } = mapper.next(); !done; { value: decodedSourceMapping, done } = mapper.next()) { + for (let iterResult = mapper.next(); !iterResult.done; iterResult = mapper.next()) { + const decodedSourceMapping = iterResult.value; const currentSourceFile = ts.isSourceMapping(decodedSourceMapping) ? program.getSourceFile(sourceMapData.inputSourceFileNames[decodedSourceMapping.sourceIndex]) : undefined; @@ -335,7 +336,8 @@ namespace Harness.SourceMapRecorder { SourceMapSpanWriter.initializeSourceMapSpanWriter(sourceMapRecorder, sourceMap, currentFile); const mapper = ts.decodeMappings(sourceMap.mappings); - for (let { value: decodedSourceMapping, done } = mapper.next(); !done; { value: decodedSourceMapping, done } = mapper.next()) { + for (let iterResult = mapper.next(); !iterResult.done; iterResult = mapper.next()) { + const decodedSourceMapping = iterResult.value; const currentSourceFile = ts.isSourceMapping(decodedSourceMapping) ? getFile(sourceFileAbsolutePaths[decodedSourceMapping.sourceIndex]) : undefined; diff --git a/src/harness/vfs.ts b/src/harness/vfs.ts index ee9519f254c..b20c04937f8 100644 --- a/src/harness/vfs.ts +++ b/src/harness/vfs.ts @@ -683,7 +683,7 @@ namespace vfs { if (isDirectory(node)) throw createIOError("EISDIR"); if (!isFile(node)) throw createIOError("EBADF"); - node.buffer = Buffer.isBuffer(data) ? data.slice() : ts.sys.bufferFrom!("" + data, encoding || "utf8"); + node.buffer = Buffer.isBuffer(data) ? data.slice() : ts.sys.bufferFrom!("" + data, encoding || "utf8") as Buffer; node.size = node.buffer.byteLength; node.mtimeMs = time; node.ctimeMs = time; @@ -1204,7 +1204,7 @@ namespace vfs { } }, readFileSync(path: string): Buffer { - return ts.sys.bufferFrom!(host.readFile(path)!, "utf8"); // TODO: GH#18217 + return ts.sys.bufferFrom!(host.readFile(path)!, "utf8") as Buffer; // TODO: GH#18217 } }; } diff --git a/src/lib/dom.generated.d.ts b/src/lib/dom.generated.d.ts index e527838fc76..e284ae377c9 100644 --- a/src/lib/dom.generated.d.ts +++ b/src/lib/dom.generated.d.ts @@ -133,6 +133,34 @@ interface AudioWorkletNodeOptions extends AudioNodeOptions { processorOptions?: any; } +interface AuthenticationExtensionsClientInputs { + appid?: string; + authnSel?: AuthenticatorSelectionList; + exts?: boolean; + loc?: boolean; + txAuthGeneric?: txAuthGenericArg; + txAuthSimple?: string; + uvi?: boolean; + uvm?: boolean; +} + +interface AuthenticationExtensionsClientOutputs { + appid?: boolean; + authnSel?: boolean; + exts?: AuthenticationExtensionsSupported; + loc?: Coordinates; + txAuthGeneric?: ArrayBuffer; + txAuthSimple?: string; + uvi?: ArrayBuffer; + uvm?: UvmEntries; +} + +interface AuthenticatorSelectionCriteria { + authenticatorAttachment?: AuthenticatorAttachment; + requireResidentKey?: boolean; + userVerification?: UserVerificationRequirement; +} + interface BiquadFilterOptions extends AudioNodeOptions { Q?: number; detune?: number; @@ -158,6 +186,7 @@ interface CacheQueryOptions { interface CanvasRenderingContext2DSettings { alpha?: boolean; + desynchronized?: boolean; } interface ChannelMergerOptions extends AudioNodeOptions { @@ -251,11 +280,13 @@ interface ConvolverOptions extends AudioNodeOptions { } interface CredentialCreationOptions { + publicKey?: PublicKeyCredentialCreationOptions; signal?: AbortSignal; } interface CredentialRequestOptions { mediation?: CredentialMediationRequirement; + publicKey?: PublicKeyCredentialRequestOptions; signal?: AbortSignal; } @@ -549,6 +580,10 @@ interface IIRFilterOptions extends AudioNodeOptions { feedforward: number[]; } +interface ImageBitmapRenderingContextSettings { + alpha?: boolean; +} + interface ImageEncodeOptions { quality?: number; type?: string; @@ -700,7 +735,6 @@ interface MediaTrackCapabilities { resizeMode?: string[]; sampleRate?: ULongRange; sampleSize?: ULongRange; - volume?: DoubleRange; width?: ULongRange; } @@ -719,7 +753,6 @@ interface MediaTrackConstraintSet { resizeMode?: ConstrainDOMString; sampleRate?: ConstrainULong; sampleSize?: ConstrainULong; - volume?: ConstrainDouble; width?: ConstrainULong; } @@ -742,7 +775,6 @@ interface MediaTrackSettings { resizeMode?: string; sampleRate?: number; sampleSize?: number; - volume?: number; width?: number; } @@ -761,7 +793,6 @@ interface MediaTrackSupportedConstraints { resizeMode?: boolean; sampleRate?: boolean; sampleSize?: boolean; - volume?: boolean; width?: boolean; } @@ -1033,6 +1064,52 @@ interface PropertyIndexedKeyframes { [property: string]: string | string[] | number | null | (number | null)[] | undefined; } +interface PublicKeyCredentialCreationOptions { + attestation?: AttestationConveyancePreference; + authenticatorSelection?: AuthenticatorSelectionCriteria; + challenge: BufferSource; + excludeCredentials?: PublicKeyCredentialDescriptor[]; + extensions?: AuthenticationExtensionsClientInputs; + pubKeyCredParams: PublicKeyCredentialParameters[]; + rp: PublicKeyCredentialRpEntity; + timeout?: number; + user: PublicKeyCredentialUserEntity; +} + +interface PublicKeyCredentialDescriptor { + id: BufferSource; + transports?: AuthenticatorTransport[]; + type: PublicKeyCredentialType; +} + +interface PublicKeyCredentialEntity { + icon?: string; + name: string; +} + +interface PublicKeyCredentialParameters { + alg: COSEAlgorithmIdentifier; + type: PublicKeyCredentialType; +} + +interface PublicKeyCredentialRequestOptions { + allowCredentials?: PublicKeyCredentialDescriptor[]; + challenge: BufferSource; + extensions?: AuthenticationExtensionsClientInputs; + rpId?: string; + timeout?: number; + userVerification?: UserVerificationRequirement; +} + +interface PublicKeyCredentialRpEntity extends PublicKeyCredentialEntity { + id?: string; +} + +interface PublicKeyCredentialUserEntity extends PublicKeyCredentialEntity { + displayName: string; + id: BufferSource; +} + interface PushPermissionDescriptor extends PermissionDescriptor { name: "push"; userVisibleOnly?: boolean; @@ -1761,6 +1838,11 @@ interface WorkletOptions { credentials?: RequestCredentials; } +interface txAuthGenericArg { + content: ArrayBuffer; + contentType: string; +} + interface EventListener { (evt: Event): void; } @@ -1974,7 +2056,7 @@ interface ApplicationCacheEventMap { "error": Event; "noupdate": Event; "obsolete": Event; - "progress": ProgressEvent; + "progress": ProgressEvent; "updateready": Event; } @@ -1992,7 +2074,7 @@ interface ApplicationCache extends EventTarget { /** @deprecated */ onobsolete: ((this: ApplicationCache, ev: Event) => any) | null; /** @deprecated */ - onprogress: ((this: ApplicationCache, ev: ProgressEvent) => any) | null; + onprogress: ((this: ApplicationCache, ev: ProgressEvent) => any) | null; /** @deprecated */ onupdateready: ((this: ApplicationCache, ev: Event) => any) | null; /** @deprecated */ @@ -2286,6 +2368,35 @@ declare var AudioWorkletNode: { new(context: BaseAudioContext, name: string, options?: AudioWorkletNodeOptions): AudioWorkletNode; }; +interface AuthenticatorAssertionResponse extends AuthenticatorResponse { + readonly authenticatorData: ArrayBuffer; + readonly signature: ArrayBuffer; + readonly userHandle: ArrayBuffer | null; +} + +declare var AuthenticatorAssertionResponse: { + prototype: AuthenticatorAssertionResponse; + new(): AuthenticatorAssertionResponse; +}; + +interface AuthenticatorAttestationResponse extends AuthenticatorResponse { + readonly attestationObject: ArrayBuffer; +} + +declare var AuthenticatorAttestationResponse: { + prototype: AuthenticatorAttestationResponse; + new(): AuthenticatorAttestationResponse; +}; + +interface AuthenticatorResponse { + readonly clientDataJSON: ArrayBuffer; +} + +declare var AuthenticatorResponse: { + prototype: AuthenticatorResponse; + new(): AuthenticatorResponse; +}; + interface BarProp { readonly visible: boolean; } @@ -2684,9 +2795,9 @@ interface CSSStyleDeclaration { captionSide: string | null; caretColor: string; clear: string | null; - clip: string | null; - clipPath: string | null; - clipRule: string | null; + clip: string; + clipPath: string; + clipRule: string; color: string | null; colorInterpolationFilters: string; columnCount: string; @@ -2793,8 +2904,13 @@ interface CSSStyleDeclaration { markerEnd: string | null; markerMid: string | null; markerStart: string | null; - mask: string | null; - maskImage: string | null; + mask: string; + maskComposite: string; + maskImage: string; + maskPosition: string; + maskRepeat: string; + maskSize: string; + maskType: string; maxHeight: string | null; maxWidth: string | null; minHeight: string | null; @@ -2856,11 +2972,11 @@ interface CSSStyleDeclaration { outlineOffset: string; outlineStyle: string; outlineWidth: string; - overflow: string | null; + overflow: string; overflowAnchor: string; overflowWrap: string; - overflowX: string | null; - overflowY: string | null; + overflowX: string; + overflowY: string; padding: string | null; paddingBottom: string | null; paddingLeft: string | null; @@ -3026,6 +3142,7 @@ interface CSSStyleDeclaration { webkitFlexWrap: string; /** @deprecated */ webkitJustifyContent: string; + webkitLineClamp: string; /** @deprecated */ webkitMask: string; /** @deprecated */ @@ -4327,59 +4444,59 @@ interface DocumentEventMap extends GlobalEventHandlersEventMap, DocumentAndEleme "fullscreenerror": Event; "pointerlockchange": Event; "pointerlockerror": Event; - "readystatechange": ProgressEvent; + "readystatechange": ProgressEvent; "visibilitychange": Event; } /** Any web page loaded in the browser and serves as an entry point into the web page's content, which is the DOM tree. */ interface Document extends Node, NonElementParentNode, DocumentOrShadowRoot, ParentNode, XPathEvaluatorBase, GlobalEventHandlers, DocumentAndElementEventHandlers { - /** - * Sets or gets the URL for the current document. + /** + * Sets or gets the URL for the current document. */ readonly URL: string; - /** - * Gets the object that has the focus when the parent document has focus. + /** + * Gets the object that has the focus when the parent document has focus. */ readonly activeElement: Element | null; - /** - * Sets or gets the color of all active links in the document. + /** + * Sets or gets the color of all active links in the document. */ /** @deprecated */ alinkColor: string; - /** - * Returns a reference to the collection of elements contained by the object. + /** + * Returns a reference to the collection of elements contained by the object. */ /** @deprecated */ readonly all: HTMLAllCollection; - /** - * Retrieves a collection of all a objects that have a name and/or id property. Objects in this collection are in HTML source order. + /** + * Retrieves a collection of all a objects that have a name and/or id property. Objects in this collection are in HTML source order. */ /** @deprecated */ readonly anchors: HTMLCollectionOf; - /** - * Retrieves a collection of all applet objects in the document. + /** + * Retrieves a collection of all applet objects in the document. */ /** @deprecated */ readonly applets: HTMLCollectionOf; - /** - * Deprecated. Sets or retrieves a value that indicates the background color behind the object. + /** + * Deprecated. Sets or retrieves a value that indicates the background color behind the object. */ /** @deprecated */ bgColor: string; - /** - * Specifies the beginning and end of the document body. + /** + * Specifies the beginning and end of the document body. */ body: HTMLElement; /** * Returns document's encoding. */ readonly characterSet: string; - /** - * Gets or sets the character set used to encode the object. + /** + * Gets or sets the character set used to encode the object. */ readonly charset: string; - /** - * Gets a value that indicates whether standards-compliant mode is switched on for the object. + /** + * Gets a value that indicates whether standards-compliant mode is switched on for the object. */ readonly compatMode: string; /** @@ -4401,41 +4518,41 @@ interface Document extends Node, NonElementParentNode, DocumentOrShadowRoot, Par */ readonly currentScript: HTMLOrSVGScriptElement | null; readonly defaultView: WindowProxy | null; - /** - * Sets or gets a value that indicates whether the document can be edited. + /** + * Sets or gets a value that indicates whether the document can be edited. */ designMode: string; - /** - * Sets or retrieves a value that indicates the reading order of the object. + /** + * Sets or retrieves a value that indicates the reading order of the object. */ dir: string; - /** - * Gets an object representing the document type declaration associated with the current document. + /** + * Gets an object representing the document type declaration associated with the current document. */ readonly doctype: DocumentType | null; - /** - * Gets a reference to the root node of the document. + /** + * Gets a reference to the root node of the document. */ readonly documentElement: HTMLElement; /** * Returns document's URL. */ readonly documentURI: string; - /** - * Sets or gets the security domain of the document. + /** + * Sets or gets the security domain of the document. */ domain: string; - /** - * Retrieves a collection of all embed objects in the document. + /** + * Retrieves a collection of all embed objects in the document. */ readonly embeds: HTMLCollectionOf; - /** - * Sets or gets the foreground (text) color of the document. + /** + * Sets or gets the foreground (text) color of the document. */ /** @deprecated */ fgColor: string; - /** - * Retrieves a collection, in source order, of all form objects in the document. + /** + * Retrieves a collection, in source order, of all form objects in the document. */ readonly forms: HTMLCollectionOf; /** @deprecated */ @@ -4449,44 +4566,44 @@ interface Document extends Node, NonElementParentNode, DocumentOrShadowRoot, Par */ readonly head: HTMLHeadElement; readonly hidden: boolean; - /** - * Retrieves a collection, in source order, of img objects in the document. + /** + * Retrieves a collection, in source order, of img objects in the document. */ readonly images: HTMLCollectionOf; - /** - * Gets the implementation object of the current document. + /** + * Gets the implementation object of the current document. */ readonly implementation: DOMImplementation; - /** - * Returns the character encoding used to create the webpage that is loaded into the document object. + /** + * Returns the character encoding used to create the webpage that is loaded into the document object. */ readonly inputEncoding: string; - /** - * Gets the date that the page was last modified, if the page supplies one. + /** + * Gets the date that the page was last modified, if the page supplies one. */ readonly lastModified: string; - /** - * Sets or gets the color of the document links. + /** + * Sets or gets the color of the document links. */ /** @deprecated */ linkColor: string; - /** - * Retrieves a collection of all a objects that specify the href property and all area objects in the document. + /** + * Retrieves a collection of all a objects that specify the href property and all area objects in the document. */ readonly links: HTMLCollectionOf; - /** - * Contains information about the current URL. + /** + * Contains information about the current URL. */ location: Location; onfullscreenchange: ((this: Document, ev: Event) => any) | null; onfullscreenerror: ((this: Document, ev: Event) => any) | null; onpointerlockchange: ((this: Document, ev: Event) => any) | null; onpointerlockerror: ((this: Document, ev: Event) => any) | null; - /** - * Fires when the state of the object has changed. - * @param ev The event + /** + * Fires when the state of the object has changed. + * @param ev The event */ - onreadystatechange: ((this: Document, ev: ProgressEvent) => any) | null; + onreadystatechange: ((this: Document, ev: ProgressEvent) => any) | null; onvisibilitychange: ((this: Document, ev: Event) => any) | null; /** * Returns document's origin. @@ -4496,27 +4613,27 @@ interface Document extends Node, NonElementParentNode, DocumentOrShadowRoot, Par * Return an HTMLCollection of the embed elements in the Document. */ readonly plugins: HTMLCollectionOf; - /** - * Retrieves a value that indicates the current state of the object. + /** + * Retrieves a value that indicates the current state of the object. */ readonly readyState: DocumentReadyState; - /** - * Gets the URL of the location that referred the user to the current page. + /** + * Gets the URL of the location that referred the user to the current page. */ readonly referrer: string; - /** - * Retrieves a collection of all script objects in the document. + /** + * Retrieves a collection of all script objects in the document. */ readonly scripts: HTMLCollectionOf; readonly scrollingElement: Element | null; readonly timeline: DocumentTimeline; - /** - * Contains the title of the document. + /** + * Contains the title of the document. */ title: string; readonly visibilityState: VisibilityState; - /** - * Sets or gets the color of the links that the user has visited. + /** + * Sets or gets the color of the links that the user has visited. */ /** @deprecated */ vlinkColor: string; @@ -4533,13 +4650,13 @@ interface Document extends Node, NonElementParentNode, DocumentOrShadowRoot, Par caretRangeFromPoint(x: number, y: number): Range; /** @deprecated */ clear(): void; - /** - * Closes an output stream and forces the sent data to display. + /** + * Closes an output stream and forces the sent data to display. */ close(): void; - /** - * Creates an attribute object with a specified name. - * @param name String that sets the attribute object's name. + /** + * Creates an attribute object with a specified name. + * @param name String that sets the attribute object's name. */ createAttribute(localName: string): Attr; createAttributeNS(namespace: string | null, qualifiedName: string): Attr; @@ -4547,18 +4664,18 @@ interface Document extends Node, NonElementParentNode, DocumentOrShadowRoot, Par * Returns a CDATASection node whose data is data. */ createCDATASection(data: string): CDATASection; - /** - * Creates a comment object with the specified data. - * @param data Sets the comment object's data. + /** + * Creates a comment object with the specified data. + * @param data Sets the comment object's data. */ createComment(data: string): Comment; - /** - * Creates a new document. + /** + * Creates a new document. */ createDocumentFragment(): DocumentFragment; - /** - * Creates an instance of the element for the specified tag. - * @param tagName The name of an element. + /** + * Creates an instance of the element for the specified tag. + * @param tagName The name of an element. */ createElement(tagName: K, options?: ElementCreationOptions): HTMLElementTagNameMap[K]; /** @deprecated */ @@ -4599,7 +4716,6 @@ interface Document extends Node, NonElementParentNode, DocumentOrShadowRoot, Par createEvent(eventInterface: "ErrorEvent"): ErrorEvent; createEvent(eventInterface: "Event"): Event; createEvent(eventInterface: "Events"): Event; - createEvent(eventInterface: "FileReaderProgressEvent"): FileReaderProgressEvent; createEvent(eventInterface: "FocusEvent"): FocusEvent; createEvent(eventInterface: "FocusNavigationEvent"): FocusNavigationEvent; createEvent(eventInterface: "GamepadEvent"): GamepadEvent; @@ -4664,49 +4780,49 @@ interface Document extends Node, NonElementParentNode, DocumentOrShadowRoot, Par createEvent(eventInterface: "WebGLContextEvent"): WebGLContextEvent; createEvent(eventInterface: "WheelEvent"): WheelEvent; createEvent(eventInterface: string): Event; - /** - * Creates a NodeIterator object that you can use to traverse filtered lists of nodes or elements in a document. - * @param root The root element or node to start traversing on. - * @param whatToShow The type of nodes or elements to appear in the node list - * @param filter A custom NodeFilter function to use. For more information, see filter. Use null for no filter. - * @param entityReferenceExpansion A flag that specifies whether entity reference nodes are expanded. + /** + * Creates a NodeIterator object that you can use to traverse filtered lists of nodes or elements in a document. + * @param root The root element or node to start traversing on. + * @param whatToShow The type of nodes or elements to appear in the node list + * @param filter A custom NodeFilter function to use. For more information, see filter. Use null for no filter. + * @param entityReferenceExpansion A flag that specifies whether entity reference nodes are expanded. */ createNodeIterator(root: Node, whatToShow?: number, filter?: NodeFilter | null): NodeIterator; /** * Returns a ProcessingInstruction node whose target is target and data is data. If target does not match the Name production an "InvalidCharacterError" DOMException will be thrown. If data contains "?>" an "InvalidCharacterError" DOMException will be thrown. */ createProcessingInstruction(target: string, data: string): ProcessingInstruction; - /** - * Returns an empty range object that has both of its boundary points positioned at the beginning of the document. + /** + * Returns an empty range object that has both of its boundary points positioned at the beginning of the document. */ createRange(): Range; - /** - * Creates a text string from the specified value. - * @param data String that specifies the nodeValue property of the text node. + /** + * Creates a text string from the specified value. + * @param data String that specifies the nodeValue property of the text node. */ createTextNode(data: string): Text; - /** - * Creates a TreeWalker object that you can use to traverse filtered lists of nodes or elements in a document. - * @param root The root element or node to start traversing on. - * @param whatToShow The type of nodes or elements to appear in the node list. For more information, see whatToShow. - * @param filter A custom NodeFilter function to use. - * @param entityReferenceExpansion A flag that specifies whether entity reference nodes are expanded. + /** + * Creates a TreeWalker object that you can use to traverse filtered lists of nodes or elements in a document. + * @param root The root element or node to start traversing on. + * @param whatToShow The type of nodes or elements to appear in the node list. For more information, see whatToShow. + * @param filter A custom NodeFilter function to use. + * @param entityReferenceExpansion A flag that specifies whether entity reference nodes are expanded. */ createTreeWalker(root: Node, whatToShow?: number, filter?: NodeFilter | null): TreeWalker; /** @deprecated */ createTreeWalker(root: Node, whatToShow: number, filter: NodeFilter | null, entityReferenceExpansion?: boolean): TreeWalker; - /** - * Returns the element for the specified x coordinate and the specified y coordinate. - * @param x The x-offset - * @param y The y-offset + /** + * Returns the element for the specified x coordinate and the specified y coordinate. + * @param x The x-offset + * @param y The y-offset */ elementFromPoint(x: number, y: number): Element | null; elementsFromPoint(x: number, y: number): Element[]; - /** - * Executes a command on the current document, current selection, or the given range. - * @param commandId String that specifies the command to execute. This command can be any of the command identifiers that can be executed in script. - * @param showUI Display the user interface, defaults to false. - * @param value Value to assign. + /** + * Executes a command on the current document, current selection, or the given range. + * @param commandId String that specifies the command to execute. This command can be any of the command identifiers that can be executed in script. + * @param showUI Display the user interface, defaults to false. + * @param value Value to assign. */ execCommand(commandId: string, showUI?: boolean, value?: string): boolean; /** @@ -4715,23 +4831,23 @@ interface Document extends Node, NonElementParentNode, DocumentOrShadowRoot, Par exitFullscreen(): Promise; exitPointerLock(): void; getAnimations(): Animation[]; - /** - * Returns a reference to the first object with the specified value of the ID or NAME attribute. - * @param elementId String that specifies the ID value. Case-insensitive. + /** + * Returns a reference to the first object with the specified value of the ID or NAME attribute. + * @param elementId String that specifies the ID value. Case-insensitive. */ getElementById(elementId: string): HTMLElement | null; /** * Returns a HTMLCollection of the elements in the object on which the method was invoked (a document or an element) that have all the classes given by classNames. The classNames argument is interpreted as a space-separated list of classes. */ getElementsByClassName(classNames: string): HTMLCollectionOf; - /** - * Gets a collection of objects based on the value of the NAME or ID attribute. - * @param elementName Gets a collection of objects based on the value of the NAME or ID attribute. + /** + * Gets a collection of objects based on the value of the NAME or ID attribute. + * @param elementName Gets a collection of objects based on the value of the NAME or ID attribute. */ getElementsByName(elementName: string): NodeListOf; - /** - * Retrieves a collection of objects based on the specified element name. - * @param name Specifies the name of an element. + /** + * Retrieves a collection of objects based on the specified element name. + * @param name Specifies the name of an element. */ getElementsByTagName(qualifiedName: K): HTMLCollectionOf; getElementsByTagName(qualifiedName: K): HTMLCollectionOf; @@ -4748,12 +4864,12 @@ interface Document extends Node, NonElementParentNode, DocumentOrShadowRoot, Par getElementsByTagNameNS(namespaceURI: "http://www.w3.org/1999/xhtml", localName: string): HTMLCollectionOf; getElementsByTagNameNS(namespaceURI: "http://www.w3.org/2000/svg", localName: string): HTMLCollectionOf; getElementsByTagNameNS(namespaceURI: string, localName: string): HTMLCollectionOf; - /** - * Returns an object representing the current selection of the document that is loaded into the object displaying a webpage. + /** + * Returns an object representing the current selection of the document that is loaded into the object displaying a webpage. */ getSelection(): Selection | null; - /** - * Gets a value indicating whether the object currently has focus. + /** + * Gets a value indicating whether the object currently has focus. */ hasFocus(): boolean; /** @@ -4762,49 +4878,49 @@ interface Document extends Node, NonElementParentNode, DocumentOrShadowRoot, Par * If node is a document or a shadow root, throws a "NotSupportedError" DOMException. */ importNode(importedNode: T, deep: boolean): T; - /** - * Opens a new window and loads a document specified by a given URL. Also, opens a new window that uses the url parameter and the name parameter to collect the output of the write method and the writeln method. - * @param url Specifies a MIME type for the document. - * @param name Specifies the name of the window. This name is used as the value for the TARGET attribute on a form or an anchor element. - * @param features Contains a list of items separated by commas. Each item consists of an option and a value, separated by an equals sign (for example, "fullscreen=yes, toolbar=yes"). The following values are supported. - * @param replace Specifies whether the existing entry for the document is replaced in the history list. + /** + * Opens a new window and loads a document specified by a given URL. Also, opens a new window that uses the url parameter and the name parameter to collect the output of the write method and the writeln method. + * @param url Specifies a MIME type for the document. + * @param name Specifies the name of the window. This name is used as the value for the TARGET attribute on a form or an anchor element. + * @param features Contains a list of items separated by commas. Each item consists of an option and a value, separated by an equals sign (for example, "fullscreen=yes, toolbar=yes"). The following values are supported. + * @param replace Specifies whether the existing entry for the document is replaced in the history list. */ open(url?: string, name?: string, features?: string, replace?: boolean): Document; - /** - * Returns a Boolean value that indicates whether a specified command can be successfully executed using execCommand, given the current state of the document. - * @param commandId Specifies a command identifier. + /** + * Returns a Boolean value that indicates whether a specified command can be successfully executed using execCommand, given the current state of the document. + * @param commandId Specifies a command identifier. */ queryCommandEnabled(commandId: string): boolean; - /** - * Returns a Boolean value that indicates whether the specified command is in the indeterminate state. - * @param commandId String that specifies a command identifier. + /** + * Returns a Boolean value that indicates whether the specified command is in the indeterminate state. + * @param commandId String that specifies a command identifier. */ queryCommandIndeterm(commandId: string): boolean; - /** - * Returns a Boolean value that indicates the current state of the command. - * @param commandId String that specifies a command identifier. + /** + * Returns a Boolean value that indicates the current state of the command. + * @param commandId String that specifies a command identifier. */ queryCommandState(commandId: string): boolean; - /** - * Returns a Boolean value that indicates whether the current command is supported on the current range. - * @param commandId Specifies a command identifier. + /** + * Returns a Boolean value that indicates whether the current command is supported on the current range. + * @param commandId Specifies a command identifier. */ queryCommandSupported(commandId: string): boolean; - /** - * Returns the current value of the document, range, or current selection for the given command. - * @param commandId String that specifies a command identifier. + /** + * Returns the current value of the document, range, or current selection for the given command. + * @param commandId String that specifies a command identifier. */ queryCommandValue(commandId: string): string; /** @deprecated */ releaseEvents(): void; - /** - * Writes one or more HTML expressions to a document in the specified window. - * @param content Specifies the text and HTML tags to write. + /** + * Writes one or more HTML expressions to a document in the specified window. + * @param content Specifies the text and HTML tags to write. */ write(...text: string[]): void; - /** - * Writes one or more HTML expressions, followed by a carriage return, to a document in the specified window. - * @param content The text and HTML tags to write. + /** + * Writes one or more HTML expressions, followed by a carriage return, to a document in the specified window. + * @param content The text and HTML tags to write. */ writeln(...text: string[]): void; addEventListener(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; @@ -4850,7 +4966,6 @@ interface DocumentEvent { createEvent(eventInterface: "ErrorEvent"): ErrorEvent; createEvent(eventInterface: "Event"): Event; createEvent(eventInterface: "Events"): Event; - createEvent(eventInterface: "FileReaderProgressEvent"): FileReaderProgressEvent; createEvent(eventInterface: "FocusEvent"): FocusEvent; createEvent(eventInterface: "FocusNavigationEvent"): FocusNavigationEvent; createEvent(eventInterface: "GamepadEvent"): GamepadEvent; @@ -4934,8 +5049,8 @@ interface DocumentOrShadowRoot { */ readonly fullscreenElement: Element | null; readonly pointerLockElement: Element | null; - /** - * Retrieves a collection of styleSheet objects representing the style sheets that correspond to each instance of a link or style object in the document. + /** + * Retrieves a collection of styleSheet objects representing the style sheets that correspond to each instance of a link or style object in the document. */ readonly styleSheets: StyleSheetList; caretPositionFromPoint(x: number, y: number): CaretPosition | null; @@ -5256,7 +5371,7 @@ interface Event { */ readonly type: string; /** - * Returns the item objects of event's path (objects on which listeners will be invoked), except for any nodes in shadow trees of which the shadow root's mode is "closed" that are not reachable from event's currentTarget. + * Returns the invocation target objects of event's path (objects on which listeners will be invoked), except for any nodes in shadow trees of which the shadow root's mode is "closed" that are not reachable from event's currentTarget. */ composedPath(): EventTarget[]; initEvent(type: string, bubbles?: boolean, cancelable?: boolean): void; @@ -5418,23 +5533,23 @@ declare var FileList: { }; interface FileReaderEventMap { - "abort": FileReaderProgressEvent; - "error": FileReaderProgressEvent; - "load": FileReaderProgressEvent; - "loadend": FileReaderProgressEvent; - "loadstart": FileReaderProgressEvent; - "progress": FileReaderProgressEvent; + "abort": ProgressEvent; + "error": ProgressEvent; + "load": ProgressEvent; + "loadend": ProgressEvent; + "loadstart": ProgressEvent; + "progress": ProgressEvent; } /** Lets web applications asynchronously read the contents of files (or raw data buffers) stored on the user's computer, using File or Blob objects to specify the file or data to read. */ interface FileReader extends EventTarget { readonly error: DOMException | null; - onabort: ((this: FileReader, ev: FileReaderProgressEvent) => any) | null; - onerror: ((this: FileReader, ev: FileReaderProgressEvent) => any) | null; - onload: ((this: FileReader, ev: FileReaderProgressEvent) => any) | null; - onloadend: ((this: FileReader, ev: FileReaderProgressEvent) => any) | null; - onloadstart: ((this: FileReader, ev: FileReaderProgressEvent) => any) | null; - onprogress: ((this: FileReader, ev: FileReaderProgressEvent) => any) | null; + onabort: ((this: FileReader, ev: ProgressEvent) => any) | null; + onerror: ((this: FileReader, ev: ProgressEvent) => any) | null; + onload: ((this: FileReader, ev: ProgressEvent) => any) | null; + onloadend: ((this: FileReader, ev: ProgressEvent) => any) | null; + onloadstart: ((this: FileReader, ev: ProgressEvent) => any) | null; + onprogress: ((this: FileReader, ev: ProgressEvent) => any) | null; readonly readyState: number; readonly result: string | ArrayBuffer | null; abort(): void; @@ -5459,10 +5574,6 @@ declare var FileReader: { readonly LOADING: number; }; -interface FileReaderProgressEvent extends ProgressEvent { - readonly target: FileReader | null; -} - /** Focus-related events like focus, blur, focusin, or focusout. */ interface FocusEvent extends UIEvent { readonly relatedTarget: EventTarget | null; @@ -5703,9 +5814,9 @@ interface GlobalEventHandlersEventMap { } interface GlobalEventHandlers { - /** - * Fires when the user aborts the download. - * @param ev The event. + /** + * Fires when the user aborts the download. + * @param ev The event. */ onabort: ((this: GlobalEventHandlers, ev: UIEvent) => any) | null; onanimationcancel: ((this: GlobalEventHandlers, ev: AnimationEvent) => any) | null; @@ -5713,177 +5824,177 @@ interface GlobalEventHandlers { onanimationiteration: ((this: GlobalEventHandlers, ev: AnimationEvent) => any) | null; onanimationstart: ((this: GlobalEventHandlers, ev: AnimationEvent) => any) | null; onauxclick: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null; - /** - * Fires when the object loses the input focus. - * @param ev The focus event. + /** + * Fires when the object loses the input focus. + * @param ev The focus event. */ onblur: ((this: GlobalEventHandlers, ev: FocusEvent) => any) | null; oncancel: ((this: GlobalEventHandlers, ev: Event) => any) | null; - /** - * Occurs when playback is possible, but would require further buffering. - * @param ev The event. + /** + * Occurs when playback is possible, but would require further buffering. + * @param ev The event. */ oncanplay: ((this: GlobalEventHandlers, ev: Event) => any) | null; oncanplaythrough: ((this: GlobalEventHandlers, ev: Event) => any) | null; - /** - * Fires when the contents of the object or selection have changed. - * @param ev The event. + /** + * Fires when the contents of the object or selection have changed. + * @param ev The event. */ onchange: ((this: GlobalEventHandlers, ev: Event) => any) | null; - /** - * Fires when the user clicks the left mouse button on the object - * @param ev The mouse event. + /** + * Fires when the user clicks the left mouse button on the object + * @param ev The mouse event. */ onclick: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null; onclose: ((this: GlobalEventHandlers, ev: Event) => any) | null; - /** - * Fires when the user clicks the right mouse button in the client area, opening the context menu. - * @param ev The mouse event. + /** + * Fires when the user clicks the right mouse button in the client area, opening the context menu. + * @param ev The mouse event. */ oncontextmenu: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null; oncuechange: ((this: GlobalEventHandlers, ev: Event) => any) | null; - /** - * Fires when the user double-clicks the object. - * @param ev The mouse event. + /** + * Fires when the user double-clicks the object. + * @param ev The mouse event. */ ondblclick: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null; - /** - * Fires on the source object continuously during a drag operation. - * @param ev The event. + /** + * Fires on the source object continuously during a drag operation. + * @param ev The event. */ ondrag: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null; - /** - * Fires on the source object when the user releases the mouse at the close of a drag operation. - * @param ev The event. + /** + * Fires on the source object when the user releases the mouse at the close of a drag operation. + * @param ev The event. */ ondragend: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null; - /** - * Fires on the target element when the user drags the object to a valid drop target. - * @param ev The drag event. + /** + * Fires on the target element when the user drags the object to a valid drop target. + * @param ev The drag event. */ ondragenter: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null; ondragexit: ((this: GlobalEventHandlers, ev: Event) => any) | null; - /** - * Fires on the target object when the user moves the mouse out of a valid drop target during a drag operation. - * @param ev The drag event. + /** + * Fires on the target object when the user moves the mouse out of a valid drop target during a drag operation. + * @param ev The drag event. */ ondragleave: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null; - /** - * Fires on the target element continuously while the user drags the object over a valid drop target. - * @param ev The event. + /** + * Fires on the target element continuously while the user drags the object over a valid drop target. + * @param ev The event. */ ondragover: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null; - /** - * Fires on the source object when the user starts to drag a text selection or selected object. - * @param ev The event. + /** + * Fires on the source object when the user starts to drag a text selection or selected object. + * @param ev The event. */ ondragstart: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null; ondrop: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null; - /** - * Occurs when the duration attribute is updated. - * @param ev The event. + /** + * Occurs when the duration attribute is updated. + * @param ev The event. */ ondurationchange: ((this: GlobalEventHandlers, ev: Event) => any) | null; - /** - * Occurs when the media element is reset to its initial state. - * @param ev The event. + /** + * Occurs when the media element is reset to its initial state. + * @param ev The event. */ onemptied: ((this: GlobalEventHandlers, ev: Event) => any) | null; - /** - * Occurs when the end of playback is reached. - * @param ev The event + /** + * Occurs when the end of playback is reached. + * @param ev The event */ onended: ((this: GlobalEventHandlers, ev: Event) => any) | null; - /** - * Fires when an error occurs during object loading. - * @param ev The event. + /** + * Fires when an error occurs during object loading. + * @param ev The event. */ onerror: OnErrorEventHandler; - /** - * Fires when the object receives focus. - * @param ev The event. + /** + * Fires when the object receives focus. + * @param ev The event. */ onfocus: ((this: GlobalEventHandlers, ev: FocusEvent) => any) | null; ongotpointercapture: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null; oninput: ((this: GlobalEventHandlers, ev: Event) => any) | null; oninvalid: ((this: GlobalEventHandlers, ev: Event) => any) | null; - /** - * Fires when the user presses a key. - * @param ev The keyboard event + /** + * Fires when the user presses a key. + * @param ev The keyboard event */ onkeydown: ((this: GlobalEventHandlers, ev: KeyboardEvent) => any) | null; - /** - * Fires when the user presses an alphanumeric key. - * @param ev The event. + /** + * Fires when the user presses an alphanumeric key. + * @param ev The event. */ onkeypress: ((this: GlobalEventHandlers, ev: KeyboardEvent) => any) | null; - /** - * Fires when the user releases a key. - * @param ev The keyboard event + /** + * Fires when the user releases a key. + * @param ev The keyboard event */ onkeyup: ((this: GlobalEventHandlers, ev: KeyboardEvent) => any) | null; - /** - * Fires immediately after the browser loads the object. - * @param ev The event. + /** + * Fires immediately after the browser loads the object. + * @param ev The event. */ onload: ((this: GlobalEventHandlers, ev: Event) => any) | null; - /** - * Occurs when media data is loaded at the current playback position. - * @param ev The event. + /** + * Occurs when media data is loaded at the current playback position. + * @param ev The event. */ onloadeddata: ((this: GlobalEventHandlers, ev: Event) => any) | null; - /** - * Occurs when the duration and dimensions of the media have been determined. - * @param ev The event. + /** + * Occurs when the duration and dimensions of the media have been determined. + * @param ev The event. */ onloadedmetadata: ((this: GlobalEventHandlers, ev: Event) => any) | null; onloadend: ((this: GlobalEventHandlers, ev: ProgressEvent) => any) | null; - /** - * Occurs when Internet Explorer begins looking for media data. - * @param ev The event. + /** + * Occurs when Internet Explorer begins looking for media data. + * @param ev The event. */ onloadstart: ((this: GlobalEventHandlers, ev: Event) => any) | null; onlostpointercapture: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null; - /** - * Fires when the user clicks the object with either mouse button. - * @param ev The mouse event. + /** + * Fires when the user clicks the object with either mouse button. + * @param ev The mouse event. */ onmousedown: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null; onmouseenter: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null; onmouseleave: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null; - /** - * Fires when the user moves the mouse over the object. - * @param ev The mouse event. + /** + * Fires when the user moves the mouse over the object. + * @param ev The mouse event. */ onmousemove: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null; - /** - * Fires when the user moves the mouse pointer outside the boundaries of the object. - * @param ev The mouse event. + /** + * Fires when the user moves the mouse pointer outside the boundaries of the object. + * @param ev The mouse event. */ onmouseout: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null; - /** - * Fires when the user moves the mouse pointer into the object. - * @param ev The mouse event. + /** + * Fires when the user moves the mouse pointer into the object. + * @param ev The mouse event. */ onmouseover: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null; - /** - * Fires when the user releases a mouse button while the mouse is over the object. - * @param ev The mouse event. + /** + * Fires when the user releases a mouse button while the mouse is over the object. + * @param ev The mouse event. */ onmouseup: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null; - /** - * Occurs when playback is paused. - * @param ev The event. + /** + * Occurs when playback is paused. + * @param ev The event. */ onpause: ((this: GlobalEventHandlers, ev: Event) => any) | null; - /** - * Occurs when the play method is requested. - * @param ev The event. + /** + * Occurs when the play method is requested. + * @param ev The event. */ onplay: ((this: GlobalEventHandlers, ev: Event) => any) | null; - /** - * Occurs when the audio or video has started playing. - * @param ev The event. + /** + * Occurs when the audio or video has started playing. + * @param ev The event. */ onplaying: ((this: GlobalEventHandlers, ev: Event) => any) | null; onpointercancel: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null; @@ -5894,59 +6005,59 @@ interface GlobalEventHandlers { onpointerout: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null; onpointerover: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null; onpointerup: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null; - /** - * Occurs to indicate progress while downloading media data. - * @param ev The event. + /** + * Occurs to indicate progress while downloading media data. + * @param ev The event. */ onprogress: ((this: GlobalEventHandlers, ev: ProgressEvent) => any) | null; - /** - * Occurs when the playback rate is increased or decreased. - * @param ev The event. + /** + * Occurs when the playback rate is increased or decreased. + * @param ev The event. */ onratechange: ((this: GlobalEventHandlers, ev: Event) => any) | null; - /** - * Fires when the user resets a form. - * @param ev The event. + /** + * Fires when the user resets a form. + * @param ev The event. */ onreset: ((this: GlobalEventHandlers, ev: Event) => any) | null; onresize: ((this: GlobalEventHandlers, ev: UIEvent) => any) | null; - /** - * Fires when the user repositions the scroll box in the scroll bar on the object. - * @param ev The event. + /** + * Fires when the user repositions the scroll box in the scroll bar on the object. + * @param ev The event. */ onscroll: ((this: GlobalEventHandlers, ev: Event) => any) | null; onsecuritypolicyviolation: ((this: GlobalEventHandlers, ev: SecurityPolicyViolationEvent) => any) | null; - /** - * Occurs when the seek operation ends. - * @param ev The event. + /** + * Occurs when the seek operation ends. + * @param ev The event. */ onseeked: ((this: GlobalEventHandlers, ev: Event) => any) | null; - /** - * Occurs when the current playback position is moved. - * @param ev The event. + /** + * Occurs when the current playback position is moved. + * @param ev The event. */ onseeking: ((this: GlobalEventHandlers, ev: Event) => any) | null; - /** - * Fires when the current selection changes. - * @param ev The event. + /** + * Fires when the current selection changes. + * @param ev The event. */ onselect: ((this: GlobalEventHandlers, ev: Event) => any) | null; onselectionchange: ((this: GlobalEventHandlers, ev: Event) => any) | null; onselectstart: ((this: GlobalEventHandlers, ev: Event) => any) | null; - /** - * Occurs when the download has stopped. - * @param ev The event. + /** + * Occurs when the download has stopped. + * @param ev The event. */ onstalled: ((this: GlobalEventHandlers, ev: Event) => any) | null; onsubmit: ((this: GlobalEventHandlers, ev: Event) => any) | null; - /** - * Occurs if the load operation has been intentionally halted. - * @param ev The event. + /** + * Occurs if the load operation has been intentionally halted. + * @param ev The event. */ onsuspend: ((this: GlobalEventHandlers, ev: Event) => any) | null; - /** - * Occurs to indicate the current playback position. - * @param ev The event. + /** + * Occurs to indicate the current playback position. + * @param ev The event. */ ontimeupdate: ((this: GlobalEventHandlers, ev: Event) => any) | null; ontoggle: ((this: GlobalEventHandlers, ev: Event) => any) | null; @@ -5958,14 +6069,14 @@ interface GlobalEventHandlers { ontransitionend: ((this: GlobalEventHandlers, ev: TransitionEvent) => any) | null; ontransitionrun: ((this: GlobalEventHandlers, ev: TransitionEvent) => any) | null; ontransitionstart: ((this: GlobalEventHandlers, ev: TransitionEvent) => any) | null; - /** - * Occurs when the volume is changed, or playback is muted or unmuted. - * @param ev The event. + /** + * Occurs when the volume is changed, or playback is muted or unmuted. + * @param ev The event. */ onvolumechange: ((this: GlobalEventHandlers, ev: Event) => any) | null; - /** - * Occurs when playback stops because the next frame of a video resource is not available. - * @param ev The event. + /** + * Occurs when playback stops because the next frame of a video resource is not available. + * @param ev The event. */ onwaiting: ((this: GlobalEventHandlers, ev: Event) => any) | null; onwheel: ((this: GlobalEventHandlers, ev: WheelEvent) => any) | null; @@ -6002,49 +6113,49 @@ declare var HTMLAllCollection: { /** Hyperlink elements and provides special properties and methods (beyond those of the regular HTMLElement object interface that they inherit from) for manipulating the layout and presentation of such elements. */ interface HTMLAnchorElement extends HTMLElement, HTMLHyperlinkElementUtils { - /** - * Sets or retrieves the character set used to encode the object. + /** + * Sets or retrieves the character set used to encode the object. */ /** @deprecated */ charset: string; - /** - * Sets or retrieves the coordinates of the object. + /** + * Sets or retrieves the coordinates of the object. */ /** @deprecated */ coords: string; download: string; - /** - * Sets or retrieves the language code of the object. + /** + * Sets or retrieves the language code of the object. */ hreflang: string; - /** - * Sets or retrieves the shape of the object. + /** + * Sets or retrieves the shape of the object. */ /** @deprecated */ name: string; ping: string; referrerPolicy: string; - /** - * Sets or retrieves the relationship between the object and the destination of the link. + /** + * Sets or retrieves the relationship between the object and the destination of the link. */ rel: string; readonly relList: DOMTokenList; - /** - * Sets or retrieves the relationship between the object and the destination of the link. + /** + * Sets or retrieves the relationship between the object and the destination of the link. */ /** @deprecated */ rev: string; - /** - * Sets or retrieves the shape of the object. + /** + * Sets or retrieves the shape of the object. */ /** @deprecated */ shape: string; - /** - * Sets or retrieves the window or frame at which to target content. + /** + * Sets or retrieves the window or frame at which to target content. */ target: string; - /** - * Retrieves or sets the text of the object as a string. + /** + * Retrieves or sets the text of the object as a string. */ text: string; type: string; @@ -6062,33 +6173,33 @@ declare var HTMLAnchorElement: { interface HTMLAppletElement extends HTMLElement { /** @deprecated */ align: string; - /** - * Sets or retrieves a text alternative to the graphic. + /** + * Sets or retrieves a text alternative to the graphic. */ /** @deprecated */ alt: string; - /** - * Sets or retrieves a character string that can be used to implement your own archive functionality for the object. + /** + * Sets or retrieves a character string that can be used to implement your own archive functionality for the object. */ /** @deprecated */ archive: string; /** @deprecated */ code: string; - /** - * Sets or retrieves the URL of the component. + /** + * Sets or retrieves the URL of the component. */ /** @deprecated */ codeBase: string; readonly form: HTMLFormElement | null; - /** - * Sets or retrieves the height of the object. + /** + * Sets or retrieves the height of the object. */ /** @deprecated */ height: string; /** @deprecated */ hspace: number; - /** - * Sets or retrieves the shape of the object. + /** + * Sets or retrieves the shape of the object. */ /** @deprecated */ name: string; @@ -6111,17 +6222,17 @@ declare var HTMLAppletElement: { /** Provides special properties and methods (beyond those of the regular object HTMLElement interface it also has available to it by inheritance) for manipulating the layout and presentation of elements. */ interface HTMLAreaElement extends HTMLElement, HTMLHyperlinkElementUtils { - /** - * Sets or retrieves a text alternative to the graphic. + /** + * Sets or retrieves a text alternative to the graphic. */ alt: string; - /** - * Sets or retrieves the coordinates of the object. + /** + * Sets or retrieves the coordinates of the object. */ coords: string; download: string; - /** - * Sets or gets whether clicks in this region cause action. + /** + * Sets or gets whether clicks in this region cause action. */ /** @deprecated */ noHref: boolean; @@ -6129,12 +6240,12 @@ interface HTMLAreaElement extends HTMLElement, HTMLHyperlinkElementUtils { referrerPolicy: string; rel: string; readonly relList: DOMTokenList; - /** - * Sets or retrieves the shape of the object. + /** + * Sets or retrieves the shape of the object. */ shape: string; - /** - * Sets or retrieves the window or frame at which to target content. + /** + * Sets or retrieves the window or frame at which to target content. */ target: string; addEventListener(type: K, listener: (this: HTMLAreaElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; @@ -6163,8 +6274,8 @@ declare var HTMLAudioElement: { /** A HTML line break element (
). It inherits from HTMLElement. */ interface HTMLBRElement extends HTMLElement { - /** - * Sets or retrieves the side on which floating objects are not to be positioned when any IHTMLBlockElement is inserted into the document. + /** + * Sets or retrieves the side on which floating objects are not to be positioned when any IHTMLBlockElement is inserted into the document. */ /** @deprecated */ clear: string; @@ -6181,12 +6292,12 @@ declare var HTMLBRElement: { /** Contains the base URI for a document. This object inherits all of the properties and methods as described in the HTMLElement interface. */ interface HTMLBaseElement extends HTMLElement { - /** - * Gets or sets the baseline URL on which relative links are based. + /** + * Gets or sets the baseline URL on which relative links are based. */ href: string; - /** - * Sets or retrieves the window or frame at which to target content. + /** + * Sets or retrieves the window or frame at which to target content. */ target: string; addEventListener(type: K, listener: (this: HTMLBaseElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; @@ -6202,13 +6313,13 @@ declare var HTMLBaseElement: { /** Provides special properties (beyond the regular HTMLElement interface it also has available to it by inheritance) for manipulating elements. */ interface HTMLBaseFontElement extends HTMLElement, DOML2DeprecatedColorProperty { - /** - * Sets or retrieves the current typeface family. + /** + * Sets or retrieves the current typeface family. */ /** @deprecated */ face: string; - /** - * Sets or retrieves the font size of the object. + /** + * Sets or retrieves the font size of the object. */ /** @deprecated */ size: number; @@ -6259,68 +6370,68 @@ declare var HTMLBodyElement: { /** Provides properties and methods (beyond the regular HTMLElement interface it also has available to it by inheritance) for manipulating