Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 36 additions & 19 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ namespace ts {
getExportsOfModule: getExportsOfModuleAsArray,

getJsxElementAttributesType,
getJsxIntrinsicTagNames
getJsxIntrinsicTagNames,
isOptionalParameter
};

let unknownSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "unknown");
Expand Down Expand Up @@ -1994,15 +1995,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
});
}

Expand Down Expand Up @@ -3517,7 +3518,19 @@ namespace ts {
}

function isOptionalParameter(node: ParameterDeclaration) {
return hasQuestionToken(node) || !!node.initializer;
if (hasQuestionToken(node)) {
return true;
}

if (node.initializer) {
let signatureDeclaration = <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 {
Expand All @@ -3535,11 +3548,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) {
Expand Down Expand Up @@ -14402,6 +14420,7 @@ namespace ts {
getBlockScopedVariableId,
getReferencedValueDeclaration,
getTypeReferenceSerializationKind,
isOptionalParameter
};
}

Expand Down Expand Up @@ -14797,17 +14816,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);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/declarationEmitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1371,7 +1371,7 @@ namespace ts {
else {
writeTextOfNode(currentSourceFile, node.name);
}
if (node.initializer || hasQuestionToken(node)) {
if (resolver.isOptionalParameter(node)) {
write("?");
}
decreaseIndent();
Expand Down
4 changes: 3 additions & 1 deletion src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[];
Expand Down Expand Up @@ -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 {
Expand Down
4 changes: 1 addition & 3 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -988,15 +988,13 @@ namespace ts {
if (node) {
switch (node.kind) {
case SyntaxKind.Parameter:
return (<ParameterDeclaration>node).questionToken !== undefined;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
return (<MethodDeclaration>node).questionToken !== undefined;
case SyntaxKind.ShorthandPropertyAssignment:
case SyntaxKind.PropertyAssignment:
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.PropertySignature:
return (<PropertyDeclaration>node).questionToken !== undefined;
return (<ParameterDeclaration | MethodDeclaration | PropertyDeclaration>node).questionToken !== undefined;
}
}

Expand Down
4 changes: 1 addition & 3 deletions src/services/signatureHelp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -611,13 +611,11 @@ namespace ts.SignatureHelp {
let displayParts = mapToDisplayParts(writer =>
typeChecker.getSymbolDisplayBuilder().buildParameterDisplay(parameter, writer, invocation));

let isOptional = hasQuestionToken(parameter.valueDeclaration);

return {
name: parameter.name,
documentation: parameter.getDocumentationComment(),
displayParts,
isOptional
isOptional: typeChecker.isOptionalParameter(<ParameterDeclaration>parameter.valueDeclaration)
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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.
// Uninitialized parameter makes the initialized one required
(arg1 = 1, arg2) => 1;
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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;
Expand Down
12 changes: 4 additions & 8 deletions tests/baselines/reference/optionalParamArgsTest.errors.txt
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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:
Expand Down Expand Up @@ -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 {
Expand Down
6 changes: 2 additions & 4 deletions tests/baselines/reference/optionalParamArgsTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
}

Expand Down Expand Up @@ -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;
Expand Down
28 changes: 28 additions & 0 deletions tests/baselines/reference/requiredInitializedParameter1.errors.txt
Original file line number Diff line number Diff line change
@@ -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);
51 changes: 51 additions & 0 deletions tests/baselines/reference/requiredInitializedParameter1.js
Original file line number Diff line number Diff line change
@@ -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);
17 changes: 17 additions & 0 deletions tests/baselines/reference/requiredInitializedParameter2.errors.txt
Original file line number Diff line number Diff line change
@@ -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) { }
}
18 changes: 18 additions & 0 deletions tests/baselines/reference/requiredInitializedParameter2.js
Original file line number Diff line number Diff line change
@@ -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;
})();
Loading