diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index a7b6db70e5..3803a9fbc2 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3583,7 +3583,11 @@ "category": "Message", "code": 90022 }, - + "Add declaration for missing method '{0}'.": { + "category": "Message", + "code": 90023 + }, + "Convert function to an ES2015 class": { "category": "Message", "code": 95001 diff --git a/src/services/codefixes/fixAddMissingMember.ts b/src/services/codefixes/fixAddMissingMember.ts index 64e7e56009..64f1618a67 100644 --- a/src/services/codefixes/fixAddMissingMember.ts +++ b/src/services/codefixes/fixAddMissingMember.ts @@ -2,7 +2,7 @@ namespace ts.codefix { registerCodeFix({ errorCodes: [Diagnostics.Property_0_does_not_exist_on_type_1.code, - Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2.code], + Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2.code], getCodeActions: getActionsForAddMissingMember }); @@ -79,20 +79,31 @@ namespace ts.codefix { } function getActionsForAddMissingMemberInTypeScriptFile(): CodeAction[] | undefined { - let typeNode: TypeNode; + const openBrace = getOpenBraceOfClassLike(classDeclaration, sourceFile); + const tokenName = token.getText(sourceFile); + let actions: CodeAction[]; + if (token.parent.parent.kind === SyntaxKind.CallExpression) { + const callExpression = token.parent.parent; + const methodDeclaration = createMethodFromCallExpression(callExpression, tokenName); + + const methodDeclarationChangeTracker = textChanges.ChangeTracker.fromCodeFixContext(context); + methodDeclarationChangeTracker.insertNodeAfter(sourceFile, openBrace, methodDeclaration, { suffix: context.newLineCharacter }); + actions = [{ + description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_declaration_for_missing_method_0), [tokenName]), + changes: methodDeclarationChangeTracker.getChanges() + }]; + } + + let typeNode: TypeNode; if (token.parent.parent.kind === SyntaxKind.BinaryExpression) { const binaryExpression = token.parent.parent as BinaryExpression; - const checker = context.program.getTypeChecker(); const widenedType = checker.getWidenedType(checker.getBaseTypeOfLiteralType(checker.getTypeAtLocation(binaryExpression.right))); typeNode = checker.typeToTypeNode(widenedType, classDeclaration); } - typeNode = typeNode || createKeywordTypeNode(SyntaxKind.AnyKeyword); - const openBrace = getOpenBraceOfClassLike(classDeclaration, sourceFile); - const property = createProperty( /*decorators*/undefined, /*modifiers*/ isStatic ? [createToken(SyntaxKind.StaticKeyword)] : undefined, @@ -103,10 +114,10 @@ namespace ts.codefix { const propertyChangeTracker = textChanges.ChangeTracker.fromCodeFixContext(context); propertyChangeTracker.insertNodeAfter(sourceFile, openBrace, property, { suffix: context.newLineCharacter }); - const actions = [{ + (actions || (actions = [])).push({ description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_declaration_for_missing_property_0), [token.getText()]), changes: propertyChangeTracker.getChanges() - }]; + }); if (!isStatic) { const stringTypeNode = createKeywordTypeNode(SyntaxKind.StringKeyword); diff --git a/src/services/codefixes/helpers.ts b/src/services/codefixes/helpers.ts index 39d8e13dde..dc51129736 100644 --- a/src/services/codefixes/helpers.ts +++ b/src/services/codefixes/helpers.ts @@ -142,6 +142,46 @@ namespace ts.codefix { } } + export function createMethodFromCallExpression(callExpression: CallExpression, methodName: string): MethodDeclaration { + const argCount = callExpression.arguments.length; + const parameters: ParameterDeclaration[] = []; + for (let i = 0; i < argCount; i++) { + const typeNode = createKeywordTypeNode(SyntaxKind.AnyKeyword); + const newParameter = createParameter( + /*decorators*/ undefined, + /*modifiers*/ undefined, + /*dotDotDotToken*/ undefined, + `arg${i}`, + /*questionToken*/ undefined, + typeNode, + /*initializer*/ undefined); + parameters.push(newParameter); + } + + const typeArgCount = callExpression.typeArguments ? callExpression.typeArguments.length : 0; + let typeParameters: TypeParameterDeclaration[]; + for (let i = 0; i < typeArgCount; i++) { + const name = typeArgCount < 8 ? String.fromCharCode(CharacterCodes.T + i) : `T${i}`; + const typeParameter = createTypeParameterDeclaration(name, /*constraint*/ undefined, /*defaultType*/ undefined); + + (typeParameters ? typeParameters : typeParameters = []).push(typeParameter); + } + + const newMethod = createMethod( + /*decorators*/ undefined, + /*modifiers*/ undefined, + /*asteriskToken*/ undefined, + methodName, + /*questionToken*/ undefined, + typeParameters, + parameters, + /*type*/ undefined, + createStubbedMethodBody() + ) + + return newMethod; + } + function createMethodImplementingSignatures(signatures: Signature[], name: PropertyName, optional: boolean, modifiers: Modifier[] | undefined): MethodDeclaration { /** This is *a* signature with the maximal number of arguments, * such that if there is a "maximal" signature without rest arguments,