diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3d9d48b1bb893..58f0013be4aec 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -195,7 +195,6 @@ import { FlowSwitchClause, FlowType, forEach, - forEachAncestor, forEachChild, forEachChildRecursively, forEachEnclosingBlockScopeContainer, @@ -458,6 +457,7 @@ import { isComputedPropertyName, isConstructorDeclaration, isConstructorTypeNode, + isConstructSignatureDeclaration, isConstTypeReference, isDeclaration, isDeclarationFileName, @@ -2062,6 +2062,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { let lastFlowNodeReachable: boolean; let flowTypeCache: Type[] | undefined; + const contextualTypeNodes: Node[] = []; + const contextualTypes: (Type | undefined)[] = []; + let contextualTypeCount = 0; + + let currentInferenceNode: Node | undefined; + let currentInferenceContext: InferenceContext | undefined; + const emptyStringType = getStringLiteralType(""); const zeroType = getNumberLiteralType(0); const zeroBigIntType = getBigIntLiteralType({ negative: false, base10Value: "0" }); @@ -2579,10 +2586,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return nodeLinks[nodeId] || (nodeLinks[nodeId] = new (NodeLinks as any)()); } - function tryGetNodeLinks(node: Node): NodeLinks | undefined { - return node.id ? nodeLinks[node.id] : undefined; - } - function isGlobalSourceFile(node: Node) { return node.kind === SyntaxKind.SourceFile && !isExternalOrCommonJsModule(node as SourceFile); } @@ -7192,7 +7195,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function typeParameterToDeclarationWithConstraint(type: TypeParameter, context: NodeBuilderContext, constraintNode: TypeNode | undefined): TypeParameterDeclaration { const savedContextFlags = context.flags; context.flags &= ~NodeBuilderFlags.WriteTypeParametersInQualifiedName; // Avoids potential infinite loop when building for a claimspace with a generic - const modifiers = factory.createModifiersFromModifierFlags(getVarianceModifiers(type)); + const modifiers = factory.createModifiersFromModifierFlags(getTypeParameterModifiers(type)); const name = typeParameterToName(type, context); const defaultParameter = getDefaultFromTypeParameter(type); const defaultParameterNode = defaultParameter && typeToTypeNodeHelper(defaultParameter, context); @@ -13200,6 +13203,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return hasNonCircularBaseConstraint(typeParameter) ? getConstraintFromTypeParameter(typeParameter) : undefined; } + function isConstTypeVariable(type: Type): boolean { + return !!(type.flags & TypeFlags.TypeParameter && some((type as TypeParameter).symbol?.declarations, d => hasSyntacticModifier(d, ModifierFlags.Const)) || + type.flags & TypeFlags.IndexedAccess && isConstTypeVariable((type as IndexedAccessType).objectType)); + } + function getConstraintOfIndexedAccess(type: IndexedAccessType) { return hasNonCircularBaseConstraint(type) ? getConstraintFromIndexedAccess(type) : undefined; } @@ -18958,14 +18966,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function checkExpressionForMutableLocationWithContextualType(next: Expression, sourcePropType: Type) { - const links = getNodeLinks(next); - links.contextualType = sourcePropType; - try { - return checkExpressionForMutableLocation(next, CheckMode.Contextual, sourcePropType); - } - finally { - links.contextualType = undefined; - } + pushContextualType(next, sourcePropType); + const result = checkExpressionForMutableLocation(next, CheckMode.Contextual); + popContextualType(); + return result; } type ElaborationIterator = IterableIterator<{ errorNode: Node, innerExpression: Expression | undefined, nameType: Type, errorMessage?: DiagnosticMessage | undefined }>; @@ -19207,20 +19211,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } // recreate a tuple from the elements, if possible // Since we're re-doing the expression type, we need to reapply the contextual type - const links = getNodeLinks(node); - const oldContext = links.contextualType; - links.contextualType = target; - try { - const tupleizedType = checkArrayLiteral(node, CheckMode.Contextual, /*forceTuple*/ true); - links.contextualType = oldContext; - if (isTupleLikeType(tupleizedType)) { - return elaborateElementwise(generateLimitedTupleElements(node, target), tupleizedType, target, relation, containingMessageChain, errorOutputContainer); - } - return false; - } - finally { - links.contextualType = oldContext; + pushContextualType(node, target); + const tupleizedType = checkArrayLiteral(node, CheckMode.Contextual, /*forceTuple*/ true); + popContextualType(); + if (isTupleLikeType(tupleizedType)) { + return elaborateElementwise(generateLimitedTupleElements(node, target), tupleizedType, target, relation, containingMessageChain, errorOutputContainer); } + return false; } function *generateObjectLiteralElements(node: ObjectLiteralExpression): ElaborationIterator { @@ -22103,7 +22100,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { links.variances = emptyArray; const variances = []; for (const tp of typeParameters) { - const modifiers = getVarianceModifiers(tp); + const modifiers = getTypeParameterModifiers(tp); let variance = modifiers & ModifierFlags.Out ? modifiers & ModifierFlags.In ? VarianceFlags.Invariant : VarianceFlags.Covariant : modifiers & ModifierFlags.In ? VarianceFlags.Contravariant : undefined; @@ -22161,9 +22158,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return markerTypes.has(getTypeId(type)); } - function getVarianceModifiers(tp: TypeParameter): ModifierFlags { - return (some(tp.symbol?.declarations, d => hasSyntacticModifier(d, ModifierFlags.In)) ? ModifierFlags.In : 0) | - (some(tp.symbol?.declarations, d => hasSyntacticModifier(d, ModifierFlags.Out)) ? ModifierFlags.Out: 0); + function getTypeParameterModifiers(tp: TypeParameter): ModifierFlags { + return reduceLeft(tp.symbol?.declarations, (modifiers, d) => modifiers | getEffectiveModifierFlags(d), ModifierFlags.None) & (ModifierFlags.In | ModifierFlags.Out | ModifierFlags.Const); } // Return true if the given type reference has a 'void' type argument for a covariant type parameter. @@ -24424,7 +24420,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // all inferences were made to top-level occurrences of the type parameter, and // the type parameter has no constraint or its constraint includes no primitive or literal types, and // the type parameter was fixed during inference or does not occur at top-level in the return type. - const primitiveConstraint = hasPrimitiveConstraint(inference.typeParameter); + const primitiveConstraint = hasPrimitiveConstraint(inference.typeParameter) || isConstTypeVariable(inference.typeParameter); const widenLiteralTypes = !primitiveConstraint && inference.topLevel && (inference.isFixed || !isTypeParameterAtTopLevel(getReturnTypeOfSignature(signature), inference.typeParameter)); const baseCandidates = primitiveConstraint ? sameMap(candidates, getRegularTypeOfLiteralType) : @@ -28645,9 +28641,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // We cannot answer semantic questions within a with block, do not proceed any further return undefined; } - const contextualType = tryGetNodeLinks(node)?.contextualType; - if (contextualType) { - return contextualType; + const index = findContextualNode(node); + if (index >= 0) { + return contextualTypes[index]; } const { parent } = node; switch (parent.kind) { @@ -28669,7 +28665,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return getContextualTypeForArgument(parent as CallExpression | NewExpression, node); case SyntaxKind.TypeAssertionExpression: case SyntaxKind.AsExpression: - return isConstTypeReference((parent as AssertionExpression).type) ? tryFindWhenConstTypeReference(parent as AssertionExpression) : getTypeFromTypeNode((parent as AssertionExpression).type); + return isConstTypeReference((parent as AssertionExpression).type) ? getContextualType(parent as AssertionExpression, contextFlags) : getTypeFromTypeNode((parent as AssertionExpression).type); case SyntaxKind.BinaryExpression: return getContextualTypeForBinaryOperand(node, contextFlags); case SyntaxKind.PropertyAssignment: @@ -28691,7 +28687,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // Like in `checkParenthesizedExpression`, an `/** @type {xyz} */` comment before a parenthesized expression acts as a type cast. const tag = isInJSFile(parent) ? getJSDocTypeTag(parent) : undefined; return !tag ? getContextualType(parent as ParenthesizedExpression, contextFlags) : - isJSDocTypeTag(tag) && isConstTypeReference(tag.typeExpression.type) ? tryFindWhenConstTypeReference(parent as ParenthesizedExpression) : + isJSDocTypeTag(tag) && isConstTypeReference(tag.typeExpression.type) ? getContextualType(parent as ParenthesizedExpression, contextFlags) : getTypeFromTypeNode(tag.typeExpression.type); } case SyntaxKind.NonNullExpression: @@ -28710,24 +28706,39 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return getContextualJsxElementAttributesType(parent as JsxOpeningLikeElement, contextFlags); } return undefined; + } - function tryFindWhenConstTypeReference(node: Expression) { - return getContextualType(node, contextFlags); + function pushContextualType(node: Node, type: Type | undefined) { + contextualTypeNodes[contextualTypeCount] = node; + contextualTypes[contextualTypeCount] = type; + contextualTypeCount++; + } + + function popContextualType() { + contextualTypeCount--; + } + + function findContextualNode(node: Node) { + for (let i = contextualTypeCount - 1; i >= 0; i--) { + if (node === contextualTypeNodes[i]) { + return i; + } } + return -1; } function getInferenceContext(node: Node) { - return forEachAncestor(node, n => tryGetNodeLinks(n)?.inferenceContext); + return isNodeDescendantOf(node, currentInferenceNode) ? currentInferenceContext : undefined; } function getContextualJsxElementAttributesType(node: JsxOpeningLikeElement, contextFlags: ContextFlags | undefined) { if (isJsxOpeningElement(node) && contextFlags !== ContextFlags.Completions) { - const contextualType = tryGetNodeLinks(node.parent)?.contextualType; - if (contextualType) { + const index = findContextualNode(node.parent); + if (index >= 0) { // Contextually applied type is moved from attributes up to the outer jsx attributes so when walking up from the children they get hit // _However_ to hit them from the _attributes_ we must look for them here; otherwise we'll used the declared type // (as below) instead! - return contextualType; + return contextualTypes[index]; } } return getContextualTypeForArgumentAtIndex(node, 0); @@ -29058,9 +29069,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const elementCount = elements.length; const elementTypes: Type[] = []; const elementFlags: ElementFlags[] = []; - const contextualType = getApparentTypeOfContextualType(node, /*contextFlags*/ undefined); + pushContextualType(node, getContextualType(node, /*contextFlags*/ undefined)); const inDestructuringPattern = isAssignmentTarget(node); const inConstContext = isConstContext(node); + const contextualType = getApparentTypeOfContextualType(node, /*contextFlags*/ undefined); + const inTupleContext = !!contextualType && someType(contextualType, isTupleLikeType); let hasOmittedExpression = false; for (let i = 0; i < elementCount; i++) { const e = elements[i]; @@ -29103,21 +29116,21 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { elementFlags.push(ElementFlags.Optional); } else { - const elementContextualType = getContextualTypeForElementExpression(contextualType, elementTypes.length); - const type = checkExpressionForMutableLocation(e, checkMode, elementContextualType, forceTuple); + const type = checkExpressionForMutableLocation(e, checkMode, 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)) { + if (inTupleContext && 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); } } } + popContextualType(); if (inDestructuringPattern) { return createTupleType(elementTypes, elementFlags); } - if (forceTuple || inConstContext || contextualType && someType(contextualType, isTupleLikeType)) { + if (forceTuple || inConstContext || inTupleContext) { return createArrayLiteralType(createTupleType(elementTypes, elementFlags, /*readonly*/ inConstContext)); } return createArrayLiteralType(createArrayType(elementTypes.length ? @@ -29239,6 +29252,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { let propertiesArray: Symbol[] = []; let spread: Type = emptyObjectType; + pushContextualType(node, getContextualType(node, /*contextFlags*/ undefined)); const contextualType = getApparentTypeOfContextualType(node, /*contextFlags*/ undefined); const contextualTypeHasPattern = contextualType && contextualType.pattern && (contextualType.pattern.kind === SyntaxKind.ObjectBindingPattern || contextualType.pattern.kind === SyntaxKind.ObjectLiteralExpression); @@ -29399,6 +29413,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } propertiesArray.push(member); } + popContextualType(); // If object literal is contextually typed by the implied type of a binding pattern, augment the result // type with those properties for which the binding pattern specifies a default value. @@ -31607,6 +31622,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const types = []; const flags = []; const names = []; + const inConstContext = isConstTypeVariable(restType); for (let i = index; i < argCount; i++) { const arg = args[i]; if (isSpreadArgument(arg)) { @@ -31623,7 +31639,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { else { const contextualType = getIndexedAccessType(restType, getNumberLiteralType(i - index), AccessFlags.Contextual); const argType = checkExpressionWithContextualType(arg, contextualType, context, checkMode); - const hasPrimitiveContextualType = maybeTypeOfKind(contextualType, TypeFlags.Primitive | TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping); + const hasPrimitiveContextualType = inConstContext || maybeTypeOfKind(contextualType, TypeFlags.Primitive | TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping); types.push(hasPrimitiveContextualType ? getRegularTypeOfLiteralType(argType) : getWidenedLiteralType(argType)); flags.push(ElementFlags.Required); } @@ -31631,7 +31647,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { names.push((arg as SyntheticExpression).tupleNameSource!); } } - return createTupleType(types, flags, /*readonly*/ false, length(names) === length(types) ? names : undefined); + return createTupleType(types, flags, inConstContext, length(names) === length(types) ? names : undefined); } function checkTypeArguments(signature: Signature, typeArgumentNodes: readonly TypeNode[], reportErrors: boolean, headMessage?: DiagnosticMessage): Type[] | undefined { @@ -33087,7 +33103,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (isJsxIntrinsicIdentifier(node.tagName)) { const result = getIntrinsicAttributesTypeFromJsxOpeningLikeElement(node); const fakeSignature = createSignatureForJSXIntrinsic(node, result); - checkTypeAssignableToAndOptionallyElaborate(checkExpressionWithContextualType(node.attributes, getEffectiveFirstArgumentForJsxSignature(fakeSignature, node), /*mapper*/ undefined, CheckMode.Normal), result, node.tagName, node.attributes); + checkTypeAssignableToAndOptionallyElaborate(checkExpressionWithContextualType(node.attributes, getEffectiveFirstArgumentForJsxSignature(fakeSignature, node), /*inferenceContext*/ undefined, CheckMode.Normal), result, node.tagName, node.attributes); if (length(node.typeArguments)) { forEach(node.typeArguments, checkSourceElement); diagnostics.add(createDiagnosticForNodeArray(getSourceFileOfNode(node), node.typeArguments!, Diagnostics.Expected_0_type_arguments_but_got_1, 0, length(node.typeArguments))); @@ -33565,12 +33581,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { op === SyntaxKind.PlusToken && arg.kind === SyntaxKind.NumericLiteral; case SyntaxKind.PropertyAccessExpression: case SyntaxKind.ElementAccessExpression: - const expr = (node as PropertyAccessExpression | ElementAccessExpression).expression; - let symbol = getTypeOfNode(expr).symbol; - if (symbol && symbol.flags & SymbolFlags.Alias) { - symbol = resolveAlias(symbol); - } - return !!(symbol && getAllSymbolFlags(symbol) & SymbolFlags.Enum); + const expr = skipParentheses((node as PropertyAccessExpression | ElementAccessExpression).expression); + const symbol = isEntityNameExpression(expr) ? resolveEntityName(expr, SymbolFlags.Value, /*ignoreErrors*/ true) : undefined; + return !!(symbol && symbol.flags & SymbolFlags.Enum); } return false; } @@ -35970,33 +35983,27 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function checkExpressionWithContextualType(node: Expression, contextualType: Type, inferenceContext: InferenceContext | undefined, checkMode: CheckMode): Type { - const context = getContextNode(node); - const links = getNodeLinks(context); - const saveContextualType = links.contextualType; - const saveInferenceContext = links.inferenceContext; - try { - links.contextualType = contextualType; - links.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. - const result = maybeTypeOfKind(type, TypeFlags.Literal) && isLiteralOfContextualType(type, instantiateContextualType(contextualType, node, /*contextFlags*/ undefined)) ? - getRegularTypeOfLiteralType(type) : type; - return result; - } - finally { - // In the event our operation is canceled or some other exception occurs, reset the contextual type - // so that we do not accidentally hold onto an instance of the checker, as a Type created in the services layer - // may hold onto the checker that created it. - links.contextualType = saveContextualType; - links.inferenceContext = saveInferenceContext; - } + const contextNode = getContextNode(node); + pushContextualType(contextNode, contextualType); + const saveInferenceNode = currentInferenceNode; + const saveInferenceContext = currentInferenceContext; + currentInferenceNode = contextNode; + currentInferenceContext = 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. + const result = maybeTypeOfKind(type, TypeFlags.Literal) && isLiteralOfContextualType(type, instantiateContextualType(contextualType, node, /*contextFlags*/ undefined)) ? + getRegularTypeOfLiteralType(type) : type; + currentInferenceNode = saveInferenceNode; + currentInferenceContext = saveInferenceContext; + popContextualType(); + return result; } function checkExpressionCached(node: Expression | QualifiedName, checkMode?: CheckMode): Type { @@ -36105,15 +36112,21 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const parent = node.parent; return isAssertionExpression(parent) && isConstTypeReference(parent.type) || isJSDocTypeAssertion(parent) && isConstTypeReference(getJSDocTypeAssertionType(parent)) || + isValidConstAssertionArgument(node) && isConstTypeParameterContext(node) || (isParenthesizedExpression(parent) || isArrayLiteralExpression(parent) || isSpreadElement(parent)) && isConstContext(parent) || (isPropertyAssignment(parent) || isShorthandPropertyAssignment(parent) || isTemplateSpan(parent)) && isConstContext(parent.parent); } - function checkExpressionForMutableLocation(node: Expression, checkMode: CheckMode | undefined, contextualType?: Type, forceTuple?: boolean): Type { + function isConstTypeParameterContext(node: Expression) { + const contextualType = getContextualType(node, ContextFlags.None); + return !!contextualType && someType(contextualType, isConstTypeVariable); + } + + function checkExpressionForMutableLocation(node: Expression, checkMode: CheckMode | undefined, forceTuple?: boolean): Type { const type = checkExpression(node, checkMode, forceTuple); return isConstContext(node) || isCommonJsExportedExpression(node) ? getRegularTypeOfLiteralType(type) : isTypeAssertion(node) ? type : - getWidenedLiteralLikeTypeForContextualType(type, instantiateContextualType(arguments.length === 2 ? getContextualType(node, /*contextFlags*/ undefined) : contextualType, node, /*contextFlags*/ undefined)); + getWidenedLiteralLikeTypeForContextualType(type, instantiateContextualType(getContextualType(node, /*contextFlags*/ undefined), node, /*contextFlags*/ undefined)); } function checkPropertyAssignment(node: PropertyAssignment, checkMode?: CheckMode): Type { @@ -36360,18 +36373,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (links.contextFreeType) { return links.contextFreeType; } - const saveContextualType = links.contextualType; - links.contextualType = anyType; - try { - const type = links.contextFreeType = checkExpression(node, CheckMode.SkipContextSensitive); - return type; - } - finally { - // In the event our operation is canceled or some other exception occurs, reset the contextual type - // so that we do not accidentally hold onto an instance of the checker, as a Type created in the services layer - // may hold onto the checker that created it. - links.contextualType = saveContextualType; - } + pushContextualType(node, anyType); + const type = links.contextFreeType = checkExpression(node, CheckMode.SkipContextSensitive); + popContextualType(); + return type; } function checkExpression(node: Expression | QualifiedName, checkMode?: CheckMode, forceTuple?: boolean): Type { @@ -36571,7 +36576,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function checkTypeParameterDeferred(node: TypeParameterDeclaration) { if (isInterfaceDeclaration(node.parent) || isClassLike(node.parent) || isTypeAliasDeclaration(node.parent)) { const typeParameter = getDeclaredTypeOfTypeParameter(getSymbolOfDeclaration(node)); - const modifiers = getVarianceModifiers(typeParameter); + const modifiers = getTypeParameterModifiers(typeParameter) & (ModifierFlags.In | ModifierFlags.Out); if (modifiers) { const symbol = getSymbolOfDeclaration(node.parent); if (isTypeAliasDeclaration(node.parent) && !(getObjectFlags(getDeclaredTypeOfSymbol(symbol)) & (ObjectFlags.Anonymous | ObjectFlags.Mapped))) { @@ -45399,16 +45404,21 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_an_index_signature, tokenToString(modifier.kind)); } } - if (modifier.kind !== SyntaxKind.InKeyword && modifier.kind !== SyntaxKind.OutKeyword) { + if (modifier.kind !== SyntaxKind.InKeyword && modifier.kind !== SyntaxKind.OutKeyword && modifier.kind !== SyntaxKind.ConstKeyword) { if (node.kind === SyntaxKind.TypeParameter) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_type_parameter, tokenToString(modifier.kind)); } } switch (modifier.kind) { case SyntaxKind.ConstKeyword: - if (node.kind !== SyntaxKind.EnumDeclaration) { + if (node.kind !== SyntaxKind.EnumDeclaration && node.kind !== SyntaxKind.TypeParameter) { return grammarErrorOnNode(node, Diagnostics.A_class_member_cannot_have_the_0_keyword, tokenToString(SyntaxKind.ConstKeyword)); } + const parent = node.parent; + if (node.kind === SyntaxKind.TypeParameter && !(isFunctionLikeDeclaration(parent) || isClassDeclaration(parent) || isFunctionTypeNode(parent) || + isConstructorTypeNode(parent) || isCallSignatureDeclaration(parent) || isConstructSignatureDeclaration(parent) || isMethodSignature(parent))) { + return grammarErrorOnNode(modifier, Diagnostics._0_modifier_can_only_appear_on_a_type_parameter_of_a_function_method_or_class, tokenToString(modifier.kind)); + } break; case SyntaxKind.OverrideKeyword: // If node.kind === SyntaxKind.Parameter, checkParameter reports an error if it's not a parameter property. diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index ad3d9ec3e3eba..533ba2380e1f0 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -907,6 +907,10 @@ "category": "Error", "code": 1276 }, + "'{0}' modifier can only appear on a type parameter of a function, method or class": { + "category": "Error", + "code": 1277 + }, "'with' statements are not allowed in an async function block.": { "category": "Error", diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index e2ec43aaa0d26..d259d7e028691 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -2814,7 +2814,7 @@ namespace Parser { case ParsingContext.ArrayBindingElements: return token() === SyntaxKind.CommaToken || token() === SyntaxKind.DotDotDotToken || isBindingIdentifierOrPrivateIdentifierOrPattern(); case ParsingContext.TypeParameters: - return token() === SyntaxKind.InKeyword || isIdentifier(); + return token() === SyntaxKind.InKeyword || token() === SyntaxKind.ConstKeyword || isIdentifier(); case ParsingContext.ArrayLiteralMembers: switch (token()) { case SyntaxKind.CommaToken: @@ -3823,7 +3823,7 @@ namespace Parser { function parseTypeParameter(): TypeParameterDeclaration { const pos = getNodePos(); - const modifiers = parseModifiers(); + const modifiers = parseModifiers(/*permitConstAsModifier*/ true); const name = parseIdentifier(); let constraint: TypeNode | undefined; let expression: Expression | undefined; @@ -5203,13 +5203,14 @@ namespace Parser { // If we have "<" not followed by an identifier, // then this definitely is not an arrow function. - if (!isIdentifier()) { + if (!isIdentifier() && token() !== SyntaxKind.ConstKeyword) { return Tristate.False; } // JSX overrides if (languageVariant === LanguageVariant.JSX) { const isArrowFunctionInJsx = lookAhead(() => { + parseOptional(SyntaxKind.ConstKeyword); const third = nextToken(); if (third === SyntaxKind.ExtendsKeyword) { const fourth = nextToken(); @@ -7701,11 +7702,11 @@ namespace Parser { return list && createNodeArray(list, pos); } - function tryParseModifier(permitInvalidConstAsModifier?: boolean, stopOnStartOfClassStaticBlock?: boolean, hasSeenStaticModifier?: boolean): Modifier | undefined { + function tryParseModifier(permitConstAsModifier?: boolean, stopOnStartOfClassStaticBlock?: boolean, hasSeenStaticModifier?: boolean): Modifier | undefined { const pos = getNodePos(); const kind = token(); - if (token() === SyntaxKind.ConstKeyword && permitInvalidConstAsModifier) { + if (token() === SyntaxKind.ConstKeyword && permitConstAsModifier) { // We need to ensure that any subsequent modifiers appear on the same line // so that when 'const' is a standalone declaration, we don't issue an error. if (!tryParse(nextTokenIsOnSameLineAndCanFollowModifier)) { @@ -7740,12 +7741,12 @@ namespace Parser { * In those situations, if we are entirely sure that 'const' is not valid on its own (such as when ASI takes effect * and turns it into a standalone declaration), then it is better to parse it and report an error later. * - * In such situations, 'permitInvalidConstAsModifier' should be set to true. + * In such situations, 'permitConstAsModifier' should be set to true. */ - function parseModifiers(permitInvalidConstAsModifier?: boolean, stopOnStartOfClassStaticBlock?: boolean): NodeArray | undefined { + function parseModifiers(permitConstAsModifier?: boolean, stopOnStartOfClassStaticBlock?: boolean): NodeArray | undefined { const pos = getNodePos(); let list, modifier, hasSeenStatic = false; - while (modifier = tryParseModifier(permitInvalidConstAsModifier, stopOnStartOfClassStaticBlock, hasSeenStatic)) { + while (modifier = tryParseModifier(permitConstAsModifier, stopOnStartOfClassStaticBlock, hasSeenStatic)) { if (modifier.kind === SyntaxKind.StaticKeyword) hasSeenStatic = true; list = append(list, modifier); } @@ -7772,7 +7773,7 @@ namespace Parser { const hasJSDoc = hasPrecedingJSDocComment(); const decorators = parseDecorators(); - const modifiers = parseModifiers(/*permitInvalidConstAsModifier*/ true, /*stopOnStartOfClassStaticBlock*/ true); + const modifiers = parseModifiers(/*permitConstAsModifier*/ true, /*stopOnStartOfClassStaticBlock*/ true); if (token() === SyntaxKind.StaticKeyword && lookAhead(nextTokenIsOpenBrace)) { return parseClassStaticBlockDeclaration(pos, hasJSDoc, decorators, modifiers); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 90f94609a3a11..82062effaf9fb 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5958,9 +5958,6 @@ export interface NodeLinks { skipDirectInference?: true; // Flag set by the API `getContextualType` call on a node when `Completions` is passed to force the checker to skip making inferences to a node's type declarationRequiresScopeChange?: boolean; // Set by `useOuterVariableScopeInParameter` in checker when downlevel emit would change the name resolution scope inside of a parameter. serializedTypes?: Map; // Collection of types serialized at this location - - contextualType?: Type; // Used to temporarily assign a contextual type during overload resolution - inferenceContext?: InferenceContext; // Inference context for contextual type } /** @internal */ diff --git a/tests/baselines/reference/tsserver/plugins/getSupportedCodeFixes-can-be-proxied.js b/tests/baselines/reference/tsserver/plugins/getSupportedCodeFixes-can-be-proxied.js index cb8ce333a59cd..f4023bb9c0f84 100644 --- a/tests/baselines/reference/tsserver/plugins/getSupportedCodeFixes-can-be-proxied.js +++ b/tests/baselines/reference/tsserver/plugins/getSupportedCodeFixes-can-be-proxied.js @@ -623,6 +623,7 @@ Info 32 [00:01:13.000] response: "1274", "1275", "1276", + "1277", "1300", "1309", "1313", @@ -1949,6 +1950,7 @@ Info 38 [00:01:19.000] response: "1274", "1275", "1276", + "1277", "1300", "1309", "1313", @@ -3187,6 +3189,7 @@ Info 40 [00:01:21.000] response: "1274", "1275", "1276", + "1277", "1300", "1309", "1313", diff --git a/tests/baselines/reference/typeParameterConstModifiers.errors.txt b/tests/baselines/reference/typeParameterConstModifiers.errors.txt new file mode 100644 index 0000000000000..06889c06a04ca --- /dev/null +++ b/tests/baselines/reference/typeParameterConstModifiers.errors.txt @@ -0,0 +1,80 @@ +tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterConstModifiers.ts(43,14): error TS1277: 'const' modifier can only appear on a type parameter of a function, method or class +tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterConstModifiers.ts(49,9): error TS1277: 'const' modifier can only appear on a type parameter of a function, method or class + + +==== tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterConstModifiers.ts (2 errors) ==== + declare function f1(x: T): T; + + const x11 = f1('a'); + const x12 = f1(['a', ['b', 'c']]); + const x13 = f1({ a: 1, b: "c", d: ["e", 2, true, { f: "g" }] }); + + declare function f2(x: T | undefined): T; + + const x21 = f2('a'); + const x22 = f2(['a', ['b', 'c']]); + const x23 = f2({ a: 1, b: "c", d: ["e", 2, true, { f: "g" }] }); + + declare function f3(x: T): T[]; + + const x31 = f3("hello"); + const x32 = f3("hello"); + + declare function f4(obj: [T, T]): T; + + const x41 = f4([[1, 'x'], [2, 'y']]); + const x42 = f4([{ a: 1, b: 'x' }, { a: 2, b: 'y' }]); + + declare function f5(obj: { x: T, y: T }): T; + + const x51 = f5({ x: [1, 'x'], y: [2, 'y'] }); + const x52 = f5({ x: { a: 1, b: 'x' }, y: { a: 2, b: 'y' } }); + + declare function f6(...args: T): T; + + const x61 = f6(1, 'b', { a: 1, b: 'x' }); + + class C1 { + constructor(x: T) {} + foo(x: U) { return x; } + } + + const c71 = new C1({ a: 1, b: "c", d: ["e", 2, true, { f: "g" }] }); + const c72 = c71.foo(['a', ['b', 'c']]); + + const fx1 = (x: T) => x; + const fx2 = (x: T) => x; + + interface I1 { x: T } // Error + ~~~~~ +!!! error TS1277: 'const' modifier can only appear on a type parameter of a function, method or class + + interface I2 { + f(x: T): T; + } + + type T1 = T; // Error + ~~~~~ +!!! error TS1277: 'const' modifier can only appear on a type parameter of a function, method or class + + type T2 = (x: T) => T; + type T3 = { (x: T): T }; + type T4 = new (x: T) => T; + type T5 = { new (x: T): T }; + + // Corrected repro from #51745 + + type Obj = { a: { b: { c: "123" } } }; + + type GetPath = + P extends readonly [] ? T : + P extends readonly [infer A extends keyof T, ...infer Rest] ? GetPath : + never; + + function set(obj: T, path: P, value: GetPath) {} + + declare let obj: Obj; + declare let value: "123"; + + set(obj, ['a', 'b', 'c'], value); + \ No newline at end of file diff --git a/tests/baselines/reference/typeParameterConstModifiers.js b/tests/baselines/reference/typeParameterConstModifiers.js new file mode 100644 index 0000000000000..4a1e057e15383 --- /dev/null +++ b/tests/baselines/reference/typeParameterConstModifiers.js @@ -0,0 +1,100 @@ +//// [typeParameterConstModifiers.ts] +declare function f1(x: T): T; + +const x11 = f1('a'); +const x12 = f1(['a', ['b', 'c']]); +const x13 = f1({ a: 1, b: "c", d: ["e", 2, true, { f: "g" }] }); + +declare function f2(x: T | undefined): T; + +const x21 = f2('a'); +const x22 = f2(['a', ['b', 'c']]); +const x23 = f2({ a: 1, b: "c", d: ["e", 2, true, { f: "g" }] }); + +declare function f3(x: T): T[]; + +const x31 = f3("hello"); +const x32 = f3("hello"); + +declare function f4(obj: [T, T]): T; + +const x41 = f4([[1, 'x'], [2, 'y']]); +const x42 = f4([{ a: 1, b: 'x' }, { a: 2, b: 'y' }]); + +declare function f5(obj: { x: T, y: T }): T; + +const x51 = f5({ x: [1, 'x'], y: [2, 'y'] }); +const x52 = f5({ x: { a: 1, b: 'x' }, y: { a: 2, b: 'y' } }); + +declare function f6(...args: T): T; + +const x61 = f6(1, 'b', { a: 1, b: 'x' }); + +class C1 { + constructor(x: T) {} + foo(x: U) { return x; } +} + +const c71 = new C1({ a: 1, b: "c", d: ["e", 2, true, { f: "g" }] }); +const c72 = c71.foo(['a', ['b', 'c']]); + +const fx1 = (x: T) => x; +const fx2 = (x: T) => x; + +interface I1 { x: T } // Error + +interface I2 { + f(x: T): T; +} + +type T1 = T; // Error + +type T2 = (x: T) => T; +type T3 = { (x: T): T }; +type T4 = new (x: T) => T; +type T5 = { new (x: T): T }; + +// Corrected repro from #51745 + +type Obj = { a: { b: { c: "123" } } }; + +type GetPath = + P extends readonly [] ? T : + P extends readonly [infer A extends keyof T, ...infer Rest] ? GetPath : + never; + +function set(obj: T, path: P, value: GetPath) {} + +declare let obj: Obj; +declare let value: "123"; + +set(obj, ['a', 'b', 'c'], value); + + +//// [typeParameterConstModifiers.js] +"use strict"; +var x11 = f1('a'); +var x12 = f1(['a', ['b', 'c']]); +var x13 = f1({ a: 1, b: "c", d: ["e", 2, true, { f: "g" }] }); +var x21 = f2('a'); +var x22 = f2(['a', ['b', 'c']]); +var x23 = f2({ a: 1, b: "c", d: ["e", 2, true, { f: "g" }] }); +var x31 = f3("hello"); +var x32 = f3("hello"); +var x41 = f4([[1, 'x'], [2, 'y']]); +var x42 = f4([{ a: 1, b: 'x' }, { a: 2, b: 'y' }]); +var x51 = f5({ x: [1, 'x'], y: [2, 'y'] }); +var x52 = f5({ x: { a: 1, b: 'x' }, y: { a: 2, b: 'y' } }); +var x61 = f6(1, 'b', { a: 1, b: 'x' }); +var C1 = /** @class */ (function () { + function C1(x) { + } + C1.prototype.foo = function (x) { return x; }; + return C1; +}()); +var c71 = new C1({ a: 1, b: "c", d: ["e", 2, true, { f: "g" }] }); +var c72 = c71.foo(['a', ['b', 'c']]); +var fx1 = function (x) { return x; }; +var fx2 = function (x) { return x; }; +function set(obj, path, value) { } +set(obj, ['a', 'b', 'c'], value); diff --git a/tests/baselines/reference/typeParameterConstModifiers.symbols b/tests/baselines/reference/typeParameterConstModifiers.symbols new file mode 100644 index 0000000000000..010301c319bc5 --- /dev/null +++ b/tests/baselines/reference/typeParameterConstModifiers.symbols @@ -0,0 +1,270 @@ +=== tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterConstModifiers.ts === +declare function f1(x: T): T; +>f1 : Symbol(f1, Decl(typeParameterConstModifiers.ts, 0, 0)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 0, 20)) +>x : Symbol(x, Decl(typeParameterConstModifiers.ts, 0, 29)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 0, 20)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 0, 20)) + +const x11 = f1('a'); +>x11 : Symbol(x11, Decl(typeParameterConstModifiers.ts, 2, 5)) +>f1 : Symbol(f1, Decl(typeParameterConstModifiers.ts, 0, 0)) + +const x12 = f1(['a', ['b', 'c']]); +>x12 : Symbol(x12, Decl(typeParameterConstModifiers.ts, 3, 5)) +>f1 : Symbol(f1, Decl(typeParameterConstModifiers.ts, 0, 0)) + +const x13 = f1({ a: 1, b: "c", d: ["e", 2, true, { f: "g" }] }); +>x13 : Symbol(x13, Decl(typeParameterConstModifiers.ts, 4, 5)) +>f1 : Symbol(f1, Decl(typeParameterConstModifiers.ts, 0, 0)) +>a : Symbol(a, Decl(typeParameterConstModifiers.ts, 4, 16)) +>b : Symbol(b, Decl(typeParameterConstModifiers.ts, 4, 22)) +>d : Symbol(d, Decl(typeParameterConstModifiers.ts, 4, 30)) +>f : Symbol(f, Decl(typeParameterConstModifiers.ts, 4, 50)) + +declare function f2(x: T | undefined): T; +>f2 : Symbol(f2, Decl(typeParameterConstModifiers.ts, 4, 64)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 6, 20)) +>U : Symbol(U, Decl(typeParameterConstModifiers.ts, 6, 28)) +>x : Symbol(x, Decl(typeParameterConstModifiers.ts, 6, 32)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 6, 20)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 6, 20)) + +const x21 = f2('a'); +>x21 : Symbol(x21, Decl(typeParameterConstModifiers.ts, 8, 5)) +>f2 : Symbol(f2, Decl(typeParameterConstModifiers.ts, 4, 64)) + +const x22 = f2(['a', ['b', 'c']]); +>x22 : Symbol(x22, Decl(typeParameterConstModifiers.ts, 9, 5)) +>f2 : Symbol(f2, Decl(typeParameterConstModifiers.ts, 4, 64)) + +const x23 = f2({ a: 1, b: "c", d: ["e", 2, true, { f: "g" }] }); +>x23 : Symbol(x23, Decl(typeParameterConstModifiers.ts, 10, 5)) +>f2 : Symbol(f2, Decl(typeParameterConstModifiers.ts, 4, 64)) +>a : Symbol(a, Decl(typeParameterConstModifiers.ts, 10, 16)) +>b : Symbol(b, Decl(typeParameterConstModifiers.ts, 10, 22)) +>d : Symbol(d, Decl(typeParameterConstModifiers.ts, 10, 30)) +>f : Symbol(f, Decl(typeParameterConstModifiers.ts, 10, 50)) + +declare function f3(x: T): T[]; +>f3 : Symbol(f3, Decl(typeParameterConstModifiers.ts, 10, 64)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 12, 20)) +>x : Symbol(x, Decl(typeParameterConstModifiers.ts, 12, 29)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 12, 20)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 12, 20)) + +const x31 = f3("hello"); +>x31 : Symbol(x31, Decl(typeParameterConstModifiers.ts, 14, 5)) +>f3 : Symbol(f3, Decl(typeParameterConstModifiers.ts, 10, 64)) + +const x32 = f3("hello"); +>x32 : Symbol(x32, Decl(typeParameterConstModifiers.ts, 15, 5)) +>f3 : Symbol(f3, Decl(typeParameterConstModifiers.ts, 10, 64)) + +declare function f4(obj: [T, T]): T; +>f4 : Symbol(f4, Decl(typeParameterConstModifiers.ts, 15, 24)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 17, 20)) +>obj : Symbol(obj, Decl(typeParameterConstModifiers.ts, 17, 29)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 17, 20)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 17, 20)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 17, 20)) + +const x41 = f4([[1, 'x'], [2, 'y']]); +>x41 : Symbol(x41, Decl(typeParameterConstModifiers.ts, 19, 5)) +>f4 : Symbol(f4, Decl(typeParameterConstModifiers.ts, 15, 24)) + +const x42 = f4([{ a: 1, b: 'x' }, { a: 2, b: 'y' }]); +>x42 : Symbol(x42, Decl(typeParameterConstModifiers.ts, 20, 5)) +>f4 : Symbol(f4, Decl(typeParameterConstModifiers.ts, 15, 24)) +>a : Symbol(a, Decl(typeParameterConstModifiers.ts, 20, 17)) +>b : Symbol(b, Decl(typeParameterConstModifiers.ts, 20, 23)) +>a : Symbol(a, Decl(typeParameterConstModifiers.ts, 20, 35)) +>b : Symbol(b, Decl(typeParameterConstModifiers.ts, 20, 41)) + +declare function f5(obj: { x: T, y: T }): T; +>f5 : Symbol(f5, Decl(typeParameterConstModifiers.ts, 20, 53)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 22, 20)) +>obj : Symbol(obj, Decl(typeParameterConstModifiers.ts, 22, 29)) +>x : Symbol(x, Decl(typeParameterConstModifiers.ts, 22, 35)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 22, 20)) +>y : Symbol(y, Decl(typeParameterConstModifiers.ts, 22, 41)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 22, 20)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 22, 20)) + +const x51 = f5({ x: [1, 'x'], y: [2, 'y'] }); +>x51 : Symbol(x51, Decl(typeParameterConstModifiers.ts, 24, 5)) +>f5 : Symbol(f5, Decl(typeParameterConstModifiers.ts, 20, 53)) +>x : Symbol(x, Decl(typeParameterConstModifiers.ts, 24, 16)) +>y : Symbol(y, Decl(typeParameterConstModifiers.ts, 24, 29)) + +const x52 = f5({ x: { a: 1, b: 'x' }, y: { a: 2, b: 'y' } }); +>x52 : Symbol(x52, Decl(typeParameterConstModifiers.ts, 25, 5)) +>f5 : Symbol(f5, Decl(typeParameterConstModifiers.ts, 20, 53)) +>x : Symbol(x, Decl(typeParameterConstModifiers.ts, 25, 16)) +>a : Symbol(a, Decl(typeParameterConstModifiers.ts, 25, 21)) +>b : Symbol(b, Decl(typeParameterConstModifiers.ts, 25, 27)) +>y : Symbol(y, Decl(typeParameterConstModifiers.ts, 25, 37)) +>a : Symbol(a, Decl(typeParameterConstModifiers.ts, 25, 42)) +>b : Symbol(b, Decl(typeParameterConstModifiers.ts, 25, 48)) + +declare function f6(...args: T): T; +>f6 : Symbol(f6, Decl(typeParameterConstModifiers.ts, 25, 61)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 27, 20)) +>args : Symbol(args, Decl(typeParameterConstModifiers.ts, 27, 56)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 27, 20)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 27, 20)) + +const x61 = f6(1, 'b', { a: 1, b: 'x' }); +>x61 : Symbol(x61, Decl(typeParameterConstModifiers.ts, 29, 5)) +>f6 : Symbol(f6, Decl(typeParameterConstModifiers.ts, 25, 61)) +>a : Symbol(a, Decl(typeParameterConstModifiers.ts, 29, 24)) +>b : Symbol(b, Decl(typeParameterConstModifiers.ts, 29, 30)) + +class C1 { +>C1 : Symbol(C1, Decl(typeParameterConstModifiers.ts, 29, 41)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 31, 9)) + + constructor(x: T) {} +>x : Symbol(x, Decl(typeParameterConstModifiers.ts, 32, 16)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 31, 9)) + + foo(x: U) { return x; } +>foo : Symbol(C1.foo, Decl(typeParameterConstModifiers.ts, 32, 24)) +>U : Symbol(U, Decl(typeParameterConstModifiers.ts, 33, 8)) +>x : Symbol(x, Decl(typeParameterConstModifiers.ts, 33, 17)) +>U : Symbol(U, Decl(typeParameterConstModifiers.ts, 33, 8)) +>x : Symbol(x, Decl(typeParameterConstModifiers.ts, 33, 17)) +} + +const c71 = new C1({ a: 1, b: "c", d: ["e", 2, true, { f: "g" }] }); +>c71 : Symbol(c71, Decl(typeParameterConstModifiers.ts, 36, 5)) +>C1 : Symbol(C1, Decl(typeParameterConstModifiers.ts, 29, 41)) +>a : Symbol(a, Decl(typeParameterConstModifiers.ts, 36, 20)) +>b : Symbol(b, Decl(typeParameterConstModifiers.ts, 36, 26)) +>d : Symbol(d, Decl(typeParameterConstModifiers.ts, 36, 34)) +>f : Symbol(f, Decl(typeParameterConstModifiers.ts, 36, 54)) + +const c72 = c71.foo(['a', ['b', 'c']]); +>c72 : Symbol(c72, Decl(typeParameterConstModifiers.ts, 37, 5)) +>c71.foo : Symbol(C1.foo, Decl(typeParameterConstModifiers.ts, 32, 24)) +>c71 : Symbol(c71, Decl(typeParameterConstModifiers.ts, 36, 5)) +>foo : Symbol(C1.foo, Decl(typeParameterConstModifiers.ts, 32, 24)) + +const fx1 = (x: T) => x; +>fx1 : Symbol(fx1, Decl(typeParameterConstModifiers.ts, 39, 5)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 39, 13)) +>x : Symbol(x, Decl(typeParameterConstModifiers.ts, 39, 22)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 39, 13)) +>x : Symbol(x, Decl(typeParameterConstModifiers.ts, 39, 22)) + +const fx2 = (x: T) => x; +>fx2 : Symbol(fx2, Decl(typeParameterConstModifiers.ts, 40, 5)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 40, 13)) +>x : Symbol(x, Decl(typeParameterConstModifiers.ts, 40, 23)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 40, 13)) +>x : Symbol(x, Decl(typeParameterConstModifiers.ts, 40, 23)) + +interface I1 { x: T } // Error +>I1 : Symbol(I1, Decl(typeParameterConstModifiers.ts, 40, 34)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 42, 13)) +>x : Symbol(I1.x, Decl(typeParameterConstModifiers.ts, 42, 23)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 42, 13)) + +interface I2 { +>I2 : Symbol(I2, Decl(typeParameterConstModifiers.ts, 42, 30)) + + f(x: T): T; +>f : Symbol(I2.f, Decl(typeParameterConstModifiers.ts, 44, 14)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 45, 6)) +>x : Symbol(x, Decl(typeParameterConstModifiers.ts, 45, 15)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 45, 6)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 45, 6)) +} + +type T1 = T; // Error +>T1 : Symbol(T1, Decl(typeParameterConstModifiers.ts, 46, 1)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 48, 8)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 48, 8)) + +type T2 = (x: T) => T; +>T2 : Symbol(T2, Decl(typeParameterConstModifiers.ts, 48, 21)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 50, 11)) +>x : Symbol(x, Decl(typeParameterConstModifiers.ts, 50, 20)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 50, 11)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 50, 11)) + +type T3 = { (x: T): T }; +>T3 : Symbol(T3, Decl(typeParameterConstModifiers.ts, 50, 31)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 51, 13)) +>x : Symbol(x, Decl(typeParameterConstModifiers.ts, 51, 22)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 51, 13)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 51, 13)) + +type T4 = new (x: T) => T; +>T4 : Symbol(T4, Decl(typeParameterConstModifiers.ts, 51, 33)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 52, 15)) +>x : Symbol(x, Decl(typeParameterConstModifiers.ts, 52, 24)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 52, 15)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 52, 15)) + +type T5 = { new (x: T): T }; +>T5 : Symbol(T5, Decl(typeParameterConstModifiers.ts, 52, 35)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 53, 17)) +>x : Symbol(x, Decl(typeParameterConstModifiers.ts, 53, 26)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 53, 17)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 53, 17)) + +// Corrected repro from #51745 + +type Obj = { a: { b: { c: "123" } } }; +>Obj : Symbol(Obj, Decl(typeParameterConstModifiers.ts, 53, 37)) +>a : Symbol(a, Decl(typeParameterConstModifiers.ts, 57, 12)) +>b : Symbol(b, Decl(typeParameterConstModifiers.ts, 57, 17)) +>c : Symbol(c, Decl(typeParameterConstModifiers.ts, 57, 22)) + +type GetPath = +>GetPath : Symbol(GetPath, Decl(typeParameterConstModifiers.ts, 57, 38)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 59, 13)) +>P : Symbol(P, Decl(typeParameterConstModifiers.ts, 59, 15)) + + P extends readonly [] ? T : +>P : Symbol(P, Decl(typeParameterConstModifiers.ts, 59, 15)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 59, 13)) + + P extends readonly [infer A extends keyof T, ...infer Rest] ? GetPath : +>P : Symbol(P, Decl(typeParameterConstModifiers.ts, 59, 15)) +>A : Symbol(A, Decl(typeParameterConstModifiers.ts, 61, 29)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 59, 13)) +>Rest : Symbol(Rest, Decl(typeParameterConstModifiers.ts, 61, 57)) +>GetPath : Symbol(GetPath, Decl(typeParameterConstModifiers.ts, 57, 38)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 59, 13)) +>A : Symbol(A, Decl(typeParameterConstModifiers.ts, 61, 29)) +>Rest : Symbol(Rest, Decl(typeParameterConstModifiers.ts, 61, 57)) + + never; + +function set(obj: T, path: P, value: GetPath) {} +>set : Symbol(set, Decl(typeParameterConstModifiers.ts, 62, 10)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 64, 13)) +>P : Symbol(P, Decl(typeParameterConstModifiers.ts, 64, 15)) +>obj : Symbol(obj, Decl(typeParameterConstModifiers.ts, 64, 51)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 64, 13)) +>path : Symbol(path, Decl(typeParameterConstModifiers.ts, 64, 58)) +>P : Symbol(P, Decl(typeParameterConstModifiers.ts, 64, 15)) +>value : Symbol(value, Decl(typeParameterConstModifiers.ts, 64, 67)) +>GetPath : Symbol(GetPath, Decl(typeParameterConstModifiers.ts, 57, 38)) +>T : Symbol(T, Decl(typeParameterConstModifiers.ts, 64, 13)) +>P : Symbol(P, Decl(typeParameterConstModifiers.ts, 64, 15)) + +declare let obj: Obj; +>obj : Symbol(obj, Decl(typeParameterConstModifiers.ts, 66, 11)) +>Obj : Symbol(Obj, Decl(typeParameterConstModifiers.ts, 53, 37)) + +declare let value: "123"; +>value : Symbol(value, Decl(typeParameterConstModifiers.ts, 67, 11)) + +set(obj, ['a', 'b', 'c'], value); +>set : Symbol(set, Decl(typeParameterConstModifiers.ts, 62, 10)) +>obj : Symbol(obj, Decl(typeParameterConstModifiers.ts, 66, 11)) +>value : Symbol(value, Decl(typeParameterConstModifiers.ts, 67, 11)) + diff --git a/tests/baselines/reference/typeParameterConstModifiers.types b/tests/baselines/reference/typeParameterConstModifiers.types new file mode 100644 index 0000000000000..e5d3d230be180 --- /dev/null +++ b/tests/baselines/reference/typeParameterConstModifiers.types @@ -0,0 +1,298 @@ +=== tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterConstModifiers.ts === +declare function f1(x: T): T; +>f1 : (x: T) => T +>x : T + +const x11 = f1('a'); +>x11 : "a" +>f1('a') : "a" +>f1 : (x: T) => T +>'a' : "a" + +const x12 = f1(['a', ['b', 'c']]); +>x12 : readonly ["a", readonly ["b", "c"]] +>f1(['a', ['b', 'c']]) : readonly ["a", readonly ["b", "c"]] +>f1 : (x: T) => T +>['a', ['b', 'c']] : ["a", ["b", "c"]] +>'a' : "a" +>['b', 'c'] : ["b", "c"] +>'b' : "b" +>'c' : "c" + +const x13 = f1({ a: 1, b: "c", d: ["e", 2, true, { f: "g" }] }); +>x13 : { readonly a: 1; readonly b: "c"; readonly d: readonly ["e", 2, true, { readonly f: "g"; }]; } +>f1({ a: 1, b: "c", d: ["e", 2, true, { f: "g" }] }) : { readonly a: 1; readonly b: "c"; readonly d: readonly ["e", 2, true, { readonly f: "g"; }]; } +>f1 : (x: T) => T +>{ a: 1, b: "c", d: ["e", 2, true, { f: "g" }] } : { a: 1; b: "c"; d: ["e", 2, true, { f: "g"; }]; } +>a : 1 +>1 : 1 +>b : "c" +>"c" : "c" +>d : ["e", 2, true, { f: "g"; }] +>["e", 2, true, { f: "g" }] : ["e", 2, true, { f: "g"; }] +>"e" : "e" +>2 : 2 +>true : true +>{ f: "g" } : { f: "g"; } +>f : "g" +>"g" : "g" + +declare function f2(x: T | undefined): T; +>f2 : (x: T | undefined) => T +>x : T | undefined + +const x21 = f2('a'); +>x21 : "a" +>f2('a') : "a" +>f2 : (x: T | undefined) => T +>'a' : "a" + +const x22 = f2(['a', ['b', 'c']]); +>x22 : readonly ["a", readonly ["b", "c"]] +>f2(['a', ['b', 'c']]) : readonly ["a", readonly ["b", "c"]] +>f2 : (x: T | undefined) => T +>['a', ['b', 'c']] : ["a", ["b", "c"]] +>'a' : "a" +>['b', 'c'] : ["b", "c"] +>'b' : "b" +>'c' : "c" + +const x23 = f2({ a: 1, b: "c", d: ["e", 2, true, { f: "g" }] }); +>x23 : { readonly a: 1; readonly b: "c"; readonly d: readonly ["e", 2, true, { readonly f: "g"; }]; } +>f2({ a: 1, b: "c", d: ["e", 2, true, { f: "g" }] }) : { readonly a: 1; readonly b: "c"; readonly d: readonly ["e", 2, true, { readonly f: "g"; }]; } +>f2 : (x: T | undefined) => T +>{ a: 1, b: "c", d: ["e", 2, true, { f: "g" }] } : { a: 1; b: "c"; d: ["e", 2, true, { f: "g"; }]; } +>a : 1 +>1 : 1 +>b : "c" +>"c" : "c" +>d : ["e", 2, true, { f: "g"; }] +>["e", 2, true, { f: "g" }] : ["e", 2, true, { f: "g"; }] +>"e" : "e" +>2 : 2 +>true : true +>{ f: "g" } : { f: "g"; } +>f : "g" +>"g" : "g" + +declare function f3(x: T): T[]; +>f3 : (x: T) => T[] +>x : T + +const x31 = f3("hello"); +>x31 : "hello"[] +>f3("hello") : "hello"[] +>f3 : (x: T) => T[] +>"hello" : "hello" + +const x32 = f3("hello"); +>x32 : "hello"[] +>f3("hello") : "hello"[] +>f3 : (x: T) => T[] +>"hello" : "hello" + +declare function f4(obj: [T, T]): T; +>f4 : (obj: [T, T]) => T +>obj : [T, T] + +const x41 = f4([[1, 'x'], [2, 'y']]); +>x41 : readonly [1, "x"] | readonly [2, "y"] +>f4([[1, 'x'], [2, 'y']]) : readonly [1, "x"] | readonly [2, "y"] +>f4 : (obj: [T, T]) => T +>[[1, 'x'], [2, 'y']] : [[1, "x"], [2, "y"]] +>[1, 'x'] : [1, "x"] +>1 : 1 +>'x' : "x" +>[2, 'y'] : [2, "y"] +>2 : 2 +>'y' : "y" + +const x42 = f4([{ a: 1, b: 'x' }, { a: 2, b: 'y' }]); +>x42 : { readonly a: 1; readonly b: "x"; } | { readonly a: 2; readonly b: "y"; } +>f4([{ a: 1, b: 'x' }, { a: 2, b: 'y' }]) : { readonly a: 1; readonly b: "x"; } | { readonly a: 2; readonly b: "y"; } +>f4 : (obj: [T, T]) => T +>[{ a: 1, b: 'x' }, { a: 2, b: 'y' }] : [{ a: 1; b: "x"; }, { a: 2; b: "y"; }] +>{ a: 1, b: 'x' } : { a: 1; b: "x"; } +>a : 1 +>1 : 1 +>b : "x" +>'x' : "x" +>{ a: 2, b: 'y' } : { a: 2; b: "y"; } +>a : 2 +>2 : 2 +>b : "y" +>'y' : "y" + +declare function f5(obj: { x: T, y: T }): T; +>f5 : (obj: { x: T; y: T;}) => T +>obj : { x: T; y: T; } +>x : T +>y : T + +const x51 = f5({ x: [1, 'x'], y: [2, 'y'] }); +>x51 : readonly [1, "x"] | readonly [2, "y"] +>f5({ x: [1, 'x'], y: [2, 'y'] }) : readonly [1, "x"] | readonly [2, "y"] +>f5 : (obj: { x: T; y: T; }) => T +>{ x: [1, 'x'], y: [2, 'y'] } : { x: [1, "x"]; y: [2, "y"]; } +>x : [1, "x"] +>[1, 'x'] : [1, "x"] +>1 : 1 +>'x' : "x" +>y : [2, "y"] +>[2, 'y'] : [2, "y"] +>2 : 2 +>'y' : "y" + +const x52 = f5({ x: { a: 1, b: 'x' }, y: { a: 2, b: 'y' } }); +>x52 : { readonly a: 1; readonly b: "x"; } | { readonly a: 2; readonly b: "y"; } +>f5({ x: { a: 1, b: 'x' }, y: { a: 2, b: 'y' } }) : { readonly a: 1; readonly b: "x"; } | { readonly a: 2; readonly b: "y"; } +>f5 : (obj: { x: T; y: T; }) => T +>{ x: { a: 1, b: 'x' }, y: { a: 2, b: 'y' } } : { x: { a: 1; b: "x"; }; y: { a: 2; b: "y"; }; } +>x : { a: 1; b: "x"; } +>{ a: 1, b: 'x' } : { a: 1; b: "x"; } +>a : 1 +>1 : 1 +>b : "x" +>'x' : "x" +>y : { a: 2; b: "y"; } +>{ a: 2, b: 'y' } : { a: 2; b: "y"; } +>a : 2 +>2 : 2 +>b : "y" +>'y' : "y" + +declare function f6(...args: T): T; +>f6 : (...args: T) => T +>args : T + +const x61 = f6(1, 'b', { a: 1, b: 'x' }); +>x61 : readonly [1, "b", { readonly a: 1; readonly b: "x"; }] +>f6(1, 'b', { a: 1, b: 'x' }) : readonly [1, "b", { readonly a: 1; readonly b: "x"; }] +>f6 : (...args: T) => T +>1 : 1 +>'b' : "b" +>{ a: 1, b: 'x' } : { a: 1; b: "x"; } +>a : 1 +>1 : 1 +>b : "x" +>'x' : "x" + +class C1 { +>C1 : C1 + + constructor(x: T) {} +>x : T + + foo(x: U) { return x; } +>foo : (x: U) => U +>x : U +>x : U +} + +const c71 = new C1({ a: 1, b: "c", d: ["e", 2, true, { f: "g" }] }); +>c71 : C1<{ readonly a: 1; readonly b: "c"; readonly d: readonly ["e", 2, true, { readonly f: "g"; }]; }> +>new C1({ a: 1, b: "c", d: ["e", 2, true, { f: "g" }] }) : C1<{ readonly a: 1; readonly b: "c"; readonly d: readonly ["e", 2, true, { readonly f: "g"; }]; }> +>C1 : typeof C1 +>{ a: 1, b: "c", d: ["e", 2, true, { f: "g" }] } : { a: 1; b: "c"; d: ["e", 2, true, { f: "g"; }]; } +>a : 1 +>1 : 1 +>b : "c" +>"c" : "c" +>d : ["e", 2, true, { f: "g"; }] +>["e", 2, true, { f: "g" }] : ["e", 2, true, { f: "g"; }] +>"e" : "e" +>2 : 2 +>true : true +>{ f: "g" } : { f: "g"; } +>f : "g" +>"g" : "g" + +const c72 = c71.foo(['a', ['b', 'c']]); +>c72 : readonly ["a", readonly ["b", "c"]] +>c71.foo(['a', ['b', 'c']]) : readonly ["a", readonly ["b", "c"]] +>c71.foo : (x: U) => U +>c71 : C1<{ readonly a: 1; readonly b: "c"; readonly d: readonly ["e", 2, true, { readonly f: "g"; }]; }> +>foo : (x: U) => U +>['a', ['b', 'c']] : ["a", ["b", "c"]] +>'a' : "a" +>['b', 'c'] : ["b", "c"] +>'b' : "b" +>'c' : "c" + +const fx1 = (x: T) => x; +>fx1 : (x: T) => T +>(x: T) => x : (x: T) => T +>x : T +>x : T + +const fx2 = (x: T) => x; +>fx2 : (x: T) => T +>(x: T) => x : (x: T) => T +>x : T +>x : T + +interface I1 { x: T } // Error +>x : T + +interface I2 { + f(x: T): T; +>f : (x: T) => T +>x : T +} + +type T1 = T; // Error +>T1 : T + +type T2 = (x: T) => T; +>T2 : (x: T) => T +>x : T + +type T3 = { (x: T): T }; +>T3 : (x: T) => T +>x : T + +type T4 = new (x: T) => T; +>T4 : new (x: T) => T +>x : T + +type T5 = { new (x: T): T }; +>T5 : new (x: T) => T +>x : T + +// Corrected repro from #51745 + +type Obj = { a: { b: { c: "123" } } }; +>Obj : { a: { b: { c: "123"; };}; } +>a : { b: { c: "123";}; } +>b : { c: "123"; } +>c : "123" + +type GetPath = +>GetPath : GetPath + + P extends readonly [] ? T : + P extends readonly [infer A extends keyof T, ...infer Rest] ? GetPath : + never; + +function set(obj: T, path: P, value: GetPath) {} +>set : (obj: T, path: P, value: GetPath) => void +>obj : T +>path : P +>value : GetPath + +declare let obj: Obj; +>obj : Obj + +declare let value: "123"; +>value : "123" + +set(obj, ['a', 'b', 'c'], value); +>set(obj, ['a', 'b', 'c'], value) : void +>set : (obj: T, path: P, value: GetPath) => void +>obj : Obj +>['a', 'b', 'c'] : ["a", "b", "c"] +>'a' : "a" +>'b' : "b" +>'c' : "c" +>value : "123" + diff --git a/tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterConstModifiers.ts b/tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterConstModifiers.ts new file mode 100644 index 0000000000000..8e074f5c53ab2 --- /dev/null +++ b/tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterConstModifiers.ts @@ -0,0 +1,72 @@ +// @strict: true + +declare function f1(x: T): T; + +const x11 = f1('a'); +const x12 = f1(['a', ['b', 'c']]); +const x13 = f1({ a: 1, b: "c", d: ["e", 2, true, { f: "g" }] }); + +declare function f2(x: T | undefined): T; + +const x21 = f2('a'); +const x22 = f2(['a', ['b', 'c']]); +const x23 = f2({ a: 1, b: "c", d: ["e", 2, true, { f: "g" }] }); + +declare function f3(x: T): T[]; + +const x31 = f3("hello"); +const x32 = f3("hello"); + +declare function f4(obj: [T, T]): T; + +const x41 = f4([[1, 'x'], [2, 'y']]); +const x42 = f4([{ a: 1, b: 'x' }, { a: 2, b: 'y' }]); + +declare function f5(obj: { x: T, y: T }): T; + +const x51 = f5({ x: [1, 'x'], y: [2, 'y'] }); +const x52 = f5({ x: { a: 1, b: 'x' }, y: { a: 2, b: 'y' } }); + +declare function f6(...args: T): T; + +const x61 = f6(1, 'b', { a: 1, b: 'x' }); + +class C1 { + constructor(x: T) {} + foo(x: U) { return x; } +} + +const c71 = new C1({ a: 1, b: "c", d: ["e", 2, true, { f: "g" }] }); +const c72 = c71.foo(['a', ['b', 'c']]); + +const fx1 = (x: T) => x; +const fx2 = (x: T) => x; + +interface I1 { x: T } // Error + +interface I2 { + f(x: T): T; +} + +type T1 = T; // Error + +type T2 = (x: T) => T; +type T3 = { (x: T): T }; +type T4 = new (x: T) => T; +type T5 = { new (x: T): T }; + +// Corrected repro from #51745 + +type Obj = { a: { b: { c: "123" } } }; + +type GetPath = + P extends readonly [] ? T : + P extends readonly [infer A extends keyof T, ...infer Rest] ? GetPath : + never; + +function set(obj: T, path: P, value: GetPath) {} + +declare let obj: Obj; +declare let value: "123"; + +set(obj, ['a', 'b', 'c'], value);