diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f5aed2e1d5..bcf3a78f05 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4973,7 +4973,7 @@ namespace ts { const typeParameterNode = typeParameterToDeclarationWithConstraint(getTypeParameterFromMappedType(type), context, appropriateConstraintTypeNode); const nameTypeNode = type.declaration.nameType ? typeToTypeNodeHelper(getNameTypeFromMappedType(type)!, context) : undefined; const templateTypeNode = typeToTypeNodeHelper(removeMissingType(getTemplateTypeFromMappedType(type), !!(getMappedTypeModifiers(type) & MappedTypeModifiers.IncludeOptional)), context); - const mappedTypeNode = factory.createMappedTypeNode(readonlyToken, typeParameterNode, nameTypeNode, questionToken, templateTypeNode); + const mappedTypeNode = factory.createMappedTypeNode(readonlyToken, typeParameterNode, nameTypeNode, questionToken, templateTypeNode, /*members*/ undefined); context.approximateLength += 10; return setEmitFlags(mappedTypeNode, EmitFlags.SingleLine); } @@ -26811,6 +26811,10 @@ namespace ts { function checkComputedPropertyName(node: ComputedPropertyName): Type { const links = getNodeLinks(node.expression); if (!links.resolvedType) { + if ((isTypeLiteralNode(node.parent.parent) || isClassLike(node.parent.parent) || isInterfaceDeclaration(node.parent.parent)) + && isBinaryExpression(node.expression) && node.expression.operatorToken.kind === SyntaxKind.InKeyword) { + return links.resolvedType = errorType; + } links.resolvedType = checkExpression(node.expression); // The computed property name of a non-static class field within a loop must be stored in a block-scoped binding. // (It needs to be bound at class evaluation time.) @@ -34736,6 +34740,7 @@ namespace ts { } function checkMappedType(node: MappedTypeNode) { + checkGrammarMappedType(node); checkSourceElement(node.typeParameter); checkSourceElement(node.nameType); checkSourceElement(node.type); @@ -34755,6 +34760,12 @@ namespace ts { } } + function checkGrammarMappedType(node: MappedTypeNode) { + if (node.members?.length) { + return grammarErrorOnNode(node.members[0], Diagnostics.A_mapped_type_may_not_declare_properties_or_methods); + } + } + function checkThisType(node: ThisTypeNode) { getTypeFromThisTypeNode(node); } @@ -43417,6 +43428,11 @@ namespace ts { } function checkGrammarProperty(node: PropertyDeclaration | PropertySignature) { + if (isComputedPropertyName(node.name) && isBinaryExpression(node.name.expression) && node.name.expression.operatorToken.kind === SyntaxKind.InKeyword) { + return grammarErrorOnNode( + (node.parent as ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode).members[0], + Diagnostics.A_mapped_type_may_not_declare_properties_or_methods); + } if (isClassLike(node.parent)) { if (isStringLiteral(node.name) && node.name.text === "constructor") { return grammarErrorOnNode(node.name, Diagnostics.Classes_may_not_have_a_field_named_constructor); @@ -43436,7 +43452,7 @@ namespace ts { return grammarErrorOnNode(node.initializer, Diagnostics.An_interface_property_cannot_have_an_initializer); } } - else if (node.parent.kind === SyntaxKind.TypeLiteral) { + else if (isTypeLiteralNode(node.parent)) { if (checkGrammarForInvalidDynamicName(node.name, Diagnostics.A_computed_property_name_in_a_type_literal_must_refer_to_an_expression_whose_type_is_a_literal_type_or_a_unique_symbol_type)) { return true; } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index d21a923a87..a40439d012 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -5993,7 +5993,10 @@ "category": "Error", "code": 7060 }, - + "A mapped type may not declare properties or methods.": { + "category": "Error", + "code": 7061 + }, "You cannot rename this element.": { "category": "Error", diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts index b87d54e5da..963ce75441 100644 --- a/src/compiler/factory/nodeFactory.ts +++ b/src/compiler/factory/nodeFactory.ts @@ -2112,25 +2112,27 @@ namespace ts { } // @api - function createMappedTypeNode(readonlyToken: ReadonlyKeyword | PlusToken | MinusToken | undefined, typeParameter: TypeParameterDeclaration, nameType: TypeNode | undefined, questionToken: QuestionToken | PlusToken | MinusToken | undefined, type: TypeNode | undefined): MappedTypeNode { + function createMappedTypeNode(readonlyToken: ReadonlyKeyword | PlusToken | MinusToken | undefined, typeParameter: TypeParameterDeclaration, nameType: TypeNode | undefined, questionToken: QuestionToken | PlusToken | MinusToken | undefined, type: TypeNode | undefined, members: readonly TypeElement[] | undefined): MappedTypeNode { const node = createBaseNode(SyntaxKind.MappedType); node.readonlyToken = readonlyToken; node.typeParameter = typeParameter; node.nameType = nameType; node.questionToken = questionToken; node.type = type; + node.members = members && createNodeArray(members); node.transformFlags = TransformFlags.ContainsTypeScript; return node; } // @api - function updateMappedTypeNode(node: MappedTypeNode, readonlyToken: ReadonlyKeyword | PlusToken | MinusToken | undefined, typeParameter: TypeParameterDeclaration, nameType: TypeNode | undefined, questionToken: QuestionToken | PlusToken | MinusToken | undefined, type: TypeNode | undefined): MappedTypeNode { + function updateMappedTypeNode(node: MappedTypeNode, readonlyToken: ReadonlyKeyword | PlusToken | MinusToken | undefined, typeParameter: TypeParameterDeclaration, nameType: TypeNode | undefined, questionToken: QuestionToken | PlusToken | MinusToken | undefined, type: TypeNode | undefined, members: NodeArray | undefined): MappedTypeNode { return node.readonlyToken !== readonlyToken || node.typeParameter !== typeParameter || node.nameType !== nameType || node.questionToken !== questionToken || node.type !== type - ? update(createMappedTypeNode(readonlyToken, typeParameter, nameType, questionToken, type), node) + || node.members !== members + ? update(createMappedTypeNode(readonlyToken, typeParameter, nameType, questionToken, type, members), node) : node; } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 59eaaa2efb..1109d36a61 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -212,7 +212,8 @@ namespace ts { visitNode(cbNode, (node as MappedTypeNode).typeParameter) || visitNode(cbNode, (node as MappedTypeNode).nameType) || visitNode(cbNode, (node as MappedTypeNode).questionToken) || - visitNode(cbNode, (node as MappedTypeNode).type); + visitNode(cbNode, (node as MappedTypeNode).type) || + visitNodes(cbNode, cbNodes, (node as MappedTypeNode).members); case SyntaxKind.LiteralType: return visitNode(cbNode, (node as LiteralTypeNode).literal); case SyntaxKind.NamedTupleMember: @@ -3534,8 +3535,9 @@ namespace ts { } const type = parseTypeAnnotation(); parseSemicolon(); + const members = parseList(ParsingContext.TypeMembers, parseTypeMember); parseExpected(SyntaxKind.CloseBraceToken); - return finishNode(factory.createMappedTypeNode(readonlyToken, typeParameter, nameType, questionToken, type), pos); + return finishNode(factory.createMappedTypeNode(readonlyToken, typeParameter, nameType, questionToken, type, members), pos); } function parseTupleElementType() { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index beff69baaa..a5b143e06f 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1707,6 +1707,8 @@ namespace ts { readonly nameType?: TypeNode; readonly questionToken?: QuestionToken | PlusToken | MinusToken; readonly type?: TypeNode; + /** Used only to produce grammar errors */ + readonly members?: NodeArray; } export interface LiteralTypeNode extends TypeNode { @@ -7212,8 +7214,8 @@ namespace ts { updateTypeOperatorNode(node: TypeOperatorNode, type: TypeNode): TypeOperatorNode; createIndexedAccessTypeNode(objectType: TypeNode, indexType: TypeNode): IndexedAccessTypeNode; updateIndexedAccessTypeNode(node: IndexedAccessTypeNode, objectType: TypeNode, indexType: TypeNode): IndexedAccessTypeNode; - createMappedTypeNode(readonlyToken: ReadonlyKeyword | PlusToken | MinusToken | undefined, typeParameter: TypeParameterDeclaration, nameType: TypeNode | undefined, questionToken: QuestionToken | PlusToken | MinusToken | undefined, type: TypeNode | undefined): MappedTypeNode; - updateMappedTypeNode(node: MappedTypeNode, readonlyToken: ReadonlyKeyword | PlusToken | MinusToken | undefined, typeParameter: TypeParameterDeclaration, nameType: TypeNode | undefined, questionToken: QuestionToken | PlusToken | MinusToken | undefined, type: TypeNode | undefined): MappedTypeNode; + createMappedTypeNode(readonlyToken: ReadonlyKeyword | PlusToken | MinusToken | undefined, typeParameter: TypeParameterDeclaration, nameType: TypeNode | undefined, questionToken: QuestionToken | PlusToken | MinusToken | undefined, type: TypeNode | undefined, members: NodeArray | undefined): MappedTypeNode; + updateMappedTypeNode(node: MappedTypeNode, readonlyToken: ReadonlyKeyword | PlusToken | MinusToken | undefined, typeParameter: TypeParameterDeclaration, nameType: TypeNode | undefined, questionToken: QuestionToken | PlusToken | MinusToken | undefined, type: TypeNode | undefined, members: NodeArray | undefined): MappedTypeNode; createLiteralTypeNode(literal: LiteralTypeNode["literal"]): LiteralTypeNode; updateLiteralTypeNode(node: LiteralTypeNode, literal: LiteralTypeNode["literal"]): LiteralTypeNode; createTemplateLiteralType(head: TemplateHead, templateSpans: readonly TemplateLiteralTypeSpan[]): TemplateLiteralTypeNode; diff --git a/src/compiler/visitorPublic.ts b/src/compiler/visitorPublic.ts index 869d4604bd..5651ac043d 100644 --- a/src/compiler/visitorPublic.ts +++ b/src/compiler/visitorPublic.ts @@ -629,7 +629,8 @@ namespace ts { nodeVisitor(node.typeParameter, visitor, isTypeParameterDeclaration), nodeVisitor(node.nameType, visitor, isTypeNode), nodeVisitor(node.questionToken, tokenVisitor, isQuestionOrPlusOrMinusToken), - nodeVisitor(node.type, visitor, isTypeNode)); + nodeVisitor(node.type, visitor, isTypeNode), + nodesVisitor(node.members, visitor, isTypeElement)); case SyntaxKind.LiteralType: Debug.type(node); diff --git a/src/services/codefixes/convertLiteralTypeToMappedType.ts b/src/services/codefixes/convertLiteralTypeToMappedType.ts index 4801d8e5f2..edcbbe0c0c 100644 --- a/src/services/codefixes/convertLiteralTypeToMappedType.ts +++ b/src/services/codefixes/convertLiteralTypeToMappedType.ts @@ -47,7 +47,12 @@ namespace ts.codefix { } function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, { container, typeNode, constraint, name }: Info): void { - changes.replaceNode(sourceFile, container, factory.createMappedTypeNode(/*readonlyToken*/ undefined, - factory.createTypeParameterDeclaration(name, factory.createTypeReferenceNode(constraint)), /*nameType*/ undefined, /*questionToken*/ undefined, typeNode)); + changes.replaceNode(sourceFile, container, factory.createMappedTypeNode( + /*readonlyToken*/ undefined, + factory.createTypeParameterDeclaration(name, factory.createTypeReferenceNode(constraint)), + /*nameType*/ undefined, + /*questionToken*/ undefined, + typeNode, + /*members*/ undefined)); } } diff --git a/src/services/codefixes/convertToMappedObjectType.ts b/src/services/codefixes/convertToMappedObjectType.ts index 76da5233a5..dc6c2f5cd5 100644 --- a/src/services/codefixes/convertToMappedObjectType.ts +++ b/src/services/codefixes/convertToMappedObjectType.ts @@ -46,7 +46,8 @@ namespace ts.codefix { mappedTypeParameter, /*nameType*/ undefined, indexSignature.questionToken, - indexSignature.type); + indexSignature.type, + /*members*/ undefined); const intersectionType = factory.createIntersectionTypeNode([ ...getAllSuperTypeNodes(container), mappedIntersectionType, diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 349e025e58..116be4316f 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -973,6 +973,8 @@ declare namespace ts { readonly nameType?: TypeNode; readonly questionToken?: QuestionToken | PlusToken | MinusToken; readonly type?: TypeNode; + /** Used only to produce grammar errors */ + readonly members?: NodeArray; } export interface LiteralTypeNode extends TypeNode { readonly kind: SyntaxKind.LiteralType; @@ -3420,8 +3422,8 @@ declare namespace ts { updateTypeOperatorNode(node: TypeOperatorNode, type: TypeNode): TypeOperatorNode; createIndexedAccessTypeNode(objectType: TypeNode, indexType: TypeNode): IndexedAccessTypeNode; updateIndexedAccessTypeNode(node: IndexedAccessTypeNode, objectType: TypeNode, indexType: TypeNode): IndexedAccessTypeNode; - createMappedTypeNode(readonlyToken: ReadonlyKeyword | PlusToken | MinusToken | undefined, typeParameter: TypeParameterDeclaration, nameType: TypeNode | undefined, questionToken: QuestionToken | PlusToken | MinusToken | undefined, type: TypeNode | undefined): MappedTypeNode; - updateMappedTypeNode(node: MappedTypeNode, readonlyToken: ReadonlyKeyword | PlusToken | MinusToken | undefined, typeParameter: TypeParameterDeclaration, nameType: TypeNode | undefined, questionToken: QuestionToken | PlusToken | MinusToken | undefined, type: TypeNode | undefined): MappedTypeNode; + createMappedTypeNode(readonlyToken: ReadonlyKeyword | PlusToken | MinusToken | undefined, typeParameter: TypeParameterDeclaration, nameType: TypeNode | undefined, questionToken: QuestionToken | PlusToken | MinusToken | undefined, type: TypeNode | undefined, members: NodeArray | undefined): MappedTypeNode; + updateMappedTypeNode(node: MappedTypeNode, readonlyToken: ReadonlyKeyword | PlusToken | MinusToken | undefined, typeParameter: TypeParameterDeclaration, nameType: TypeNode | undefined, questionToken: QuestionToken | PlusToken | MinusToken | undefined, type: TypeNode | undefined, members: NodeArray | undefined): MappedTypeNode; createLiteralTypeNode(literal: LiteralTypeNode["literal"]): LiteralTypeNode; updateLiteralTypeNode(node: LiteralTypeNode, literal: LiteralTypeNode["literal"]): LiteralTypeNode; createTemplateLiteralType(head: TemplateHead, templateSpans: readonly TemplateLiteralTypeSpan[]): TemplateLiteralTypeNode; @@ -10779,9 +10781,9 @@ declare namespace ts { /** @deprecated Use `factory.updateIndexedAccessTypeNode` or the factory supplied by your transformation context instead. */ const updateIndexedAccessTypeNode: (node: IndexedAccessTypeNode, objectType: TypeNode, indexType: TypeNode) => IndexedAccessTypeNode; /** @deprecated Use `factory.createMappedTypeNode` or the factory supplied by your transformation context instead. */ - const createMappedTypeNode: (readonlyToken: ReadonlyKeyword | PlusToken | MinusToken | undefined, typeParameter: TypeParameterDeclaration, nameType: TypeNode | undefined, questionToken: QuestionToken | PlusToken | MinusToken | undefined, type: TypeNode | undefined) => MappedTypeNode; + const createMappedTypeNode: (readonlyToken: ReadonlyKeyword | PlusToken | MinusToken | undefined, typeParameter: TypeParameterDeclaration, nameType: TypeNode | undefined, questionToken: QuestionToken | PlusToken | MinusToken | undefined, type: TypeNode | undefined, members: NodeArray | undefined) => MappedTypeNode; /** @deprecated Use `factory.updateMappedTypeNode` or the factory supplied by your transformation context instead. */ - const updateMappedTypeNode: (node: MappedTypeNode, readonlyToken: ReadonlyKeyword | PlusToken | MinusToken | undefined, typeParameter: TypeParameterDeclaration, nameType: TypeNode | undefined, questionToken: QuestionToken | PlusToken | MinusToken | undefined, type: TypeNode | undefined) => MappedTypeNode; + const updateMappedTypeNode: (node: MappedTypeNode, readonlyToken: ReadonlyKeyword | PlusToken | MinusToken | undefined, typeParameter: TypeParameterDeclaration, nameType: TypeNode | undefined, questionToken: QuestionToken | PlusToken | MinusToken | undefined, type: TypeNode | undefined, members: NodeArray | undefined) => MappedTypeNode; /** @deprecated Use `factory.createLiteralTypeNode` or the factory supplied by your transformation context instead. */ const createLiteralTypeNode: (literal: LiteralExpression | BooleanLiteral | PrefixUnaryExpression | NullLiteral) => LiteralTypeNode; /** @deprecated Use `factory.updateLiteralTypeNode` or the factory supplied by your transformation context instead. */ diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 2a2c398553..56d764c664 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -973,6 +973,8 @@ declare namespace ts { readonly nameType?: TypeNode; readonly questionToken?: QuestionToken | PlusToken | MinusToken; readonly type?: TypeNode; + /** Used only to produce grammar errors */ + readonly members?: NodeArray; } export interface LiteralTypeNode extends TypeNode { readonly kind: SyntaxKind.LiteralType; @@ -3420,8 +3422,8 @@ declare namespace ts { updateTypeOperatorNode(node: TypeOperatorNode, type: TypeNode): TypeOperatorNode; createIndexedAccessTypeNode(objectType: TypeNode, indexType: TypeNode): IndexedAccessTypeNode; updateIndexedAccessTypeNode(node: IndexedAccessTypeNode, objectType: TypeNode, indexType: TypeNode): IndexedAccessTypeNode; - createMappedTypeNode(readonlyToken: ReadonlyKeyword | PlusToken | MinusToken | undefined, typeParameter: TypeParameterDeclaration, nameType: TypeNode | undefined, questionToken: QuestionToken | PlusToken | MinusToken | undefined, type: TypeNode | undefined): MappedTypeNode; - updateMappedTypeNode(node: MappedTypeNode, readonlyToken: ReadonlyKeyword | PlusToken | MinusToken | undefined, typeParameter: TypeParameterDeclaration, nameType: TypeNode | undefined, questionToken: QuestionToken | PlusToken | MinusToken | undefined, type: TypeNode | undefined): MappedTypeNode; + createMappedTypeNode(readonlyToken: ReadonlyKeyword | PlusToken | MinusToken | undefined, typeParameter: TypeParameterDeclaration, nameType: TypeNode | undefined, questionToken: QuestionToken | PlusToken | MinusToken | undefined, type: TypeNode | undefined, members: NodeArray | undefined): MappedTypeNode; + updateMappedTypeNode(node: MappedTypeNode, readonlyToken: ReadonlyKeyword | PlusToken | MinusToken | undefined, typeParameter: TypeParameterDeclaration, nameType: TypeNode | undefined, questionToken: QuestionToken | PlusToken | MinusToken | undefined, type: TypeNode | undefined, members: NodeArray | undefined): MappedTypeNode; createLiteralTypeNode(literal: LiteralTypeNode["literal"]): LiteralTypeNode; updateLiteralTypeNode(node: LiteralTypeNode, literal: LiteralTypeNode["literal"]): LiteralTypeNode; createTemplateLiteralType(head: TemplateHead, templateSpans: readonly TemplateLiteralTypeSpan[]): TemplateLiteralTypeNode; @@ -6978,9 +6980,9 @@ declare namespace ts { /** @deprecated Use `factory.updateIndexedAccessTypeNode` or the factory supplied by your transformation context instead. */ const updateIndexedAccessTypeNode: (node: IndexedAccessTypeNode, objectType: TypeNode, indexType: TypeNode) => IndexedAccessTypeNode; /** @deprecated Use `factory.createMappedTypeNode` or the factory supplied by your transformation context instead. */ - const createMappedTypeNode: (readonlyToken: ReadonlyKeyword | PlusToken | MinusToken | undefined, typeParameter: TypeParameterDeclaration, nameType: TypeNode | undefined, questionToken: QuestionToken | PlusToken | MinusToken | undefined, type: TypeNode | undefined) => MappedTypeNode; + const createMappedTypeNode: (readonlyToken: ReadonlyKeyword | PlusToken | MinusToken | undefined, typeParameter: TypeParameterDeclaration, nameType: TypeNode | undefined, questionToken: QuestionToken | PlusToken | MinusToken | undefined, type: TypeNode | undefined, members: NodeArray | undefined) => MappedTypeNode; /** @deprecated Use `factory.updateMappedTypeNode` or the factory supplied by your transformation context instead. */ - const updateMappedTypeNode: (node: MappedTypeNode, readonlyToken: ReadonlyKeyword | PlusToken | MinusToken | undefined, typeParameter: TypeParameterDeclaration, nameType: TypeNode | undefined, questionToken: QuestionToken | PlusToken | MinusToken | undefined, type: TypeNode | undefined) => MappedTypeNode; + const updateMappedTypeNode: (node: MappedTypeNode, readonlyToken: ReadonlyKeyword | PlusToken | MinusToken | undefined, typeParameter: TypeParameterDeclaration, nameType: TypeNode | undefined, questionToken: QuestionToken | PlusToken | MinusToken | undefined, type: TypeNode | undefined, members: NodeArray | undefined) => MappedTypeNode; /** @deprecated Use `factory.createLiteralTypeNode` or the factory supplied by your transformation context instead. */ const createLiteralTypeNode: (literal: LiteralExpression | BooleanLiteral | PrefixUnaryExpression | NullLiteral) => LiteralTypeNode; /** @deprecated Use `factory.updateLiteralTypeNode` or the factory supplied by your transformation context instead. */ diff --git a/tests/baselines/reference/mappedTypeProperties.errors.txt b/tests/baselines/reference/mappedTypeProperties.errors.txt new file mode 100644 index 0000000000..9f64d7e856 --- /dev/null +++ b/tests/baselines/reference/mappedTypeProperties.errors.txt @@ -0,0 +1,87 @@ +tests/cases/conformance/types/mapped/mappedTypeProperties.ts(3,5): error TS7061: A mapped type may not declare properties or methods. +tests/cases/conformance/types/mapped/mappedTypeProperties.ts(9,5): error TS7061: A mapped type may not declare properties or methods. +tests/cases/conformance/types/mapped/mappedTypeProperties.ts(14,5): error TS7061: A mapped type may not declare properties or methods. +tests/cases/conformance/types/mapped/mappedTypeProperties.ts(18,5): error TS7061: A mapped type may not declare properties or methods. +tests/cases/conformance/types/mapped/mappedTypeProperties.ts(23,5): error TS7061: A mapped type may not declare properties or methods. +tests/cases/conformance/types/mapped/mappedTypeProperties.ts(27,5): error TS7061: A mapped type may not declare properties or methods. +tests/cases/conformance/types/mapped/mappedTypeProperties.ts(31,5): error TS7061: A mapped type may not declare properties or methods. +tests/cases/conformance/types/mapped/mappedTypeProperties.ts(34,5): error TS7061: A mapped type may not declare properties or methods. +tests/cases/conformance/types/mapped/mappedTypeProperties.ts(37,5): error TS7061: A mapped type may not declare properties or methods. +tests/cases/conformance/types/mapped/mappedTypeProperties.ts(40,5): error TS1166: A computed property name in a class property declaration must have a simple literal type or a 'unique symbol' type. +tests/cases/conformance/types/mapped/mappedTypeProperties.ts(40,6): error TS2304: Cannot find name 'P'. +tests/cases/conformance/types/mapped/mappedTypeProperties.ts(40,6): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. +tests/cases/conformance/types/mapped/mappedTypeProperties.ts(40,11): error TS2361: The right-hand side of an 'in' expression must not be a primitive. +tests/cases/conformance/types/mapped/mappedTypeProperties.ts(40,17): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. + + +==== tests/cases/conformance/types/mapped/mappedTypeProperties.ts (14 errors) ==== + export type PlaceType = 'openSky' | 'roofed' | 'garage' + type Before = { + model: 'hour' | 'day'; + ~~~~~ +!!! error TS7061: A mapped type may not declare properties or methods. + [placeType in PlaceType]: void; + } + + type After = { + [placeType in PlaceType]: void; + model: 'hour' | 'day' + ~~~~~ +!!! error TS7061: A mapped type may not declare properties or methods. + } + + type AfterQuestion = { + [placeType in PlaceType]?: void; + model: 'hour' | 'day'; + ~~~~~ +!!! error TS7061: A mapped type may not declare properties or methods. + } + type AfterMethod = { + [placeType in PlaceType]?: void; + model(duration: number): 'hour' | 'day'; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS7061: A mapped type may not declare properties or methods. + } + + type AfterImplicit = { + [placeType in PlaceType] + model: 'hour' | 'day'; + ~~~~~ +!!! error TS7061: A mapped type may not declare properties or methods. + } + type AfterImplicitQ = { + [placeType in PlaceType]? + model: 'hour' | 'day' + ~~~~~ +!!! error TS7061: A mapped type may not declare properties or methods. + } + + interface I { + [P in PlaceType]: any + ~~~~~~~~~~~~~~~~ +!!! error TS7061: A mapped type may not declare properties or methods. + } + class C { + [P in PlaceType]: any + ~~~~~~~~~~~~~~~~ +!!! error TS7061: A mapped type may not declare properties or methods. + } + const D = class { + [P in PlaceType]: any + ~~~~~~~~~~~~~~~~ +!!! error TS7061: A mapped type may not declare properties or methods. + } + const E = class { + [P in 'a' | 'b']: any + ~~~~~~~~~~~~~~~~ +!!! error TS1166: A computed property name in a class property declaration must have a simple literal type or a 'unique symbol' type. + ~ +!!! error TS2304: Cannot find name 'P'. + ~~~~~~~~ +!!! error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. + ~~~ +!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. + ~~~ +!!! error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. + } + \ No newline at end of file diff --git a/tests/baselines/reference/mappedTypeProperties.js b/tests/baselines/reference/mappedTypeProperties.js new file mode 100644 index 0000000000..fce8364b0c --- /dev/null +++ b/tests/baselines/reference/mappedTypeProperties.js @@ -0,0 +1,72 @@ +//// [mappedTypeProperties.ts] +export type PlaceType = 'openSky' | 'roofed' | 'garage' +type Before = { + model: 'hour' | 'day'; + [placeType in PlaceType]: void; +} + +type After = { + [placeType in PlaceType]: void; + model: 'hour' | 'day' +} + +type AfterQuestion = { + [placeType in PlaceType]?: void; + model: 'hour' | 'day'; +} +type AfterMethod = { + [placeType in PlaceType]?: void; + model(duration: number): 'hour' | 'day'; +} + +type AfterImplicit = { + [placeType in PlaceType] + model: 'hour' | 'day'; +} +type AfterImplicitQ = { + [placeType in PlaceType]? + model: 'hour' | 'day' +} + +interface I { + [P in PlaceType]: any +} +class C { + [P in PlaceType]: any +} +const D = class { + [P in PlaceType]: any +} +const E = class { + [P in 'a' | 'b']: any +} + + +//// [mappedTypeProperties.js] +"use strict"; +var _a, _b; +exports.__esModule = true; +var C = /** @class */ (function () { + function C() { + } + return C; +}()); +P in PlaceType; +var D = (_a = /** @class */ (function () { + function class_1() { + } + return class_1; + }()), + P in PlaceType, + _a); +var E = (_b = /** @class */ (function () { + function class_2() { + } + return class_2; + }()), + P in 'a' | 'b', + _b); + + +//// [mappedTypeProperties.d.ts] +export declare type PlaceType = 'openSky' | 'roofed' | 'garage'; diff --git a/tests/baselines/reference/mappedTypeProperties.symbols b/tests/baselines/reference/mappedTypeProperties.symbols new file mode 100644 index 0000000000..a38d36c240 --- /dev/null +++ b/tests/baselines/reference/mappedTypeProperties.symbols @@ -0,0 +1,93 @@ +=== tests/cases/conformance/types/mapped/mappedTypeProperties.ts === +export type PlaceType = 'openSky' | 'roofed' | 'garage' +>PlaceType : Symbol(PlaceType, Decl(mappedTypeProperties.ts, 0, 0)) + +type Before = { +>Before : Symbol(Before, Decl(mappedTypeProperties.ts, 0, 55)) + + model: 'hour' | 'day'; +>model : Symbol(model, Decl(mappedTypeProperties.ts, 1, 15)) + + [placeType in PlaceType]: void; +>[placeType in PlaceType] : Symbol([placeType in PlaceType], Decl(mappedTypeProperties.ts, 2, 26)) +} + +type After = { +>After : Symbol(After, Decl(mappedTypeProperties.ts, 4, 1)) + + [placeType in PlaceType]: void; +>placeType : Symbol(placeType, Decl(mappedTypeProperties.ts, 7, 5)) +>PlaceType : Symbol(PlaceType, Decl(mappedTypeProperties.ts, 0, 0)) + + model: 'hour' | 'day' +>model : Symbol(model, Decl(mappedTypeProperties.ts, 7, 35)) +} + +type AfterQuestion = { +>AfterQuestion : Symbol(AfterQuestion, Decl(mappedTypeProperties.ts, 9, 1)) + + [placeType in PlaceType]?: void; +>placeType : Symbol(placeType, Decl(mappedTypeProperties.ts, 12, 5)) +>PlaceType : Symbol(PlaceType, Decl(mappedTypeProperties.ts, 0, 0)) + + model: 'hour' | 'day'; +>model : Symbol(model, Decl(mappedTypeProperties.ts, 12, 36)) +} +type AfterMethod = { +>AfterMethod : Symbol(AfterMethod, Decl(mappedTypeProperties.ts, 14, 1)) + + [placeType in PlaceType]?: void; +>placeType : Symbol(placeType, Decl(mappedTypeProperties.ts, 16, 5)) +>PlaceType : Symbol(PlaceType, Decl(mappedTypeProperties.ts, 0, 0)) + + model(duration: number): 'hour' | 'day'; +>model : Symbol(model, Decl(mappedTypeProperties.ts, 16, 36)) +>duration : Symbol(duration, Decl(mappedTypeProperties.ts, 17, 10)) +} + +type AfterImplicit = { +>AfterImplicit : Symbol(AfterImplicit, Decl(mappedTypeProperties.ts, 18, 1)) + + [placeType in PlaceType] +>placeType : Symbol(placeType, Decl(mappedTypeProperties.ts, 21, 5)) +>PlaceType : Symbol(PlaceType, Decl(mappedTypeProperties.ts, 0, 0)) + + model: 'hour' | 'day'; +>model : Symbol(model, Decl(mappedTypeProperties.ts, 21, 28)) +} +type AfterImplicitQ = { +>AfterImplicitQ : Symbol(AfterImplicitQ, Decl(mappedTypeProperties.ts, 23, 1)) + + [placeType in PlaceType]? +>placeType : Symbol(placeType, Decl(mappedTypeProperties.ts, 25, 5)) +>PlaceType : Symbol(PlaceType, Decl(mappedTypeProperties.ts, 0, 0)) + + model: 'hour' | 'day' +>model : Symbol(model, Decl(mappedTypeProperties.ts, 25, 29)) +} + +interface I { +>I : Symbol(I, Decl(mappedTypeProperties.ts, 27, 1)) + + [P in PlaceType]: any +>[P in PlaceType] : Symbol(I[P in PlaceType], Decl(mappedTypeProperties.ts, 29, 13)) +} +class C { +>C : Symbol(C, Decl(mappedTypeProperties.ts, 31, 1)) + + [P in PlaceType]: any +>[P in PlaceType] : Symbol(C[P in PlaceType], Decl(mappedTypeProperties.ts, 32, 9)) +} +const D = class { +>D : Symbol(D, Decl(mappedTypeProperties.ts, 35, 5)) + + [P in PlaceType]: any +>[P in PlaceType] : Symbol(D[P in PlaceType], Decl(mappedTypeProperties.ts, 35, 17)) +} +const E = class { +>E : Symbol(E, Decl(mappedTypeProperties.ts, 38, 5)) + + [P in 'a' | 'b']: any +>[P in 'a' | 'b'] : Symbol(E[P in 'a' | 'b'], Decl(mappedTypeProperties.ts, 38, 17)) +} + diff --git a/tests/baselines/reference/mappedTypeProperties.types b/tests/baselines/reference/mappedTypeProperties.types new file mode 100644 index 0000000000..bb40340eac --- /dev/null +++ b/tests/baselines/reference/mappedTypeProperties.types @@ -0,0 +1,95 @@ +=== tests/cases/conformance/types/mapped/mappedTypeProperties.ts === +export type PlaceType = 'openSky' | 'roofed' | 'garage' +>PlaceType : PlaceType + +type Before = { +>Before : Before + + model: 'hour' | 'day'; +>model : "hour" | "day" + + [placeType in PlaceType]: void; +>[placeType in PlaceType] : void +>placeType in PlaceType : boolean +>placeType : any +>PlaceType : any +} + +type After = { +>After : After + + [placeType in PlaceType]: void; + model: 'hour' | 'day' +>model : "hour" | "day" +} + +type AfterQuestion = { +>AfterQuestion : AfterQuestion + + [placeType in PlaceType]?: void; + model: 'hour' | 'day'; +>model : "hour" | "day" +} +type AfterMethod = { +>AfterMethod : AfterMethod + + [placeType in PlaceType]?: void; + model(duration: number): 'hour' | 'day'; +>model : (duration: number) => 'hour' | 'day' +>duration : number +} + +type AfterImplicit = { +>AfterImplicit : AfterImplicit + + [placeType in PlaceType] + model: 'hour' | 'day'; +>model : "hour" | "day" +} +type AfterImplicitQ = { +>AfterImplicitQ : AfterImplicitQ + + [placeType in PlaceType]? + model: 'hour' | 'day' +>model : "hour" | "day" +} + +interface I { + [P in PlaceType]: any +>[P in PlaceType] : any +>P in PlaceType : boolean +>P : any +>PlaceType : any +} +class C { +>C : C + + [P in PlaceType]: any +>[P in PlaceType] : any +>P in PlaceType : boolean +>P : any +>PlaceType : any +} +const D = class { +>D : typeof D +>class { [P in PlaceType]: any} : typeof D + + [P in PlaceType]: any +>[P in PlaceType] : any +>P in PlaceType : boolean +>P : any +>PlaceType : any +} +const E = class { +>E : typeof E +>class { [P in 'a' | 'b']: any} : typeof E + + [P in 'a' | 'b']: any +>[P in 'a' | 'b'] : any +>P in 'a' | 'b' : number +>P in 'a' : boolean +>P : any +>'a' : "a" +>'b' : "b" +} + diff --git a/tests/baselines/reference/smartSelection_complex.baseline b/tests/baselines/reference/smartSelection_complex.baseline index c16b1912ae..2fc5c68c44 100644 --- a/tests/baselines/reference/smartSelection_complex.baseline +++ b/tests/baselines/reference/smartSelection_complex.baseline @@ -4,6 +4,7 @@ type X = IsExactlyAny

extends true ? T : ({ [K in keyof P]: IsExactlyAn P[K] K extends keyof T ? T[K] : P[K] IsExactlyAny extends true ? K extends keyof T ? T[K] : P[K] : P[K] + IsExactlyAny extends true ? K extends keyof T ? T[K] : P[K] : P[K]; [K in keyof P]: IsExactlyAny extends true ? K extends keyof T ? T[K] : P[K] : P[K]; { [K in keyof P]: IsExactlyAny extends true ? K extends keyof T ? T[K] : P[K] : P[K]; } { [K in keyof P]: IsExactlyAny extends true ? K extends keyof T ? T[K] : P[K] : P[K]; } & Pick> diff --git a/tests/cases/conformance/types/mapped/mappedTypeProperties.ts b/tests/cases/conformance/types/mapped/mappedTypeProperties.ts new file mode 100644 index 0000000000..407e0eaf56 --- /dev/null +++ b/tests/cases/conformance/types/mapped/mappedTypeProperties.ts @@ -0,0 +1,42 @@ +// @declaration: true +export type PlaceType = 'openSky' | 'roofed' | 'garage' +type Before = { + model: 'hour' | 'day'; + [placeType in PlaceType]: void; +} + +type After = { + [placeType in PlaceType]: void; + model: 'hour' | 'day' +} + +type AfterQuestion = { + [placeType in PlaceType]?: void; + model: 'hour' | 'day'; +} +type AfterMethod = { + [placeType in PlaceType]?: void; + model(duration: number): 'hour' | 'day'; +} + +type AfterImplicit = { + [placeType in PlaceType] + model: 'hour' | 'day'; +} +type AfterImplicitQ = { + [placeType in PlaceType]? + model: 'hour' | 'day' +} + +interface I { + [P in PlaceType]: any +} +class C { + [P in PlaceType]: any +} +const D = class { + [P in PlaceType]: any +} +const E = class { + [P in 'a' | 'b']: any +}