diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f9b84e857dd11..bdfbc90c3e2ac 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -79,6 +79,7 @@ namespace ts { getIndexTypeOfType, getBaseTypes, getReturnTypeOfSignature, + getNonNullableType, getSymbolsInScope, getSymbolAtLocation, getShorthandAssignmentValueSymbol, @@ -2885,6 +2886,10 @@ namespace ts { return undefined; } + function addOptionality(type: Type, optional: boolean): Type { + return strictNullChecks && optional ? addNullableKind(type, TypeFlags.Undefined) : type; + } + // Return the inferred type for a variable, parameter, or property declaration function getTypeForVariableLikeDeclaration(declaration: VariableLikeDeclaration): Type { if (declaration.flags & NodeFlags.JavaScriptFile) { @@ -2916,8 +2921,7 @@ namespace ts { // Use type from type annotation if one is present if (declaration.type) { - const type = getTypeFromTypeNode(declaration.type); - return strictNullChecks && declaration.questionToken ? addNullableKind(type, TypeFlags.Undefined) : type; + return addOptionality(getTypeFromTypeNode(declaration.type), /*optional*/ !!declaration.questionToken); } if (declaration.kind === SyntaxKind.Parameter) { @@ -2939,13 +2943,13 @@ namespace ts { ? getContextuallyTypedThisType(func) : getContextuallyTypedParameterType(declaration); if (type) { - return strictNullChecks && declaration.questionToken ? addNullableKind(type, TypeFlags.Undefined) : type; + return addOptionality(type, /*optional*/ !!declaration.questionToken); } } // Use the type of the initializer expression if one is present if (declaration.initializer) { - return checkExpressionCached(declaration.initializer); + return addOptionality(checkExpressionCached(declaration.initializer), /*optional*/ !!declaration.questionToken); } // If it is a short-hand property assignment, use the type of the identifier @@ -3216,7 +3220,9 @@ namespace ts { function getTypeOfFuncClassEnumModule(symbol: Symbol): Type { const links = getSymbolLinks(symbol); if (!links.type) { - links.type = createObjectType(TypeFlags.Anonymous, symbol); + const type = createObjectType(TypeFlags.Anonymous, symbol); + links.type = strictNullChecks && symbol.flags & SymbolFlags.Optional ? + addNullableKind(type, TypeFlags.Undefined) : type; } return links.type; } @@ -7615,11 +7621,12 @@ namespace ts { getInitialTypeOfBindingElement(node); } - function getFlowTypeOfReference(reference: Node, declaredType: Type, initialType: Type) { + function getFlowTypeOfReference(reference: Node, declaredType: Type, assumeInitialized: boolean) { let key: string; - if (!reference.flowNode || declaredType === initialType && !(declaredType.flags & TypeFlags.Narrowable)) { + if (!reference.flowNode || assumeInitialized && !(declaredType.flags & TypeFlags.Narrowable)) { return declaredType; } + const initialType = assumeInitialized ? declaredType : addNullableKind(declaredType, TypeFlags.Undefined); const visitedFlowStart = visitedFlowCount; const result = getTypeAtFlowNode(reference.flowNode); visitedFlowCount = visitedFlowStart; @@ -8093,11 +8100,11 @@ namespace ts { return type; } const declaration = localOrExportSymbol.valueDeclaration; - const defaultsToDeclaredType = !strictNullChecks || type.flags & TypeFlags.Any || !declaration || + const assumeInitialized = !strictNullChecks || (type.flags & TypeFlags.Any) !== 0 || !declaration || getRootDeclaration(declaration).kind === SyntaxKind.Parameter || isInAmbientContext(declaration) || getContainingFunctionOrModule(declaration) !== getContainingFunctionOrModule(node); - const flowType = getFlowTypeOfReference(node, type, defaultsToDeclaredType ? type : addNullableKind(type, TypeFlags.Undefined)); - if (strictNullChecks && !(type.flags & TypeFlags.Any) && !(getNullableKind(type) & TypeFlags.Undefined) && getNullableKind(flowType) & TypeFlags.Undefined) { + const flowType = getFlowTypeOfReference(node, type, assumeInitialized); + if (!assumeInitialized && !(getNullableKind(type) & TypeFlags.Undefined) && getNullableKind(flowType) & TypeFlags.Undefined) { error(node, Diagnostics.Variable_0_is_used_before_being_assigned, symbolToString(symbol)); // Return the declared type to reduce follow-on errors return type; @@ -8345,7 +8352,7 @@ namespace ts { if (isClassLike(container.parent)) { const symbol = getSymbolOfNode(container.parent); const type = container.flags & NodeFlags.Static ? getTypeOfSymbol(symbol) : (getDeclaredTypeOfSymbol(symbol)).thisType; - return getFlowTypeOfReference(node, type, type); + return getFlowTypeOfReference(node, type, /*assumeInitialized*/ true); } if (isInJavaScriptFile(node)) { @@ -9920,7 +9927,8 @@ namespace ts { } const propType = getTypeOfSymbol(prop); - if (node.kind !== SyntaxKind.PropertyAccessExpression || !(prop.flags & (SymbolFlags.Variable | SymbolFlags.Property | SymbolFlags.Accessor)) || isAssignmentTarget(node)) { + if (node.kind !== SyntaxKind.PropertyAccessExpression || isAssignmentTarget(node) || + !(propType.flags & TypeFlags.Union) && !(prop.flags & (SymbolFlags.Variable | SymbolFlags.Property | SymbolFlags.Accessor))) { return propType; } const leftmostNode = getLeftmostIdentifierOrThis(node); @@ -9937,7 +9945,7 @@ namespace ts { return propType; } } - return getFlowTypeOfReference(node, propType, propType); + return getFlowTypeOfReference(node, propType, /*assumeInitialized*/ true); } function isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean { @@ -13395,7 +13403,7 @@ namespace ts { // Abstract methods can't have an implementation -- in particular, they don't need one. if (!isExportSymbolInsideModule && lastSeenNonAmbientDeclaration && !lastSeenNonAmbientDeclaration.body && - !(lastSeenNonAmbientDeclaration.flags & NodeFlags.Abstract)) { + !(lastSeenNonAmbientDeclaration.flags & NodeFlags.Abstract) && !lastSeenNonAmbientDeclaration.questionToken) { reportImplementationExpectedError(lastSeenNonAmbientDeclaration); } @@ -18289,7 +18297,7 @@ namespace ts { } if (node.parent.kind === SyntaxKind.ObjectLiteralExpression) { - if (checkGrammarForInvalidQuestionMark(node, node.questionToken, Diagnostics.A_class_member_cannot_be_declared_optional)) { + if (checkGrammarForInvalidQuestionMark(node, node.questionToken, Diagnostics.An_object_member_cannot_be_declared_optional)) { return true; } else if (node.body === undefined) { @@ -18298,9 +18306,6 @@ namespace ts { } if (isClassLike(node.parent)) { - if (checkGrammarForInvalidQuestionMark(node, node.questionToken, Diagnostics.A_class_member_cannot_be_declared_optional)) { - return true; - } // Technically, computed properties in ambient contexts is disallowed // for property declarations and accessors too, not just methods. // However, property declarations disallow computed names in general, @@ -18522,8 +18527,7 @@ namespace ts { function checkGrammarProperty(node: PropertyDeclaration) { if (isClassLike(node.parent)) { - if (checkGrammarForInvalidQuestionMark(node, node.questionToken, Diagnostics.A_class_member_cannot_be_declared_optional) || - checkGrammarForNonSymbolComputedProperty(node.name, Diagnostics.A_computed_property_name_in_a_class_property_declaration_must_directly_refer_to_a_built_in_symbol)) { + if (checkGrammarForNonSymbolComputedProperty(node.name, Diagnostics.A_computed_property_name_in_a_class_property_declaration_must_directly_refer_to_a_built_in_symbol)) { return true; } } diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index aceec9332277d..a94c9a58d2185 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -1129,7 +1129,7 @@ namespace ts { // what we want, namely the name expression enclosed in brackets. writeTextOfNode(currentText, node.name); // If optional property emit ? - if ((node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertySignature) && hasQuestionToken(node)) { + if ((node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertySignature || node.kind === SyntaxKind.Parameter) && hasQuestionToken(node)) { write("?"); } if ((node.kind === SyntaxKind.PropertyDeclaration || node.kind === SyntaxKind.PropertySignature) && node.parent.kind === SyntaxKind.TypeLiteral) { diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 899b4292f04d1..cb347234ec7ed 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -315,10 +315,6 @@ "category": "Error", "code": 1110 }, - "A class member cannot be declared optional.": { - "category": "Error", - "code": 1112 - }, "A 'default' clause cannot appear more than once in a 'switch' statement.": { "category": "Error", "code": 1113 diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 3a5602717c156..d924ac168c8f0 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1770,6 +1770,7 @@ namespace ts { getIndexTypeOfType(type: Type, kind: IndexKind): Type; getBaseTypes(type: InterfaceType): ObjectType[]; getReturnTypeOfSignature(signature: Signature): Type; + getNonNullableType(type: Type): Type; getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[]; getSymbolAtLocation(node: Node): Symbol; diff --git a/src/services/services.ts b/src/services/services.ts index 83f0d7152fd75..d8abb4e6842b8 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -50,6 +50,7 @@ namespace ts { getStringIndexType(): Type; getNumberIndexType(): Type; getBaseTypes(): ObjectType[]; + getNonNullableType(): Type; } export interface Signature { @@ -735,6 +736,9 @@ namespace ts { ? this.checker.getBaseTypes(this) : undefined; } + getNonNullableType(): Type { + return this.checker.getNonNullableType(this); + } } class SignatureObject implements Signature { @@ -4366,7 +4370,7 @@ namespace ts { (location.kind === SyntaxKind.ConstructorKeyword && location.parent.kind === SyntaxKind.Constructor)) { // At constructor keyword of constructor declaration // get the signature from the declaration and write it const functionDeclaration = location.parent; - const allSignatures = functionDeclaration.kind === SyntaxKind.Constructor ? type.getConstructSignatures() : type.getCallSignatures(); + const allSignatures = functionDeclaration.kind === SyntaxKind.Constructor ? type.getNonNullableType().getConstructSignatures() : type.getNonNullableType().getCallSignatures(); if (!typeChecker.isImplementationOfOverload(functionDeclaration)) { signature = typeChecker.getSignatureFromDeclaration(functionDeclaration); } @@ -4564,7 +4568,7 @@ namespace ts { symbolFlags & SymbolFlags.Signature || symbolFlags & SymbolFlags.Accessor || symbolKind === ScriptElementKind.memberFunctionElement) { - const allSignatures = type.getCallSignatures(); + const allSignatures = type.getNonNullableType().getCallSignatures(); addSignatureDisplayParts(allSignatures[0], allSignatures); } } diff --git a/tests/baselines/reference/classWithOptionalParameter.errors.txt b/tests/baselines/reference/classWithOptionalParameter.errors.txt deleted file mode 100644 index 6e512c2a02e60..0000000000000 --- a/tests/baselines/reference/classWithOptionalParameter.errors.txt +++ /dev/null @@ -1,26 +0,0 @@ -tests/cases/conformance/types/namedTypes/classWithOptionalParameter.ts(4,6): error TS1112: A class member cannot be declared optional. -tests/cases/conformance/types/namedTypes/classWithOptionalParameter.ts(5,6): error TS1112: A class member cannot be declared optional. -tests/cases/conformance/types/namedTypes/classWithOptionalParameter.ts(9,6): error TS1112: A class member cannot be declared optional. -tests/cases/conformance/types/namedTypes/classWithOptionalParameter.ts(10,6): error TS1112: A class member cannot be declared optional. - - -==== tests/cases/conformance/types/namedTypes/classWithOptionalParameter.ts (4 errors) ==== - // classes do not permit optional parameters, these are errors - - class C { - x?: string; - ~ -!!! error TS1112: A class member cannot be declared optional. - f?() {} - ~ -!!! error TS1112: A class member cannot be declared optional. - } - - class C2 { - x?: T; - ~ -!!! error TS1112: A class member cannot be declared optional. - f?(x: T) {} - ~ -!!! error TS1112: A class member cannot be declared optional. - } \ No newline at end of file diff --git a/tests/baselines/reference/classWithOptionalParameter.symbols b/tests/baselines/reference/classWithOptionalParameter.symbols new file mode 100644 index 0000000000000..b85cd5d7a07b5 --- /dev/null +++ b/tests/baselines/reference/classWithOptionalParameter.symbols @@ -0,0 +1,26 @@ +=== tests/cases/conformance/types/namedTypes/classWithOptionalParameter.ts === +// classes do not permit optional parameters, these are errors + +class C { +>C : Symbol(C, Decl(classWithOptionalParameter.ts, 0, 0)) + + x?: string; +>x : Symbol(C.x, Decl(classWithOptionalParameter.ts, 2, 9)) + + f?() {} +>f : Symbol(C.f, Decl(classWithOptionalParameter.ts, 3, 15)) +} + +class C2 { +>C2 : Symbol(C2, Decl(classWithOptionalParameter.ts, 5, 1)) +>T : Symbol(T, Decl(classWithOptionalParameter.ts, 7, 9)) + + x?: T; +>x : Symbol(C2.x, Decl(classWithOptionalParameter.ts, 7, 13)) +>T : Symbol(T, Decl(classWithOptionalParameter.ts, 7, 9)) + + f?(x: T) {} +>f : Symbol(C2.f, Decl(classWithOptionalParameter.ts, 8, 10)) +>x : Symbol(x, Decl(classWithOptionalParameter.ts, 9, 7)) +>T : Symbol(T, Decl(classWithOptionalParameter.ts, 7, 9)) +} diff --git a/tests/baselines/reference/classWithOptionalParameter.types b/tests/baselines/reference/classWithOptionalParameter.types new file mode 100644 index 0000000000000..2c62aee6faba5 --- /dev/null +++ b/tests/baselines/reference/classWithOptionalParameter.types @@ -0,0 +1,26 @@ +=== tests/cases/conformance/types/namedTypes/classWithOptionalParameter.ts === +// classes do not permit optional parameters, these are errors + +class C { +>C : C + + x?: string; +>x : string + + f?() {} +>f : () => void +} + +class C2 { +>C2 : C2 +>T : T + + x?: T; +>x : T +>T : T + + f?(x: T) {} +>f : (x: T) => void +>x : T +>T : T +} diff --git a/tests/baselines/reference/declFileConstructors.js b/tests/baselines/reference/declFileConstructors.js index 44e816712fbcd..8e92b1a36aef6 100644 --- a/tests/baselines/reference/declFileConstructors.js +++ b/tests/baselines/reference/declFileConstructors.js @@ -247,7 +247,7 @@ export declare class ConstructorWithPrivateParameterProperty { constructor(x: string); } export declare class ConstructorWithOptionalParameterProperty { - x: string; + x?: string; constructor(x?: string); } export declare class ConstructorWithParameterInitializer { @@ -281,7 +281,7 @@ declare class GlobalConstructorWithPrivateParameterProperty { constructor(x: string); } declare class GlobalConstructorWithOptionalParameterProperty { - x: string; + x?: string; constructor(x?: string); } declare class GlobalConstructorWithParameterInitializer { diff --git a/tests/baselines/reference/objectLiteralMemberWithQuestionMark1.errors.txt b/tests/baselines/reference/objectLiteralMemberWithQuestionMark1.errors.txt index b10ba7011435e..f77bad8664cec 100644 --- a/tests/baselines/reference/objectLiteralMemberWithQuestionMark1.errors.txt +++ b/tests/baselines/reference/objectLiteralMemberWithQuestionMark1.errors.txt @@ -1,7 +1,7 @@ -tests/cases/compiler/objectLiteralMemberWithQuestionMark1.ts(1,14): error TS1112: A class member cannot be declared optional. +tests/cases/compiler/objectLiteralMemberWithQuestionMark1.ts(1,14): error TS1162: An object member cannot be declared optional. ==== tests/cases/compiler/objectLiteralMemberWithQuestionMark1.ts (1 errors) ==== var v = { foo?() { } } ~ -!!! error TS1112: A class member cannot be declared optional. \ No newline at end of file +!!! error TS1162: An object member cannot be declared optional. \ No newline at end of file diff --git a/tests/baselines/reference/objectTypesWithOptionalProperties.errors.txt b/tests/baselines/reference/objectTypesWithOptionalProperties.errors.txt index a2269c29d11d7..74057195c6449 100644 --- a/tests/baselines/reference/objectTypesWithOptionalProperties.errors.txt +++ b/tests/baselines/reference/objectTypesWithOptionalProperties.errors.txt @@ -1,9 +1,7 @@ -tests/cases/conformance/types/objectTypeLiteral/methodSignatures/objectTypesWithOptionalProperties.ts(12,6): error TS1112: A class member cannot be declared optional. -tests/cases/conformance/types/objectTypeLiteral/methodSignatures/objectTypesWithOptionalProperties.ts(20,6): error TS1112: A class member cannot be declared optional. tests/cases/conformance/types/objectTypeLiteral/methodSignatures/objectTypesWithOptionalProperties.ts(24,6): error TS1162: An object member cannot be declared optional. -==== tests/cases/conformance/types/objectTypeLiteral/methodSignatures/objectTypesWithOptionalProperties.ts (3 errors) ==== +==== tests/cases/conformance/types/objectTypeLiteral/methodSignatures/objectTypesWithOptionalProperties.ts (1 errors) ==== // Basic uses of optional properties var a: { @@ -15,9 +13,7 @@ tests/cases/conformance/types/objectTypeLiteral/methodSignatures/objectTypesWith } class C { - x?: number; // error - ~ -!!! error TS1112: A class member cannot be declared optional. + x?: number; // ok } interface I2 { @@ -25,9 +21,7 @@ tests/cases/conformance/types/objectTypeLiteral/methodSignatures/objectTypesWith } class C2 { - x?: T; // error - ~ -!!! error TS1112: A class member cannot be declared optional. + x?: T; // ok } var b = { diff --git a/tests/baselines/reference/objectTypesWithOptionalProperties.js b/tests/baselines/reference/objectTypesWithOptionalProperties.js index 98141c048dd0b..9b2ada010d920 100644 --- a/tests/baselines/reference/objectTypesWithOptionalProperties.js +++ b/tests/baselines/reference/objectTypesWithOptionalProperties.js @@ -10,7 +10,7 @@ interface I { } class C { - x?: number; // error + x?: number; // ok } interface I2 { @@ -18,7 +18,7 @@ interface I2 { } class C2 { - x?: T; // error + x?: T; // ok } var b = { diff --git a/tests/baselines/reference/optionalMethods.js b/tests/baselines/reference/optionalMethods.js new file mode 100644 index 0000000000000..42decf512e4d7 --- /dev/null +++ b/tests/baselines/reference/optionalMethods.js @@ -0,0 +1,147 @@ +//// [optionalMethods.ts] + +interface Foo { + a: number; + b?: number; + f(): number; + g?(): number; +} + +function test1(x: Foo) { + x.a; + x.b; + x.f; + x.g; + let f1 = x.f(); + let g1 = x.g && x.g(); + let g2 = x.g ? x.g() : 0; +} + +class Bar { + a: number; + b?: number; + c? = 2; + constructor(public d?: number, public e = 10) {} + f() { + return 1; + } + g?(): number; // Body of optional method can be omitted + h?() { + return 2; + } +} + +function test2(x: Bar) { + x.a; + x.b; + x.c; + x.d; + x.e; + x.f; + x.g; + let f1 = x.f(); + let g1 = x.g && x.g(); + let g2 = x.g ? x.g() : 0; + let h1 = x.h && x.h(); + let h2 = x.h ? x.h() : 0; +} + +class Base { + a?: number; + f?(): number; +} + +class Derived extends Base { + a = 1; + f(): number { return 1; } +} + + +//// [optionalMethods.js] +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +function test1(x) { + x.a; + x.b; + x.f; + x.g; + var f1 = x.f(); + var g1 = x.g && x.g(); + var g2 = x.g ? x.g() : 0; +} +var Bar = (function () { + function Bar(d, e) { + if (e === void 0) { e = 10; } + this.d = d; + this.e = e; + this.c = 2; + } + Bar.prototype.f = function () { + return 1; + }; + Bar.prototype.h = function () { + return 2; + }; + return Bar; +}()); +function test2(x) { + x.a; + x.b; + x.c; + x.d; + x.e; + x.f; + x.g; + var f1 = x.f(); + var g1 = x.g && x.g(); + var g2 = x.g ? x.g() : 0; + var h1 = x.h && x.h(); + var h2 = x.h ? x.h() : 0; +} +var Base = (function () { + function Base() { + } + return Base; +}()); +var Derived = (function (_super) { + __extends(Derived, _super); + function Derived() { + _super.apply(this, arguments); + this.a = 1; + } + Derived.prototype.f = function () { return 1; }; + return Derived; +}(Base)); + + +//// [optionalMethods.d.ts] +interface Foo { + a: number; + b?: number; + f(): number; + g?(): number; +} +declare function test1(x: Foo): void; +declare class Bar { + d?: number; + e: number; + a: number; + b?: number; + c?: number | undefined; + constructor(d?: number, e?: number); + f(): number; + g?(): number; + h?(): number; +} +declare function test2(x: Bar): void; +declare class Base { + a?: number; + f?(): number; +} +declare class Derived extends Base { + a: number; + f(): number; +} diff --git a/tests/baselines/reference/optionalMethods.symbols b/tests/baselines/reference/optionalMethods.symbols new file mode 100644 index 0000000000000..55c5ec958da97 --- /dev/null +++ b/tests/baselines/reference/optionalMethods.symbols @@ -0,0 +1,203 @@ +=== tests/cases/conformance/types/namedTypes/optionalMethods.ts === + +interface Foo { +>Foo : Symbol(Foo, Decl(optionalMethods.ts, 0, 0)) + + a: number; +>a : Symbol(Foo.a, Decl(optionalMethods.ts, 1, 15)) + + b?: number; +>b : Symbol(Foo.b, Decl(optionalMethods.ts, 2, 14)) + + f(): number; +>f : Symbol(Foo.f, Decl(optionalMethods.ts, 3, 15)) + + g?(): number; +>g : Symbol(Foo.g, Decl(optionalMethods.ts, 4, 16)) +} + +function test1(x: Foo) { +>test1 : Symbol(test1, Decl(optionalMethods.ts, 6, 1)) +>x : Symbol(x, Decl(optionalMethods.ts, 8, 15)) +>Foo : Symbol(Foo, Decl(optionalMethods.ts, 0, 0)) + + x.a; +>x.a : Symbol(Foo.a, Decl(optionalMethods.ts, 1, 15)) +>x : Symbol(x, Decl(optionalMethods.ts, 8, 15)) +>a : Symbol(Foo.a, Decl(optionalMethods.ts, 1, 15)) + + x.b; +>x.b : Symbol(Foo.b, Decl(optionalMethods.ts, 2, 14)) +>x : Symbol(x, Decl(optionalMethods.ts, 8, 15)) +>b : Symbol(Foo.b, Decl(optionalMethods.ts, 2, 14)) + + x.f; +>x.f : Symbol(Foo.f, Decl(optionalMethods.ts, 3, 15)) +>x : Symbol(x, Decl(optionalMethods.ts, 8, 15)) +>f : Symbol(Foo.f, Decl(optionalMethods.ts, 3, 15)) + + x.g; +>x.g : Symbol(Foo.g, Decl(optionalMethods.ts, 4, 16)) +>x : Symbol(x, Decl(optionalMethods.ts, 8, 15)) +>g : Symbol(Foo.g, Decl(optionalMethods.ts, 4, 16)) + + let f1 = x.f(); +>f1 : Symbol(f1, Decl(optionalMethods.ts, 13, 7)) +>x.f : Symbol(Foo.f, Decl(optionalMethods.ts, 3, 15)) +>x : Symbol(x, Decl(optionalMethods.ts, 8, 15)) +>f : Symbol(Foo.f, Decl(optionalMethods.ts, 3, 15)) + + let g1 = x.g && x.g(); +>g1 : Symbol(g1, Decl(optionalMethods.ts, 14, 7)) +>x.g : Symbol(Foo.g, Decl(optionalMethods.ts, 4, 16)) +>x : Symbol(x, Decl(optionalMethods.ts, 8, 15)) +>g : Symbol(Foo.g, Decl(optionalMethods.ts, 4, 16)) +>x.g : Symbol(Foo.g, Decl(optionalMethods.ts, 4, 16)) +>x : Symbol(x, Decl(optionalMethods.ts, 8, 15)) +>g : Symbol(Foo.g, Decl(optionalMethods.ts, 4, 16)) + + let g2 = x.g ? x.g() : 0; +>g2 : Symbol(g2, Decl(optionalMethods.ts, 15, 7)) +>x.g : Symbol(Foo.g, Decl(optionalMethods.ts, 4, 16)) +>x : Symbol(x, Decl(optionalMethods.ts, 8, 15)) +>g : Symbol(Foo.g, Decl(optionalMethods.ts, 4, 16)) +>x.g : Symbol(Foo.g, Decl(optionalMethods.ts, 4, 16)) +>x : Symbol(x, Decl(optionalMethods.ts, 8, 15)) +>g : Symbol(Foo.g, Decl(optionalMethods.ts, 4, 16)) +} + +class Bar { +>Bar : Symbol(Bar, Decl(optionalMethods.ts, 16, 1)) + + a: number; +>a : Symbol(Bar.a, Decl(optionalMethods.ts, 18, 11)) + + b?: number; +>b : Symbol(Bar.b, Decl(optionalMethods.ts, 19, 14)) + + c? = 2; +>c : Symbol(Bar.c, Decl(optionalMethods.ts, 20, 15)) + + constructor(public d?: number, public e = 10) {} +>d : Symbol(Bar.d, Decl(optionalMethods.ts, 22, 16)) +>e : Symbol(Bar.e, Decl(optionalMethods.ts, 22, 34)) + + f() { +>f : Symbol(Bar.f, Decl(optionalMethods.ts, 22, 52)) + + return 1; + } + g?(): number; // Body of optional method can be omitted +>g : Symbol(Bar.g, Decl(optionalMethods.ts, 25, 5)) + + h?() { +>h : Symbol(Bar.h, Decl(optionalMethods.ts, 26, 17)) + + return 2; + } +} + +function test2(x: Bar) { +>test2 : Symbol(test2, Decl(optionalMethods.ts, 30, 1)) +>x : Symbol(x, Decl(optionalMethods.ts, 32, 15)) +>Bar : Symbol(Bar, Decl(optionalMethods.ts, 16, 1)) + + x.a; +>x.a : Symbol(Bar.a, Decl(optionalMethods.ts, 18, 11)) +>x : Symbol(x, Decl(optionalMethods.ts, 32, 15)) +>a : Symbol(Bar.a, Decl(optionalMethods.ts, 18, 11)) + + x.b; +>x.b : Symbol(Bar.b, Decl(optionalMethods.ts, 19, 14)) +>x : Symbol(x, Decl(optionalMethods.ts, 32, 15)) +>b : Symbol(Bar.b, Decl(optionalMethods.ts, 19, 14)) + + x.c; +>x.c : Symbol(Bar.c, Decl(optionalMethods.ts, 20, 15)) +>x : Symbol(x, Decl(optionalMethods.ts, 32, 15)) +>c : Symbol(Bar.c, Decl(optionalMethods.ts, 20, 15)) + + x.d; +>x.d : Symbol(Bar.d, Decl(optionalMethods.ts, 22, 16)) +>x : Symbol(x, Decl(optionalMethods.ts, 32, 15)) +>d : Symbol(Bar.d, Decl(optionalMethods.ts, 22, 16)) + + x.e; +>x.e : Symbol(Bar.e, Decl(optionalMethods.ts, 22, 34)) +>x : Symbol(x, Decl(optionalMethods.ts, 32, 15)) +>e : Symbol(Bar.e, Decl(optionalMethods.ts, 22, 34)) + + x.f; +>x.f : Symbol(Bar.f, Decl(optionalMethods.ts, 22, 52)) +>x : Symbol(x, Decl(optionalMethods.ts, 32, 15)) +>f : Symbol(Bar.f, Decl(optionalMethods.ts, 22, 52)) + + x.g; +>x.g : Symbol(Bar.g, Decl(optionalMethods.ts, 25, 5)) +>x : Symbol(x, Decl(optionalMethods.ts, 32, 15)) +>g : Symbol(Bar.g, Decl(optionalMethods.ts, 25, 5)) + + let f1 = x.f(); +>f1 : Symbol(f1, Decl(optionalMethods.ts, 40, 7)) +>x.f : Symbol(Bar.f, Decl(optionalMethods.ts, 22, 52)) +>x : Symbol(x, Decl(optionalMethods.ts, 32, 15)) +>f : Symbol(Bar.f, Decl(optionalMethods.ts, 22, 52)) + + let g1 = x.g && x.g(); +>g1 : Symbol(g1, Decl(optionalMethods.ts, 41, 7)) +>x.g : Symbol(Bar.g, Decl(optionalMethods.ts, 25, 5)) +>x : Symbol(x, Decl(optionalMethods.ts, 32, 15)) +>g : Symbol(Bar.g, Decl(optionalMethods.ts, 25, 5)) +>x.g : Symbol(Bar.g, Decl(optionalMethods.ts, 25, 5)) +>x : Symbol(x, Decl(optionalMethods.ts, 32, 15)) +>g : Symbol(Bar.g, Decl(optionalMethods.ts, 25, 5)) + + let g2 = x.g ? x.g() : 0; +>g2 : Symbol(g2, Decl(optionalMethods.ts, 42, 7)) +>x.g : Symbol(Bar.g, Decl(optionalMethods.ts, 25, 5)) +>x : Symbol(x, Decl(optionalMethods.ts, 32, 15)) +>g : Symbol(Bar.g, Decl(optionalMethods.ts, 25, 5)) +>x.g : Symbol(Bar.g, Decl(optionalMethods.ts, 25, 5)) +>x : Symbol(x, Decl(optionalMethods.ts, 32, 15)) +>g : Symbol(Bar.g, Decl(optionalMethods.ts, 25, 5)) + + let h1 = x.h && x.h(); +>h1 : Symbol(h1, Decl(optionalMethods.ts, 43, 7)) +>x.h : Symbol(Bar.h, Decl(optionalMethods.ts, 26, 17)) +>x : Symbol(x, Decl(optionalMethods.ts, 32, 15)) +>h : Symbol(Bar.h, Decl(optionalMethods.ts, 26, 17)) +>x.h : Symbol(Bar.h, Decl(optionalMethods.ts, 26, 17)) +>x : Symbol(x, Decl(optionalMethods.ts, 32, 15)) +>h : Symbol(Bar.h, Decl(optionalMethods.ts, 26, 17)) + + let h2 = x.h ? x.h() : 0; +>h2 : Symbol(h2, Decl(optionalMethods.ts, 44, 7)) +>x.h : Symbol(Bar.h, Decl(optionalMethods.ts, 26, 17)) +>x : Symbol(x, Decl(optionalMethods.ts, 32, 15)) +>h : Symbol(Bar.h, Decl(optionalMethods.ts, 26, 17)) +>x.h : Symbol(Bar.h, Decl(optionalMethods.ts, 26, 17)) +>x : Symbol(x, Decl(optionalMethods.ts, 32, 15)) +>h : Symbol(Bar.h, Decl(optionalMethods.ts, 26, 17)) +} + +class Base { +>Base : Symbol(Base, Decl(optionalMethods.ts, 45, 1)) + + a?: number; +>a : Symbol(Base.a, Decl(optionalMethods.ts, 47, 12)) + + f?(): number; +>f : Symbol(Base.f, Decl(optionalMethods.ts, 48, 15)) +} + +class Derived extends Base { +>Derived : Symbol(Derived, Decl(optionalMethods.ts, 50, 1)) +>Base : Symbol(Base, Decl(optionalMethods.ts, 45, 1)) + + a = 1; +>a : Symbol(Derived.a, Decl(optionalMethods.ts, 52, 28)) + + f(): number { return 1; } +>f : Symbol(Derived.f, Decl(optionalMethods.ts, 53, 10)) +} + diff --git a/tests/baselines/reference/optionalMethods.types b/tests/baselines/reference/optionalMethods.types new file mode 100644 index 0000000000000..70fda27c01235 --- /dev/null +++ b/tests/baselines/reference/optionalMethods.types @@ -0,0 +1,226 @@ +=== tests/cases/conformance/types/namedTypes/optionalMethods.ts === + +interface Foo { +>Foo : Foo + + a: number; +>a : number + + b?: number; +>b : number | undefined + + f(): number; +>f : () => number + + g?(): number; +>g : (() => number) | undefined +} + +function test1(x: Foo) { +>test1 : (x: Foo) => void +>x : Foo +>Foo : Foo + + x.a; +>x.a : number +>x : Foo +>a : number + + x.b; +>x.b : number | undefined +>x : Foo +>b : number | undefined + + x.f; +>x.f : () => number +>x : Foo +>f : () => number + + x.g; +>x.g : (() => number) | undefined +>x : Foo +>g : (() => number) | undefined + + let f1 = x.f(); +>f1 : number +>x.f() : number +>x.f : () => number +>x : Foo +>f : () => number + + let g1 = x.g && x.g(); +>g1 : number | undefined +>x.g && x.g() : number | undefined +>x.g : (() => number) | undefined +>x : Foo +>g : (() => number) | undefined +>x.g() : number +>x.g : () => number +>x : Foo +>g : () => number + + let g2 = x.g ? x.g() : 0; +>g2 : number +>x.g ? x.g() : 0 : number +>x.g : (() => number) | undefined +>x : Foo +>g : (() => number) | undefined +>x.g() : number +>x.g : () => number +>x : Foo +>g : () => number +>0 : number +} + +class Bar { +>Bar : Bar + + a: number; +>a : number + + b?: number; +>b : number | undefined + + c? = 2; +>c : number | undefined +>2 : number + + constructor(public d?: number, public e = 10) {} +>d : number | undefined +>e : number +>10 : number + + f() { +>f : () => number + + return 1; +>1 : number + } + g?(): number; // Body of optional method can be omitted +>g : (() => number) | undefined + + h?() { +>h : (() => number) | undefined + + return 2; +>2 : number + } +} + +function test2(x: Bar) { +>test2 : (x: Bar) => void +>x : Bar +>Bar : Bar + + x.a; +>x.a : number +>x : Bar +>a : number + + x.b; +>x.b : number | undefined +>x : Bar +>b : number | undefined + + x.c; +>x.c : number | undefined +>x : Bar +>c : number | undefined + + x.d; +>x.d : number | undefined +>x : Bar +>d : number | undefined + + x.e; +>x.e : number +>x : Bar +>e : number + + x.f; +>x.f : () => number +>x : Bar +>f : () => number + + x.g; +>x.g : (() => number) | undefined +>x : Bar +>g : (() => number) | undefined + + let f1 = x.f(); +>f1 : number +>x.f() : number +>x.f : () => number +>x : Bar +>f : () => number + + let g1 = x.g && x.g(); +>g1 : number | undefined +>x.g && x.g() : number | undefined +>x.g : (() => number) | undefined +>x : Bar +>g : (() => number) | undefined +>x.g() : number +>x.g : () => number +>x : Bar +>g : () => number + + let g2 = x.g ? x.g() : 0; +>g2 : number +>x.g ? x.g() : 0 : number +>x.g : (() => number) | undefined +>x : Bar +>g : (() => number) | undefined +>x.g() : number +>x.g : () => number +>x : Bar +>g : () => number +>0 : number + + let h1 = x.h && x.h(); +>h1 : number | undefined +>x.h && x.h() : number | undefined +>x.h : (() => number) | undefined +>x : Bar +>h : (() => number) | undefined +>x.h() : number +>x.h : () => number +>x : Bar +>h : () => number + + let h2 = x.h ? x.h() : 0; +>h2 : number +>x.h ? x.h() : 0 : number +>x.h : (() => number) | undefined +>x : Bar +>h : (() => number) | undefined +>x.h() : number +>x.h : () => number +>x : Bar +>h : () => number +>0 : number +} + +class Base { +>Base : Base + + a?: number; +>a : number | undefined + + f?(): number; +>f : (() => number) | undefined +} + +class Derived extends Base { +>Derived : Derived +>Base : Base + + a = 1; +>a : number +>1 : number + + f(): number { return 1; } +>f : () => number +>1 : number +} + diff --git a/tests/cases/conformance/types/namedTypes/optionalMethods.ts b/tests/cases/conformance/types/namedTypes/optionalMethods.ts new file mode 100644 index 0000000000000..932521425f9f6 --- /dev/null +++ b/tests/cases/conformance/types/namedTypes/optionalMethods.ts @@ -0,0 +1,58 @@ +// @strictNullChecks: true +// @declaration: true + +interface Foo { + a: number; + b?: number; + f(): number; + g?(): number; +} + +function test1(x: Foo) { + x.a; + x.b; + x.f; + x.g; + let f1 = x.f(); + let g1 = x.g && x.g(); + let g2 = x.g ? x.g() : 0; +} + +class Bar { + a: number; + b?: number; + c? = 2; + constructor(public d?: number, public e = 10) {} + f() { + return 1; + } + g?(): number; // Body of optional method can be omitted + h?() { + return 2; + } +} + +function test2(x: Bar) { + x.a; + x.b; + x.c; + x.d; + x.e; + x.f; + x.g; + let f1 = x.f(); + let g1 = x.g && x.g(); + let g2 = x.g ? x.g() : 0; + let h1 = x.h && x.h(); + let h2 = x.h ? x.h() : 0; +} + +class Base { + a?: number; + f?(): number; +} + +class Derived extends Base { + a = 1; + f(): number { return 1; } +} diff --git a/tests/cases/conformance/types/objectTypeLiteral/methodSignatures/objectTypesWithOptionalProperties.ts b/tests/cases/conformance/types/objectTypeLiteral/methodSignatures/objectTypesWithOptionalProperties.ts index f80c3a5952db0..7c05ff09e5631 100644 --- a/tests/cases/conformance/types/objectTypeLiteral/methodSignatures/objectTypesWithOptionalProperties.ts +++ b/tests/cases/conformance/types/objectTypeLiteral/methodSignatures/objectTypesWithOptionalProperties.ts @@ -9,7 +9,7 @@ interface I { } class C { - x?: number; // error + x?: number; // ok } interface I2 { @@ -17,7 +17,7 @@ interface I2 { } class C2 { - x?: T; // error + x?: T; // ok } var b = {