Skip to content

Commit 8686242

Browse files
committed
Check for constructor initialisation
1 parent feb0fb6 commit 8686242

7 files changed

+132
-7
lines changed

src/compiler/checker.ts

+15-6
Original file line numberDiff line numberDiff line change
@@ -29520,14 +29520,23 @@ namespace ts {
2952029520
continue;
2952129521
}
2952229522
if (base.flags & SymbolFlags.PropertyOrAccessor && derived.flags & SymbolFlags.PropertyOrAccessor) {
29523-
if (derived.flags & SymbolFlags.Property
29523+
const uninitialized = find(derived.declarations, d => d.kind === SyntaxKind.PropertyDeclaration && !(d as PropertyDeclaration).initializer);
29524+
if (uninitialized
29525+
&& derived.flags & SymbolFlags.Property
2952429526
&& !(derived.flags & SymbolFlags.Transient)
2952529527
&& !(baseDeclarationFlags & ModifierFlags.Abstract)
29526-
&& !(derivedDeclarationFlags & ModifierFlags.Ambient)
29527-
&& !derived.declarations.some(d => d.flags & NodeFlags.Ambient)
29528-
&& derived.declarations.some(d => d.kind === SyntaxKind.PropertyDeclaration && !(d as PropertyDeclaration).initializer)) {
29529-
const errorMessage = Diagnostics.Class_0_defines_instance_member_property_1_so_extended_class_2_must_provide_an_initializer_with_this_override;
29530-
error(getNameOfDeclaration(derived.valueDeclaration) || derived.valueDeclaration, errorMessage, typeToString(baseType), symbolToString(base), typeToString(type));
29528+
&& !(derivedDeclarationFlags & (ModifierFlags.Ambient | ModifierFlags.Abstract))
29529+
&& !derived.declarations.some(d => d.flags & NodeFlags.Ambient)) {
29530+
const constructor = findConstructorDeclaration(getClassLikeDeclarationOfSymbol(type.symbol)!);
29531+
const propName = (uninitialized as PropertyDeclaration).name;
29532+
if ((uninitialized as PropertyDeclaration).exclamationToken
29533+
|| !constructor
29534+
|| !isIdentifier(propName)
29535+
|| !strictNullChecks
29536+
|| !isPropertyInitializedInConstructor(propName, type, constructor)) {
29537+
const errorMessage = Diagnostics.Class_0_defines_instance_member_property_1_so_extended_class_2_must_provide_an_initializer_with_this_override;
29538+
error(getNameOfDeclaration(derived.valueDeclaration) || derived.valueDeclaration, errorMessage, typeToString(baseType), symbolToString(base), typeToString(type));
29539+
}
2953129540
}
2953229541
continue;
2953329542
}

tests/baselines/reference/derivedUninitializedPropertyDeclaration.errors.txt

+13
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,17 @@ tests/cases/conformance/classes/propertyMemberDeclarations/derivedUninitializedP
6969
!!! error TS2610: Class 'E' defines instance member property 'p1', so extended class 'F' must provide an initializer with this override.
7070
declare p2: 'alpha'
7171
}
72+
73+
class G extends E {
74+
p1: 'z'
75+
constructor() {
76+
super()
77+
this.p1 = 'z'
78+
}
79+
}
80+
81+
abstract class H extends E {
82+
abstract p1: 'a' | 'b' | 'c'
83+
declare abstract p2: 'a' | 'b' | 'c'
84+
}
7285

tests/baselines/reference/derivedUninitializedPropertyDeclaration.js

+29
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,19 @@ class F extends E {
4040
p1!: 'z'
4141
declare p2: 'alpha'
4242
}
43+
44+
class G extends E {
45+
p1: 'z'
46+
constructor() {
47+
super()
48+
this.p1 = 'z'
49+
}
50+
}
51+
52+
abstract class H extends E {
53+
abstract p1: 'a' | 'b' | 'c'
54+
declare abstract p2: 'a' | 'b' | 'c'
55+
}
4356

4457

