Merge pull request #8625 from Microsoft/optionalClassProperties

Optional class properties
This commit is contained in:
Anders Hejlsberg
2016-05-18 11:32:50 -07:00
17 changed files with 729 additions and 70 deletions

View File

@@ -79,6 +79,7 @@ namespace ts {
getIndexTypeOfType,
getBaseTypes,
getReturnTypeOfSignature,
getNonNullableType,
getSymbolsInScope,
getSymbolAtLocation,
getShorthandAssignmentValueSymbol,
@@ -2884,6 +2885,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) {
@@ -2915,8 +2920,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) {
@@ -2938,13 +2942,13 @@ namespace ts {
? getContextuallyTypedThisType(func)
: getContextuallyTypedParameterType(<ParameterDeclaration>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
@@ -3215,7 +3219,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;
}
@@ -7614,11 +7620,12 @@ namespace ts {
getInitialTypeOfBindingElement(<BindingElement>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;
@@ -8092,11 +8099,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;
@@ -8344,7 +8351,7 @@ namespace ts {
if (isClassLike(container.parent)) {
const symbol = getSymbolOfNode(container.parent);
const type = container.flags & NodeFlags.Static ? getTypeOfSymbol(symbol) : (<InterfaceType>getDeclaredTypeOfSymbol(symbol)).thisType;
return getFlowTypeOfReference(node, type, type);
return getFlowTypeOfReference(node, type, /*assumeInitialized*/ true);
}
if (isInJavaScriptFile(node)) {
@@ -9919,7 +9926,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);
@@ -9936,7 +9944,7 @@ namespace ts {
return propType;
}
}
return getFlowTypeOfReference(node, propType, propType);
return getFlowTypeOfReference(node, propType, /*assumeInitialized*/ true);
}
function isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean {
@@ -13400,7 +13408,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);
}
@@ -18290,7 +18298,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) {
@@ -18299,9 +18307,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,
@@ -18523,8 +18528,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;
}
}

View File

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

View File

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

View File

@@ -1772,6 +1772,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;

View File

@@ -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(<InterfaceType><Type>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 = <FunctionLikeDeclaration>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);
}
}

View File

@@ -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<T> {
x?: T;
~
!!! error TS1112: A class member cannot be declared optional.
f?(x: T) {}
~
!!! error TS1112: A class member cannot be declared optional.
}

View File

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

View File

@@ -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<T> {
>C2 : C2<T>
>T : T
x?: T;
>x : T
>T : T
f?(x: T) {}
>f : (x: T) => void
>x : T
>T : T
}

View File

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

View File

@@ -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.
!!! error TS1162: An object member cannot be declared optional.

View File

@@ -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<T> {
@@ -25,9 +21,7 @@ tests/cases/conformance/types/objectTypeLiteral/methodSignatures/objectTypesWith
}
class C2<T> {
x?: T; // error
~
!!! error TS1112: A class member cannot be declared optional.
x?: T; // ok
}
var b = {

View File

@@ -10,7 +10,7 @@ interface I {
}
class C {
x?: number; // error
x?: number; // ok
}
interface I2<T> {
@@ -18,7 +18,7 @@ interface I2<T> {
}
class C2<T> {
x?: T; // error
x?: T; // ok
}
var b = {

View File

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

View File

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

View File

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

View File

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

View File

@@ -9,7 +9,7 @@ interface I {
}
class C {
x?: number; // error
x?: number; // ok
}
interface I2<T> {
@@ -17,7 +17,7 @@ interface I2<T> {
}
class C2<T> {
x?: T; // error
x?: T; // ok
}
var b = {