mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-27 22:39:59 -05:00
Add Support for multiple signatures
This commit is contained in:
@@ -88,6 +88,7 @@ namespace ts {
|
||||
getReturnTypeOfSignature,
|
||||
getNonNullableType,
|
||||
getSymbolsInScope,
|
||||
createSymbol,
|
||||
getSymbolAtLocation,
|
||||
getShorthandAssignmentValueSymbol,
|
||||
getExportSpecifierLocalTargetSymbol,
|
||||
|
||||
@@ -640,9 +640,9 @@ namespace ts {
|
||||
|
||||
export interface ParameterDeclaration extends Declaration {
|
||||
kind: SyntaxKind.Parameter;
|
||||
dotDotDotToken?: DotDotDotToken; // Present on rest parameter
|
||||
dotDotDotToken?: DotDotDotToken; // Present on rest parameter
|
||||
name: BindingName; // Declared parameter name
|
||||
questionToken?: QuestionToken; // Present on optional parameter
|
||||
questionToken?: QuestionToken; // Present on optional parameter
|
||||
type?: TypeNode; // Optional type annotation
|
||||
initializer?: Expression; // Optional initializer
|
||||
}
|
||||
@@ -658,14 +658,14 @@ namespace ts {
|
||||
export interface PropertySignature extends TypeElement {
|
||||
kind: SyntaxKind.PropertySignature | SyntaxKind.JSDocRecordMember;
|
||||
name: PropertyName; // Declared property name
|
||||
questionToken?: QuestionToken; // Present on optional property
|
||||
questionToken?: QuestionToken; // Present on optional property
|
||||
type?: TypeNode; // Optional type annotation
|
||||
initializer?: Expression; // Optional initializer
|
||||
}
|
||||
|
||||
export interface PropertyDeclaration extends ClassElement {
|
||||
kind: SyntaxKind.PropertyDeclaration;
|
||||
questionToken?: QuestionToken; // Present for use with reporting a grammar error
|
||||
questionToken?: QuestionToken; // Present for use with reporting a grammar error
|
||||
name: PropertyName;
|
||||
type?: TypeNode;
|
||||
initializer?: Expression; // Optional initializer
|
||||
@@ -2354,6 +2354,7 @@ namespace ts {
|
||||
signatureToString(signature: Signature, enclosingDeclaration?: Node, flags?: TypeFormatFlags, kind?: SignatureKind): string;
|
||||
typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string;
|
||||
symbolToString(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): string;
|
||||
createSymbol(flags: SymbolFlags, name: string): Symbol;
|
||||
getSymbolDisplayBuilder(): SymbolDisplayBuilder;
|
||||
getFullyQualifiedName(symbol: Symbol): string;
|
||||
getAugmentedPropertiesOfType(type: Type): Symbol[];
|
||||
|
||||
@@ -38,14 +38,42 @@ namespace ts.codefix {
|
||||
|
||||
case SyntaxKind.MethodSignature:
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
// The signature for the implementation appears as an entry in `signatures` iff
|
||||
// there is only one signature.
|
||||
// If there are overloads and an implementation signature, it appears as an
|
||||
// extra declaration that isn't a signature for `type`.
|
||||
// If there is more than one overload but no implementation signature
|
||||
// (eg: an abstract method or interface declaration), there is a 1-1
|
||||
// correspondence of declarations and signatures.
|
||||
const signatures = checker.getSignaturesOfType(type, SignatureKind.Call);
|
||||
if (!(signatures && signatures.length > 0)) {
|
||||
return "";
|
||||
}
|
||||
// TODO: (arozga) Deal with multiple signatures.
|
||||
const sigString = checker.signatureToString(signatures[0], enclosingDeclaration, TypeFormatFlags.SuppressAnyReturnType, SignatureKind.Call);
|
||||
if (declarations.length === 1) {
|
||||
Debug.assert(signatures.length === 1);
|
||||
const sigString = checker.signatureToString(signatures[0], enclosingDeclaration, TypeFormatFlags.SuppressAnyReturnType, SignatureKind.Call);
|
||||
return `${visibility}${name}${sigString}${getMethodBodyStub(newlineChar)}`;
|
||||
}
|
||||
|
||||
return `${visibility}${name}${sigString}${getMethodBodyStub(newlineChar)}`;
|
||||
let result = "";
|
||||
for (let i = 0; i < signatures.length; i++) {
|
||||
const sigString = checker.signatureToString(signatures[i], enclosingDeclaration, TypeFormatFlags.SuppressAnyReturnType, SignatureKind.Call);
|
||||
result += `${visibility}${name}${sigString};${newlineChar}`;
|
||||
}
|
||||
|
||||
// If there is a declaration with a body, it is the last declaration,
|
||||
// and it isn't caught by `getSignaturesOfType`.
|
||||
let bodySig: Signature | undefined = undefined;
|
||||
if (declarations.length > signatures.length) {
|
||||
bodySig = checker.getSignatureFromDeclaration(declarations[declarations.length - 1] as SignatureDeclaration);
|
||||
}
|
||||
else {
|
||||
bodySig = createBodyDeclarationSignatureWithAnyTypes(declarations as SignatureDeclaration[], signatures, enclosingDeclaration, checker);
|
||||
}
|
||||
const sigString = checker.signatureToString(bodySig, enclosingDeclaration, TypeFormatFlags.SuppressAnyReturnType, SignatureKind.Call);
|
||||
result += `${visibility}${name}${sigString}${getMethodBodyStub(newlineChar)}`;
|
||||
|
||||
return result;
|
||||
case SyntaxKind.ComputedPropertyName:
|
||||
if (hasDynamicName(node)) {
|
||||
return "";
|
||||
@@ -59,8 +87,73 @@ namespace ts.codefix {
|
||||
}
|
||||
}
|
||||
|
||||
function createBodyDeclarationSignatureWithAnyTypes(signatureDecls: SignatureDeclaration[], signatures: Signature[], enclosingDeclaration: ClassLikeDeclaration, checker: TypeChecker): Signature {
|
||||
Debug.assert(signatureDecls.length === signatures.length);
|
||||
|
||||
const newSignatureDeclaration = createNode(SyntaxKind.CallSignature) as SignatureDeclaration;
|
||||
newSignatureDeclaration.parent = enclosingDeclaration;
|
||||
newSignatureDeclaration.name = signatureDecls[0].name;
|
||||
|
||||
let maxArgs = -1, maxArgsIndex = 0;
|
||||
let minArgumentCount = signatures[0].minArgumentCount;
|
||||
let hasRestParameter = false;
|
||||
for (let i = 0; i < signatures.length; i++) {
|
||||
const sig = signatures[i];
|
||||
minArgumentCount = Math.min(sig.minArgumentCount, minArgumentCount);
|
||||
if (sig.parameters.length > maxArgs) {
|
||||
maxArgs = sig.parameters.length;
|
||||
maxArgsIndex = i;
|
||||
}
|
||||
hasRestParameter = hasRestParameter || sig.hasRestParameter;
|
||||
}
|
||||
|
||||
const anyTypeNode: TypeNode = createNode(SyntaxKind.AnyKeyword) as TypeNode;
|
||||
const optionalToken = createToken(SyntaxKind.QuestionToken);
|
||||
|
||||
newSignatureDeclaration.parameters = createNodeArray<ParameterDeclaration>();
|
||||
for (let i = 0; i < maxArgs - 1; i++) {
|
||||
const newParameter = createParameterDeclaration(i, minArgumentCount, newSignatureDeclaration);
|
||||
newSignatureDeclaration.parameters.push(newParameter);
|
||||
}
|
||||
|
||||
const lastParameter = createParameterDeclaration(maxArgs - 1, minArgumentCount, newSignatureDeclaration);
|
||||
if (hasRestParameter) {
|
||||
lastParameter.dotDotDotToken = createToken(SyntaxKind.DotDotDotToken);
|
||||
|
||||
let allMaxArgsAreRest = true;
|
||||
for (const sig of signatures) {
|
||||
allMaxArgsAreRest = allMaxArgsAreRest && sig.parameters[maxArgs - 1] && sig.hasRestParameter;
|
||||
}
|
||||
if (!allMaxArgsAreRest) {
|
||||
const newParameter = createParameterDeclaration(maxArgs - 1, minArgumentCount, newSignatureDeclaration);
|
||||
newSignatureDeclaration.parameters.push(newParameter);
|
||||
}
|
||||
}
|
||||
|
||||
newSignatureDeclaration.parameters.push(lastParameter);
|
||||
|
||||
newSignatureDeclaration.type = anyTypeNode;
|
||||
newSignatureDeclaration.type.parent = newSignatureDeclaration;
|
||||
|
||||
return checker.getSignatureFromDeclaration(newSignatureDeclaration);
|
||||
|
||||
function createParameterDeclaration(index: number, minArgCount: number, enclosingSignature: SignatureDeclaration): ParameterDeclaration {
|
||||
const newParameter = createNode(SyntaxKind.Parameter) as ParameterDeclaration;
|
||||
newParameter.symbol = checker.createSymbol(SymbolFlags.FunctionScopedVariable, "arg" + index);
|
||||
newParameter.symbol.valueDeclaration = newParameter;
|
||||
newParameter.symbol.declarations = [newParameter];
|
||||
newParameter.type = anyTypeNode;
|
||||
newParameter.parent = enclosingSignature;
|
||||
if (index >= minArgCount) {
|
||||
newParameter.questionToken = optionalToken;
|
||||
}
|
||||
|
||||
return newParameter;
|
||||
}
|
||||
}
|
||||
|
||||
function getMethodBodyStub(newLineChar: string) {
|
||||
return `{${newLineChar}throw new Error('Method not implemented.');${newLineChar}}${newLineChar}`;
|
||||
return ` {${newLineChar}throw new Error('Method not implemented.');${newLineChar}}${newLineChar}`;
|
||||
}
|
||||
|
||||
function getVisibilityPrefix(flags: ModifierFlags): string {
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
//// abstract class A {
|
||||
//// abstract f();
|
||||
//// abstract f(a: number, b: string): boolean;
|
||||
//// abstract f(a: string, b: number): Function;
|
||||
//// abstract f(a: string): Function;
|
||||
//// }
|
||||
////
|
||||
//// class C extends A {[|
|
||||
//// |]}
|
||||
//// class C extends A {[| |]}
|
||||
|
||||
verify.rangeAfterCodeFix(`f(){
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
`);
|
||||
verify.rangeAfterCodeFix(`
|
||||
f(a: number, b: string): boolean;
|
||||
f(a: string, b: number): Function;
|
||||
f(a: string): Function;
|
||||
f(arg0: any, arg1? any) {
|
||||
throw new Error("Method not implemented");
|
||||
}
|
||||
`);
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
//// class A {
|
||||
//// method(a: number, b: string): boolean;
|
||||
//// method(a: string | number, b?: string | number): boolean | Function { return true; }
|
||||
////
|
||||
//// class C implements A {[| |]}
|
||||
|
||||
verify.rangeAfterCodeFix(`
|
||||
method(a: number, b: string): boolean;
|
||||
method(a: string | number, b?: string | number): boolean | Function {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
`);
|
||||
@@ -0,0 +1,18 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
//// class A {
|
||||
//// method(a: any, b: string): boolean;
|
||||
//// method(a: string, b: number): Function;
|
||||
//// method(a: string): Function;
|
||||
//// method(a: string | number, b?: string | number): boolean | Function { return true; }
|
||||
////
|
||||
//// class C implements A {[| |]}
|
||||
|
||||
verify.rangeAfterCodeFix(`
|
||||
method(a: any, b: string): boolean;
|
||||
method(a: string, b: number): Function;
|
||||
method(a: string): Function;
|
||||
method(a: string | number, b?: string | number): boolean | Function {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
`);
|
||||
@@ -1,17 +1,5 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
//// namespace N1 {
|
||||
//// export interface I1 {
|
||||
//// f1():string;
|
||||
//// }
|
||||
//// }
|
||||
//// interface I1 {
|
||||
//// f1();
|
||||
//// }
|
||||
////
|
||||
//// class C1 implements N1.I1 {[|
|
||||
//// |]}
|
||||
|
||||
//// interface I {
|
||||
//// method(a: number, b: string): boolean;
|
||||
//// method(a: string, b: number): Function;
|
||||
@@ -24,7 +12,7 @@ verify.rangeAfterCodeFix(`
|
||||
method(a: number, b: string): boolean;
|
||||
method(a: string, b: number): Function;
|
||||
method(a: string): Function;
|
||||
method(a: number | string, b?: string | number): boolean | Function {
|
||||
throw new Error("Method not implemented");
|
||||
method(arg0: any, arg1?: any) {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
`);
|
||||
|
||||
Reference in New Issue
Block a user