From aac34519d410857c3fb31856c64428e3022b4046 Mon Sep 17 00:00:00 2001 From: Jason Freeman Date: Thu, 23 Jul 2015 16:27:04 -0700 Subject: [PATCH 1/5] Remove error for parameter with default initializer before a required parameter --- src/compiler/checker.ts | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3363987322a..217a6103198 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1994,15 +1994,15 @@ namespace ts { } return _displayBuilder || (_displayBuilder = { - buildSymbolDisplay: buildSymbolDisplay, - buildTypeDisplay: buildTypeDisplay, - buildTypeParameterDisplay: buildTypeParameterDisplay, - buildParameterDisplay: buildParameterDisplay, - buildDisplayForParametersAndDelimiters: buildDisplayForParametersAndDelimiters, - buildDisplayForTypeParametersAndDelimiters: buildDisplayForTypeParametersAndDelimiters, - buildTypeParameterDisplayFromSymbol: buildTypeParameterDisplayFromSymbol, - buildSignatureDisplay: buildSignatureDisplay, - buildReturnTypeDisplay: buildReturnTypeDisplay + buildSymbolDisplay, + buildTypeDisplay, + buildTypeParameterDisplay, + buildParameterDisplay, + buildDisplayForParametersAndDelimiters, + buildDisplayForTypeParametersAndDelimiters, + buildTypeParameterDisplayFromSymbol, + buildSignatureDisplay, + buildReturnTypeDisplay }); } @@ -14771,17 +14771,15 @@ namespace ts { return grammarErrorOnNode(parameter.name, Diagnostics.A_rest_parameter_cannot_have_an_initializer); } } - else if (parameter.questionToken || parameter.initializer) { + else if (parameter.questionToken) { seenOptionalParameter = true; - if (parameter.questionToken && parameter.initializer) { + if (parameter.initializer) { return grammarErrorOnNode(parameter.name, Diagnostics.Parameter_cannot_have_question_mark_and_initializer); } } - else { - if (seenOptionalParameter) { - return grammarErrorOnNode(parameter.name, Diagnostics.A_required_parameter_cannot_follow_an_optional_parameter); - } + else if (seenOptionalParameter && !parameter.initializer) { + return grammarErrorOnNode(parameter.name, Diagnostics.A_required_parameter_cannot_follow_an_optional_parameter); } } } From a47e02aeac8c952832ec12f320693499b032a9b2 Mon Sep 17 00:00:00 2001 From: Jason Freeman Date: Fri, 24 Jul 2015 13:59:20 -0700 Subject: [PATCH 2/5] Adjust notion of minArgumentCount to account for required parameters with default initializers --- src/compiler/checker.ts | 23 ++++++++++++++++++++--- src/compiler/utilities.ts | 4 +--- src/services/signatureHelp.ts | 18 +++++++++++++++--- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 217a6103198..ec6c6d2f8e5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3517,7 +3517,19 @@ namespace ts { } function isOptionalParameter(node: ParameterDeclaration) { - return hasQuestionToken(node) || !!node.initializer; + if (hasQuestionToken(node)) { + return true; + } + + if (node.initializer) { + let signatureDeclaration = node.parent; + let signature = getSignatureFromDeclaration(signatureDeclaration); + let parameterIndex = signatureDeclaration.parameters.indexOf(node); + Debug.assert(parameterIndex >= 0); + return parameterIndex >= signature.minArgumentCount; + } + + return false; } function getSignatureFromDeclaration(declaration: SignatureDeclaration): Signature { @@ -3535,11 +3547,16 @@ namespace ts { if (param.type && param.type.kind === SyntaxKind.StringLiteral) { hasStringLiterals = true; } - if (minArgumentCount < 0) { - if (param.initializer || param.questionToken || param.dotDotDotToken) { + + if (param.initializer || param.questionToken || param.dotDotDotToken) { + if (minArgumentCount < 0) { minArgumentCount = i; } } + else { + // If we see any required parameters, it means the prior ones were not in fact optional. + minArgumentCount = -1; + } } if (minArgumentCount < 0) { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 56eb2415cf9..312a3666757 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -988,15 +988,13 @@ namespace ts { if (node) { switch (node.kind) { case SyntaxKind.Parameter: - return (node).questionToken !== undefined; case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodSignature: - return (node).questionToken !== undefined; case SyntaxKind.ShorthandPropertyAssignment: case SyntaxKind.PropertyAssignment: case SyntaxKind.PropertyDeclaration: case SyntaxKind.PropertySignature: - return (node).questionToken !== undefined; + return (node).questionToken !== undefined; } } diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts index f44ebade2a7..6230a66910e 100644 --- a/src/services/signatureHelp.ts +++ b/src/services/signatureHelp.ts @@ -569,7 +569,15 @@ namespace ts.SignatureHelp { prefixDisplayParts.push(punctuationPart(SyntaxKind.OpenParenToken)); let parameters = candidateSignature.parameters; - signatureHelpParameters = parameters.length > 0 ? map(parameters, createSignatureHelpParameterForParameter) : emptyArray; + if (parameters.length > 0) { + signatureHelpParameters = []; + for (let i = 0; i < parameters.length; i++) { + signatureHelpParameters.push(createSignatureHelpParameterAtIndex(candidateSignature, i)); + } + } + else { + signatureHelpParameters = emptyArray; + } suffixDisplayParts.push(punctuationPart(SyntaxKind.CloseParenToken)); } @@ -607,11 +615,15 @@ namespace ts.SignatureHelp { argumentCount }; - function createSignatureHelpParameterForParameter(parameter: Symbol): SignatureHelpParameter { + function createSignatureHelpParameterAtIndex(signature: Signature, parameterIndex: number): SignatureHelpParameter { + let parameter = signature.parameters[parameterIndex]; let displayParts = mapToDisplayParts(writer => typeChecker.getSymbolDisplayBuilder().buildParameterDisplay(parameter, writer, invocation)); - let isOptional = hasQuestionToken(parameter.valueDeclaration); + let parameterDeclaration = parameter.valueDeclaration; + let isOptional = + hasQuestionToken(parameterDeclaration) || + parameterDeclaration.initializer && parameterIndex >= signature.minArgumentCount; return { name: parameter.name, From f44bcbda3c5c4c5552c93f498fe146ad8c030a9e Mon Sep 17 00:00:00 2001 From: Jason Freeman Date: Fri, 24 Jul 2015 14:58:53 -0700 Subject: [PATCH 3/5] Accept baselines and fix test comments --- .../fatarrowfunctionsOptionalArgsErrors1.errors.txt | 9 +++------ .../fatarrowfunctionsOptionalArgsErrors1.js | 4 ++-- .../reference/optionalParamArgsTest.errors.txt | 12 ++++-------- tests/baselines/reference/optionalParamArgsTest.js | 6 ++---- .../compiler/fatarrowfunctionsOptionalArgsErrors1.ts | 2 +- tests/cases/compiler/optionalParamArgsTest.ts | 3 +-- 6 files changed, 13 insertions(+), 23 deletions(-) diff --git a/tests/baselines/reference/fatarrowfunctionsOptionalArgsErrors1.errors.txt b/tests/baselines/reference/fatarrowfunctionsOptionalArgsErrors1.errors.txt index 5934ab52ccf..b32a7b67b61 100644 --- a/tests/baselines/reference/fatarrowfunctionsOptionalArgsErrors1.errors.txt +++ b/tests/baselines/reference/fatarrowfunctionsOptionalArgsErrors1.errors.txt @@ -1,10 +1,9 @@ tests/cases/compiler/fatarrowfunctionsOptionalArgsErrors1.ts(1,9): error TS1016: A required parameter cannot follow an optional parameter. tests/cases/compiler/fatarrowfunctionsOptionalArgsErrors1.ts(2,8): error TS1047: A rest parameter cannot be optional. tests/cases/compiler/fatarrowfunctionsOptionalArgsErrors1.ts(4,5): error TS1048: A rest parameter cannot have an initializer. -tests/cases/compiler/fatarrowfunctionsOptionalArgsErrors1.ts(7,12): error TS1016: A required parameter cannot follow an optional parameter. -==== tests/cases/compiler/fatarrowfunctionsOptionalArgsErrors1.ts (4 errors) ==== +==== tests/cases/compiler/fatarrowfunctionsOptionalArgsErrors1.ts (3 errors) ==== (arg1?, arg2) => 101; ~~~~ !!! error TS1016: A required parameter cannot follow an optional parameter. @@ -16,7 +15,5 @@ tests/cases/compiler/fatarrowfunctionsOptionalArgsErrors1.ts(7,12): error TS1016 ~~~ !!! error TS1048: A rest parameter cannot have an initializer. - // Non optional parameter following an optional one - (arg1 = 1, arg2) => 1; - ~~~~ -!!! error TS1016: A required parameter cannot follow an optional parameter. \ No newline at end of file + // Uninitialized parameter makes the initialized one required + (arg1 = 1, arg2) => 1; \ No newline at end of file diff --git a/tests/baselines/reference/fatarrowfunctionsOptionalArgsErrors1.js b/tests/baselines/reference/fatarrowfunctionsOptionalArgsErrors1.js index e746b24a066..4d27f8230eb 100644 --- a/tests/baselines/reference/fatarrowfunctionsOptionalArgsErrors1.js +++ b/tests/baselines/reference/fatarrowfunctionsOptionalArgsErrors1.js @@ -4,7 +4,7 @@ (...arg) => 103; (...arg:number [] = []) => 104; -// Non optional parameter following an optional one +// Uninitialized parameter makes the initialized one required (arg1 = 1, arg2) => 1; //// [fatarrowfunctionsOptionalArgsErrors1.js] @@ -30,7 +30,7 @@ } return 104; }); -// Non optional parameter following an optional one +// Uninitialized parameter makes the initialized one required (function (arg1, arg2) { if (arg1 === void 0) { arg1 = 1; } return 1; diff --git a/tests/baselines/reference/optionalParamArgsTest.errors.txt b/tests/baselines/reference/optionalParamArgsTest.errors.txt index 3c4d2e37be9..05fefa85065 100644 --- a/tests/baselines/reference/optionalParamArgsTest.errors.txt +++ b/tests/baselines/reference/optionalParamArgsTest.errors.txt @@ -1,6 +1,6 @@ tests/cases/compiler/optionalParamArgsTest.ts(31,12): error TS2393: Duplicate function implementation. -tests/cases/compiler/optionalParamArgsTest.ts(35,12): error TS2393: Duplicate function implementation. -tests/cases/compiler/optionalParamArgsTest.ts(35,47): error TS1016: A required parameter cannot follow an optional parameter. +tests/cases/compiler/optionalParamArgsTest.ts(34,12): error TS2393: Duplicate function implementation. +tests/cases/compiler/optionalParamArgsTest.ts(98,1): error TS2346: Supplied parameters do not match any signature of call target. tests/cases/compiler/optionalParamArgsTest.ts(99,1): error TS2346: Supplied parameters do not match any signature of call target. tests/cases/compiler/optionalParamArgsTest.ts(100,1): error TS2346: Supplied parameters do not match any signature of call target. tests/cases/compiler/optionalParamArgsTest.ts(101,1): error TS2346: Supplied parameters do not match any signature of call target. @@ -20,10 +20,9 @@ tests/cases/compiler/optionalParamArgsTest.ts(114,1): error TS2346: Supplied par tests/cases/compiler/optionalParamArgsTest.ts(115,1): error TS2346: Supplied parameters do not match any signature of call target. tests/cases/compiler/optionalParamArgsTest.ts(116,1): error TS2346: Supplied parameters do not match any signature of call target. tests/cases/compiler/optionalParamArgsTest.ts(117,1): error TS2346: Supplied parameters do not match any signature of call target. -tests/cases/compiler/optionalParamArgsTest.ts(118,1): error TS2346: Supplied parameters do not match any signature of call target. -==== tests/cases/compiler/optionalParamArgsTest.ts (23 errors) ==== +==== tests/cases/compiler/optionalParamArgsTest.ts (22 errors) ==== // Optional parameter and default argument tests // Key: @@ -58,13 +57,10 @@ tests/cases/compiler/optionalParamArgsTest.ts(118,1): error TS2346: Supplied par ~~~~ !!! error TS2393: Duplicate function implementation. - // Negative test - // "Optional parameters may only be followed by other optional parameters" + // Uninitialized parameter makes the initialized one required public C1M5(C1M5A1:number,C1M5A2:number=0,C1M5A3:number) { return C1M5A1 + C1M5A2; } ~~~~ !!! error TS2393: Duplicate function implementation. - ~~~~~~ -!!! error TS1016: A required parameter cannot follow an optional parameter. } class C2 extends C1 { diff --git a/tests/baselines/reference/optionalParamArgsTest.js b/tests/baselines/reference/optionalParamArgsTest.js index 414f474598d..0c5a5d82814 100644 --- a/tests/baselines/reference/optionalParamArgsTest.js +++ b/tests/baselines/reference/optionalParamArgsTest.js @@ -31,8 +31,7 @@ class C1 { public C1M5(C1M5A1:number,C1M5A2:number=0,C1M5A3?:number) { return C1M5A1 + C1M5A2; } - // Negative test - // "Optional parameters may only be followed by other optional parameters" + // Uninitialized parameter makes the initialized one required public C1M5(C1M5A1:number,C1M5A2:number=0,C1M5A3:number) { return C1M5A1 + C1M5A2; } } @@ -152,8 +151,7 @@ var C1 = (function () { if (C1M5A2 === void 0) { C1M5A2 = 0; } return C1M5A1 + C1M5A2; }; - // Negative test - // "Optional parameters may only be followed by other optional parameters" + // Uninitialized parameter makes the initialized one required C1.prototype.C1M5 = function (C1M5A1, C1M5A2, C1M5A3) { if (C1M5A2 === void 0) { C1M5A2 = 0; } return C1M5A1 + C1M5A2; diff --git a/tests/cases/compiler/fatarrowfunctionsOptionalArgsErrors1.ts b/tests/cases/compiler/fatarrowfunctionsOptionalArgsErrors1.ts index d8ddcf4bb8f..814f1b41619 100644 --- a/tests/cases/compiler/fatarrowfunctionsOptionalArgsErrors1.ts +++ b/tests/cases/compiler/fatarrowfunctionsOptionalArgsErrors1.ts @@ -3,5 +3,5 @@ (...arg) => 103; (...arg:number [] = []) => 104; -// Non optional parameter following an optional one +// Uninitialized parameter makes the initialized one required (arg1 = 1, arg2) => 1; \ No newline at end of file diff --git a/tests/cases/compiler/optionalParamArgsTest.ts b/tests/cases/compiler/optionalParamArgsTest.ts index ce34acae869..609cbfe4734 100644 --- a/tests/cases/compiler/optionalParamArgsTest.ts +++ b/tests/cases/compiler/optionalParamArgsTest.ts @@ -30,8 +30,7 @@ class C1 { public C1M5(C1M5A1:number,C1M5A2:number=0,C1M5A3?:number) { return C1M5A1 + C1M5A2; } - // Negative test - // "Optional parameters may only be followed by other optional parameters" + // Uninitialized parameter makes the initialized one required public C1M5(C1M5A1:number,C1M5A2:number=0,C1M5A3:number) { return C1M5A1 + C1M5A2; } } From 628d63cf7586171385e528f2770f1f3efa22a50d Mon Sep 17 00:00:00 2001 From: Jason Freeman Date: Fri, 24 Jul 2015 14:59:01 -0700 Subject: [PATCH 4/5] Add tests --- .../requiredInitializedParameter1.errors.txt | 28 ++++++++++ .../requiredInitializedParameter1.js | 51 +++++++++++++++++++ .../requiredInitializedParameter2.errors.txt | 17 +++++++ .../requiredInitializedParameter2.js | 18 +++++++ .../requiredInitializedParameter3.js | 27 ++++++++++ .../requiredInitializedParameter3.symbols | 17 +++++++ .../requiredInitializedParameter3.types | 18 +++++++ .../compiler/requiredInitializedParameter1.ts | 19 +++++++ .../compiler/requiredInitializedParameter2.ts | 7 +++ .../compiler/requiredInitializedParameter3.ts | 8 +++ 10 files changed, 210 insertions(+) create mode 100644 tests/baselines/reference/requiredInitializedParameter1.errors.txt create mode 100644 tests/baselines/reference/requiredInitializedParameter1.js create mode 100644 tests/baselines/reference/requiredInitializedParameter2.errors.txt create mode 100644 tests/baselines/reference/requiredInitializedParameter2.js create mode 100644 tests/baselines/reference/requiredInitializedParameter3.js create mode 100644 tests/baselines/reference/requiredInitializedParameter3.symbols create mode 100644 tests/baselines/reference/requiredInitializedParameter3.types create mode 100644 tests/cases/compiler/requiredInitializedParameter1.ts create mode 100644 tests/cases/compiler/requiredInitializedParameter2.ts create mode 100644 tests/cases/compiler/requiredInitializedParameter3.ts diff --git a/tests/baselines/reference/requiredInitializedParameter1.errors.txt b/tests/baselines/reference/requiredInitializedParameter1.errors.txt new file mode 100644 index 00000000000..ef958dbdec9 --- /dev/null +++ b/tests/baselines/reference/requiredInitializedParameter1.errors.txt @@ -0,0 +1,28 @@ +tests/cases/compiler/requiredInitializedParameter1.ts(11,1): error TS2346: Supplied parameters do not match any signature of call target. +tests/cases/compiler/requiredInitializedParameter1.ts(16,1): error TS2346: Supplied parameters do not match any signature of call target. + + +==== tests/cases/compiler/requiredInitializedParameter1.ts (2 errors) ==== + function f1(a, b = 0, c) { } + function f2(a, b = 0, c = 0) { } + function f3(a, b = 0, c?) { } + function f4(a, b = 0, ...c) { } + + f1(0, 1, 2); + f2(0, 1, 2); + f3(0, 1, 2); + f4(0, 1, 2); + + f1(0, 1); + ~~~~~~~~ +!!! error TS2346: Supplied parameters do not match any signature of call target. + f2(0, 1); + f3(0, 1); + f4(0, 1); + + f1(0); + ~~~~~ +!!! error TS2346: Supplied parameters do not match any signature of call target. + f2(0); + f3(0); + f4(0); \ No newline at end of file diff --git a/tests/baselines/reference/requiredInitializedParameter1.js b/tests/baselines/reference/requiredInitializedParameter1.js new file mode 100644 index 00000000000..0fe07503056 --- /dev/null +++ b/tests/baselines/reference/requiredInitializedParameter1.js @@ -0,0 +1,51 @@ +//// [requiredInitializedParameter1.ts] +function f1(a, b = 0, c) { } +function f2(a, b = 0, c = 0) { } +function f3(a, b = 0, c?) { } +function f4(a, b = 0, ...c) { } + +f1(0, 1, 2); +f2(0, 1, 2); +f3(0, 1, 2); +f4(0, 1, 2); + +f1(0, 1); +f2(0, 1); +f3(0, 1); +f4(0, 1); + +f1(0); +f2(0); +f3(0); +f4(0); + +//// [requiredInitializedParameter1.js] +function f1(a, b, c) { + if (b === void 0) { b = 0; } +} +function f2(a, b, c) { + if (b === void 0) { b = 0; } + if (c === void 0) { c = 0; } +} +function f3(a, b, c) { + if (b === void 0) { b = 0; } +} +function f4(a, b) { + if (b === void 0) { b = 0; } + var c = []; + for (var _i = 2; _i < arguments.length; _i++) { + c[_i - 2] = arguments[_i]; + } +} +f1(0, 1, 2); +f2(0, 1, 2); +f3(0, 1, 2); +f4(0, 1, 2); +f1(0, 1); +f2(0, 1); +f3(0, 1); +f4(0, 1); +f1(0); +f2(0); +f3(0); +f4(0); diff --git a/tests/baselines/reference/requiredInitializedParameter2.errors.txt b/tests/baselines/reference/requiredInitializedParameter2.errors.txt new file mode 100644 index 00000000000..5dcec536e1b --- /dev/null +++ b/tests/baselines/reference/requiredInitializedParameter2.errors.txt @@ -0,0 +1,17 @@ +tests/cases/compiler/requiredInitializedParameter2.ts(5,7): error TS2420: Class 'C1' incorrectly implements interface 'I1'. + Types of property 'method' are incompatible. + Type '(a: number, b: any) => void' is not assignable to type '() => any'. + + +==== tests/cases/compiler/requiredInitializedParameter2.ts (1 errors) ==== + interface I1 { + method(); + } + + class C1 implements I1 { + ~~ +!!! error TS2420: Class 'C1' incorrectly implements interface 'I1'. +!!! error TS2420: Types of property 'method' are incompatible. +!!! error TS2420: Type '(a: number, b: any) => void' is not assignable to type '() => any'. + method(a = 0, b) { } + } \ No newline at end of file diff --git a/tests/baselines/reference/requiredInitializedParameter2.js b/tests/baselines/reference/requiredInitializedParameter2.js new file mode 100644 index 00000000000..3c69d54e1d9 --- /dev/null +++ b/tests/baselines/reference/requiredInitializedParameter2.js @@ -0,0 +1,18 @@ +//// [requiredInitializedParameter2.ts] +interface I1 { + method(); +} + +class C1 implements I1 { + method(a = 0, b) { } +} + +//// [requiredInitializedParameter2.js] +var C1 = (function () { + function C1() { + } + C1.prototype.method = function (a, b) { + if (a === void 0) { a = 0; } + }; + return C1; +})(); diff --git a/tests/baselines/reference/requiredInitializedParameter3.js b/tests/baselines/reference/requiredInitializedParameter3.js new file mode 100644 index 00000000000..68e1aa696ea --- /dev/null +++ b/tests/baselines/reference/requiredInitializedParameter3.js @@ -0,0 +1,27 @@ +//// [requiredInitializedParameter3.ts] +interface I1 { + method(); +} + +class C1 implements I1 { + method(a = 0, b?) { } +} + +//// [requiredInitializedParameter3.js] +var C1 = (function () { + function C1() { + } + C1.prototype.method = function (a, b) { + if (a === void 0) { a = 0; } + }; + return C1; +})(); + + +//// [requiredInitializedParameter3.d.ts] +interface I1 { + method(): any; +} +declare class C1 implements I1 { + method(a?: number, b?: any): void; +} diff --git a/tests/baselines/reference/requiredInitializedParameter3.symbols b/tests/baselines/reference/requiredInitializedParameter3.symbols new file mode 100644 index 00000000000..e9f35b21bcf --- /dev/null +++ b/tests/baselines/reference/requiredInitializedParameter3.symbols @@ -0,0 +1,17 @@ +=== tests/cases/compiler/requiredInitializedParameter3.ts === +interface I1 { +>I1 : Symbol(I1, Decl(requiredInitializedParameter3.ts, 0, 0)) + + method(); +>method : Symbol(method, Decl(requiredInitializedParameter3.ts, 0, 14)) +} + +class C1 implements I1 { +>C1 : Symbol(C1, Decl(requiredInitializedParameter3.ts, 2, 1)) +>I1 : Symbol(I1, Decl(requiredInitializedParameter3.ts, 0, 0)) + + method(a = 0, b?) { } +>method : Symbol(method, Decl(requiredInitializedParameter3.ts, 4, 24)) +>a : Symbol(a, Decl(requiredInitializedParameter3.ts, 5, 11)) +>b : Symbol(b, Decl(requiredInitializedParameter3.ts, 5, 17)) +} diff --git a/tests/baselines/reference/requiredInitializedParameter3.types b/tests/baselines/reference/requiredInitializedParameter3.types new file mode 100644 index 00000000000..aa37f5e13dd --- /dev/null +++ b/tests/baselines/reference/requiredInitializedParameter3.types @@ -0,0 +1,18 @@ +=== tests/cases/compiler/requiredInitializedParameter3.ts === +interface I1 { +>I1 : I1 + + method(); +>method : () => any +} + +class C1 implements I1 { +>C1 : C1 +>I1 : I1 + + method(a = 0, b?) { } +>method : (a?: number, b?: any) => void +>a : number +>0 : number +>b : any +} diff --git a/tests/cases/compiler/requiredInitializedParameter1.ts b/tests/cases/compiler/requiredInitializedParameter1.ts new file mode 100644 index 00000000000..35d606426f0 --- /dev/null +++ b/tests/cases/compiler/requiredInitializedParameter1.ts @@ -0,0 +1,19 @@ +function f1(a, b = 0, c) { } +function f2(a, b = 0, c = 0) { } +function f3(a, b = 0, c?) { } +function f4(a, b = 0, ...c) { } + +f1(0, 1, 2); +f2(0, 1, 2); +f3(0, 1, 2); +f4(0, 1, 2); + +f1(0, 1); +f2(0, 1); +f3(0, 1); +f4(0, 1); + +f1(0); +f2(0); +f3(0); +f4(0); \ No newline at end of file diff --git a/tests/cases/compiler/requiredInitializedParameter2.ts b/tests/cases/compiler/requiredInitializedParameter2.ts new file mode 100644 index 00000000000..48d42869ee7 --- /dev/null +++ b/tests/cases/compiler/requiredInitializedParameter2.ts @@ -0,0 +1,7 @@ +interface I1 { + method(); +} + +class C1 implements I1 { + method(a = 0, b) { } +} \ No newline at end of file diff --git a/tests/cases/compiler/requiredInitializedParameter3.ts b/tests/cases/compiler/requiredInitializedParameter3.ts new file mode 100644 index 00000000000..803eefb6342 --- /dev/null +++ b/tests/cases/compiler/requiredInitializedParameter3.ts @@ -0,0 +1,8 @@ +//@declaration: true +interface I1 { + method(); +} + +class C1 implements I1 { + method(a = 0, b?) { } +} \ No newline at end of file From 4673d618a7e92c1c49bdf47c655428033543bbe6 Mon Sep 17 00:00:00 2001 From: Jason Freeman Date: Tue, 28 Jul 2015 14:42:08 -0700 Subject: [PATCH 5/5] Make declaration emitter call resolver.isOptionalParameter --- src/compiler/checker.ts | 4 +++- src/compiler/declarationEmitter.ts | 2 +- src/compiler/types.ts | 4 +++- src/services/signatureHelp.ts | 20 +++---------------- .../requiredInitializedParameter4.js | 20 +++++++++++++++++++ .../requiredInitializedParameter4.symbols | 9 +++++++++ .../requiredInitializedParameter4.types | 10 ++++++++++ .../compiler/requiredInitializedParameter4.ts | 4 ++++ 8 files changed, 53 insertions(+), 20 deletions(-) create mode 100644 tests/baselines/reference/requiredInitializedParameter4.js create mode 100644 tests/baselines/reference/requiredInitializedParameter4.symbols create mode 100644 tests/baselines/reference/requiredInitializedParameter4.types create mode 100644 tests/cases/compiler/requiredInitializedParameter4.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 05e1f1963c9..a98dfc1805e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -91,7 +91,8 @@ namespace ts { getExportsOfModule: getExportsOfModuleAsArray, getJsxElementAttributesType, - getJsxIntrinsicTagNames + getJsxIntrinsicTagNames, + isOptionalParameter }; let unknownSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "unknown"); @@ -14402,6 +14403,7 @@ namespace ts { getBlockScopedVariableId, getReferencedValueDeclaration, getTypeReferenceSerializationKind, + isOptionalParameter }; } diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index 6c0803f8761..63d71b0339e 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -1371,7 +1371,7 @@ namespace ts { else { writeTextOfNode(currentSourceFile, node.name); } - if (node.initializer || hasQuestionToken(node)) { + if (resolver.isOptionalParameter(node)) { write("?"); } decreaseIndent(); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 21eed30c244..fb3003b9c78 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1431,6 +1431,7 @@ namespace ts { getJsxElementAttributesType(elementNode: JsxOpeningLikeElement): Type; getJsxIntrinsicTagNames(): Symbol[]; + isOptionalParameter(node: ParameterDeclaration): boolean; // Should not be called directly. Should only be accessed through the Program instance. /* @internal */ getDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): Diagnostic[]; @@ -1574,7 +1575,8 @@ namespace ts { getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number; getBlockScopedVariableId(node: Identifier): number; getReferencedValueDeclaration(reference: Identifier): Declaration; - getTypeReferenceSerializationKind(node: TypeReferenceNode): TypeReferenceSerializationKind; + getTypeReferenceSerializationKind(node: TypeReferenceNode): TypeReferenceSerializationKind; + isOptionalParameter(node: ParameterDeclaration): boolean; } export const enum SymbolFlags { diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts index 6230a66910e..df321647e5c 100644 --- a/src/services/signatureHelp.ts +++ b/src/services/signatureHelp.ts @@ -569,15 +569,7 @@ namespace ts.SignatureHelp { prefixDisplayParts.push(punctuationPart(SyntaxKind.OpenParenToken)); let parameters = candidateSignature.parameters; - if (parameters.length > 0) { - signatureHelpParameters = []; - for (let i = 0; i < parameters.length; i++) { - signatureHelpParameters.push(createSignatureHelpParameterAtIndex(candidateSignature, i)); - } - } - else { - signatureHelpParameters = emptyArray; - } + signatureHelpParameters = parameters.length > 0 ? map(parameters, createSignatureHelpParameterForParameter) : emptyArray; suffixDisplayParts.push(punctuationPart(SyntaxKind.CloseParenToken)); } @@ -615,21 +607,15 @@ namespace ts.SignatureHelp { argumentCount }; - function createSignatureHelpParameterAtIndex(signature: Signature, parameterIndex: number): SignatureHelpParameter { - let parameter = signature.parameters[parameterIndex]; + function createSignatureHelpParameterForParameter(parameter: Symbol): SignatureHelpParameter { let displayParts = mapToDisplayParts(writer => typeChecker.getSymbolDisplayBuilder().buildParameterDisplay(parameter, writer, invocation)); - let parameterDeclaration = parameter.valueDeclaration; - let isOptional = - hasQuestionToken(parameterDeclaration) || - parameterDeclaration.initializer && parameterIndex >= signature.minArgumentCount; - return { name: parameter.name, documentation: parameter.getDocumentationComment(), displayParts, - isOptional + isOptional: typeChecker.isOptionalParameter(parameter.valueDeclaration) }; } diff --git a/tests/baselines/reference/requiredInitializedParameter4.js b/tests/baselines/reference/requiredInitializedParameter4.js new file mode 100644 index 00000000000..3b23f119391 --- /dev/null +++ b/tests/baselines/reference/requiredInitializedParameter4.js @@ -0,0 +1,20 @@ +//// [requiredInitializedParameter4.ts] +class C1 { + method(a = 0, b) { } +} + +//// [requiredInitializedParameter4.js] +var C1 = (function () { + function C1() { + } + C1.prototype.method = function (a, b) { + if (a === void 0) { a = 0; } + }; + return C1; +})(); + + +//// [requiredInitializedParameter4.d.ts] +declare class C1 { + method(a: number, b: any): void; +} diff --git a/tests/baselines/reference/requiredInitializedParameter4.symbols b/tests/baselines/reference/requiredInitializedParameter4.symbols new file mode 100644 index 00000000000..2790b33698c --- /dev/null +++ b/tests/baselines/reference/requiredInitializedParameter4.symbols @@ -0,0 +1,9 @@ +=== tests/cases/compiler/requiredInitializedParameter4.ts === +class C1 { +>C1 : Symbol(C1, Decl(requiredInitializedParameter4.ts, 0, 0)) + + method(a = 0, b) { } +>method : Symbol(method, Decl(requiredInitializedParameter4.ts, 0, 10)) +>a : Symbol(a, Decl(requiredInitializedParameter4.ts, 1, 11)) +>b : Symbol(b, Decl(requiredInitializedParameter4.ts, 1, 17)) +} diff --git a/tests/baselines/reference/requiredInitializedParameter4.types b/tests/baselines/reference/requiredInitializedParameter4.types new file mode 100644 index 00000000000..c483146a1ba --- /dev/null +++ b/tests/baselines/reference/requiredInitializedParameter4.types @@ -0,0 +1,10 @@ +=== tests/cases/compiler/requiredInitializedParameter4.ts === +class C1 { +>C1 : C1 + + method(a = 0, b) { } +>method : (a: number, b: any) => void +>a : number +>0 : number +>b : any +} diff --git a/tests/cases/compiler/requiredInitializedParameter4.ts b/tests/cases/compiler/requiredInitializedParameter4.ts new file mode 100644 index 00000000000..4ba15878e43 --- /dev/null +++ b/tests/cases/compiler/requiredInitializedParameter4.ts @@ -0,0 +1,4 @@ +//@declaration: true +class C1 { + method(a = 0, b) { } +} \ No newline at end of file