Merge pull request #4022 from Microsoft/defaultInitializer

Default initializers on required parameters
This commit is contained in:
Jason Freeman 2015-07-28 15:27:04 -07:00
commit bbadd702e7
25 changed files with 308 additions and 50 deletions

View file

@ -91,7 +91,8 @@ namespace ts {
getExportsOfModule: getExportsOfModuleAsArray,
getJsxElementAttributesType,
getJsxIntrinsicTagNames
getJsxIntrinsicTagNames,
isOptionalParameter
};
let unknownSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "unknown");
@ -1998,15 +1999,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
});
}
@ -3521,7 +3522,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 {
@ -3539,11 +3552,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) {
@ -14413,6 +14431,7 @@ namespace ts {
getBlockScopedVariableId,
getReferencedValueDeclaration,
getTypeReferenceSerializationKind,
isOptionalParameter
};
}
@ -14808,17 +14827,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);
}
}
}

View file

@ -1371,7 +1371,7 @@ namespace ts {
else {
writeTextOfNode(currentSourceFile, node.name);
}
if (node.initializer || hasQuestionToken(node)) {
if (resolver.isOptionalParameter(node)) {
write("?");
}
decreaseIndent();

View file

@ -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 {

View file

@ -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;
}
}

View file

@ -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)
};
}

View file

@ -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.
// Uninitialized parameter makes the initialized one required
(arg1 = 1, arg2) => 1;

View file

@ -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;

View file

@ -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 {

View file

@ -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;

View file

@ -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);

View file

@ -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);

View file

@ -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) { }
}

View file

@ -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;
})();

View file

@ -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;
}

View file

@ -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))
}

View file

@ -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
}

View file

@ -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;
}

View file

@ -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))
}

View file

@ -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
}

View file

@ -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;

View file

@ -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; }
}

View file

@ -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);

View file

@ -0,0 +1,7 @@
interface I1 {
method();
}
class C1 implements I1 {
method(a = 0, b) { }
}

View file

@ -0,0 +1,8 @@
//@declaration: true
interface I1 {
method();
}
class C1 implements I1 {
method(a = 0, b?) { }
}

View file

@ -0,0 +1,4 @@
//@declaration: true
class C1 {
method(a = 0, b) { }
}