Skip to content

Commit ac96739

Browse files
committed
Disallow uninitialised property overrides
This causes quite a few test breaks. We'll probably want to revert many of them by switching to the upcoming `declare x: number` syntax.
1 parent a4bacf3 commit ac96739

File tree

43 files changed

+1030
-42
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1030
-42
lines changed

src/compiler/checker.ts

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29442,22 +29442,22 @@ namespace ts {
2944229442
d.kind === SyntaxKind.ClassDeclaration || d.kind === SyntaxKind.InterfaceDeclaration);
2944329443
}
2944429444

29445+
/**
29446+
* TypeScript 1.0 spec (April 2014): 8.2.3
29447+
* A derived class inherits all members from its base class it doesn't override.
29448+
* Inheritance means that a derived class implicitly contains all non - overridden members of the base class.
29449+
* Both public and private property members are inherited, but only public property members can be overridden.
29450+
* A property member in a derived class is said to override a property member in a base class
29451+
* when the derived class property member has the same name and kind(instance or static)
29452+
* as the base class property member.
29453+
* The type of an overriding property member must be assignable(section 3.8.4)
29454+
* to the type of the overridden property member, or otherwise a compile - time error occurs.
29455+
* Base class instance member functions can be overridden by derived class instance member functions,
29456+
* but not by other kinds of members.
29457+
* Base class instance member variables and accessors can be overridden by
29458+
* derived class instance member variables and accessors, but not by other kinds of members.
29459+
*/
2944529460
function checkKindsOfPropertyMemberOverrides(type: InterfaceType, baseType: BaseType): void {
29446-
29447-
// TypeScript 1.0 spec (April 2014): 8.2.3
29448-
// A derived class inherits all members from its base class it doesn't override.
29449-
// Inheritance means that a derived class implicitly contains all non - overridden members of the base class.
29450-
// Both public and private property members are inherited, but only public property members can be overridden.
29451-
// A property member in a derived class is said to override a property member in a base class
29452-
// when the derived class property member has the same name and kind(instance or static)
29453-
// as the base class property member.
29454-
// The type of an overriding property member must be assignable(section 3.8.4)
29455-
// to the type of the overridden property member, or otherwise a compile - time error occurs.
29456-
// Base class instance member functions can be overridden by derived class instance member functions,
29457-
// but not by other kinds of members.
29458-
// Base class instance member variables and accessors can be overridden by
29459-
// derived class instance member variables and accessors, but not by other kinds of members.
29460-
2946129461
// NOTE: assignability is checked in checkClassDeclaration
2946229462
const baseProperties = getPropertiesOfType(baseType);
2946329463
basePropertyCheck: for (const baseProperty of baseProperties) {
@@ -29514,10 +29514,21 @@ namespace ts {
2951429514
continue;
2951529515
}
2951629516

29517-
if (isPrototypeProperty(base) || base.flags & SymbolFlags.PropertyOrAccessor && derived.flags & SymbolFlags.PropertyOrAccessor) {
29517+
if (isPrototypeProperty(base)) {
2951829518
// method is overridden with method or property/accessor is overridden with property/accessor - correct case
2951929519
continue;
2952029520
}
29521+
if (base.flags & SymbolFlags.PropertyOrAccessor && derived.flags & SymbolFlags.PropertyOrAccessor) {
29522+
if (derived.flags & SymbolFlags.Property
29523+
&& !(derived.flags & SymbolFlags.Transient)
29524+
&& !(baseDeclarationFlags & ModifierFlags.Abstract)
29525+
&& !derived.declarations.some(d => d.flags & NodeFlags.Ambient)
29526+
&& derived.declarations.some(d => d.kind === SyntaxKind.PropertyDeclaration && !(d as PropertyDeclaration).initializer)) {
29527+
const errorMessage = Diagnostics.Class_0_defines_instance_member_property_1_so_extended_class_2_must_provide_an_initializer_with_this_override;
29528+
error(getNameOfDeclaration(derived.valueDeclaration) || derived.valueDeclaration, errorMessage, typeToString(baseType), symbolToString(base), typeToString(type));
29529+
}
29530+
continue;
29531+
}
2952129532

2952229533
let errorMessage: DiagnosticMessage;
2952329534
if (isPrototypeProperty(base)) {

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2204,6 +2204,10 @@
22042204
"category": "Error",
22052205
"code": 2609
22062206
},
2207+
"Class '{0}' defines instance member property '{1}', so extended class '{2}' must provide an initializer with this override.": {
2208+
"category": "Error",
2209+
"code": 2610
2210+
},
22072211
"Cannot augment module '{0}' with value exports because it resolves to a non-module entity.": {
22082212
"category": "Error",
22092213
"code": 2649

tests/baselines/reference/apparentTypeSubtyping.errors.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
tests/cases/conformance/types/typeRelationships/apparentType/apparentTypeSubtyping.ts(10,5): error TS2416: Property 'x' in type 'Derived<U>' is not assignable to the same property in base type 'Base<string>'.
22
Type 'String' is not assignable to type 'string'.
33
'string' is a primitive, but 'String' is a wrapper object. Prefer using 'string' when possible.
4+
tests/cases/conformance/types/typeRelationships/apparentType/apparentTypeSubtyping.ts(10,5): error TS2610: Class 'Base<string>' defines instance member property 'x', so extended class 'Derived<U>' must provide an initializer with this override.
5+
tests/cases/conformance/types/typeRelationships/apparentType/apparentTypeSubtyping.ts(20,5): error TS2610: Class 'Base2' defines instance member property 'x', so extended class 'Derived2<U>' must provide an initializer with this override.
46

57

6-
==== tests/cases/conformance/types/typeRelationships/apparentType/apparentTypeSubtyping.ts (1 errors) ====
8+
==== tests/cases/conformance/types/typeRelationships/apparentType/apparentTypeSubtyping.ts (3 errors) ====
79
// subtype checks use the apparent type of the target type
810
// S is a subtype of a type T, and T is a supertype of S, if one of the following is true, where S' denotes the apparent type (section 3.8.1) of S:
911

@@ -18,6 +20,8 @@ tests/cases/conformance/types/typeRelationships/apparentType/apparentTypeSubtypi
1820
!!! error TS2416: Property 'x' in type 'Derived<U>' is not assignable to the same property in base type 'Base<string>'.
1921
!!! error TS2416: Type 'String' is not assignable to type 'string'.
2022
!!! error TS2416: 'string' is a primitive, but 'String' is a wrapper object. Prefer using 'string' when possible.
23+
~
24+
!!! error TS2610: Class 'Base<string>' defines instance member property 'x', so extended class 'Derived<U>' must provide an initializer with this override.
2125
}
2226

2327
class Base2 {
@@ -28,4 +32,6 @@ tests/cases/conformance/types/typeRelationships/apparentType/apparentTypeSubtypi
2832
// is U extends String (S) a subtype of String (T)? Apparent type of U is String so it succeeds
2933
class Derived2<U extends String> extends Base2 { // error because of the prototype's not matching, not because of the instance side
3034
x: U;
35+
~
36+
!!! error TS2610: Class 'Base2' defines instance member property 'x', so extended class 'Derived2<U>' must provide an initializer with this override.
3137
}

tests/baselines/reference/apparentTypeSupertype.errors.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ tests/cases/conformance/types/typeRelationships/apparentType/apparentTypeSuperty
22
Type 'U' is not assignable to type 'string'.
33
Type 'String' is not assignable to type 'string'.
44
'string' is a primitive, but 'String' is a wrapper object. Prefer using 'string' when possible.
5+
tests/cases/conformance/types/typeRelationships/apparentType/apparentTypeSupertype.ts(10,5): error TS2610: Class 'Base' defines instance member property 'x', so extended class 'Derived<U>' must provide an initializer with this override.
56

67

7-
==== tests/cases/conformance/types/typeRelationships/apparentType/apparentTypeSupertype.ts (1 errors) ====
8+
==== tests/cases/conformance/types/typeRelationships/apparentType/apparentTypeSupertype.ts (2 errors) ====
89
// subtype checks use the apparent type of the target type
910
// S is a subtype of a type T, and T is a supertype of S, if one of the following is true, where S' denotes the apparent type (section 3.8.1) of S:
1011

@@ -20,4 +21,6 @@ tests/cases/conformance/types/typeRelationships/apparentType/apparentTypeSuperty
2021
!!! error TS2416: Type 'U' is not assignable to type 'string'.
2122
!!! error TS2416: Type 'String' is not assignable to type 'string'.
2223
!!! error TS2416: 'string' is a primitive, but 'String' is a wrapper object. Prefer using 'string' when possible.
24+
~
25+
!!! error TS2610: Class 'Base' defines instance member property 'x', so extended class 'Derived<U>' must provide an initializer with this override.
2326
}

tests/baselines/reference/baseClassImprovedMismatchErrors.errors.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ tests/cases/compiler/baseClassImprovedMismatchErrors.ts(8,5): error TS2416: Prop
55
Types of property 'n' are incompatible.
66
Type 'string | Derived' is not assignable to type 'string | Base'.
77
Type 'Derived' is not assignable to type 'string | Base'.
8+
tests/cases/compiler/baseClassImprovedMismatchErrors.ts(8,5): error TS2610: Class 'Base' defines instance member property 'n', so extended class 'Derived' must provide an initializer with this override.
89
tests/cases/compiler/baseClassImprovedMismatchErrors.ts(9,5): error TS2416: Property 'fn' in type 'Derived' is not assignable to the same property in base type 'Base'.
910
Type '() => string | number' is not assignable to type '() => number'.
1011
Type 'string | number' is not assignable to type 'number'.
@@ -22,7 +23,7 @@ tests/cases/compiler/baseClassImprovedMismatchErrors.ts(15,5): error TS2416: Pro
2223
Type 'string' is not assignable to type 'number'.
2324

2425

25-
==== tests/cases/compiler/baseClassImprovedMismatchErrors.ts (4 errors) ====
26+
==== tests/cases/compiler/baseClassImprovedMismatchErrors.ts (5 errors) ====
2627
class Base {
2728
n: Base | string;
2829
fn() {
@@ -39,6 +40,8 @@ tests/cases/compiler/baseClassImprovedMismatchErrors.ts(15,5): error TS2416: Pro
3940
!!! error TS2416: Types of property 'n' are incompatible.
4041
!!! error TS2416: Type 'string | Derived' is not assignable to type 'string | Base'.
4142
!!! error TS2416: Type 'Derived' is not assignable to type 'string | Base'.
43+
~
44+
!!! error TS2610: Class 'Base' defines instance member property 'n', so extended class 'Derived' must provide an initializer with this override.
4245
fn() {
4346
~~
4447
!!! error TS2416: Property 'fn' in type 'Derived' is not assignable to the same property in base type 'Base'.

tests/baselines/reference/classIsSubtypeOfBaseType.errors.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
1+
tests/cases/conformance/classes/classDeclarations/classHeritageSpecification/classIsSubtypeOfBaseType.ts(6,5): error TS2610: Class 'Base<{ bar: string; }>' defines instance member property 'foo', so extended class 'Derived' must provide an initializer with this override.
12
tests/cases/conformance/classes/classDeclarations/classHeritageSpecification/classIsSubtypeOfBaseType.ts(12,5): error TS2416: Property 'foo' in type 'Derived2' is not assignable to the same property in base type 'Base<{ bar: string; }>'.
23
Type '{ bar?: string; }' is not assignable to type '{ bar: string; }'.
34
Property 'bar' is optional in type '{ bar?: string; }' but required in type '{ bar: string; }'.
5+
tests/cases/conformance/classes/classDeclarations/classHeritageSpecification/classIsSubtypeOfBaseType.ts(12,5): error TS2610: Class 'Base<{ bar: string; }>' defines instance member property 'foo', so extended class 'Derived2' must provide an initializer with this override.
46

57

6-
==== tests/cases/conformance/classes/classDeclarations/classHeritageSpecification/classIsSubtypeOfBaseType.ts (1 errors) ====
8+
==== tests/cases/conformance/classes/classDeclarations/classHeritageSpecification/classIsSubtypeOfBaseType.ts (3 errors) ====
79
class Base<T> {
810
foo: T;
911
}
1012

1113
class Derived extends Base<{ bar: string; }> {
1214
foo: {
15+
~~~
16+
!!! error TS2610: Class 'Base<{ bar: string; }>' defines instance member property 'foo', so extended class 'Derived' must provide an initializer with this override.
1317
bar: string; baz: number; // ok
1418
}
1519
}
@@ -20,6 +24,8 @@ tests/cases/conformance/classes/classDeclarations/classHeritageSpecification/cla
2024
!!! error TS2416: Property 'foo' in type 'Derived2' is not assignable to the same property in base type 'Base<{ bar: string; }>'.
2125
!!! error TS2416: Type '{ bar?: string; }' is not assignable to type '{ bar: string; }'.
2226
!!! error TS2416: Property 'bar' is optional in type '{ bar?: string; }' but required in type '{ bar: string; }'.
27+
~~~
28+
!!! error TS2610: Class 'Base<{ bar: string; }>' defines instance member property 'foo', so extended class 'Derived2' must provide an initializer with this override.
2329
bar?: string; // error
2430
}
2531
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
tests/cases/compiler/declarationEmitProtectedMembers.ts(34,5): error TS2610: Class 'C2' defines instance member property 'x', so extended class 'C3' must provide an initializer with this override.
2+
3+
4+
==== tests/cases/compiler/declarationEmitProtectedMembers.ts (1 errors) ====
5+
// Class with protected members
6+
class C1 {
7+
protected x: number;
8+
9+
protected f() {
10+
return this.x;
11+
}
12+
13+
protected set accessor(a: number) { }
14+
protected get accessor() { return 0; }
15+
16+
protected static sx: number;
17+
18+
protected static sf() {
19+
return this.sx;
20+
}
21+
22+
protected static set staticSetter(a: number) { }
23+
protected static get staticGetter() { return 0; }
24+
}
25+
26+
// Derived class overriding protected members
27+
class C2 extends C1 {
28+
protected f() {
29+
return super.f() + this.x;
30+
}
31+
protected static sf() {
32+
return super.sf() + this.sx;
33+
}
34+
}
35+
36+
// Derived class making protected members public
37+
class C3 extends C2 {
38+
x: number;
39+
~
40+
!!! error TS2610: Class 'C2' defines instance member property 'x', so extended class 'C3' must provide an initializer with this override.
41+
static sx: number;
42+
f() {
43+
return super.f();
44+
}
45+
static sf() {
46+
return super.sf();
47+
}
48+
49+
static get staticGetter() { return 1; }
50+
}
51+
52+
// Protected properties in constructors
53+
class C4 {
54+
constructor(protected a: number, protected b) { }
55+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
tests/cases/conformance/classes/members/inheritanceAndOverriding/derivedClassOverridesProtectedMembers.ts(21,15): error TS2610: Class 'Base' defines instance member property 'a', so extended class 'Derived' must provide an initializer with this override.
2+
tests/cases/conformance/classes/members/inheritanceAndOverriding/derivedClassOverridesProtectedMembers.ts(25,15): error TS2610: Class 'Base' defines instance member property 'd', so extended class 'Derived' must provide an initializer with this override.
3+
4+
5+
==== tests/cases/conformance/classes/members/inheritanceAndOverriding/derivedClassOverridesProtectedMembers.ts (2 errors) ====
6+
var x: { foo: string; }
7+
var y: { foo: string; bar: string; }
8+
9+
class Base {
10+
protected a: typeof x;
11+
protected b(a: typeof x) { }
12+
protected get c() { return x; }
13+
protected set c(v: typeof x) { }
14+
protected d: (a: typeof x) => void;
15+
16+
protected static r: typeof x;
17+
protected static s(a: typeof x) { }
18+
protected static get t() { return x; }
19+
protected static set t(v: typeof x) { }
20+
protected static u: (a: typeof x) => void;
21+
22+
constructor(a: typeof x) { }
23+
}
24+
25+
class Derived extends Base {
26+
protected a: typeof y;
27+
~
28+
!!! error TS2610: Class 'Base' defines instance member property 'a', so extended class 'Derived' must provide an initializer with this override.
29+
protected b(a: typeof y) { }
30+
protected get c() { return y; }
31+
protected set c(v: typeof y) { }
32+
protected d: (a: typeof y) => void;
33+
~
34+
!!! error TS2610: Class 'Base' defines instance member property 'd', so extended class 'Derived' must provide an initializer with this override.
35+
36+
protected static r: typeof y;
37+
protected static s(a: typeof y) { }
38+
protected static get t() { return y; }
39+
protected static set t(a: typeof y) { }
40+
protected static u: (a: typeof y) => void;
41+
42+
constructor(a: typeof y) { super(x) }
43+
}
44+
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
tests/cases/conformance/classes/members/inheritanceAndOverriding/derivedClassOverridesProtectedMembers2.ts(22,5): error TS2610: Class 'Base' defines instance member property 'a', so extended class 'Derived' must provide an initializer with this override.
2+
tests/cases/conformance/classes/members/inheritanceAndOverriding/derivedClassOverridesProtectedMembers2.ts(26,5): error TS2610: Class 'Base' defines instance member property 'd', so extended class 'Derived' must provide an initializer with this override.
3+
4+
5+
==== tests/cases/conformance/classes/members/inheritanceAndOverriding/derivedClassOverridesProtectedMembers2.ts (2 errors) ====
6+
var x: { foo: string; }
7+
var y: { foo: string; bar: string; }
8+
9+
class Base {
10+
protected a: typeof x;
11+
protected b(a: typeof x) { }
12+
protected get c() { return x; }
13+
protected set c(v: typeof x) { }
14+
protected d: (a: typeof x) => void ;
15+
16+
protected static r: typeof x;
17+
protected static s(a: typeof x) { }
18+
protected static get t() { return x; }
19+
protected static set t(v: typeof x) { }
20+
protected static u: (a: typeof x) => void ;
21+
22+
constructor(a: typeof x) { }
23+
}
24+
25+
// Increase visibility of all protected members to public
26+
class Derived extends Base {
27+
a: typeof y;
28+
~
29+
!!! error TS2610: Class 'Base' defines instance member property 'a', so extended class 'Derived' must provide an initializer with this override.
30+
b(a: typeof y) { }
31+
get c() { return y; }
32+
set c(v: typeof y) { }
33+
d: (a: typeof y) => void;
34+
~
35+
!!! error TS2610: Class 'Base' defines instance member property 'd', so extended class 'Derived' must provide an initializer with this override.
36+
37+
static r: typeof y;
38+
static s(a: typeof y) { }
39+
static get t() { return y; }
40+
static set t(a: typeof y) { }
41+
static u: (a: typeof y) => void;
42+
43+
constructor(a: typeof y) { super(a); }
44+
}
45+
46+
var d: Derived = new Derived(y);
47+
var r1 = d.a;
48+
var r2 = d.b(y);
49+
var r3 = d.c;
50+
var r3a = d.d;
51+
d.c = y;
52+
var r4 = Derived.r;
53+
var r5 = Derived.s(y);
54+
var r6 = Derived.t;
55+
var r6a = Derived.u;
56+
Derived.t = y;
57+
58+
class Base2 {
59+
[i: string]: Object;
60+
[i: number]: typeof x;
61+
}
62+
63+
class Derived2 extends Base2 {
64+
[i: string]: typeof x;
65+
[i: number]: typeof y;
66+
}
67+
68+
var d2: Derived2;
69+
var r7 = d2[''];
70+
var r8 = d2[1];
71+
72+

0 commit comments

Comments
 (0)