diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ca105c41139..e9cea047eaf 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -108,6 +108,7 @@ namespace ts { getNonNullableType, createTypeNode, createTypeParameterDeclarationFromType, + createIndexSignatureFromIndexInfo, getSymbolsInScope: (location, meaning) => { location = getParseTreeNode(location); return location ? getSymbolsInScope(location, meaning) : []; @@ -2190,16 +2191,13 @@ namespace ts { return result; } - function createTypeParameterDeclarationFromType(type: Type): TypeParameterDeclaration { + function createTypeParameterDeclarationFromType(type: TypeParameter): TypeParameterDeclaration { if (!(type && type.symbol && type.flags & TypeFlags.TypeParameter)) { return undefined; } - const constraint = createTypeNode(getConstraintFromTypeParameter(type)) as TypeNode; - const defaultParameter = createTypeNode(getDefaultFromTypeParameter(type)) as TypeNode; - if (!type.symbol) { - return undefined; - } + const constraint = createTypeNode(getConstraintFromTypeParameter(type)) as TypeNode; + const defaultParameter = createTypeNode(getDefaultFromTypeParameter(type)) as TypeNode; const name = symbolToString(type.symbol); return createTypeParameterDeclaration(name, constraint, defaultParameter); @@ -2212,8 +2210,10 @@ namespace ts { let checkAlias = true; let result = createTypeNodeWorker(type); - (result).__type_source = type; - (result).__type_source_str = typeToString(type); + if (result) { + (result).__type_source = type; + (result).__type_source_str = typeToString(type); + } return result; function createTypeNodeWorker(type: Type): TypeNode { @@ -2299,7 +2299,16 @@ namespace ts { return createTypeReferenceNode(name, /*typeParameters*/undefined); } if (type.flags & TypeFlags.TypeParameter) { - throw new Error("Type Parameter declarations only handled in other worker."); + const constraint = createTypeNode(getConstraintFromTypeParameter(type)) as TypeNode; + const defaultParameter = createTypeNode(getDefaultFromTypeParameter(type)) as TypeNode; + if(constraint || defaultParameter) { + // Type parameters in type position can't have constraints or defaults. + encounteredError = true; + return undefined; + } + // TODO: get qualified name when necessary instead of string. + const name = symbolToString(type.symbol); + return createTypeReferenceNode(name, /*typeArguments*/ undefined); } // accessible type aliasSymbol @@ -2312,7 +2321,7 @@ namespace ts { checkAlias = false; if (type.flags & TypeFlags.Union) { - return createUnionOrIntersectionTypeNode(SyntaxKind.UnionType, mapToTypeNodeArray((type as UnionType).types)); + return createUnionOrIntersectionTypeNode(SyntaxKind.UnionType, mapToTypeNodeArray(formatUnionTypes((type).types))); } if (type.flags & TypeFlags.Intersection) { return createUnionOrIntersectionTypeNode(SyntaxKind.IntersectionType, mapToTypeNodeArray((type as UnionType).types)); @@ -2467,34 +2476,29 @@ namespace ts { }); return typeElements.length ? typeElements : undefined; } - - function createIndexSignatureFromIndexInfo(indexInfo: IndexInfo, kind: IndexKind) { - const stringTypeNode = createKeywordTypeNode(kind === IndexKind.String ? SyntaxKind.StringKeyword : SyntaxKind.NumberKeyword); - - const name = (indexInfo.declaration && indexInfo.declaration.name && getTextOfPropertyName(indexInfo.declaration.name)) || "x"; - const indexingParameter = createParameter( - /*decorators*/ undefined - , /*modifiers*/ undefined - , /*dotDotDotToken*/ undefined - , name - , /*questionToken*/ undefined - , stringTypeNode - , /*initializer*/ undefined); - const typeNode = createTypeNode(indexInfo.type); - return createIndexSignatureDeclaration( - [indexingParameter] - , typeNode - , /*decoarators*/ undefined - , indexInfo.isReadonly ? [createToken(SyntaxKind.ReadonlyKeyword)] : undefined); - } - - // /** Note that mapToTypeNodeArray(undefined) === undefined. */ - // function mapToTypeParameterArray(types: Type[]): NodeArray { - // return asNodeArray(types && types.map(createTypeParameterDeclarationFromType) as TypeNode[]); - // } } } + function createIndexSignatureFromIndexInfo(indexInfo: IndexInfo, kind: IndexKind): IndexSignatureDeclaration { + const indexerTypeNode = createKeywordTypeNode(kind === IndexKind.String ? SyntaxKind.StringKeyword : SyntaxKind.NumberKeyword); + + const name = getNameFromIndexInfo(indexInfo); + const indexingParameter = createParameter( + /*decorators*/ undefined + , /*modifiers*/ undefined + , /*dotDotDotToken*/ undefined + , name + , /*questionToken*/ undefined + , indexerTypeNode + , /*initializer*/ undefined); + const typeNode = createTypeNode(indexInfo.type); + return createIndexSignatureDeclaration( + [indexingParameter] + , typeNode + , /*decorators*/ undefined + , indexInfo.isReadonly ? [createToken(SyntaxKind.ReadonlyKeyword)] : undefined); + } + function typePredicateToString(typePredicate: TypePredicate, enclosingDeclaration?: Declaration, flags?: TypeFormatFlags): string { const writer = getSingleLineStringWriter(); getSymbolDisplayBuilder().buildTypePredicateDisplay(typePredicate, writer, enclosingDeclaration, flags); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index bccf0f1c4f9..28c293ae533 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2471,6 +2471,8 @@ namespace ts { createTypeNode(type: Type): TypeNode; /** Note that the resulting type node cannot be checked. */ createTypeParameterDeclarationFromType(type: Type): TypeParameterDeclaration; + /** Note that the resulting type node cannot be checked. */ + createIndexSignatureFromIndexInfo(indexInfo: IndexInfo, kind: IndexKind): IndexSignatureDeclaration getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[]; getSymbolAtLocation(node: Node): Symbol; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 030b66813a5..b4d65958f3c 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -524,6 +524,10 @@ namespace ts { export function declarationNameToString(name: DeclarationName) { return getFullWidth(name) === 0 ? "(Missing)" : getTextOfNode(name); } + + export function getNameFromIndexInfo(info: IndexInfo) { + return info.declaration ? declarationNameToString(info.declaration.parameters[0].name) : undefined; + } export function getTextOfPropertyName(name: PropertyName): string { switch (name.kind) { diff --git a/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts b/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts index 220cea55d6f..33b4a10dffc 100644 --- a/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts +++ b/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts @@ -53,18 +53,7 @@ namespace ts.codefix { if (!indexInfoOfKind) { return undefined; } - const typeNode = checker.createTypeNode(indexInfoOfKind.type); - const newIndexSignatureDeclaration = createIndexSignatureDeclaration( - [createParameter( - /*decorators*/undefined - , /*modifiers*/ undefined - , /*dotDotDotToken*/ undefined - , getNameFromIndexInfo(indexInfoOfKind) - , /*questionToken*/ undefined - , kind === IndexKind.String ? createKeywordTypeNode(SyntaxKind.StringKeyword) : createKeywordTypeNode(SyntaxKind.NumberKeyword))] - , typeNode - , /*decorators*/undefined - , /*modifiers*/ undefined); + const newIndexSignatureDeclaration = checker.createIndexSignatureFromIndexInfo(indexInfoOfKind, kind); newNodes.push(newIndexSignatureDeclaration); } diff --git a/src/services/codefixes/helpers.ts b/src/services/codefixes/helpers.ts index 2db14f81df1..c51687962b3 100644 --- a/src/services/codefixes/helpers.ts +++ b/src/services/codefixes/helpers.ts @@ -52,10 +52,10 @@ namespace ts.codefix { const declaration = declarations[0] as Declaration; // TODO: get name as identifier or computer property name, etc. - const name = declaration.name ? declaration.name.getText() : undefined; + const name = declaration.name ? getSynthesizedDeepClone(declaration.name) as PropertyName : undefined; const visibilityModifier = createVisibilityModifier(getModifierFlags(declaration)); const modifiers = visibilityModifier ? [visibilityModifier] : undefined; - const type = checker.getTypeOfSymbolAtLocation(symbol, enclosingDeclaration); + const type = checker.getWidenedType(checker.getTypeOfSymbolAtLocation(symbol, enclosingDeclaration)); switch (declaration.kind) { case SyntaxKind.GetAccessor: @@ -94,7 +94,7 @@ namespace ts.codefix { const newTypeParameters = signature.typeParameters && signature.typeParameters.map(checker.createTypeParameterDeclarationFromType); const newParameterNodes = signature.getParameters().map(symbol => createParameterDeclarationFromSymbol(symbol, enclosingDeclaration, checker)); - const returnType = checker.createTypeNode(checker.getReturnTypeOfSignature(signature)); + const returnType = checker.createTypeNode(checker.getWidenedType(checker.getReturnTypeOfSignature(signature))); return createStubbedMethod(modifiers, name, newTypeParameters, newParameterNodes, returnType); } @@ -121,7 +121,7 @@ namespace ts.codefix { let signature = checker.getSignatureFromDeclaration(declarations[declarations.length - 1] as SignatureDeclaration); const newTypeParameters = signature.typeParameters && signature.typeParameters.map(checker.createTypeParameterDeclarationFromType); const newParameterNodes = signature.getParameters().map(symbol => createParameterDeclarationFromSymbol(symbol, enclosingDeclaration, checker)); - const returnType = checker.createTypeNode(signature.resolvedReturnType); + const returnType = checker.createTypeNode(checker.getWidenedType(checker.getReturnTypeOfSignature(signature))); signatureDeclarations.push(createStubbedMethod(modifiers, name, newTypeParameters, newParameterNodes, returnType)); } else { @@ -135,7 +135,7 @@ namespace ts.codefix { } } - function createMethodImplementingSignatures(signatures: Signature[], name: string, modifiers: Modifier[] | undefined): MethodDeclaration { + function createMethodImplementingSignatures(signatures: Signature[], name: PropertyName, modifiers: Modifier[] | undefined): MethodDeclaration { Debug.assert(signatures && signatures.length > 0); let maxNonRestArgs = -1; @@ -189,7 +189,7 @@ namespace ts.codefix { , /*returnType*/ undefined); } - export function createStubbedMethod(modifiers: Modifier[], name: string, typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], returnType: TypeNode | undefined) { + export function createStubbedMethod(modifiers: Modifier[], name: PropertyName, typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], returnType: TypeNode | undefined) { return createMethod( /*decorators*/undefined , modifiers @@ -223,7 +223,7 @@ namespace ts.codefix { function createParameterDeclarationFromSymbol(parameterSymbol: Symbol, enclosingDeclaration: ClassLikeDeclaration, checker: TypeChecker) { const parameterDeclaration = parameterSymbol.getDeclarations()[0] as ParameterDeclaration; - const parameterType = checker.getTypeOfSymbolAtLocation(parameterSymbol, enclosingDeclaration); + const parameterType = checker.getWidenedType(checker.getTypeOfSymbolAtLocation(parameterSymbol, enclosingDeclaration)); const parameterTypeNode = checker.createTypeNode(parameterType); // TODO: deep cloning of decorators/any node. const parameterNode = createParameter( @@ -236,8 +236,4 @@ namespace ts.codefix { , /*initializer*/ undefined); return parameterNode; } - - export function getNameFromIndexInfo(info: IndexInfo) { - return info.declaration ? declarationNameToString(info.declaration.parameters[0].name) : "x" - } } \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixClassImplementInterface36.ts b/tests/cases/fourslash/codeFixClassImplementInterface36.ts deleted file mode 100644 index 5b0459ab663..00000000000 --- a/tests/cases/fourslash/codeFixClassImplementInterface36.ts +++ /dev/null @@ -1,18 +0,0 @@ -/// - -//// abstract class C1 { -//// -//// } -//// -//// abstract class C2 { -//// abstract f1(); -//// } -//// -//// interface I1 extends C1, C2 {} -//// -//// class C3 implements I1 {[| |]} - -verify.rangeAfterCodeFix(`f1(){ - throw new Error("Method not implemented."); -} -`); \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixClassImplementInterface39.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceInNamespace.ts similarity index 100% rename from tests/cases/fourslash/codeFixClassImplementInterface39.ts rename to tests/cases/fourslash/codeFixClassImplementInterfaceInNamespace.ts diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceInheritsAbstractMethod.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceInheritsAbstractMethod.ts new file mode 100644 index 00000000000..b49d42dab54 --- /dev/null +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceInheritsAbstractMethod.ts @@ -0,0 +1,18 @@ +/// + +// abstract class C1 { +// +// } +// +// abstract class C2 { +// abstract f1(); +// } +// +// interface I1 extends C1, C2 {} +// +// class C3 implements I1 {[| |]} + +verify.rangeAfterCodeFix(`f1(){ + throw new Error("Method not implemented."); +} +`); \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleImplements1.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleImplements1.ts index 5043a4d9c23..13d58d0ad6e 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleImplements1.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleImplements1.ts @@ -7,8 +7,8 @@ //// y: number; //// } //// -//// class C implements I1,I2 {[| |] -//// y: number; +//// class C implements I1,I2 {[| +//// |]y: number; //// } verify.rangeAfterCodeFix(` diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleImplements2.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleImplements2.ts index ead23751680..e5100b88f6c 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleImplements2.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleImplements2.ts @@ -7,9 +7,9 @@ //// y: number; //// } //// -//// class C implements I1,I2 {[| |] -//// x: number; -//// |]} +//// class C implements I1,I2 {[| +//// |]x: number; +//// } verify.rangeAfterCodeFix(` y: number;