4558
//// [derivedUninitializedPropertyDeclaration.js]
@@ -126,3 +139,19 @@ var F = /** @class */ (function (_super) {
126139
}
127140
return F;
128141
}(E));
142+
var G = /** @class */ (function (_super) {
143+
__extends(G, _super);
144+
function G() {
145+
var _this = _super.call(this) || this;
146+
_this.p1 = 'z';
147+
return _this;
148+
}
149+
return G;
150+
}(E));
151+
var H = /** @class */ (function (_super) {
152+
__extends(H, _super);
153+
function H() {
154+
return _super !== null && _super.apply(this, arguments) || this;
155+
}
156+
return H;
157+
}(E));

tests/baselines/reference/derivedUninitializedPropertyDeclaration.symbols

+29
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,32 @@ class F extends E {
9191
>p2 : Symbol(F.p2, Decl(derivedUninitializedPropertyDeclaration.ts, 38, 12))
9292
}
9393

94+
class G extends E {
95+
>G : Symbol(G, Decl(derivedUninitializedPropertyDeclaration.ts, 40, 1))
96+
>E : Symbol(E, Decl(derivedUninitializedPropertyDeclaration.ts, 30, 1))
97+
98+
p1: 'z'
99+
>p1 : Symbol(G.p1, Decl(derivedUninitializedPropertyDeclaration.ts, 42, 19))
100+
101+
constructor() {
102+
super()
103+
>super : Symbol(E, Decl(derivedUninitializedPropertyDeclaration.ts, 30, 1))
104+
105+
this.p1 = 'z'
106+
>this.p1 : Symbol(G.p1, Decl(derivedUninitializedPropertyDeclaration.ts, 42, 19))
107+
>this : Symbol(G, Decl(derivedUninitializedPropertyDeclaration.ts, 40, 1))
108+
>p1 : Symbol(G.p1, Decl(derivedUninitializedPropertyDeclaration.ts, 42, 19))
109+
}
110+
}
111+
112+
abstract class H extends E {
113+
>H : Symbol(H, Decl(derivedUninitializedPropertyDeclaration.ts, 48, 1))
114+
>E : Symbol(E, Decl(derivedUninitializedPropertyDeclaration.ts, 30, 1))
115+
116+
abstract p1: 'a' | 'b' | 'c'
117+
>p1 : Symbol(H.p1, Decl(derivedUninitializedPropertyDeclaration.ts, 50, 28))
118+
119+
declare abstract p2: 'a' | 'b' | 'c'
120+
>p2 : Symbol(H.p2, Decl(derivedUninitializedPropertyDeclaration.ts, 51, 32))
121+
}
122+

tests/baselines/reference/derivedUninitializedPropertyDeclaration.types

+32
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,35 @@ class F extends E {
9595
>p2 : "alpha"
9696
}
9797

98+
class G extends E {
99+
>G : G
100+
>E : E
101+
102+
p1: 'z'
103+
>p1 : "z"
104+
105+
constructor() {
106+
super()
107+
>super() : void
108+
>super : typeof E
109+
110+
this.p1 = 'z'
111+
>this.p1 = 'z' : "z"
112+
>this.p1 : "z"
113+
>this : this
114+
>p1 : "z"
115+
>'z' : "z"
116+
}
117+
}
118+
119+
abstract class H extends E {
120+
>H : H
121+
>E : E
122+
123+
abstract p1: 'a' | 'b' | 'c'
124+
>p1 : "a" | "b" | "c"
125+
126+
declare abstract p2: 'a' | 'b' | 'c'
127+
>p2 : "a" | "b" | "c"
128+
}
129+

tests/cases/conformance/classes/propertyMemberDeclarations/derivedUninitializedPropertyDeclaration.ts

+13
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,16 @@ class F extends E {
4040
p1!: 'z'
4141
declare p2: 'alpha'
4242
}
43+
44+
class G extends E {
45+
p1: 'z'
46+
constructor() {
47+
super()
48+
this.p1 = 'z'
49+
}
50+
}
51+
52+
abstract class H extends E {
53+
abstract p1: 'a' | 'b' | 'c'
54+
declare abstract p2: 'a' | 'b' | 'c'
55+
}

tests/cases/fourslash/derivedTypeIndexerWithGenericConstraints.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
////}
1414

1515
////class DbSet<TEntity extends Entity> extends BaseCollection<TEntity> { // error
16-
//// _itemsByKey: { [key: string]: TEntity; };
16+
//// _itemsByKey: { [key: string]: TEntity; } = {};
1717
////}
1818

1919
////var a: BaseCollection<CollectionItem>;

0 commit comments

Comments
 (0)