diff --git a/scripts/build/options.js b/scripts/build/options.js index 5d4aad6f5fc..646ae1f826b 100644 --- a/scripts/build/options.js +++ b/scripts/build/options.js @@ -31,7 +31,7 @@ module.exports = minimist(process.argv.slice(2), { reporter: process.env.reporter || process.env.r, lint: process.env.lint || true, fix: process.env.fix || process.env.f, - workers: process.env.workerCount || os.cpus().length, + workers: process.env.workerCount || ((os.cpus().length - (process.env.CI ? 0 : 1)) || 1), failed: false, keepFailed: false, lkg: true, diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 92005a36b65..0f2b62bf493 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5009,7 +5009,20 @@ namespace ts { if (type.flags & TypeFlags.TypeParameter || objectFlags & ObjectFlags.ClassOrInterface) { if (type.flags & TypeFlags.TypeParameter && contains(context.inferTypeParameters, type)) { context.approximateLength += (symbolName(type.symbol).length + 6); - return factory.createInferTypeNode(typeParameterToDeclarationWithConstraint(type as TypeParameter, context, /*constraintNode*/ undefined)); + let constraintNode: TypeNode | undefined; + const constraint = getConstraintOfTypeParameter(type as TypeParameter); + if (constraint) { + // If the infer type has a constraint that is not the same as the constraint + // we would have normally inferred based on context, we emit the constraint + // using `infer T extends ?`. We omit inferred constraints from type references + // as they may be elided. + const inferredConstraint = getInferredTypeParameterConstraint(type as TypeParameter, /*omitTypeReferences*/ true); + if (!(inferredConstraint && isTypeIdenticalTo(constraint, inferredConstraint))) { + context.approximateLength += 9; + constraintNode = constraint && typeToTypeNodeHelper(constraint, context); + } + } + return factory.createInferTypeNode(typeParameterToDeclarationWithConstraint(type as TypeParameter, context, constraintNode)); } if (context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams && type.flags & TypeFlags.TypeParameter && @@ -5089,13 +5102,49 @@ namespace ts { function conditionalTypeToTypeNode(type: ConditionalType) { const checkTypeNode = typeToTypeNodeHelper(type.checkType, context); + context.approximateLength += 15; + if (context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams && type.root.isDistributive && !(type.checkType.flags & TypeFlags.TypeParameter)) { + const newParam = createTypeParameter(createSymbol(SymbolFlags.TypeParameter, "T" as __String)); + const name = typeParameterToName(newParam, context); + const newTypeVariable = factory.createTypeReferenceNode(name); + context.approximateLength += 37; // 15 each for two added conditionals, 7 for an added infer type + const newMapper = prependTypeMapping(type.root.checkType, newParam, type.combinedMapper || type.mapper); + const saveInferTypeParameters = context.inferTypeParameters; + context.inferTypeParameters = type.root.inferTypeParameters; + const extendsTypeNode = typeToTypeNodeHelper(instantiateType(type.root.extendsType, newMapper), context); + context.inferTypeParameters = saveInferTypeParameters; + const trueTypeNode = typeToTypeNodeOrCircularityElision(instantiateType(getTypeFromTypeNode(type.root.node.trueType), newMapper)); + const falseTypeNode = typeToTypeNodeOrCircularityElision(instantiateType(getTypeFromTypeNode(type.root.node.falseType), newMapper)); + + + // outermost conditional makes `T` a type parameter, allowing the inner conditionals to be distributive + // second conditional makes `T` have `T & checkType` substitution, so it is correctly usable as the checkType + // inner conditional runs the check the user provided on the check type (distributively) and returns the result + // checkType extends infer T ? T extends checkType ? T extends extendsType ? trueType : falseType : never : never; + // this is potentially simplifiable to + // checkType extends infer T ? T extends checkType & extendsType ? trueType : falseType : never; + // but that may confuse users who read the output more. + // On the other hand, + // checkType extends infer T extends checkType ? T extends extendsType ? trueType : falseType : never; + // may also work with `infer ... extends ...` in, but would produce declarations only compatible with the latest TS. + return factory.createConditionalTypeNode( + checkTypeNode, + factory.createInferTypeNode(factory.createTypeParameterDeclaration(/*modifiers*/ undefined, factory.cloneNode(newTypeVariable.typeName) as Identifier)), + factory.createConditionalTypeNode( + factory.createTypeReferenceNode(factory.cloneNode(name)), + typeToTypeNodeHelper(type.checkType, context), + factory.createConditionalTypeNode(newTypeVariable, extendsTypeNode, trueTypeNode, falseTypeNode), + factory.createKeywordTypeNode(SyntaxKind.NeverKeyword) + ), + factory.createKeywordTypeNode(SyntaxKind.NeverKeyword) + ); + } const saveInferTypeParameters = context.inferTypeParameters; context.inferTypeParameters = type.root.inferTypeParameters; const extendsTypeNode = typeToTypeNodeHelper(type.extendsType, context); context.inferTypeParameters = saveInferTypeParameters; const trueTypeNode = typeToTypeNodeOrCircularityElision(getTrueTypeFromConditionalType(type)); const falseTypeNode = typeToTypeNodeOrCircularityElision(getFalseTypeFromConditionalType(type)); - context.approximateLength += 15; return factory.createConditionalTypeNode(checkTypeNode, extendsTypeNode, trueTypeNode, falseTypeNode); } @@ -13257,7 +13306,7 @@ namespace ts { return mapDefined(filter(type.symbol && type.symbol.declarations, isTypeParameterDeclaration), getEffectiveConstraintOfTypeParameter)[0]; } - function getInferredTypeParameterConstraint(typeParameter: TypeParameter) { + function getInferredTypeParameterConstraint(typeParameter: TypeParameter, omitTypeReferences?: boolean) { let inferences: Type[] | undefined; if (typeParameter.symbol?.declarations) { for (const declaration of typeParameter.symbol.declarations) { @@ -13267,7 +13316,7 @@ namespace ts { // corresponding type parameter in 'Foo'. When multiple 'infer T' declarations are // present, we form an intersection of the inferred constraint types. const [childTypeParameter = declaration.parent, grandParent] = walkUpParenthesizedTypesAndGetParentAndChild(declaration.parent.parent); - if (grandParent.kind === SyntaxKind.TypeReference) { + if (grandParent.kind === SyntaxKind.TypeReference && !omitTypeReferences) { const typeReference = grandParent as TypeReferenceNode; const typeParameters = getTypeParametersForTypeReference(typeReference); if (typeParameters) { @@ -15860,7 +15909,11 @@ namespace ts { } function isSingletonTupleType(node: TypeNode) { - return isTupleTypeNode(node) && length(node.elements) === 1 && !isOptionalTypeNode(node.elements[0]) && !isRestTypeNode(node.elements[0]); + return isTupleTypeNode(node) && + length(node.elements) === 1 && + !isOptionalTypeNode(node.elements[0]) && + !isRestTypeNode(node.elements[0]) && + !(isNamedTupleMember(node.elements[0]) && (node.elements[0].questionToken || node.elements[0].dotDotDotToken)); } /** @@ -20640,6 +20693,9 @@ namespace ts { function createMarkerType(symbol: Symbol, source: TypeParameter, target: Type) { const mapper = makeUnaryTypeMapper(source, target); const type = getDeclaredTypeOfSymbol(symbol); + if (isErrorType(type)) { + return type; + } const result = symbol.flags & SymbolFlags.TypeAlias ? getTypeAliasInstantiation(symbol, instantiateTypes(getSymbolLinks(symbol).typeParameters!, mapper)) : createTypeReference(type as GenericType, instantiateTypes((type as GenericType).typeParameters, mapper)); @@ -21729,6 +21785,9 @@ namespace ts { const inference = inferences[i]; if (t === inference.typeParameter) { if (fix && !inference.isFixed) { + // Before we commit to a particular inference (and thus lock out any further inferences), + // we infer from any intra-expression inference sites we have collected. + inferFromIntraExpressionSites(context); clearCachedInferences(inferences); inference.isFixed = true; } @@ -21746,6 +21805,37 @@ namespace ts { } } + function addIntraExpressionInferenceSite(context: InferenceContext, node: Expression | MethodDeclaration, type: Type) { + (context.intraExpressionInferenceSites ??= []).push({ node, type }); + } + + // We collect intra-expression inference sites within object and array literals to handle cases where + // inferred types flow between context sensitive element expressions. For example: + // + // declare function foo(arg: [(n: number) => T, (x: T) => void]): void; + // foo([_a => 0, n => n.toFixed()]); + // + // Above, both arrow functions in the tuple argument are context sensitive, thus both are omitted from the + // pass that collects inferences from the non-context sensitive parts of the arguments. In the subsequent + // pass where nothing is omitted, we need to commit to an inference for T in order to contextually type the + // parameter in the second arrow function, but we want to first infer from the return type of the first + // arrow function. This happens automatically when the arrow functions are discrete arguments (because we + // infer from each argument before processing the next), but when the arrow functions are elements of an + // object or array literal, we need to perform intra-expression inferences early. + function inferFromIntraExpressionSites(context: InferenceContext) { + if (context.intraExpressionInferenceSites) { + for (const { node, type } of context.intraExpressionInferenceSites) { + const contextualType = node.kind === SyntaxKind.MethodDeclaration ? + getContextualTypeForObjectLiteralMethod(node as MethodDeclaration, ContextFlags.NoConstraints) : + getContextualType(node, ContextFlags.NoConstraints); + if (contextualType) { + inferTypes(context.inferences, type, contextualType); + } + } + context.intraExpressionInferenceSites = undefined; + } + } + function createInferenceInfo(typeParameter: TypeParameter): InferenceInfo { return { typeParameter, @@ -25304,6 +25394,7 @@ namespace ts { // a generic type without a nullable constraint and x is a generic type. This is because when both obj // and x are of generic types T and K, we want the resulting type to be T[K]. return parent.kind === SyntaxKind.PropertyAccessExpression || + parent.kind === SyntaxKind.QualifiedName || parent.kind === SyntaxKind.CallExpression && (parent as CallExpression).expression === node || parent.kind === SyntaxKind.ElementAccessExpression && (parent as ElementAccessExpression).expression === node && !(someType(type, isGenericTypeWithoutNullableConstraint) && isGenericIndexType(getTypeOfExpression((parent as ElementAccessExpression).argumentExpression))); @@ -27408,6 +27499,11 @@ namespace ts { const type = checkExpressionForMutableLocation(e, checkMode, elementContextualType, forceTuple); elementTypes.push(addOptionality(type, /*isProperty*/ true, hasOmittedExpression)); elementFlags.push(hasOmittedExpression ? ElementFlags.Optional : ElementFlags.Required); + if (contextualType && someType(contextualType, isTupleLikeType) && checkMode && checkMode & CheckMode.Inferential && !(checkMode & CheckMode.SkipContextSensitive) && isContextSensitive(e)) { + const inferenceContext = getInferenceContext(node); + Debug.assert(inferenceContext); // In CheckMode.Inferential we should always have an inference context + addIntraExpressionInferenceSite(inferenceContext, e, type); + } } } if (inDestructuringPattern) { @@ -27625,6 +27721,14 @@ namespace ts { prop.target = member; member = prop; allPropertiesTable?.set(prop.escapedName, prop); + + if (contextualType && checkMode && checkMode & CheckMode.Inferential && !(checkMode & CheckMode.SkipContextSensitive) && + (memberDecl.kind === SyntaxKind.PropertyAssignment || memberDecl.kind === SyntaxKind.MethodDeclaration) && isContextSensitive(memberDecl)) { + const inferenceContext = getInferenceContext(node); + Debug.assert(inferenceContext); // In CheckMode.Inferential we should always have an inference context + const inferenceNode = memberDecl.kind === SyntaxKind.PropertyAssignment ? memberDecl.initializer : memberDecl; + addIntraExpressionInferenceSite(inferenceContext, inferenceNode, type); + } } else if (memberDecl.kind === SyntaxKind.SpreadAssignment) { if (languageVersion < ScriptTarget.ES2015) { @@ -29727,34 +29831,36 @@ namespace ts { if (node.kind !== SyntaxKind.Decorator) { const contextualType = getContextualType(node, every(signature.typeParameters, p => !!getDefaultFromTypeParameter(p)) ? ContextFlags.SkipBindingPatterns : ContextFlags.None); if (contextualType) { - // We clone the inference context to avoid disturbing a resolution in progress for an - // outer call expression. Effectively we just want a snapshot of whatever has been - // inferred for any outer call expression so far. - const outerContext = getInferenceContext(node); - const outerMapper = getMapperFromContext(cloneInferenceContext(outerContext, InferenceFlags.NoDefault)); - const instantiatedType = instantiateType(contextualType, outerMapper); - // If the contextual type is a generic function type with a single call signature, we - // instantiate the type with its own type parameters and type arguments. This ensures that - // the type parameters are not erased to type any during type inference such that they can - // be inferred as actual types from the contextual type. For example: - // declare function arrayMap(f: (x: T) => U): (a: T[]) => U[]; - // const boxElements: (a: A[]) => { value: A }[] = arrayMap(value => ({ value })); - // Above, the type of the 'value' parameter is inferred to be 'A'. - const contextualSignature = getSingleCallSignature(instantiatedType); - const inferenceSourceType = contextualSignature && contextualSignature.typeParameters ? - getOrCreateTypeFromSignature(getSignatureInstantiationWithoutFillingInTypeArguments(contextualSignature, contextualSignature.typeParameters)) : - instantiatedType; const inferenceTargetType = getReturnTypeOfSignature(signature); - // Inferences made from return types have lower priority than all other inferences. - inferTypes(context.inferences, inferenceSourceType, inferenceTargetType, InferencePriority.ReturnType); - // Create a type mapper for instantiating generic contextual types using the inferences made - // from the return type. We need a separate inference pass here because (a) instantiation of - // the source type uses the outer context's return mapper (which excludes inferences made from - // outer arguments), and (b) we don't want any further inferences going into this context. - const returnContext = createInferenceContext(signature.typeParameters!, signature, context.flags); - const returnSourceType = instantiateType(contextualType, outerContext && outerContext.returnMapper); - inferTypes(returnContext.inferences, returnSourceType, inferenceTargetType); - context.returnMapper = some(returnContext.inferences, hasInferenceCandidates) ? getMapperFromContext(cloneInferredPartOfContext(returnContext)) : undefined; + if (couldContainTypeVariables(inferenceTargetType)) { + // We clone the inference context to avoid disturbing a resolution in progress for an + // outer call expression. Effectively we just want a snapshot of whatever has been + // inferred for any outer call expression so far. + const outerContext = getInferenceContext(node); + const outerMapper = getMapperFromContext(cloneInferenceContext(outerContext, InferenceFlags.NoDefault)); + const instantiatedType = instantiateType(contextualType, outerMapper); + // If the contextual type is a generic function type with a single call signature, we + // instantiate the type with its own type parameters and type arguments. This ensures that + // the type parameters are not erased to type any during type inference such that they can + // be inferred as actual types from the contextual type. For example: + // declare function arrayMap(f: (x: T) => U): (a: T[]) => U[]; + // const boxElements: (a: A[]) => { value: A }[] = arrayMap(value => ({ value })); + // Above, the type of the 'value' parameter is inferred to be 'A'. + const contextualSignature = getSingleCallSignature(instantiatedType); + const inferenceSourceType = contextualSignature && contextualSignature.typeParameters ? + getOrCreateTypeFromSignature(getSignatureInstantiationWithoutFillingInTypeArguments(contextualSignature, contextualSignature.typeParameters)) : + instantiatedType; + // Inferences made from return types have lower priority than all other inferences. + inferTypes(context.inferences, inferenceSourceType, inferenceTargetType, InferencePriority.ReturnType); + // Create a type mapper for instantiating generic contextual types using the inferences made + // from the return type. We need a separate inference pass here because (a) instantiation of + // the source type uses the outer context's return mapper (which excludes inferences made from + // outer arguments), and (b) we don't want any further inferences going into this context. + const returnContext = createInferenceContext(signature.typeParameters!, signature, context.flags); + const returnSourceType = instantiateType(contextualType, outerContext && outerContext.returnMapper); + inferTypes(returnContext.inferences, returnSourceType, inferenceTargetType); + context.returnMapper = some(returnContext.inferences, hasInferenceCandidates) ? getMapperFromContext(cloneInferredPartOfContext(returnContext)) : undefined; + } } } @@ -29768,7 +29874,7 @@ namespace ts { } const thisType = getThisTypeOfSignature(signature); - if (thisType) { + if (thisType && couldContainTypeVariables(thisType)) { const thisArgumentNode = getThisArgumentOfCall(node); inferTypes(context.inferences, getThisArgumentType(thisArgumentNode), thisType); } @@ -29777,12 +29883,14 @@ namespace ts { const arg = args[i]; if (arg.kind !== SyntaxKind.OmittedExpression && !(checkMode & CheckMode.IsForStringLiteralArgumentCompletions && hasSkipDirectInferenceFlag(arg))) { const paramType = getTypeAtPosition(signature, i); - const argType = checkExpressionWithContextualType(arg, paramType, context, checkMode); - inferTypes(context.inferences, argType, paramType); + if (couldContainTypeVariables(paramType)) { + const argType = checkExpressionWithContextualType(arg, paramType, context, checkMode); + inferTypes(context.inferences, argType, paramType); + } } } - if (restType) { + if (restType && couldContainTypeVariables(restType)) { const spreadType = getSpreadArgumentType(args, argCount, args.length, restType, context, checkMode); inferTypes(context.inferences, spreadType, restType); } @@ -34141,6 +34249,11 @@ namespace ts { context.contextualType = contextualType; context.inferenceContext = inferenceContext; const type = checkExpression(node, checkMode | CheckMode.Contextual | (inferenceContext ? CheckMode.Inferential : 0)); + // In CheckMode.Inferential we collect intra-expression inference sites to process before fixing any type + // parameters. This information is no longer needed after the call to checkExpression. + if (inferenceContext && inferenceContext.intraExpressionInferenceSites) { + inferenceContext.intraExpressionInferenceSites = undefined; + } // We strip literal freshness when an appropriate contextual type is present such that contextually typed // literals always preserve their literal types (otherwise they might widen during type inference). An alternative // here would be to not mark contextually typed literals as fresh in the first place. @@ -34718,14 +34831,19 @@ namespace ts { } if (node.parent.kind === SyntaxKind.InterfaceDeclaration || node.parent.kind === SyntaxKind.ClassDeclaration || node.parent.kind === SyntaxKind.TypeAliasDeclaration) { const modifiers = getVarianceModifiers(typeParameter); - if (modifiers === ModifierFlags.In || modifiers === ModifierFlags.Out) { + if (modifiers) { const symbol = getSymbolOfNode(node.parent); - const source = createMarkerType(symbol, typeParameter, modifiers === ModifierFlags.Out ? markerSubType : markerSuperType); - const target = createMarkerType(symbol, typeParameter, modifiers === ModifierFlags.Out ? markerSuperType : markerSubType); - const saveVarianceTypeParameter = typeParameter; - varianceTypeParameter = typeParameter; - checkTypeAssignableTo(source, target, node, Diagnostics.Type_0_is_not_assignable_to_type_1_as_implied_by_variance_annotation); - varianceTypeParameter = saveVarianceTypeParameter; + if (node.parent.kind === SyntaxKind.TypeAliasDeclaration && !(getObjectFlags(getDeclaredTypeOfSymbol(symbol)) & (ObjectFlags.Anonymous | ObjectFlags.Mapped))) { + error(node, Diagnostics.Variance_annotations_are_only_supported_in_type_aliases_for_object_function_constructor_and_mapped_types); + } + else if (modifiers === ModifierFlags.In || modifiers === ModifierFlags.Out) { + const source = createMarkerType(symbol, typeParameter, modifiers === ModifierFlags.Out ? markerSubType : markerSuperType); + const target = createMarkerType(symbol, typeParameter, modifiers === ModifierFlags.Out ? markerSuperType : markerSubType); + const saveVarianceTypeParameter = typeParameter; + varianceTypeParameter = typeParameter; + checkTypeAssignableTo(source, target, node, Diagnostics.Type_0_is_not_assignable_to_type_1_as_implied_by_variance_annotation); + varianceTypeParameter = saveVarianceTypeParameter; + } } } addLazyDiagnostic(() => checkTypeNameIsReserved(node.name, Diagnostics.Type_parameter_name_cannot_be_0)); @@ -35606,6 +35724,22 @@ namespace ts { grammarErrorOnNode(node, Diagnostics.infer_declarations_are_only_permitted_in_the_extends_clause_of_a_conditional_type); } checkSourceElement(node.typeParameter); + const symbol = getSymbolOfNode(node.typeParameter); + if (symbol.declarations && symbol.declarations.length > 1) { + const links = getSymbolLinks(symbol); + if (!links.typeParametersChecked) { + links.typeParametersChecked = true; + const typeParameter = getDeclaredTypeOfTypeParameter(symbol); + const declarations: TypeParameterDeclaration[] = getDeclarationsOfKind(symbol, SyntaxKind.TypeParameter); + if (!areTypeParametersIdentical(declarations, [typeParameter], decl => [decl])) { + // Report an error on every conflicting declaration. + const name = symbolToString(symbol); + for (const declaration of declarations) { + error(declaration.name, Diagnostics.All_declarations_of_0_must_have_identical_constraints, name); + } + } + } + } registerForUnusedIdentifiersCheck(node); } @@ -39116,7 +39250,7 @@ namespace ts { } const type = getDeclaredTypeOfSymbol(symbol) as InterfaceType; - if (!areTypeParametersIdentical(declarations, type.localTypeParameters!)) { + if (!areTypeParametersIdentical(declarations, type.localTypeParameters!, getEffectiveTypeParameterDeclarations)) { // Report an error on every conflicting declaration. const name = symbolToString(symbol); for (const declaration of declarations) { @@ -39126,13 +39260,13 @@ namespace ts { } } - function areTypeParametersIdentical(declarations: readonly (ClassDeclaration | InterfaceDeclaration)[], targetParameters: TypeParameter[]) { + function areTypeParametersIdentical(declarations: readonly T[], targetParameters: TypeParameter[], getTypeParameterDeclarations: (node: T) => readonly TypeParameterDeclaration[]) { const maxTypeArgumentCount = length(targetParameters); const minTypeArgumentCount = getMinTypeArgumentCount(targetParameters); for (const declaration of declarations) { // If this declaration has too few or too many type parameters, we report an error - const sourceParameters = getEffectiveTypeParameterDeclarations(declaration); + const sourceParameters = getTypeParameterDeclarations(declaration); const numTypeParameters = sourceParameters.length; if (numTypeParameters < minTypeArgumentCount || numTypeParameters > maxTypeArgumentCount) { return false; diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 9ef4e2c6c16..e8a765e514c 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2743,6 +2743,10 @@ "category": "Error", "code": 2636 }, + "Variance annotations are only supported in type aliases for object, function, constructor, and mapped types.": { + "category": "Error", + "code": 2637 + }, "Cannot augment module '{0}' with value exports because it resolves to a non-module entity.": { "category": "Error", @@ -3433,6 +3437,10 @@ "category": "Error", "code": 2837 }, + "All declarations of '{0}' must have identical constraints.": { + "category": "Error", + "code": 2838 + }, "Import declaration '{0}' is using private name '{1}'.": { "category": "Error", diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index e06b0e6b8b1..a0fbb74b33b 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -909,6 +909,9 @@ namespace ts { let currentParenthesizerRule: ((node: Node) => Node) | undefined; const { enter: enterComment, exit: exitComment } = performance.createTimerIf(extendedDiagnostics, "commentTime", "beforeComment", "afterComment"); const parenthesizer = factory.parenthesizer; + const typeArgumentParenthesizerRuleSelector: OrdinalParentheizerRuleSelector = { + select: index => index === 0 ? parenthesizer.parenthesizeLeadingTypeArgument : undefined + }; const emitBinaryExpression = createEmitBinaryExpression(); reset(); @@ -2241,7 +2244,7 @@ namespace ts { } function emitArrayType(node: ArrayTypeNode) { - emit(node.elementType, parenthesizer.parenthesizeElementTypeOfArrayType); + emit(node.elementType, parenthesizer.parenthesizeNonArrayTypeOfPostfixType); writePunctuation("["); writePunctuation("]"); } @@ -2254,7 +2257,7 @@ namespace ts { function emitTupleType(node: TupleTypeNode) { emitTokenWithComment(SyntaxKind.OpenBracketToken, node.pos, writePunctuation, node); const flags = getEmitFlags(node) & EmitFlags.SingleLine ? ListFormat.SingleLineTupleTypeElements : ListFormat.MultiLineTupleTypeElements; - emitList(node, node.elements, flags | ListFormat.NoSpaceIfEmpty); + emitList(node, node.elements, flags | ListFormat.NoSpaceIfEmpty, parenthesizer.parenthesizeElementTypeOfTupleType); emitTokenWithComment(SyntaxKind.CloseBracketToken, node.elements.end, writePunctuation, node); } @@ -2268,24 +2271,24 @@ namespace ts { } function emitOptionalType(node: OptionalTypeNode) { - emit(node.type, parenthesizer.parenthesizeElementTypeOfArrayType); + emit(node.type, parenthesizer.parenthesizeTypeOfOptionalType); writePunctuation("?"); } function emitUnionType(node: UnionTypeNode) { - emitList(node, node.types, ListFormat.UnionTypeConstituents, parenthesizer.parenthesizeMemberOfElementType); + emitList(node, node.types, ListFormat.UnionTypeConstituents, parenthesizer.parenthesizeConstituentTypeOfUnionType); } function emitIntersectionType(node: IntersectionTypeNode) { - emitList(node, node.types, ListFormat.IntersectionTypeConstituents, parenthesizer.parenthesizeMemberOfElementType); + emitList(node, node.types, ListFormat.IntersectionTypeConstituents, parenthesizer.parenthesizeConstituentTypeOfIntersectionType); } function emitConditionalType(node: ConditionalTypeNode) { - emit(node.checkType, parenthesizer.parenthesizeMemberOfConditionalType); + emit(node.checkType, parenthesizer.parenthesizeCheckTypeOfConditionalType); writeSpace(); writeKeyword("extends"); writeSpace(); - emit(node.extendsType, parenthesizer.parenthesizeMemberOfConditionalType); + emit(node.extendsType, parenthesizer.parenthesizeExtendsTypeOfConditionalType); writeSpace(); writePunctuation("?"); writeSpace(); @@ -2315,11 +2318,15 @@ namespace ts { function emitTypeOperator(node: TypeOperatorNode) { writeTokenText(node.operator, writeKeyword); writeSpace(); - emit(node.type, parenthesizer.parenthesizeMemberOfElementType); + + const parenthesizerRule = node.operator === SyntaxKind.ReadonlyKeyword ? + parenthesizer.parenthesizeOperandOfReadonlyTypeOperator : + parenthesizer.parenthesizeOperandOfTypeOperator; + emit(node.type, parenthesizerRule); } function emitIndexedAccessType(node: IndexedAccessTypeNode) { - emit(node.objectType, parenthesizer.parenthesizeMemberOfElementType); + emit(node.objectType, parenthesizer.parenthesizeNonArrayTypeOfPostfixType); writePunctuation("["); emit(node.indexType); writePunctuation("]"); @@ -4256,7 +4263,7 @@ namespace ts { } function emitTypeArguments(parentNode: Node, typeArguments: NodeArray | undefined) { - emitList(parentNode, typeArguments, ListFormat.TypeArguments, parenthesizer.parenthesizeMemberOfElementType); + emitList(parentNode, typeArguments, ListFormat.TypeArguments, typeArgumentParenthesizerRuleSelector); } function emitTypeParameters(parentNode: SignatureDeclaration | InterfaceDeclaration | TypeAliasDeclaration | ClassDeclaration | ClassExpression, typeParameters: NodeArray | undefined) { @@ -4324,15 +4331,15 @@ namespace ts { } } - function emitList(parentNode: Node | undefined, children: NodeArray | undefined, format: ListFormat, parenthesizerRule?: (node: Node) => Node, start?: number, count?: number) { + function emitList(parentNode: Node | undefined, children: NodeArray | undefined, format: ListFormat, parenthesizerRule?: ParenthesizerRuleOrSelector, start?: number, count?: number) { emitNodeList(emit, parentNode, children, format, parenthesizerRule, start, count); } - function emitExpressionList(parentNode: Node | undefined, children: NodeArray | undefined, format: ListFormat, parenthesizerRule?: (node: Expression) => Expression, start?: number, count?: number) { + function emitExpressionList(parentNode: Node | undefined, children: NodeArray | undefined, format: ListFormat, parenthesizerRule?: ParenthesizerRuleOrSelector, start?: number, count?: number) { emitNodeList(emitExpression, parentNode, children, format, parenthesizerRule, start, count); } - function emitNodeList(emit: (node: Node, parenthesizerRule?: ((node: Node) => Node) | undefined) => void, parentNode: Node | undefined, children: NodeArray | undefined, format: ListFormat, parenthesizerRule: ((node: Node) => Node) | undefined, start = 0, count = children ? children.length - start : 0) { + function emitNodeList(emit: (node: Node, parenthesizerRule?: ((node: Node) => Node) | undefined) => void, parentNode: Node | undefined, children: NodeArray | undefined, format: ListFormat, parenthesizerRule: ParenthesizerRuleOrSelector | undefined, start = 0, count = children ? children.length - start : 0) { const isUndefined = children === undefined; if (isUndefined && format & ListFormat.OptionalIfUndefined) { return; @@ -4388,6 +4395,8 @@ namespace ts { increaseIndent(); } + const emitListItem = getEmitListItem(emit, parenthesizerRule); + // Emit each child. let previousSibling: Node | undefined; let previousSourceFileTextKind: ReturnType; @@ -4443,12 +4452,7 @@ namespace ts { } nextListElementPos = child.pos; - if (emit.length === 1) { - emit(child); - } - else { - emit(child, parenthesizerRule); - } + emitListItem(child, emit, parenthesizerRule, i); if (shouldDecreaseIndentAfterEmit) { decreaseIndent(); @@ -5890,4 +5894,30 @@ namespace ts { CountMask = 0x0FFFFFFF, // Temp variable counter _i = 0x10000000, // Use/preference flag for '_i' } + + interface OrdinalParentheizerRuleSelector { + select(index: number): ((node: T) => T) | undefined; + } + + type ParenthesizerRule = (node: T) => T; + + type ParenthesizerRuleOrSelector = OrdinalParentheizerRuleSelector | ParenthesizerRule; + + function emitListItemNoParenthesizer(node: Node, emit: (node: Node, parenthesizerRule?: ((node: Node) => Node) | undefined) => void, _parenthesizerRule: ParenthesizerRuleOrSelector | undefined, _index: number) { + emit(node); + } + + function emitListItemWithParenthesizerRuleSelector(node: Node, emit: (node: Node, parenthesizerRule?: ((node: Node) => Node) | undefined) => void, parenthesizerRuleSelector: OrdinalParentheizerRuleSelector, index: number) { + emit(node, parenthesizerRuleSelector.select(index)); + } + + function emitListItemWithParenthesizerRule(node: Node, emit: (node: Node, parenthesizerRule?: ((node: Node) => Node) | undefined) => void, parenthesizerRule: ParenthesizerRule | undefined, _index: number) { + emit(node, parenthesizerRule); + } + + function getEmitListItem | undefined>(emit: (node: Node, parenthesizerRule?: ((node: Node) => Node) | undefined) => void, parenthesizerRule: R): (node: Node, emit: (node: Node, parenthesizerRule?: ((node: Node) => Node) | undefined) => void, parenthesizerRule: R, index: number) => void { + return emit.length === 1 ? emitListItemNoParenthesizer : + typeof parenthesizerRule === "object" ? emitListItemWithParenthesizerRuleSelector : + emitListItemWithParenthesizerRule; + } } diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts index 412fc581085..cbeb8df5e5b 100644 --- a/src/compiler/factory/nodeFactory.ts +++ b/src/compiler/factory/nodeFactory.ts @@ -34,6 +34,8 @@ namespace ts { const getJSDocPrimaryTypeCreateFunction = memoizeOne((kind: T["kind"]) => () => createJSDocPrimaryTypeWorker(kind)); const getJSDocUnaryTypeCreateFunction = memoizeOne((kind: T["kind"]) => (type: T["type"]) => createJSDocUnaryTypeWorker(kind, type)); const getJSDocUnaryTypeUpdateFunction = memoizeOne((kind: T["kind"]) => (node: T, type: T["type"]) => updateJSDocUnaryTypeWorker(kind, node, type)); + const getJSDocPrePostfixUnaryTypeCreateFunction = memoizeOne((kind: T["kind"]) => (type: T["type"], postfix?: boolean) => createJSDocPrePostfixUnaryTypeWorker(kind, type, postfix)); + const getJSDocPrePostfixUnaryTypeUpdateFunction = memoizeOne((kind: T["kind"]) => (node: T, type: T["type"]) => updateJSDocPrePostfixUnaryTypeWorker(kind, node, type)); const getJSDocSimpleTagCreateFunction = memoizeOne((kind: T["kind"]) => (tagName: Identifier | undefined, comment?: NodeArray) => createJSDocSimpleTagWorker(kind, tagName, comment)); const getJSDocSimpleTagUpdateFunction = memoizeOne((kind: T["kind"]) => (node: T, tagName: Identifier | undefined, comment?: NodeArray) => updateJSDocSimpleTagWorker(kind, node, tagName, comment)); const getJSDocTypeLikeTagCreateFunction = memoizeOne((kind: T["kind"]) => (tagName: Identifier | undefined, typeExpression?: JSDocTypeExpression, comment?: NodeArray) => createJSDocTypeLikeTagWorker(kind, tagName, typeExpression, comment)); @@ -42,6 +44,8 @@ namespace ts { const factory: NodeFactory = { get parenthesizer() { return parenthesizerRules(); }, get converters() { return converters(); }, + baseFactory, + flags, createNodeArray, createNumericLiteral, createBigIntLiteral, @@ -317,10 +321,10 @@ namespace ts { // lazily load factory members for JSDoc types with similar structure get createJSDocAllType() { return getJSDocPrimaryTypeCreateFunction(SyntaxKind.JSDocAllType); }, get createJSDocUnknownType() { return getJSDocPrimaryTypeCreateFunction(SyntaxKind.JSDocUnknownType); }, - get createJSDocNonNullableType() { return getJSDocUnaryTypeCreateFunction(SyntaxKind.JSDocNonNullableType); }, - get updateJSDocNonNullableType() { return getJSDocUnaryTypeUpdateFunction(SyntaxKind.JSDocNonNullableType); }, - get createJSDocNullableType() { return getJSDocUnaryTypeCreateFunction(SyntaxKind.JSDocNullableType); }, - get updateJSDocNullableType() { return getJSDocUnaryTypeUpdateFunction(SyntaxKind.JSDocNullableType); }, + get createJSDocNonNullableType() { return getJSDocPrePostfixUnaryTypeCreateFunction(SyntaxKind.JSDocNonNullableType); }, + get updateJSDocNonNullableType() { return getJSDocPrePostfixUnaryTypeUpdateFunction(SyntaxKind.JSDocNonNullableType); }, + get createJSDocNullableType() { return getJSDocPrePostfixUnaryTypeCreateFunction(SyntaxKind.JSDocNullableType); }, + get updateJSDocNullableType() { return getJSDocPrePostfixUnaryTypeUpdateFunction(SyntaxKind.JSDocNullableType); }, get createJSDocOptionalType() { return getJSDocUnaryTypeCreateFunction(SyntaxKind.JSDocOptionalType); }, get updateJSDocOptionalType() { return getJSDocUnaryTypeUpdateFunction(SyntaxKind.JSDocOptionalType); }, get createJSDocVariadicType() { return getJSDocUnaryTypeCreateFunction(SyntaxKind.JSDocVariadicType); }, @@ -1912,7 +1916,7 @@ namespace ts { // @api function createArrayTypeNode(elementType: TypeNode) { const node = createBaseNode(SyntaxKind.ArrayType); - node.elementType = parenthesizerRules().parenthesizeElementTypeOfArrayType(elementType); + node.elementType = parenthesizerRules().parenthesizeNonArrayTypeOfPostfixType(elementType); node.transformFlags = TransformFlags.ContainsTypeScript; return node; } @@ -1927,7 +1931,7 @@ namespace ts { // @api function createTupleTypeNode(elements: readonly (TypeNode | NamedTupleMember)[]) { const node = createBaseNode(SyntaxKind.TupleType); - node.elements = createNodeArray(elements); + node.elements = createNodeArray(parenthesizerRules().parenthesizeElementTypesOfTupleType(elements)); node.transformFlags = TransformFlags.ContainsTypeScript; return node; } @@ -1963,7 +1967,7 @@ namespace ts { // @api function createOptionalTypeNode(type: TypeNode) { const node = createBaseNode(SyntaxKind.OptionalType); - node.type = parenthesizerRules().parenthesizeElementTypeOfArrayType(type); + node.type = parenthesizerRules().parenthesizeTypeOfOptionalType(type); node.transformFlags = TransformFlags.ContainsTypeScript; return node; } @@ -1990,44 +1994,44 @@ namespace ts { : node; } - function createUnionOrIntersectionTypeNode(kind: SyntaxKind.UnionType | SyntaxKind.IntersectionType, types: readonly TypeNode[]) { + function createUnionOrIntersectionTypeNode(kind: SyntaxKind.UnionType | SyntaxKind.IntersectionType, types: readonly TypeNode[], parenthesize: (nodes: readonly TypeNode[]) => readonly TypeNode[]) { const node = createBaseNode(kind); - node.types = parenthesizerRules().parenthesizeConstituentTypesOfUnionOrIntersectionType(types); + node.types = factory.createNodeArray(parenthesize(types)); node.transformFlags = TransformFlags.ContainsTypeScript; return node; } - function updateUnionOrIntersectionTypeNode(node: T, types: NodeArray): T { + function updateUnionOrIntersectionTypeNode(node: T, types: NodeArray, parenthesize: (nodes: readonly TypeNode[]) => readonly TypeNode[]): T { return node.types !== types - ? update(createUnionOrIntersectionTypeNode(node.kind, types) as T, node) + ? update(createUnionOrIntersectionTypeNode(node.kind, types, parenthesize) as T, node) : node; } // @api function createUnionTypeNode(types: readonly TypeNode[]): UnionTypeNode { - return createUnionOrIntersectionTypeNode(SyntaxKind.UnionType, types) as UnionTypeNode; + return createUnionOrIntersectionTypeNode(SyntaxKind.UnionType, types, parenthesizerRules().parenthesizeConstituentTypesOfUnionType) as UnionTypeNode; } // @api function updateUnionTypeNode(node: UnionTypeNode, types: NodeArray) { - return updateUnionOrIntersectionTypeNode(node, types); + return updateUnionOrIntersectionTypeNode(node, types, parenthesizerRules().parenthesizeConstituentTypesOfUnionType); } // @api function createIntersectionTypeNode(types: readonly TypeNode[]): IntersectionTypeNode { - return createUnionOrIntersectionTypeNode(SyntaxKind.IntersectionType, types) as IntersectionTypeNode; + return createUnionOrIntersectionTypeNode(SyntaxKind.IntersectionType, types, parenthesizerRules().parenthesizeConstituentTypesOfIntersectionType) as IntersectionTypeNode; } // @api function updateIntersectionTypeNode(node: IntersectionTypeNode, types: NodeArray) { - return updateUnionOrIntersectionTypeNode(node, types); + return updateUnionOrIntersectionTypeNode(node, types, parenthesizerRules().parenthesizeConstituentTypesOfIntersectionType); } // @api function createConditionalTypeNode(checkType: TypeNode, extendsType: TypeNode, trueType: TypeNode, falseType: TypeNode) { const node = createBaseNode(SyntaxKind.ConditionalType); - node.checkType = parenthesizerRules().parenthesizeMemberOfConditionalType(checkType); - node.extendsType = parenthesizerRules().parenthesizeMemberOfConditionalType(extendsType); + node.checkType = parenthesizerRules().parenthesizeCheckTypeOfConditionalType(checkType); + node.extendsType = parenthesizerRules().parenthesizeExtendsTypeOfConditionalType(extendsType); node.trueType = trueType; node.falseType = falseType; node.transformFlags = TransformFlags.ContainsTypeScript; @@ -2154,7 +2158,9 @@ namespace ts { function createTypeOperatorNode(operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword, type: TypeNode): TypeOperatorNode { const node = createBaseNode(SyntaxKind.TypeOperator); node.operator = operator; - node.type = parenthesizerRules().parenthesizeMemberOfElementType(type); + node.type = operator === SyntaxKind.ReadonlyKeyword ? + parenthesizerRules().parenthesizeOperandOfReadonlyTypeOperator(type) : + parenthesizerRules().parenthesizeOperandOfTypeOperator(type); node.transformFlags = TransformFlags.ContainsTypeScript; return node; } @@ -2169,7 +2175,7 @@ namespace ts { // @api function createIndexedAccessTypeNode(objectType: TypeNode, indexType: TypeNode) { const node = createBaseNode(SyntaxKind.IndexedAccessType); - node.objectType = parenthesizerRules().parenthesizeMemberOfElementType(objectType); + node.objectType = parenthesizerRules().parenthesizeNonArrayTypeOfPostfixType(objectType); node.indexType = indexType; node.transformFlags = TransformFlags.ContainsTypeScript; return node; @@ -4360,12 +4366,21 @@ namespace ts { } // @api - // createJSDocNonNullableType // createJSDocNullableType + // createJSDocNonNullableType + function createJSDocPrePostfixUnaryTypeWorker(kind: T["kind"], type: T["type"], postfix = false): T { + const node = createJSDocUnaryTypeWorker( + kind, + postfix ? type && parenthesizerRules().parenthesizeNonArrayTypeOfPostfixType(type) : type + ) as Mutable; + node.postfix = postfix; + return node; + } + + // @api // createJSDocOptionalType // createJSDocVariadicType // createJSDocNamepathType - function createJSDocUnaryTypeWorker(kind: T["kind"], type: T["type"]): T { const node = createBaseNode(kind); node.type = type; @@ -4375,6 +4390,13 @@ namespace ts { // @api // updateJSDocNonNullableType // updateJSDocNullableType + function updateJSDocPrePostfixUnaryTypeWorker(kind: T["kind"], node: T, type: T["type"]): T { + return node.type !== type + ? update(createJSDocPrePostfixUnaryTypeWorker(kind, type, node.postfix), node) + : node; + } + + // @api // updateJSDocOptionalType // updateJSDocVariadicType // updateJSDocNamepathType diff --git a/src/compiler/factory/parenthesizerRules.ts b/src/compiler/factory/parenthesizerRules.ts index 1e2cb936582..c824c13fd16 100644 --- a/src/compiler/factory/parenthesizerRules.ts +++ b/src/compiler/factory/parenthesizerRules.ts @@ -25,11 +25,20 @@ namespace ts { parenthesizeExpressionForDisallowedComma, parenthesizeExpressionOfExpressionStatement, parenthesizeConciseBodyOfArrowFunction, - parenthesizeMemberOfConditionalType, - parenthesizeMemberOfElementType, - parenthesizeElementTypeOfArrayType, - parenthesizeConstituentTypesOfUnionOrIntersectionType, + parenthesizeCheckTypeOfConditionalType, + parenthesizeExtendsTypeOfConditionalType, + parenthesizeConstituentTypesOfUnionType, + parenthesizeConstituentTypeOfUnionType, + parenthesizeConstituentTypesOfIntersectionType, + parenthesizeConstituentTypeOfIntersectionType, + parenthesizeOperandOfTypeOperator, + parenthesizeOperandOfReadonlyTypeOperator, + parenthesizeNonArrayTypeOfPostfixType, + parenthesizeElementTypesOfTupleType, + parenthesizeElementTypeOfTupleType, + parenthesizeTypeOfOptionalType, parenthesizeTypeArguments, + parenthesizeLeadingTypeArgument, }; function getParenthesizeLeftSideOfBinaryForOperator(operatorKind: BinaryOperator) { @@ -388,38 +397,199 @@ namespace ts { return body; } - function parenthesizeMemberOfConditionalType(member: TypeNode): TypeNode { - return member.kind === SyntaxKind.ConditionalType ? factory.createParenthesizedType(member) : member; - } + // Type[Extends] : + // FunctionOrConstructorType + // ConditionalType[?Extends] - function parenthesizeMemberOfElementType(member: TypeNode): TypeNode { - switch (member.kind) { - case SyntaxKind.UnionType: - case SyntaxKind.IntersectionType: + // ConditionalType[Extends] : + // UnionType[?Extends] + // [~Extends] UnionType[~Extends] `extends` Type[+Extends] `?` Type[~Extends] `:` Type[~Extends] + // + // - The check type (the `UnionType`, above) does not allow function, constructor, or conditional types (they must be parenthesized) + // - The extends type (the first `Type`, above) does not allow conditional types (they must be parenthesized). Function and constructor types are fine. + // - The true and false branch types (the second and third `Type` non-terminals, above) allow any type + function parenthesizeCheckTypeOfConditionalType(checkType: TypeNode): TypeNode { + switch (checkType.kind) { case SyntaxKind.FunctionType: case SyntaxKind.ConstructorType: - return factory.createParenthesizedType(member); + case SyntaxKind.ConditionalType: + return factory.createParenthesizedType(checkType); } - return parenthesizeMemberOfConditionalType(member); + return checkType; } - function parenthesizeElementTypeOfArrayType(member: TypeNode): TypeNode { - switch (member.kind) { - case SyntaxKind.TypeQuery: + function parenthesizeExtendsTypeOfConditionalType(extendsType: TypeNode): TypeNode { + switch (extendsType.kind) { + case SyntaxKind.ConditionalType: + return factory.createParenthesizedType(extendsType); + } + return extendsType; + } + + // UnionType[Extends] : + // `|`? IntersectionType[?Extends] + // UnionType[?Extends] `|` IntersectionType[?Extends] + // + // - A union type constituent has the same precedence as the check type of a conditional type + function parenthesizeConstituentTypeOfUnionType(type: TypeNode) { + switch (type.kind) { + case SyntaxKind.UnionType: // Not strictly necessary, but a union containing a union should have been flattened + case SyntaxKind.IntersectionType: // Not strictly necessary, but makes generated output more readable and avoids breaks in DT tests + return factory.createParenthesizedType(type); + } + return parenthesizeCheckTypeOfConditionalType(type); + } + + function parenthesizeConstituentTypesOfUnionType(members: readonly TypeNode[]): NodeArray { + return factory.createNodeArray(sameMap(members, parenthesizeConstituentTypeOfUnionType)); + } + + // IntersectionType[Extends] : + // `&`? TypeOperator[?Extends] + // IntersectionType[?Extends] `&` TypeOperator[?Extends] + // + // - An intersection type constituent does not allow function, constructor, conditional, or union types (they must be parenthesized) + function parenthesizeConstituentTypeOfIntersectionType(type: TypeNode) { + switch (type.kind) { + case SyntaxKind.UnionType: + case SyntaxKind.IntersectionType: // Not strictly necessary, but an intersection containing an intersection should have been flattened + return factory.createParenthesizedType(type); + } + return parenthesizeConstituentTypeOfUnionType(type); + } + + function parenthesizeConstituentTypesOfIntersectionType(members: readonly TypeNode[]): NodeArray { + return factory.createNodeArray(sameMap(members, parenthesizeConstituentTypeOfIntersectionType)); + } + + // TypeOperator[Extends] : + // PostfixType + // InferType[?Extends] + // `keyof` TypeOperator[?Extends] + // `unique` TypeOperator[?Extends] + // `readonly` TypeOperator[?Extends] + // + function parenthesizeOperandOfTypeOperator(type: TypeNode) { + switch (type.kind) { + case SyntaxKind.IntersectionType: + return factory.createParenthesizedType(type); + } + return parenthesizeConstituentTypeOfIntersectionType(type); + } + + function parenthesizeOperandOfReadonlyTypeOperator(type: TypeNode) { + switch (type.kind) { case SyntaxKind.TypeOperator: - case SyntaxKind.InferType: - return factory.createParenthesizedType(member); + return factory.createParenthesizedType(type); } - return parenthesizeMemberOfElementType(member); + return parenthesizeOperandOfTypeOperator(type); } - function parenthesizeConstituentTypesOfUnionOrIntersectionType(members: readonly TypeNode[]): NodeArray { - return factory.createNodeArray(sameMap(members, parenthesizeMemberOfElementType)); + // PostfixType : + // NonArrayType + // NonArrayType [no LineTerminator here] `!` // JSDoc + // NonArrayType [no LineTerminator here] `?` // JSDoc + // IndexedAccessType + // ArrayType + // + // IndexedAccessType : + // NonArrayType `[` Type[~Extends] `]` + // + // ArrayType : + // NonArrayType `[` `]` + // + function parenthesizeNonArrayTypeOfPostfixType(type: TypeNode) { + switch (type.kind) { + case SyntaxKind.InferType: + case SyntaxKind.TypeOperator: + case SyntaxKind.TypeQuery: // Not strictly necessary, but makes generated output more readable and avoids breaks in DT tests + return factory.createParenthesizedType(type); + } + return parenthesizeOperandOfTypeOperator(type); + } + // TupleType : + // `[` Elision? `]` + // `[` NamedTupleElementTypes `]` + // `[` NamedTupleElementTypes `,` Elision? `]` + // `[` TupleElementTypes `]` + // `[` TupleElementTypes `,` Elision? `]` + // + // NamedTupleElementTypes : + // Elision? NamedTupleMember + // NamedTupleElementTypes `,` Elision? NamedTupleMember + // + // NamedTupleMember : + // Identifier `?`? `:` Type[~Extends] + // `...` Identifier `:` Type[~Extends] + // + // TupleElementTypes : + // Elision? TupleElementType + // TupleElementTypes `,` Elision? TupleElementType + // + // TupleElementType : + // Type[~Extends] // NOTE: Needs cover grammar to disallow JSDoc postfix-optional + // OptionalType + // RestType + // + // OptionalType : + // Type[~Extends] `?` // NOTE: Needs cover grammar to disallow JSDoc postfix-optional + // + // RestType : + // `...` Type[~Extends] + // + function parenthesizeElementTypesOfTupleType(types: readonly (TypeNode | NamedTupleMember)[]): NodeArray { + return factory.createNodeArray(sameMap(types, parenthesizeElementTypeOfTupleType)); + } + + function parenthesizeElementTypeOfTupleType(type: TypeNode | NamedTupleMember): TypeNode { + if (hasJSDocPostfixQuestion(type)) return factory.createParenthesizedType(type); + return type; + } + + function hasJSDocPostfixQuestion(type: TypeNode | NamedTupleMember): boolean { + if (isJSDocNullableType(type)) return type.postfix; + if (isNamedTupleMember(type)) return hasJSDocPostfixQuestion(type.type); + if (isFunctionTypeNode(type) || isConstructorTypeNode(type) || isTypeOperatorNode(type)) return hasJSDocPostfixQuestion(type.type); + if (isConditionalTypeNode(type)) return hasJSDocPostfixQuestion(type.falseType); + if (isUnionTypeNode(type)) return hasJSDocPostfixQuestion(last(type.types)); + if (isIntersectionTypeNode(type)) return hasJSDocPostfixQuestion(last(type.types)); + if (isInferTypeNode(type)) return !!type.typeParameter.constraint && hasJSDocPostfixQuestion(type.typeParameter.constraint); + return false; + } + + function parenthesizeTypeOfOptionalType(type: TypeNode): TypeNode { + if (hasJSDocPostfixQuestion(type)) return factory.createParenthesizedType(type); + return parenthesizeNonArrayTypeOfPostfixType(type); + } + + // function parenthesizeMemberOfElementType(member: TypeNode): TypeNode { + // switch (member.kind) { + // case SyntaxKind.UnionType: + // case SyntaxKind.IntersectionType: + // case SyntaxKind.FunctionType: + // case SyntaxKind.ConstructorType: + // return factory.createParenthesizedType(member); + // } + // return parenthesizeMemberOfConditionalType(member); + // } + + // function parenthesizeElementTypeOfArrayType(member: TypeNode): TypeNode { + // switch (member.kind) { + // case SyntaxKind.TypeQuery: + // case SyntaxKind.TypeOperator: + // case SyntaxKind.InferType: + // return factory.createParenthesizedType(member); + // } + // return parenthesizeMemberOfElementType(member); + // } + + function parenthesizeLeadingTypeArgument(node: TypeNode) { + return isFunctionOrConstructorTypeNode(node) && node.typeParameters ? factory.createParenthesizedType(node) : node; } function parenthesizeOrdinalTypeArgument(node: TypeNode, i: number) { - return i === 0 && isFunctionOrConstructorTypeNode(node) && node.typeParameters ? factory.createParenthesizedType(node) : node; + return i === 0 ? parenthesizeLeadingTypeArgument(node) : node; } function parenthesizeTypeArguments(typeArguments: NodeArray | undefined): NodeArray | undefined { @@ -446,10 +616,19 @@ namespace ts { parenthesizeExpressionForDisallowedComma: identity, parenthesizeExpressionOfExpressionStatement: identity, parenthesizeConciseBodyOfArrowFunction: identity, - parenthesizeMemberOfConditionalType: identity, - parenthesizeMemberOfElementType: identity, - parenthesizeElementTypeOfArrayType: identity, - parenthesizeConstituentTypesOfUnionOrIntersectionType: nodes => cast(nodes, isNodeArray), + parenthesizeCheckTypeOfConditionalType: identity, + parenthesizeExtendsTypeOfConditionalType: identity, + parenthesizeConstituentTypesOfUnionType: nodes => cast(nodes, isNodeArray), + parenthesizeConstituentTypeOfUnionType: identity, + parenthesizeConstituentTypesOfIntersectionType: nodes => cast(nodes, isNodeArray), + parenthesizeConstituentTypeOfIntersectionType: identity, + parenthesizeOperandOfTypeOperator: identity, + parenthesizeOperandOfReadonlyTypeOperator: identity, + parenthesizeNonArrayTypeOfPostfixType: identity, + parenthesizeElementTypesOfTupleType: nodes => cast(nodes, isNodeArray), + parenthesizeElementTypeOfTupleType: identity, + parenthesizeTypeOfOptionalType: identity, parenthesizeTypeArguments: nodes => nodes && cast(nodes, isNodeArray), + parenthesizeLeadingTypeArgument: identity, }; } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 5f9c7f0085e..7642612e27a 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1391,6 +1391,14 @@ namespace ts { return doInsideOfContext(NodeFlags.DisallowInContext, func); } + function allowConditionalTypesAnd(func: () => T): T { + return doOutsideOfContext(NodeFlags.DisallowConditionalTypesContext, func); + } + + function disallowConditionalTypesAnd(func: () => T): T { + return doInsideOfContext(NodeFlags.DisallowConditionalTypesContext, func); + } + function doInYieldContext(func: () => T): T { return doInsideOfContext(NodeFlags.YieldContext, func); } @@ -1427,6 +1435,10 @@ namespace ts { return inContext(NodeFlags.DisallowInContext); } + function inDisallowConditionalTypesContext() { + return inContext(NodeFlags.DisallowConditionalTypesContext); + } + function inDecoratorContext() { return inContext(NodeFlags.DecoratorContext); } @@ -2374,7 +2386,7 @@ namespace ts { return createNodeArray(list, listPos); } - function parseListElement(parsingContext: ParsingContext, parseElement: () => T): T { + function parseListElement(parsingContext: ParsingContext, parseElement: () => T): T { const node = currentNode(parsingContext); if (node) { return consumeNode(node) as T; @@ -2719,7 +2731,9 @@ namespace ts { } // Parses a comma-delimited list of elements - function parseDelimitedList(kind: ParsingContext, parseElement: () => T, considerSemicolonAsDelimiter?: boolean): NodeArray { + function parseDelimitedList(kind: ParsingContext, parseElement: () => T, considerSemicolonAsDelimiter?: boolean): NodeArray; + function parseDelimitedList(kind: ParsingContext, parseElement: () => T, considerSemicolonAsDelimiter?: boolean): NodeArray> | undefined; + function parseDelimitedList(kind: ParsingContext, parseElement: () => T, considerSemicolonAsDelimiter?: boolean): NodeArray> | undefined { const saveParsingContext = parsingContext; parsingContext |= 1 << kind; const list = []; @@ -2729,7 +2743,11 @@ namespace ts { while (true) { if (isListElement(kind, /*inErrorRecovery*/ false)) { const startPos = scanner.getStartPos(); - list.push(parseListElement(kind, parseElement)); + const result = parseListElement(kind, parseElement); + if (!result) { + return undefined; + } + list.push(result as NonNullable); commaStart = scanner.getTokenPos(); if (parseOptional(SyntaxKind.CommaToken)) { @@ -3067,7 +3085,7 @@ namespace ts { function parseJSDocNonNullableType(): TypeNode { const pos = getNodePos(); nextToken(); - return finishNode(factory.createJSDocNonNullableType(parseNonArrayType()), pos); + return finishNode(factory.createJSDocNonNullableType(parseNonArrayType(), /*postfix*/ false), pos); } function parseJSDocUnknownOrNullableType(): JSDocUnknownType | JSDocNullableType { @@ -3094,7 +3112,7 @@ namespace ts { return finishNode(factory.createJSDocUnknownType(), pos); } else { - return finishNode(factory.createJSDocNullableType(parseType()), pos); + return finishNode(factory.createJSDocNullableType(parseType(), /*postfix*/ false), pos); } } @@ -3239,15 +3257,24 @@ namespace ts { return name; } - function parseParameterInOuterAwaitContext() { - return parseParameterWorker(/*inOuterAwaitContext*/ true); + function isParameterNameStart() { + // Be permissive about await and yield by calling isBindingIdentifier instead of isIdentifier; disallowing + // them during a speculative parse leads to many more follow-on errors than allowing the function to parse then later + // complaining about the use of the keywords. + return isBindingIdentifier() || token() === SyntaxKind.OpenBracketToken || token() === SyntaxKind.OpenBraceToken; } - function parseParameter(): ParameterDeclaration { - return parseParameterWorker(/*inOuterAwaitContext*/ false); + function parseParameter(inOuterAwaitContext: boolean): ParameterDeclaration { + return parseParameterWorker(inOuterAwaitContext); } - function parseParameterWorker(inOuterAwaitContext: boolean): ParameterDeclaration { + function parseParameterForSpeculation(inOuterAwaitContext: boolean): ParameterDeclaration | undefined { + return parseParameterWorker(inOuterAwaitContext, /*allowAmbiguity*/ false); + } + + function parseParameterWorker(inOuterAwaitContext: boolean): ParameterDeclaration; + function parseParameterWorker(inOuterAwaitContext: boolean, allowAmbiguity: false): ParameterDeclaration | undefined; + function parseParameterWorker(inOuterAwaitContext: boolean, allowAmbiguity = true): ParameterDeclaration | undefined { const pos = getNodePos(); const hasJSDoc = hasPrecedingJSDocComment(); @@ -3277,13 +3304,20 @@ namespace ts { const savedTopLevel = topLevel; topLevel = false; + const modifiers = parseModifiers(); + const dotDotDotToken = parseOptionalToken(SyntaxKind.DotDotDotToken); + + if (!allowAmbiguity && !isParameterNameStart()) { + return undefined; + } + const node = withJSDoc( finishNode( factory.createParameterDeclaration( decorators, modifiers, - parseOptionalToken(SyntaxKind.DotDotDotToken), + dotDotDotToken, parseNameOfParameter(modifiers), parseOptionalToken(SyntaxKind.QuestionToken), parseTypeAnnotation(), @@ -3301,7 +3335,7 @@ namespace ts { function parseReturnType(returnToken: SyntaxKind.ColonToken | SyntaxKind.EqualsGreaterThanToken, isType: boolean): TypeNode | undefined; function parseReturnType(returnToken: SyntaxKind.ColonToken | SyntaxKind.EqualsGreaterThanToken, isType: boolean) { if (shouldParseReturnType(returnToken, isType)) { - return parseTypeOrTypePredicate(); + return allowConditionalTypesAnd(parseTypeOrTypePredicate); } } @@ -3322,7 +3356,9 @@ namespace ts { return false; } - function parseParametersWorker(flags: SignatureFlags) { + function parseParametersWorker(flags: SignatureFlags, allowAmbiguity: true): NodeArray; + function parseParametersWorker(flags: SignatureFlags, allowAmbiguity: false): NodeArray | undefined; + function parseParametersWorker(flags: SignatureFlags, allowAmbiguity: boolean): NodeArray | undefined { // FormalParameters [Yield,Await]: (modified) // [empty] // FormalParameterList[?Yield,Await] @@ -3344,7 +3380,7 @@ namespace ts { const parameters = flags & SignatureFlags.JSDoc ? parseDelimitedList(ParsingContext.JSDocParameters, parseJSDocParameter) : - parseDelimitedList(ParsingContext.Parameters, savedAwaitContext ? parseParameterInOuterAwaitContext : parseParameter); + parseDelimitedList(ParsingContext.Parameters, () => allowAmbiguity ? parseParameter(savedAwaitContext) : parseParameterForSpeculation(savedAwaitContext)); setYieldContext(savedYieldContext); setAwaitContext(savedAwaitContext); @@ -3370,7 +3406,7 @@ namespace ts { return createMissingList(); } - const parameters = parseParametersWorker(flags); + const parameters = parseParametersWorker(flags, /*allowAmbiguity*/ true); parseExpected(SyntaxKind.CloseParenToken); return parameters; } @@ -3463,7 +3499,7 @@ namespace ts { } function parseIndexSignatureDeclaration(pos: number, hasJSDoc: boolean, decorators: NodeArray | undefined, modifiers: NodeArray | undefined): IndexSignatureDeclaration { - const parameters = parseBracketedList(ParsingContext.Parameters, parseParameter, SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken); + const parameters = parseBracketedList(ParsingContext.Parameters, () => parseParameter(/*inOuterAwaitContext*/ false), SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken); const type = parseTypeAnnotation(); parseTypeMemberSemicolon(); const node = factory.createIndexSignature(decorators, modifiers, parameters, type); @@ -3924,7 +3960,7 @@ namespace ts { switch (token()) { case SyntaxKind.ExclamationToken: nextToken(); - type = finishNode(factory.createJSDocNonNullableType(type), pos); + type = finishNode(factory.createJSDocNonNullableType(type, /*postfix*/ true), pos); break; case SyntaxKind.QuestionToken: // If next token is start of a type we have a conditional type @@ -3932,7 +3968,7 @@ namespace ts { return type; } nextToken(); - type = finishNode(factory.createJSDocNullableType(type), pos); + type = finishNode(factory.createJSDocNullableType(type, /*postfix*/ true), pos); break; case SyntaxKind.OpenBracketToken: parseExpected(SyntaxKind.OpenBracketToken); @@ -3959,17 +3995,21 @@ namespace ts { return finishNode(factory.createTypeOperatorNode(operator, parseTypeOperatorOrHigher()), pos); } - function parseTypeParameterOfInferType() { + function tryParseConstraintOfInferType() { + if (parseOptional(SyntaxKind.ExtendsKeyword)) { + const constraint = disallowConditionalTypesAnd(parseType); + if (inDisallowConditionalTypesContext() || token() !== SyntaxKind.QuestionToken) { + return constraint; + } + } + } + + function parseTypeParameterOfInferType(): TypeParameterDeclaration { const pos = getNodePos(); - return finishNode( - factory.createTypeParameterDeclaration( - /*modifiers*/ undefined, - parseIdentifier(), - /*constraint*/ undefined, - /*defaultType*/ undefined - ), - pos - ); + const name = parseIdentifier(); + const constraint = tryParse(tryParseConstraintOfInferType); + const node = factory.createTypeParameterDeclaration(/*modifiers*/ undefined, name, constraint); + return finishNode(node, pos); } function parseInferType(): InferTypeNode { @@ -3988,7 +4028,7 @@ namespace ts { case SyntaxKind.InferKeyword: return parseInferType(); } - return parsePostfixTypeOrHigher(); + return allowConditionalTypesAnd(parsePostfixTypeOrHigher); } function parseFunctionOrConstructorTypeToError( @@ -4137,24 +4177,22 @@ namespace ts { } function parseType(): TypeNode { - // The rules about 'yield' only apply to actual code/expression contexts. They don't - // apply to 'type' contexts. So we disable these parameters here before moving on. - return doOutsideOfContext(NodeFlags.TypeExcludesFlags, parseTypeWorker); - } + if (contextFlags & NodeFlags.TypeExcludesFlags) { + return doOutsideOfContext(NodeFlags.TypeExcludesFlags, parseType); + } - function parseTypeWorker(noConditionalTypes?: boolean): TypeNode { if (isStartOfFunctionTypeOrConstructorType()) { return parseFunctionOrConstructorType(); } const pos = getNodePos(); const type = parseUnionTypeOrHigher(); - if (!noConditionalTypes && !scanner.hasPrecedingLineBreak() && parseOptional(SyntaxKind.ExtendsKeyword)) { + if (!inDisallowConditionalTypesContext() && !scanner.hasPrecedingLineBreak() && parseOptional(SyntaxKind.ExtendsKeyword)) { // The type following 'extends' is not permitted to be another conditional type - const extendsType = parseTypeWorker(/*noConditionalTypes*/ true); + const extendsType = disallowConditionalTypesAnd(parseType); parseExpected(SyntaxKind.QuestionToken); - const trueType = parseTypeWorker(); + const trueType = allowConditionalTypesAnd(parseType); parseExpected(SyntaxKind.ColonToken); - const falseType = parseTypeWorker(); + const falseType = allowConditionalTypesAnd(parseType); return finishNode(factory.createConditionalTypeNode(type, extendsType, trueType, falseType), pos); } return type; @@ -4641,7 +4679,16 @@ namespace ts { parameters = createMissingList(); } else { - parameters = parseParametersWorker(isAsync); + if (!allowAmbiguity) { + const maybeParameters = parseParametersWorker(isAsync, allowAmbiguity); + if (!maybeParameters) { + return undefined; + } + parameters = maybeParameters; + } + else { + parameters = parseParametersWorker(isAsync, allowAmbiguity); + } if (!parseExpected(SyntaxKind.CloseParenToken) && !allowAmbiguity) { return undefined; } diff --git a/src/compiler/program.ts b/src/compiler/program.ts index a17654e981f..021c34ce3a7 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1958,6 +1958,10 @@ namespace ts { } function isSourceFileDefaultLibrary(file: SourceFile): boolean { + if (!file.isDeclarationFile) { + return false; + } + if (file.hasNoDefaultLib) { return true; } @@ -3326,21 +3330,6 @@ namespace ts { } function verifyCompilerOptions() { - const isNightly = stringContains(version, "-dev") || stringContains(version, "-insiders"); - if (!isNightly) { - if (getEmitModuleKind(options) === ModuleKind.Node12) { - createOptionValueDiagnostic("module", Diagnostics.Compiler_option_0_of_value_1_is_unstable_Use_nightly_TypeScript_to_silence_this_error_Try_updating_with_npm_install_D_typescript_next, "module", "node12"); - } - else if (getEmitModuleKind(options) === ModuleKind.NodeNext) { - createOptionValueDiagnostic("module", Diagnostics.Compiler_option_0_of_value_1_is_unstable_Use_nightly_TypeScript_to_silence_this_error_Try_updating_with_npm_install_D_typescript_next, "module", "nodenext"); - } - else if (getEmitModuleResolutionKind(options) === ModuleResolutionKind.Node12) { - createOptionValueDiagnostic("moduleResolution", Diagnostics.Compiler_option_0_of_value_1_is_unstable_Use_nightly_TypeScript_to_silence_this_error_Try_updating_with_npm_install_D_typescript_next, "moduleResolution", "node12"); - } - else if (getEmitModuleResolutionKind(options) === ModuleResolutionKind.NodeNext) { - createOptionValueDiagnostic("moduleResolution", Diagnostics.Compiler_option_0_of_value_1_is_unstable_Use_nightly_TypeScript_to_silence_this_error_Try_updating_with_npm_install_D_typescript_next, "moduleResolution", "nodenext"); - } - } if (options.strictPropertyInitialization && !getStrictOptionValue(options, "strictNullChecks")) { createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "strictPropertyInitialization", "strictNullChecks"); } diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts index bfbd4d44712..3a3e237c5ea 100644 --- a/src/compiler/transformers/declarations.ts +++ b/src/compiler/transformers/declarations.ts @@ -1163,6 +1163,9 @@ namespace ts { } function transformTopLevelDeclaration(input: LateVisibilityPaintedStatement) { + if (lateMarkedStatements) { + while (orderedRemoveItem(lateMarkedStatements, input)); + } if (shouldStripInternal(input)) return; switch (input.kind) { case SyntaxKind.ImportEqualsDeclaration: { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index ac4faa72e97..2302c95322f 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -763,10 +763,11 @@ namespace ts { YieldContext = 1 << 13, // If node was parsed in the 'yield' context created when parsing a generator DecoratorContext = 1 << 14, // If node was parsed as part of a decorator AwaitContext = 1 << 15, // If node was parsed in the 'await' context created when parsing an async function - ThisNodeHasError = 1 << 16, // If the parser encountered an error when parsing the code that created this node - JavaScriptFile = 1 << 17, // If node was parsed in a JavaScript - ThisNodeOrAnySubNodesHasError = 1 << 18, // If this node or any of its children had an error - HasAggregatedChildData = 1 << 19, // If we've computed data from children and cached it in this node + DisallowConditionalTypesContext = 1 << 16, // If node was parsed in a context where conditional types are not allowed + ThisNodeHasError = 1 << 17, // If the parser encountered an error when parsing the code that created this node + JavaScriptFile = 1 << 18, // If node was parsed in a JavaScript + ThisNodeOrAnySubNodesHasError = 1 << 19, // If this node or any of its children had an error + HasAggregatedChildData = 1 << 20, // If we've computed data from children and cached it in this node // These flags will be set when the parser encounters a dynamic import expression or 'import.meta' to avoid // walking the tree if the flags are not set. However, these flags are just a approximation @@ -777,15 +778,15 @@ namespace ts { // removal, it is likely that users will add the import anyway. // The advantage of this approach is its simplicity. For the case of batch compilation, // we guarantee that users won't have to pay the price of walking the tree if a dynamic import isn't used. - /* @internal */ PossiblyContainsDynamicImport = 1 << 20, - /* @internal */ PossiblyContainsImportMeta = 1 << 21, + /* @internal */ PossiblyContainsDynamicImport = 1 << 21, + /* @internal */ PossiblyContainsImportMeta = 1 << 22, - JSDoc = 1 << 22, // If node was parsed inside jsdoc - /* @internal */ Ambient = 1 << 23, // If node was inside an ambient context -- a declaration file, or inside something with the `declare` modifier. - /* @internal */ InWithStatement = 1 << 24, // If any ancestor of node was the `statement` of a WithStatement (not the `expression`) - JsonFile = 1 << 25, // If node was parsed in a Json - /* @internal */ TypeCached = 1 << 26, // If a type was cached for node at any point - /* @internal */ Deprecated = 1 << 27, // If has '@deprecated' JSDoc tag + JSDoc = 1 << 23, // If node was parsed inside jsdoc + /* @internal */ Ambient = 1 << 24, // If node was inside an ambient context -- a declaration file, or inside something with the `declare` modifier. + /* @internal */ InWithStatement = 1 << 25, // If any ancestor of node was the `statement` of a WithStatement (not the `expression`) + JsonFile = 1 << 26, // If node was parsed in a Json + /* @internal */ TypeCached = 1 << 27, // If a type was cached for node at any point + /* @internal */ Deprecated = 1 << 28, // If has '@deprecated' JSDoc tag BlockScoped = Let | Const, @@ -793,7 +794,7 @@ namespace ts { ReachabilityAndEmitFlags = ReachabilityCheckFlags | HasAsyncFunctions, // Parsing context flags - ContextFlags = DisallowInContext | YieldContext | DecoratorContext | AwaitContext | JavaScriptFile | InWithStatement | Ambient, + ContextFlags = DisallowInContext | DisallowConditionalTypesContext | YieldContext | DecoratorContext | AwaitContext | JavaScriptFile | InWithStatement | Ambient, // Exclude these flags when parsing a Type TypeExcludesFlags = YieldContext | AwaitContext, @@ -3234,11 +3235,13 @@ namespace ts { export interface JSDocNonNullableType extends JSDocType { readonly kind: SyntaxKind.JSDocNonNullableType; readonly type: TypeNode; + readonly postfix: boolean; } export interface JSDocNullableType extends JSDocType { readonly kind: SyntaxKind.JSDocNullableType; readonly type: TypeNode; + readonly postfix: boolean; } export interface JSDocOptionalType extends JSDocType { @@ -3458,7 +3461,6 @@ namespace ts { | FlowStart | FlowLabel | FlowAssignment - | FlowCall | FlowCondition | FlowSwitchClause | FlowArrayMutation @@ -5913,6 +5915,13 @@ namespace ts { nonFixingMapper: TypeMapper; // Mapper that doesn't fix inferences returnMapper?: TypeMapper; // Type mapper for inferences from return types (if any) inferredTypeParameters?: readonly TypeParameter[]; // Inferred type parameters for function result + intraExpressionInferenceSites?: IntraExpressionInferenceSite[]; + } + + /* @internal */ + export interface IntraExpressionInferenceSite { + node: Expression | MethodDeclaration; + type: Type; } /* @internal */ @@ -6613,7 +6622,7 @@ namespace ts { realpath?(path: string): string; getCurrentDirectory?(): string; getDirectories?(path: string): string[]; - useCaseSensitiveFileNames?: boolean | (() => boolean); + useCaseSensitiveFileNames?: boolean | (() => boolean) | undefined; } /** @@ -7130,10 +7139,19 @@ namespace ts { parenthesizeExpressionOfExpressionStatement(expression: Expression): Expression; parenthesizeConciseBodyOfArrowFunction(body: Expression): Expression; parenthesizeConciseBodyOfArrowFunction(body: ConciseBody): ConciseBody; - parenthesizeMemberOfConditionalType(member: TypeNode): TypeNode; - parenthesizeMemberOfElementType(member: TypeNode): TypeNode; - parenthesizeElementTypeOfArrayType(member: TypeNode): TypeNode; - parenthesizeConstituentTypesOfUnionOrIntersectionType(members: readonly TypeNode[]): NodeArray; + parenthesizeCheckTypeOfConditionalType(type: TypeNode): TypeNode; + parenthesizeExtendsTypeOfConditionalType(type: TypeNode): TypeNode; + parenthesizeOperandOfTypeOperator(type: TypeNode): TypeNode; + parenthesizeOperandOfReadonlyTypeOperator(type: TypeNode): TypeNode; + parenthesizeNonArrayTypeOfPostfixType(type: TypeNode): TypeNode; + parenthesizeElementTypesOfTupleType(types: readonly (TypeNode | NamedTupleMember)[]): NodeArray; + parenthesizeElementTypeOfTupleType(type: TypeNode | NamedTupleMember): TypeNode | NamedTupleMember; + parenthesizeTypeOfOptionalType(type: TypeNode): TypeNode; + parenthesizeConstituentTypeOfUnionType(type: TypeNode): TypeNode; + parenthesizeConstituentTypesOfUnionType(constituents: readonly TypeNode[]): NodeArray; + parenthesizeConstituentTypeOfIntersectionType(type: TypeNode): TypeNode; + parenthesizeConstituentTypesOfIntersectionType(constituents: readonly TypeNode[]): NodeArray; + parenthesizeLeadingTypeArgument(typeNode: TypeNode): TypeNode; parenthesizeTypeArguments(typeParameters: readonly TypeNode[] | undefined): NodeArray | undefined; } @@ -7152,6 +7170,8 @@ namespace ts { export interface NodeFactory { /* @internal */ readonly parenthesizer: ParenthesizerRules; /* @internal */ readonly converters: NodeConverters; + /* @internal */ readonly baseFactory: BaseNodeFactory; + /* @internal */ readonly flags: NodeFactoryFlags; createNodeArray(elements?: readonly T[], hasTrailingComma?: boolean): NodeArray; // @@ -7550,9 +7570,9 @@ namespace ts { createJSDocAllType(): JSDocAllType; createJSDocUnknownType(): JSDocUnknownType; - createJSDocNonNullableType(type: TypeNode): JSDocNonNullableType; + createJSDocNonNullableType(type: TypeNode, postfix?: boolean): JSDocNonNullableType; updateJSDocNonNullableType(node: JSDocNonNullableType, type: TypeNode): JSDocNonNullableType; - createJSDocNullableType(type: TypeNode): JSDocNullableType; + createJSDocNullableType(type: TypeNode, postfix?: boolean): JSDocNullableType; updateJSDocNullableType(node: JSDocNullableType, type: TypeNode): JSDocNullableType; createJSDocOptionalType(type: TypeNode): JSDocOptionalType; updateJSDocOptionalType(node: JSDocOptionalType, type: TypeNode): JSDocOptionalType; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 2465c336345..3b303c65eb8 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2825,7 +2825,11 @@ namespace ts { export function getHostSignatureFromJSDoc(node: Node): SignatureDeclaration | undefined { const host = getEffectiveJSDocHost(node); - return host && isFunctionLike(host) ? host : undefined; + if (host) { + return isPropertySignature(host) && host.type && isFunctionLike(host.type) ? host.type : + isFunctionLike(host) ? host : undefined; + } + return undefined; } export function getEffectiveJSDocHost(node: Node): Node | undefined { diff --git a/src/executeCommandLine/executeCommandLine.ts b/src/executeCommandLine/executeCommandLine.ts index a7d776acd80..68d2e51e9d4 100644 --- a/src/executeCommandLine/executeCommandLine.ts +++ b/src/executeCommandLine/executeCommandLine.ts @@ -649,7 +649,7 @@ namespace ts { return false; } - export type ExecuteCommandLineCallbacks = (program: Program | EmitAndSemanticDiagnosticsBuilderProgram | ParsedCommandLine) => void; + export type ExecuteCommandLineCallbacks = (program: Program | BuilderProgram | ParsedCommandLine) => void; export function executeCommandLine( system: System, cb: ExecuteCommandLineCallbacks, diff --git a/src/harness/client.ts b/src/harness/client.ts index fab3c070fdf..296832388ba 100644 --- a/src/harness/client.ts +++ b/src/harness/client.ts @@ -583,7 +583,6 @@ namespace ts.server { fileName: entry.file, textSpan: this.decodeSpan(entry), isWriteAccess: entry.isWriteAccess, - isDefinition: false })); } diff --git a/src/harness/fakesHosts.ts b/src/harness/fakesHosts.ts index cbdc9e50efa..43b842f45f9 100644 --- a/src/harness/fakesHosts.ts +++ b/src/harness/fakesHosts.ts @@ -510,18 +510,19 @@ ${indentText}${text}`; buildInfo.version = ts.version; return ts.getBuildInfoText(buildInfo); }; + return patchHostForBuildInfoWrite(sys, version); + } + + export function patchHostForBuildInfoWrite(sys: T, version: string) { const originalWrite = sys.write; sys.write = msg => originalWrite.call(sys, msg.replace(ts.version, version)); - - if (sys.writeFile) { - const originalWriteFile = sys.writeFile; - sys.writeFile = (fileName: string, content: string, writeByteOrderMark: boolean) => { - if (!ts.isBuildInfoFile(fileName)) return originalWriteFile.call(sys, fileName, content, writeByteOrderMark); - const buildInfo = ts.getBuildInfo(content); - buildInfo.version = version; - originalWriteFile.call(sys, fileName, ts.getBuildInfoText(buildInfo), writeByteOrderMark); - }; - } + const originalWriteFile = sys.writeFile; + sys.writeFile = (fileName: string, content: string, writeByteOrderMark: boolean) => { + if (!ts.isBuildInfoFile(fileName)) return originalWriteFile.call(sys, fileName, content, writeByteOrderMark); + const buildInfo = ts.getBuildInfo(content); + buildInfo.version = version; + originalWriteFile.call(sys, fileName, ts.getBuildInfoText(buildInfo), writeByteOrderMark); + }; return sys; } diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index 71886f8ff5f..f4b2832c00b 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -1122,83 +1122,18 @@ namespace FourSlash { } } - private verifyDocumentHighlightsRespectFilesList(files: readonly string[]): void { - const startFile = this.activeFile.fileName; - for (const fileName of files) { - const searchFileNames = startFile === fileName ? [startFile] : [startFile, fileName]; - const highlights = this.getDocumentHighlightsAtCurrentPosition(searchFileNames); - if (highlights && !highlights.every(dh => ts.contains(searchFileNames, dh.fileName))) { - this.raiseError(`When asking for document highlights only in files ${searchFileNames}, got document highlights in ${unique(highlights, dh => dh.fileName)}`); - } - } - } - - public verifyReferenceGroups(starts: ArrayOrSingle | ArrayOrSingle, parts: readonly FourSlashInterface.ReferenceGroup[]): void { - interface ReferenceGroupJson { - definition: string | { text: string, range: ts.TextSpan }; - references: ts.ReferenceEntry[]; - } - interface RangeMarkerData { - id?: string; - isWriteAccess?: boolean, - isDefinition?: boolean, - isInString?: true, - contextRangeIndex?: number, - contextRangeDelta?: number, - contextRangeId?: string - } - const fullExpected = ts.map(parts, ({ definition, ranges }) => ({ - definition: typeof definition === "string" ? definition : { ...definition, range: ts.createTextSpanFromRange(definition.range) }, - references: ranges.map(r => { - const { isWriteAccess = false, isDefinition = false, isInString, contextRangeIndex, contextRangeDelta, contextRangeId } = (r.marker && r.marker.data || {}) as RangeMarkerData; - let contextSpan: ts.TextSpan | undefined; - if (contextRangeDelta !== undefined) { - const allRanges = this.getRanges(); - const index = allRanges.indexOf(r); - if (index !== -1) { - contextSpan = ts.createTextSpanFromRange(allRanges[index + contextRangeDelta]); - } - } - else if (contextRangeId !== undefined) { - const allRanges = this.getRanges(); - const contextRange = ts.find(allRanges, range => (range.marker?.data as RangeMarkerData)?.id === contextRangeId); - if (contextRange) { - contextSpan = ts.createTextSpanFromRange(contextRange); - } - } - else if (contextRangeIndex !== undefined) { - contextSpan = ts.createTextSpanFromRange(this.getRanges()[contextRangeIndex]); - } - return { - textSpan: ts.createTextSpanFromRange(r), - fileName: r.fileName, - ...(contextSpan ? { contextSpan } : undefined), - isWriteAccess, - isDefinition, - ...(isInString ? { isInString: true } : undefined), - }; - }), - })); - - for (const start of toArray(starts)) { - this.goToMarkerOrRange(start); - const fullActual = ts.map(this.findReferencesAtCaret(), ({ definition, references }, i) => { - const text = definition.displayParts.map(d => d.text).join(""); - return { - definition: fullExpected.length > i && typeof fullExpected[i].definition === "string" ? text : { text, range: definition.textSpan }, - references, - }; - }); - this.assertObjectsEqual(fullActual, fullExpected); - - if (parts) { - this.verifyDocumentHighlightsRespectFilesList(unique(ts.flatMap(parts, p => p.ranges), r => r.fileName)); - } - } - } - public verifyBaselineFindAllReferences(...markerNames: string[]) { + ts.Debug.assert(markerNames.length > 0, "Must pass at least one marker name to `verifyBaselineFindAllReferences()`"); + this.verifyBaselineFindAllReferencesWorker("", markerNames); + } + + // Used when a single test needs to produce multiple baselines + public verifyBaselineFindAllReferencesMulti(seq: number, ...markerNames: string[]) { ts.Debug.assert(markerNames.length > 0, "Must pass at least one marker name to `baselineFindAllReferences()`"); + this.verifyBaselineFindAllReferencesWorker(`.${seq}`, markerNames); + } + + private verifyBaselineFindAllReferencesWorker(suffix: string, markerNames: string[]) { const baseline = markerNames.map(markerName => { this.goToMarker(markerName); const marker = this.getMarkerByName(markerName); @@ -1213,7 +1148,7 @@ namespace FourSlash { // Write response JSON return baselineContent + JSON.stringify(references, undefined, 2); }).join("\n\n"); - Harness.Baseline.runBaseline(this.getBaselineFileNameForContainingTestFile(".baseline.jsonc"), baseline); + Harness.Baseline.runBaseline(this.getBaselineFileNameForContainingTestFile(`${suffix}.baseline.jsonc`), baseline); } public verifyBaselineGetFileReferences(fileName: string) { @@ -1280,11 +1215,6 @@ namespace FourSlash { } } - public verifySingleReferenceGroup(definition: FourSlashInterface.ReferenceGroupDefinition, ranges?: Range[] | string) { - ranges = ts.isString(ranges) ? this.rangesByText().get(ranges)! : ranges || this.getRanges(); - this.verifyReferenceGroups(ranges, [{ definition, ranges }]); - } - private assertObjectsEqual(fullActual: T, fullExpected: T, msgPrefix = ""): void { const recur = (actual: U, expected: U, path: string) => { const fail = (msg: string) => { @@ -2120,7 +2050,8 @@ namespace FourSlash { } private getBaselineFileNameForContainingTestFile(ext = ".baseline") { - return ts.getBaseFileName(this.originalInputFileName).replace(ts.Extension.Ts, ext); + return this.testData.globalOptions[MetadataOptionNames.baselineFile] || + ts.getBaseFileName(this.originalInputFileName).replace(ts.Extension.Ts, ext); } private getSignatureHelp({ triggerReason }: FourSlashInterface.VerifySignatureHelpOptions): ts.SignatureHelpItems | undefined { @@ -2402,15 +2333,6 @@ namespace FourSlash { this.goToPosition(len); } - private goToMarkerOrRange(markerOrRange: string | Range) { - if (typeof markerOrRange === "string") { - this.goToMarker(markerOrRange); - } - else { - this.goToRangeStart(markerOrRange); - } - } - public goToRangeStart({ fileName, pos }: Range) { this.openFile(fileName); this.goToPosition(pos); @@ -3528,9 +3450,10 @@ namespace FourSlash { }; } - public verifyRefactorAvailable(negative: boolean, triggerReason: ts.RefactorTriggerReason, name: string, actionName?: string) { + public verifyRefactorAvailable(negative: boolean, triggerReason: ts.RefactorTriggerReason, name: string, actionName?: string, actionDescription?: string) { let refactors = this.getApplicableRefactorsAtSelection(triggerReason); - refactors = refactors.filter(r => r.name === name && (actionName === undefined || r.actions.some(a => a.name === actionName))); + refactors = refactors.filter(r => + r.name === name && (actionName === undefined || r.actions.some(a => a.name === actionName)) && (actionDescription === undefined || r.actions.some(a => a.description === actionDescription))); const isAvailable = refactors.length > 0; if (negative) { diff --git a/src/harness/fourslashInterfaceImpl.ts b/src/harness/fourslashInterfaceImpl.ts index dfe3808c8e5..ea07f4f386f 100644 --- a/src/harness/fourslashInterfaceImpl.ts +++ b/src/harness/fourslashInterfaceImpl.ts @@ -215,8 +215,8 @@ namespace FourSlashInterface { this.state.verifyRefactorsAvailable(names); } - public refactorAvailable(name: string, actionName?: string) { - this.state.verifyRefactorAvailable(this.negative, "implicit", name, actionName); + public refactorAvailable(name: string, actionName?: string, actionDescription?: string) { + this.state.verifyRefactorAvailable(this.negative, "implicit", name, actionName, actionDescription); } public refactorAvailableForTriggerReason(triggerReason: ts.RefactorTriggerReason, name: string, actionName?: string) { @@ -352,12 +352,12 @@ namespace FourSlashInterface { this.state.verifyBaselineFindAllReferences(...markerNames); } - public baselineGetFileReferences(fileName: string) { - this.state.verifyBaselineGetFileReferences(fileName); + public baselineFindAllReferencesMulti(seq: number, ...markerNames: string[]) { + this.state.verifyBaselineFindAllReferencesMulti(seq, ...markerNames); } - public singleReferenceGroup(definition: ReferenceGroupDefinition, ranges?: FourSlash.Range[] | string) { - this.state.verifySingleReferenceGroup(definition, ranges); + public baselineGetFileReferences(fileName: string) { + this.state.verifyBaselineGetFileReferences(fileName); } public findReferencesDefinitionDisplayPartsAtCaretAre(expected: ts.SymbolDisplayPart[]) { diff --git a/src/harness/vfsUtil.ts b/src/harness/vfsUtil.ts index 5eca1ce30bb..b32bb785683 100644 --- a/src/harness/vfsUtil.ts +++ b/src/harness/vfsUtil.ts @@ -809,7 +809,11 @@ namespace vfs { const baseBuffer = base._getBuffer(baseNode); // no difference if both buffers are the same reference - if (changedBuffer === baseBuffer) return false; + if (changedBuffer === baseBuffer) { + if (!options.includeChangedFileWithSameContent || changedNode.mtimeMs === baseNode.mtimeMs) return false; + container[basename] = new SameFileWithModifiedTime(changedBuffer); + return true; + } // no difference if both buffers are identical if (Buffer.compare(changedBuffer, baseBuffer) === 0) { @@ -1391,6 +1395,12 @@ namespace vfs { } } + export class SameFileWithModifiedTime extends File { + constructor(data: Buffer | string, metaAndEncoding?: { encoding?: string, meta?: Record }) { + super(data, metaAndEncoding); + } + } + /** Extended options for a hard link in a `FileSet` */ export class Link { public readonly path: string; @@ -1579,6 +1589,9 @@ namespace vfs { else if (entry instanceof Directory) { text += formatPatchWorker(file, entry.files); } + else if (entry instanceof SameFileWithModifiedTime) { + text += `//// [${file}] file changed its modified time\r\n`; + } else if (entry instanceof SameFileContentFile) { text += `//// [${file}] file written with same contents\r\n`; } diff --git a/src/lib/dom.generated.d.ts b/src/lib/dom.generated.d.ts index 2ef38a96af1..f5705e695c2 100644 --- a/src/lib/dom.generated.d.ts +++ b/src/lib/dom.generated.d.ts @@ -701,6 +701,19 @@ interface LockOptions { steal?: boolean; } +interface MIDIConnectionEventInit extends EventInit { + port?: MIDIPort; +} + +interface MIDIMessageEventInit extends EventInit { + data?: Uint8Array; +} + +interface MIDIOptions { + software?: boolean; + sysex?: boolean; +} + interface MediaCapabilitiesDecodingInfo extends MediaCapabilitiesInfo { configuration?: MediaDecodingConfiguration; } @@ -931,6 +944,11 @@ interface MutationObserverInit { subtree?: boolean; } +interface NavigationPreloadState { + enabled?: boolean; + headerValue?: string; +} + interface NotificationAction { action: string; icon?: string; @@ -1243,6 +1261,35 @@ interface RTCDtlsFingerprint { value?: string; } +interface RTCEncodedAudioFrameMetadata { + contributingSources?: number[]; + synchronizationSource?: number; +} + +interface RTCEncodedVideoFrameMetadata { + contributingSources?: number[]; + dependencies?: number[]; + frameId?: number; + height?: number; + spatialIndex?: number; + synchronizationSource?: number; + temporalIndex?: number; + width?: number; +} + +interface RTCErrorEventInit extends EventInit { + error: RTCError; +} + +interface RTCErrorInit { + errorDetail: RTCErrorDetailType; + httpRequestStatusCode?: number; + receivedAlert?: number; + sctpCauseCode?: number; + sdpLineNumber?: number; + sentAlert?: number; +} + interface RTCIceCandidateInit { candidate?: string; sdpMLineIndex?: number | null; @@ -1748,6 +1795,13 @@ interface UnderlyingSource { type?: undefined; } +interface VideoColorSpaceInit { + fullRange?: boolean; + matrix?: VideoMatrixCoefficients; + primaries?: VideoColorPrimaries; + transfer?: VideoTransferCharacteristics; +} + interface VideoConfiguration { bitrate: number; colorGamut?: ColorGamut; @@ -1760,6 +1814,19 @@ interface VideoConfiguration { width: number; } +interface VideoFrameMetadata { + captureTime?: DOMHighResTimeStamp; + expectedDisplayTime: DOMHighResTimeStamp; + height: number; + mediaTime: number; + presentationTime: DOMHighResTimeStamp; + presentedFrames: number; + processingDuration?: number; + receiveTime?: DOMHighResTimeStamp; + rtpTimestamp?: number; + width: number; +} + interface WaveShaperOptions extends AudioNodeOptions { curve?: number[] | Float32Array; oversample?: OverSampleType; @@ -1894,6 +1961,8 @@ interface AbortSignal extends EventTarget { /** Returns true if this AbortSignal's AbortController has signaled to abort, and false otherwise. */ readonly aborted: boolean; onabort: ((this: AbortSignal, ev: Event) => any) | null; + readonly reason: any; + throwIfAborted(): void; addEventListener(type: K, listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; removeEventListener(type: K, listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, options?: boolean | EventListenerOptions): void; @@ -2389,7 +2458,7 @@ interface Blob { readonly type: string; arrayBuffer(): Promise; slice(start?: number, end?: number, contentType?: string): Blob; - stream(): ReadableStream; + stream(): ReadableStream; text(): Promise; } @@ -2764,6 +2833,7 @@ interface CSSStyleDeclaration { columns: string; contain: string; content: string; + contentVisibility: string; counterIncrement: string; counterReset: string; counterSet: string; @@ -2799,7 +2869,6 @@ interface CSSStyleDeclaration { fontStyle: string; fontSynthesis: string; fontVariant: string; - /** @deprecated */ fontVariantAlternates: string; fontVariantCaps: string; fontVariantEastAsian: string; @@ -2873,6 +2942,14 @@ interface CSSStyleDeclaration { markerMid: string; markerStart: string; mask: string; + maskClip: string; + maskComposite: string; + maskImage: string; + maskMode: string; + maskOrigin: string; + maskPosition: string; + maskRepeat: string; + maskSize: string; maskType: string; maxBlockSize: string; maxHeight: string; @@ -2886,7 +2963,6 @@ interface CSSStyleDeclaration { objectFit: string; objectPosition: string; offset: string; - offsetAnchor: string; offsetDistance: string; offsetPath: string; offsetRotate: string; @@ -2931,6 +3007,7 @@ interface CSSStyleDeclaration { placeSelf: string; pointerEvents: string; position: string; + printColorAdjust: string; quotes: string; resize: string; right: string; @@ -3231,13 +3308,13 @@ declare var CSSTransition: { * Available only in secure contexts. */ interface Cache { - add(request: RequestInfo): Promise; + add(request: RequestInfo | URL): Promise; addAll(requests: RequestInfo[]): Promise; - delete(request: RequestInfo, options?: CacheQueryOptions): Promise; - keys(request?: RequestInfo, options?: CacheQueryOptions): Promise>; - match(request: RequestInfo, options?: CacheQueryOptions): Promise; - matchAll(request?: RequestInfo, options?: CacheQueryOptions): Promise>; - put(request: RequestInfo, response: Response): Promise; + delete(request: RequestInfo | URL, options?: CacheQueryOptions): Promise; + keys(request?: RequestInfo | URL, options?: CacheQueryOptions): Promise>; + match(request: RequestInfo | URL, options?: CacheQueryOptions): Promise; + matchAll(request?: RequestInfo | URL, options?: CacheQueryOptions): Promise>; + put(request: RequestInfo | URL, response: Response): Promise; } declare var Cache: { @@ -3253,7 +3330,7 @@ interface CacheStorage { delete(cacheName: string): Promise; has(cacheName: string): Promise; keys(): Promise; - match(request: RequestInfo, options?: MultiCacheQueryOptions): Promise; + match(request: RequestInfo | URL, options?: MultiCacheQueryOptions): Promise; open(cacheName: string): Promise; } @@ -3518,6 +3595,7 @@ declare var ClipboardEvent: { new(type: string, eventInitDict?: ClipboardEventInit): ClipboardEvent; }; +/** Available only in secure contexts. */ interface ClipboardItem { readonly types: ReadonlyArray; getType(type: string): Promise; @@ -3525,7 +3603,7 @@ interface ClipboardItem { declare var ClipboardItem: { prototype: ClipboardItem; - new(items: Record>, options?: ClipboardItemOptions): ClipboardItem; + new(items: Record>, options?: ClipboardItemOptions): ClipboardItem; }; /** A CloseEvent is sent to clients using WebSockets when the connection is closed. This is delivered to the listener indicated by the WebSocket object's onclose attribute. */ @@ -4195,6 +4273,7 @@ declare var DeviceOrientationEvent: { }; interface DocumentEventMap extends DocumentAndElementEventHandlersEventMap, GlobalEventHandlersEventMap { + "DOMContentLoaded": Event; "fullscreenchange": Event; "fullscreenerror": Event; "pointerlockchange": Event; @@ -4412,6 +4491,8 @@ interface Document extends Node, DocumentAndElementEventHandlers, DocumentOrShad createEvent(eventInterface: "DeviceOrientationEvent"): DeviceOrientationEvent; createEvent(eventInterface: "DragEvent"): DragEvent; createEvent(eventInterface: "ErrorEvent"): ErrorEvent; + createEvent(eventInterface: "Event"): Event; + createEvent(eventInterface: "Events"): Event; createEvent(eventInterface: "FocusEvent"): FocusEvent; createEvent(eventInterface: "FontFaceSetLoadEvent"): FontFaceSetLoadEvent; createEvent(eventInterface: "FormDataEvent"): FormDataEvent; @@ -4420,6 +4501,8 @@ interface Document extends Node, DocumentAndElementEventHandlers, DocumentOrShad createEvent(eventInterface: "IDBVersionChangeEvent"): IDBVersionChangeEvent; createEvent(eventInterface: "InputEvent"): InputEvent; createEvent(eventInterface: "KeyboardEvent"): KeyboardEvent; + createEvent(eventInterface: "MIDIConnectionEvent"): MIDIConnectionEvent; + createEvent(eventInterface: "MIDIMessageEvent"): MIDIMessageEvent; createEvent(eventInterface: "MediaEncryptedEvent"): MediaEncryptedEvent; createEvent(eventInterface: "MediaKeyMessageEvent"): MediaKeyMessageEvent; createEvent(eventInterface: "MediaQueryListEvent"): MediaQueryListEvent; @@ -4440,6 +4523,7 @@ interface Document extends Node, DocumentAndElementEventHandlers, DocumentOrShad createEvent(eventInterface: "PromiseRejectionEvent"): PromiseRejectionEvent; createEvent(eventInterface: "RTCDTMFToneChangeEvent"): RTCDTMFToneChangeEvent; createEvent(eventInterface: "RTCDataChannelEvent"): RTCDataChannelEvent; + createEvent(eventInterface: "RTCErrorEvent"): RTCErrorEvent; createEvent(eventInterface: "RTCPeerConnectionIceErrorEvent"): RTCPeerConnectionIceErrorEvent; createEvent(eventInterface: "RTCPeerConnectionIceEvent"): RTCPeerConnectionIceEvent; createEvent(eventInterface: "RTCTrackEvent"): RTCTrackEvent; @@ -4871,8 +4955,20 @@ interface ElementContentEditable { } interface ElementInternals extends ARIAMixin { + /** Returns the form owner of internals's target element. */ + readonly form: HTMLFormElement | null; + /** Returns a NodeList of all the label elements that internals's target element is associated with. */ + readonly labels: NodeList; /** Returns the ShadowRoot for internals's target element, if the target element is a shadow host, or null otherwise. */ readonly shadowRoot: ShadowRoot | null; + /** Returns true if internals's target element will be validated when the form is submitted; false otherwise. */ + readonly willValidate: boolean; + /** + * Sets both the state and submission value of internals's target element to value. + * + * If value is null, the element won't participate in form submission. + */ + setFormValue(value: File | string | FormData | null, state?: File | string | FormData | null): void; } declare var ElementInternals: { @@ -4946,6 +5042,15 @@ declare var Event: { readonly NONE: number; }; +interface EventCounts { + forEach(callbackfn: (value: number, key: string, parent: EventCounts) => void, thisArg?: any): void; +} + +declare var EventCounts: { + prototype: EventCounts; + new(): EventCounts; +}; + interface EventListener { (evt: Event): void; } @@ -6139,14 +6244,29 @@ declare var HTMLDetailsElement: { new(): HTMLDetailsElement; }; -/** @deprecated this is not available in most browsers */ interface HTMLDialogElement extends HTMLElement { + open: boolean; + returnValue: string; + /** + * Closes the dialog element. + * + * The argument, if provided, provides a return value. + */ + close(returnValue?: string): void; + /** Displays the dialog element. */ + show(): void; + showModal(): void; addEventListener(type: K, listener: (this: HTMLDialogElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; removeEventListener(type: K, listener: (this: HTMLDialogElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void; removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void; } +declare var HTMLDialogElement: { + prototype: HTMLDialogElement; + new(): HTMLDialogElement; +}; + /** @deprecated */ interface HTMLDirectoryElement extends HTMLElement { /** @deprecated */ @@ -7594,6 +7714,7 @@ interface HTMLScriptElement extends HTMLElement { declare var HTMLScriptElement: { prototype: HTMLScriptElement; new(): HTMLScriptElement; + supports(type: string): boolean; }; /** A