diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bc718e4c1d2..625f4d75eba 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2322,10 +2322,18 @@ namespace ts { if (objectFlags & ObjectFlags.Mapped) { Debug.assert(!!(type.flags & TypeFlags.Object)); - // const typeParameter = getTypeParameterFromMappedType(type); + + // TODO: does typeParameter have the same constraint or do we need to overwrite it somehow? + const typeParameter = getTypeParameterFromMappedType(type); // const constraintType = getConstraintTypeFromMappedType(type); - // const templateType = getTemplateTypeFromMappedType(type); - throw new Error("Mapped types not implemented"); + const typeParameterNode = createTypeParameterDeclarationFromType(typeParameter); + + const templateTypeNode = createTypeNode(getTemplateTypeFromMappedType(type)); + const readonlyToken = (type).declaration && (type).declaration.readonlyToken ? createToken(SyntaxKind.ReadonlyKeyword) : undefined; + const questionToken = (type).declaration && (type).declaration.questionToken ? createToken(SyntaxKind.QuestionToken) : undefined; + + // TODO: test. + return createMappedTypeNode(readonlyToken, typeParameterNode, questionToken, templateTypeNode); } if (objectFlags & ObjectFlags.Anonymous) { @@ -2334,6 +2342,7 @@ namespace ts { if (!type.symbol) { // Anonymous types without symbols are literals. // TODO: handle this case correctly. + // TODO: test. noop(); } @@ -2343,9 +2352,11 @@ namespace ts { // TODO: string or number literal here or above? if (type.flags & TypeFlags.Index) { + // TODO: implement and test. throw new Error("index not implemented"); } if (type.flags & TypeFlags.IndexedAccess) { + // TODO: implement and test. throw new Error("indexed access not implemented"); } @@ -2414,7 +2425,6 @@ namespace ts { } function createTypeLiteralNodeFromType(type: ObjectType) { - // TODO: do we need to do something for mapped types here??? const resolvedType = resolveStructuredTypeMembers(type); const newMembers = createTypeNodesFromResolvedType(resolvedType); return createTypeLiteralNode(newMembers); @@ -2446,21 +2456,29 @@ namespace ts { } const kind = oldDeclaration.kind; - const memberName = symbolToString(memberSymbol); + const memberName = getSynthesizedDeepClone(oldDeclaration.name); + const memberType = getTypeOfSymbol(memberSymbol); switch (kind) { case SyntaxKind.PropertySignature: const optional = !!oldDeclaration.questionToken; - const typeOfOldMember = getTypeOfSymbol(memberSymbol); typeElements.push(createPropertySignature( - createIdentifier(memberName) + memberName , optional ? createToken(SyntaxKind.QuestionToken) : undefined - , createTypeNode(typeOfOldMember) + , createTypeNode(memberType) , /*initializer*/undefined)); break; case SyntaxKind.MethodSignature: case SyntaxKind.CallSignature: case SyntaxKind.ConstructSignature: + const signatureType = getSignaturesOfSymbol(memberSymbol); + signatureType + createSignatureDeclaration + throw new Error("signature problems."); + // name ?: PropertyName; + // typeParameters ?: NodeArray; + // parameters: NodeArray; + // type ?: TypeNode; case SyntaxKind.IndexSignature: throw new Error("type literal constituent not implemented."); default: diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index bd46e208784..c9fef5b0263 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -255,6 +255,22 @@ namespace ts { : node; } + export function createSignatureDeclaration(kind: SyntaxKind, name: string | PropertyName | undefined, typeParameters: NodeArray | undefined, parameters: NodeArray, type: TypeNode | undefined): T { + const signatureDeclaration = createSynthesizedNode(kind) as T; + signatureDeclaration.name = asName(name); + signatureDeclaration.typeParameters = asNodeArray(typeParameters); + signatureDeclaration.type = type; + return signatureDeclaration; + } + + export function updateSignatureDeclaration(node: T, name: string | PropertyName | undefined, typeParameters: NodeArray | undefined, parameters: NodeArray, type: TypeNode | undefined): T { + return node.name !== name + || node.typeParameters !== typeParameters + || node.type !== type + ? updateNode(createSignatureDeclaration(node.kind, name, typeParameters, parameters, type), node) + : node; + } + export function createTypeReferenceNode(typeName: string | EntityName, typeArguments: NodeArray | undefined) { const typeReference = createSynthesizedNode(SyntaxKind.TypeReference) as TypeReferenceNode; @@ -321,6 +337,24 @@ namespace ts { : node; } + export function createMappedTypeNode(readonlyToken: ReadonlyToken | undefined, typeParameter: TypeParameterDeclaration, questionToken: QuestionToken | undefined, type: TypeNode | undefined): MappedTypeNode { + const mappedTypeNode = createSynthesizedNode(SyntaxKind.MappedType) as MappedTypeNode; + mappedTypeNode.readonlyToken = readonlyToken; + mappedTypeNode.typeParameter = typeParameter; + mappedTypeNode.questionToken = questionToken; + mappedTypeNode.type = type; + return mappedTypeNode; + } + + export function updateMappedTypeNode(node: MappedTypeNode, readonlyToken: ReadonlyToken | undefined, typeParameter: TypeParameterDeclaration, questionToken: QuestionToken | undefined, type: TypeNode | undefined): MappedTypeNode { + return node.readonlyToken !== readonlyToken + || node.typeParameter !== typeParameter + || node.questionToken !== questionToken + || node.type !== type + ? updateNode(createMappedTypeNode(readonlyToken, typeParameter, questionToken, type), node) + : node; + } + // Type Declarations export function createTypeParameterDeclaration(name: string | Identifier, constraint: TypeNode | undefined, defaultParameter: TypeNode | undefined) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 28c293ae533..7f6c56aa1ff 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -856,6 +856,7 @@ namespace ts { | SyntaxKind.BooleanKeyword | SyntaxKind.StringKeyword | SyntaxKind.SymbolKeyword + | SyntaxKind.ThisKeyword | SyntaxKind.VoidKeyword | SyntaxKind.UndefinedKeyword | SyntaxKind.NullKeyword diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index b4d65958f3c..47642291c2c 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3717,10 +3717,14 @@ namespace ts { return (kind >= SyntaxKind.FirstTypeNode && kind <= SyntaxKind.LastTypeNode) || kind === SyntaxKind.AnyKeyword || kind === SyntaxKind.NumberKeyword + || kind === SyntaxKind.ObjectKeyword || kind === SyntaxKind.BooleanKeyword || kind === SyntaxKind.StringKeyword || kind === SyntaxKind.SymbolKeyword + || kind === SyntaxKind.ThisKeyword || kind === SyntaxKind.VoidKeyword + || kind === SyntaxKind.UndefinedKeyword + || kind === SyntaxKind.NullKeyword || kind === SyntaxKind.NeverKeyword || kind === SyntaxKind.ExpressionWithTypeArguments; } diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index ba348a558bd..206e269f366 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -258,13 +258,23 @@ namespace ts { return updateComputedPropertyName(node, visitNode((node).expression, visitor, isExpression)); - // Signature elements + // Signatures and Signature Elements + case SyntaxKind.FunctionType: + case SyntaxKind.CallSignature: + case SyntaxKind.ConstructSignature: + case SyntaxKind.MethodSignature: + return updateSignatureDeclaration(node + , visitNode((node).name, visitor, isPropertyName) + , nodesVisitor((node).typeParameters, visitor, isTypeParameter) + , nodesVisitor((node).parameters, visitor, isParameter) + , visitNode((node).type, visitor, isTypeNode)); + case SyntaxKind.IndexSignature: return updateIndexSignatureDeclaration(node - , nodesVisitor((node).parameters, visitor) - , visitNode((node).type, visitor) - , nodesVisitor((node).decorators, visitor, isDecorator) - , nodesVisitor((node).modifiers, visitor, isModifier)); + , nodesVisitor((node).parameters, visitor) + , visitNode((node).type, visitor, isTypeNode) + , nodesVisitor((node).decorators, visitor, isDecorator) + , nodesVisitor((node).modifiers, visitor, isModifier)); case SyntaxKind.Parameter: return updateParameter(node, diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index d7638ffc033..08370480aa4 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -1673,7 +1673,7 @@ namespace FourSlash { // We get back a set of edits, but langSvc.editScript only accepts one at a time. Use this to keep track // of the incremental offset from each edit to the next. Assumption is that these edit ranges don't overlap let runningOffset = 0; - edits = edits.sort((a, b) => a.span.start - b.span.start); + edits = ts.stableSort(edits, (a, b) => a.span.start - b.span.start); // Get a snapshot of the content of the file so we can make sure any formatting edits didn't destroy non-whitespace characters const oldContent = this.getFileContent(fileName); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceComments.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceComments.ts new file mode 100644 index 00000000000..56d56a5a504 --- /dev/null +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceComments.ts @@ -0,0 +1,14 @@ +/// + +// @lib: es2017 + +//// /** interface prefix */ +//// interface /**interface name prefix */ I /**open-brace prefix*/{ +//// /** property prefix*/ x /**colon prefix*/: /**number prefix*/ number; +//// +//// /**close-brace prefix*/ } +//// class C implements I {[| |]} + +verify.rangeAfterCodeFix(` + x: number; +`); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceComputedPropertyNameWellKnownSymbols.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceComputedPropertyNameWellKnownSymbols.ts index 61b4267e7f0..31fc167bb97 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceComputedPropertyNameWellKnownSymbols.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceComputedPropertyNameWellKnownSymbols.ts @@ -17,7 +17,6 @@ //// [Symbol.toStringTag]: string; //// [Symbol.unscopables]: any; //// } -//// //// class C implements I {[| |]} verify.rangeAfterCodeFix(` diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceEmptyTypeLiteral.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceEmptyTypeLiteral.ts new file mode 100644 index 00000000000..98059017098 --- /dev/null +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceEmptyTypeLiteral.ts @@ -0,0 +1,13 @@ +/// + +//// interface I { +//// x: {}; +//// } +//// +//// class C implements I {[| +//// |]constructor() { } +//// } + +verify.rangeAfterCodeFix(` +x: {}; +`); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMemberOrdering.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMemberOrdering.ts new file mode 100644 index 00000000000..dbfd623be17 --- /dev/null +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMemberOrdering.ts @@ -0,0 +1,58 @@ +/// + +// @lib: es2017 + +//// /** asdf */ +//// interface I { +//// 1; +//// 2; +//// 3; +//// 4; +//// 5; +//// 6; +//// 7; +//// 8; +//// 9; +//// 10; +//// 11; +//// 12; +//// 13; +//// 14; +//// 15; +//// 16; +//// 17; +//// 18; +//// 19; +//// 20; +//// 21; +//// 22; +//// /** a nice safe prime */ +//// 23; +//// } +//// class C implements I {[| |]} + +verify.rangeAfterCodeFix(` + 1: any; + 2: any; + 3: any; + 4: any; + 5: any; + 6: any; + 7: any; + 8: any; + 9: any; + 10: any; + 11: any; + 12: any; + 13: any; + 14: any; + 15: any; + 16: any; + 17: any; + 18: any; + 19: any; + 20: any; + 21: any; + 22: any; + 23: any; +`);