Merge pull request #20075 from Microsoft/strictPropertyInitialization

Strict property initialization checks in classes
This commit is contained in:
Anders Hejlsberg 2017-11-20 10:44:27 -08:00 committed by GitHub
commit 148dc4e013
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 1054 additions and 93 deletions

View file

@ -517,16 +517,15 @@ namespace ts {
const isIIFE = containerFlags & ContainerFlags.IsFunctionExpression && !hasModifier(node, ModifierFlags.Async) && !!getImmediatelyInvokedFunctionExpression(node);
// A non-async IIFE is considered part of the containing control flow. Return statements behave
// similarly to break statements that exit to a label just past the statement body.
if (isIIFE) {
currentReturnTarget = createBranchLabel();
}
else {
if (!isIIFE) {
currentFlow = { flags: FlowFlags.Start };
if (containerFlags & (ContainerFlags.IsFunctionExpression | ContainerFlags.IsObjectLiteralOrClassExpressionMethod)) {
(<FlowStart>currentFlow).container = <FunctionExpression | ArrowFunction | MethodDeclaration>node;
}
currentReturnTarget = undefined;
}
// We create a return control flow graph for IIFEs and constructors. For constructors
// we use the return control flow graph in strict property intialization checks.
currentReturnTarget = isIIFE || node.kind === SyntaxKind.Constructor ? createBranchLabel() : undefined;
currentBreakTarget = undefined;
currentContinueTarget = undefined;
activeLabels = undefined;
@ -541,11 +540,14 @@ namespace ts {
if (node.kind === SyntaxKind.SourceFile) {
node.flags |= emitFlags;
}
if (isIIFE) {
if (currentReturnTarget) {
addAntecedent(currentReturnTarget, currentFlow);
currentFlow = finishFlowLabel(currentReturnTarget);
if (node.kind === SyntaxKind.Constructor) {
(<ConstructorDeclaration>node).returnFlowNode = currentFlow;
}
}
else {
if (!isIIFE) {
currentFlow = saveCurrentFlow;
}
currentBreakTarget = saveBreakTarget;

View file

@ -69,6 +69,7 @@ namespace ts {
const allowSyntheticDefaultImports = getAllowSyntheticDefaultImports(compilerOptions);
const strictNullChecks = getStrictOptionValue(compilerOptions, "strictNullChecks");
const strictFunctionTypes = getStrictOptionValue(compilerOptions, "strictFunctionTypes");
const strictPropertyInitialization = getStrictOptionValue(compilerOptions, "strictPropertyInitialization");
const noImplicitAny = getStrictOptionValue(compilerOptions, "noImplicitAny");
const noImplicitThis = getStrictOptionValue(compilerOptions, "noImplicitThis");
@ -12283,7 +12284,7 @@ namespace ts {
// on empty arrays are possible without implicit any errors and new element types can be inferred without
// type mismatch errors.
const resultType = getObjectFlags(evolvedType) & ObjectFlags.EvolvingArray && isEvolvingArrayOperationTarget(reference) ? anyArrayType : finalizeEvolvingArrayType(evolvedType);
if (reference.parent.kind === SyntaxKind.NonNullExpression && getTypeWithFacts(resultType, TypeFacts.NEUndefinedOrNull).flags & TypeFlags.Never) {
if (reference.parent && reference.parent.kind === SyntaxKind.NonNullExpression && getTypeWithFacts(resultType, TypeFacts.NEUndefinedOrNull).flags & TypeFlags.Never) {
return declaredType;
}
return resultType;
@ -15561,63 +15562,68 @@ namespace ts {
}
function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, right: Identifier) {
const type = checkNonNullExpression(left);
if (isTypeAny(type) || type === silentNeverType) {
return type;
}
const apparentType = getApparentType(getWidenedType(type));
if (apparentType === unknownType || (type.flags & TypeFlags.TypeParameter && isTypeAny(apparentType))) {
// handle cases when type is Type parameter with invalid or any constraint
let propType: Type;
const leftType = checkNonNullExpression(left);
const apparentType = getApparentType(getWidenedType(leftType));
if (isTypeAny(apparentType) || apparentType === silentNeverType) {
return apparentType;
}
const assignmentKind = getAssignmentTargetKind(node);
const prop = getPropertyOfType(apparentType, right.escapedText);
if (!prop) {
const indexInfo = getIndexInfoOfType(apparentType, IndexKind.String);
if (indexInfo && indexInfo.type) {
if (indexInfo.isReadonly && (isAssignmentTarget(node) || isDeleteTarget(node))) {
error(node, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(apparentType));
if (!(indexInfo && indexInfo.type)) {
if (right.escapedText && !checkAndReportErrorForExtendingInterface(node)) {
reportNonexistentProperty(right, leftType.flags & TypeFlags.TypeParameter && (leftType as TypeParameter).isThisType ? apparentType : leftType);
}
return getFlowTypeOfPropertyAccess(node, /*prop*/ undefined, indexInfo.type, getAssignmentTargetKind(node));
}
if (right.escapedText && !checkAndReportErrorForExtendingInterface(node)) {
reportNonexistentProperty(right, type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType ? apparentType : type);
}
return unknownType;
}
checkPropertyNotUsedBeforeDeclaration(prop, node, right);
markPropertyAsReferenced(prop, node, left.kind === SyntaxKind.ThisKeyword);
getNodeLinks(node).resolvedSymbol = prop;
checkPropertyAccessibility(node, left, apparentType, prop);
const propType = getDeclaredOrApparentType(prop, node);
const assignmentKind = getAssignmentTargetKind(node);
if (assignmentKind) {
if (isReferenceToReadonlyEntity(<Expression>node, prop) || isReferenceThroughNamespaceImport(<Expression>node)) {
error(right, Diagnostics.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, idText(right));
return unknownType;
}
if (indexInfo.isReadonly && (isAssignmentTarget(node) || isDeleteTarget(node))) {
error(node, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(apparentType));
}
propType = indexInfo.type;
}
return getFlowTypeOfPropertyAccess(node, prop, propType, assignmentKind);
}
/**
* Only compute control flow type if this is a property access expression that isn't an
* assignment target, and the referenced property was declared as a variable, property,
* accessor, or optional method.
*/
function getFlowTypeOfPropertyAccess(node: PropertyAccessExpression | QualifiedName, prop: Symbol | undefined, type: Type, assignmentKind: AssignmentKind) {
else {
checkPropertyNotUsedBeforeDeclaration(prop, node, right);
markPropertyAsReferenced(prop, node, left.kind === SyntaxKind.ThisKeyword);
getNodeLinks(node).resolvedSymbol = prop;
checkPropertyAccessibility(node, left, apparentType, prop);
if (assignmentKind) {
if (isReferenceToReadonlyEntity(<Expression>node, prop) || isReferenceThroughNamespaceImport(<Expression>node)) {
error(right, Diagnostics.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, idText(right));
return unknownType;
}
}
propType = getDeclaredOrApparentType(prop, node);
}
// Only compute control flow type if this is a property access expression that isn't an
// assignment target, and the referenced property was declared as a variable, property,
// accessor, or optional method.
if (node.kind !== SyntaxKind.PropertyAccessExpression ||
assignmentKind === AssignmentKind.Definite ||
prop && !(prop.flags & (SymbolFlags.Variable | SymbolFlags.Property | SymbolFlags.Accessor)) && !(prop.flags & SymbolFlags.Method && type.flags & TypeFlags.Union)) {
return type;
prop && !(prop.flags & (SymbolFlags.Variable | SymbolFlags.Property | SymbolFlags.Accessor)) && !(prop.flags & SymbolFlags.Method && propType.flags & TypeFlags.Union)) {
return propType;
}
// If strict null checks and strict property initialization checks are enabled, if we have
// a this.xxx property access, if the property is an instance property without an initializer,
// and if we are in a constructor of the same class as the property declaration, assume that
// the property is uninitialized at the top of the control flow.
let assumeUninitialized = false;
if (strictNullChecks && strictPropertyInitialization && left.kind === SyntaxKind.ThisKeyword) {
const declaration = prop && prop.valueDeclaration;
if (declaration && isInstancePropertyWithoutInitializer(declaration)) {
const flowContainer = getControlFlowContainer(node);
if (flowContainer.kind === SyntaxKind.Constructor && flowContainer.parent === declaration.parent) {
assumeUninitialized = true;
}
}
}
const flowType = getFlowTypeOfReference(node, propType, assumeUninitialized ? getOptionalType(propType) : propType);
if (assumeUninitialized && !(getFalsyFlags(propType) & TypeFlags.Undefined) && getFalsyFlags(flowType) & TypeFlags.Undefined) {
error(right, Diagnostics.Property_0_is_used_before_being_assigned, symbolToString(prop));
// Return the declared type to reduce follow-on errors
return propType;
}
const flowType = getFlowTypeOfReference(node, type);
return assignmentKind ? getBaseTypeOfLiteralType(flowType) : flowType;
}
@ -22483,6 +22489,7 @@ namespace ts {
if (produceDiagnostics) {
checkIndexConstraints(type);
checkTypeForDuplicateIndexSignatures(node);
checkPropertyInitialization(node);
}
}
@ -22639,6 +22646,39 @@ namespace ts {
return ok;
}
function checkPropertyInitialization(node: ClassLikeDeclaration) {
if (!strictNullChecks || !strictPropertyInitialization || node.flags & NodeFlags.Ambient) {
return;
}
const constructor = findConstructorDeclaration(node);
for (const member of node.members) {
if (isInstancePropertyWithoutInitializer(member)) {
const propName = (<PropertyDeclaration>member).name;
if (isIdentifier(propName)) {
const type = getTypeOfSymbol(getSymbolOfNode(member));
if (!(type.flags & TypeFlags.Any || getFalsyFlags(type) & TypeFlags.Undefined)) {
if (!constructor || !isPropertyInitializedInConstructor(propName, type, constructor)) {
error(member.name, Diagnostics.Property_0_has_no_initializer_and_is_not_definitely_assigned_in_the_constructor, declarationNameToString(propName));
}
}
}
}
}
}
function isInstancePropertyWithoutInitializer(node: Node) {
return node.kind === SyntaxKind.PropertyDeclaration &&
!hasModifier(node, ModifierFlags.Static | ModifierFlags.Abstract) &&
!(<PropertyDeclaration>node).initializer;
}
function isPropertyInitializedInConstructor(propName: Identifier, propType: Type, constructor: ConstructorDeclaration) {
const reference = createPropertyAccess(createThis(), propName);
reference.flowNode = constructor.returnFlowNode;
const flowType = getFlowTypeOfReference(reference, propType, getOptionalType(propType));
return !(getFalsyFlags(flowType) & TypeFlags.Undefined);
}
function checkInterfaceDeclaration(node: InterfaceDeclaration) {
// Grammar checking
if (!checkGrammarDecoratorsAndModifiers(node)) checkGrammarInterfaceDeclaration(node);

View file

@ -277,6 +277,13 @@ namespace ts {
category: Diagnostics.Strict_Type_Checking_Options,
description: Diagnostics.Enable_strict_checking_of_function_types
},
{
name: "strictPropertyInitialization",
type: "boolean",
showInSimplifiedHelpView: true,
category: Diagnostics.Strict_Type_Checking_Options,
description: Diagnostics.Enable_strict_checking_of_property_initialization_in_classes
},
{
name: "noImplicitThis",
type: "boolean",

View file

@ -1967,7 +1967,7 @@ namespace ts {
: moduleKind === ModuleKind.System;
}
export type StrictOptionName = "noImplicitAny" | "noImplicitThis" | "strictNullChecks" | "strictFunctionTypes" | "alwaysStrict";
export type StrictOptionName = "noImplicitAny" | "noImplicitThis" | "strictNullChecks" | "strictFunctionTypes" | "strictPropertyInitialization" | "alwaysStrict";
export function getStrictOptionValue(compilerOptions: CompilerOptions, flag: StrictOptionName): boolean {
return compilerOptions[flag] === undefined ? compilerOptions.strict : compilerOptions[flag];

View file

@ -1952,6 +1952,14 @@
"category": "Error",
"code": 2563
},
"Property '{0}' has no initializer and is not definitely assigned in the constructor.": {
"category": "Error",
"code": 2564
},
"Property '{0}' is used before being assigned.": {
"category": "Error",
"code": 2565
},
"JSX element attributes type '{0}' may not be a union type.": {
"category": "Error",
"code": 2600
@ -3391,6 +3399,10 @@
"category": "Message",
"code": 6186
},
"Enable strict checking of property initialization in classes.": {
"category": "Message",
"code": 6187
},
"Variable '{0}' implicitly has an '{1}' type.": {
"category": "Error",
"code": 7005

View file

@ -947,6 +947,7 @@ namespace ts {
kind: SyntaxKind.Constructor;
parent?: ClassDeclaration | ClassExpression;
body?: FunctionBody;
/* @internal */ returnFlowNode?: FlowNode;
}
/** For when we encounter a semicolon in a class declaration. ES6 allows these as class elements. */
@ -3854,6 +3855,7 @@ namespace ts {
strict?: boolean;
strictFunctionTypes?: boolean; // Always combine with strict property
strictNullChecks?: boolean; // Always combine with strict property
strictPropertyInitialization?: boolean; // Always combine with strict property
/* @internal */ stripInternal?: boolean;
suppressExcessPropertyErrors?: boolean;
suppressImplicitAnyIndexErrors?: boolean;

View file

@ -2287,6 +2287,7 @@ declare namespace ts {
strict?: boolean;
strictFunctionTypes?: boolean;
strictNullChecks?: boolean;
strictPropertyInitialization?: boolean;
suppressExcessPropertyErrors?: boolean;
suppressImplicitAnyIndexErrors?: boolean;
target?: ScriptTarget;

View file

@ -2287,6 +2287,7 @@ declare namespace ts {
strict?: boolean;
strictFunctionTypes?: boolean;
strictNullChecks?: boolean;
strictPropertyInitialization?: boolean;
suppressExcessPropertyErrors?: boolean;
suppressImplicitAnyIndexErrors?: boolean;
target?: ScriptTarget;

View file

@ -10,7 +10,7 @@ type Data<T> = {
};
class Parent<M> {
private data: Data<M>;
constructor(private data: Data<M>) {}
getData(): Data<M> {
return this.data;
}
@ -50,7 +50,8 @@ var __extends = (this && this.__extends) || (function () {
})();
exports.__esModule = true;
var Parent = /** @class */ (function () {
function Parent() {
function Parent(data) {
this.data = data;
}
Parent.prototype.getData = function () {
return this.data;

View file

@ -29,20 +29,20 @@ class Parent<M> {
>Parent : Symbol(Parent, Decl(indexedAccessTypeConstraints.ts, 8, 2))
>M : Symbol(M, Decl(indexedAccessTypeConstraints.ts, 10, 13))
private data: Data<M>;
>data : Symbol(Parent.data, Decl(indexedAccessTypeConstraints.ts, 10, 17))
constructor(private data: Data<M>) {}
>data : Symbol(Parent.data, Decl(indexedAccessTypeConstraints.ts, 11, 16))
>Data : Symbol(Data, Decl(indexedAccessTypeConstraints.ts, 4, 1))
>M : Symbol(M, Decl(indexedAccessTypeConstraints.ts, 10, 13))
getData(): Data<M> {
>getData : Symbol(Parent.getData, Decl(indexedAccessTypeConstraints.ts, 11, 26))
>getData : Symbol(Parent.getData, Decl(indexedAccessTypeConstraints.ts, 11, 41))
>Data : Symbol(Data, Decl(indexedAccessTypeConstraints.ts, 4, 1))
>M : Symbol(M, Decl(indexedAccessTypeConstraints.ts, 10, 13))
return this.data;
>this.data : Symbol(Parent.data, Decl(indexedAccessTypeConstraints.ts, 10, 17))
>this.data : Symbol(Parent.data, Decl(indexedAccessTypeConstraints.ts, 11, 16))
>this : Symbol(Parent, Decl(indexedAccessTypeConstraints.ts, 8, 2))
>data : Symbol(Parent.data, Decl(indexedAccessTypeConstraints.ts, 10, 17))
>data : Symbol(Parent.data, Decl(indexedAccessTypeConstraints.ts, 11, 16))
}
}
@ -59,9 +59,9 @@ export class Foo<C> extends Parent<IData<C>> {
return this.getData().get('content');
>this.getData().get : Symbol(get, Decl(indexedAccessTypeConstraints.ts, 6, 16))
>this.getData : Symbol(Parent.getData, Decl(indexedAccessTypeConstraints.ts, 11, 26))
>this.getData : Symbol(Parent.getData, Decl(indexedAccessTypeConstraints.ts, 11, 41))
>this : Symbol(Foo, Decl(indexedAccessTypeConstraints.ts, 15, 1))
>getData : Symbol(Parent.getData, Decl(indexedAccessTypeConstraints.ts, 11, 26))
>getData : Symbol(Parent.getData, Decl(indexedAccessTypeConstraints.ts, 11, 41))
>get : Symbol(get, Decl(indexedAccessTypeConstraints.ts, 6, 16))
}
}
@ -81,9 +81,9 @@ export class Bar<C, T extends IData<C>> extends Parent<T> {
return this.getData().get('content');
>this.getData().get : Symbol(get, Decl(indexedAccessTypeConstraints.ts, 6, 16))
>this.getData : Symbol(Parent.getData, Decl(indexedAccessTypeConstraints.ts, 11, 26))
>this.getData : Symbol(Parent.getData, Decl(indexedAccessTypeConstraints.ts, 11, 41))
>this : Symbol(Bar, Decl(indexedAccessTypeConstraints.ts, 21, 1))
>getData : Symbol(Parent.getData, Decl(indexedAccessTypeConstraints.ts, 11, 26))
>getData : Symbol(Parent.getData, Decl(indexedAccessTypeConstraints.ts, 11, 41))
>get : Symbol(get, Decl(indexedAccessTypeConstraints.ts, 6, 16))
}
}

View file

@ -29,7 +29,7 @@ class Parent<M> {
>Parent : Parent<M>
>M : M
private data: Data<M>;
constructor(private data: Data<M>) {}
>data : { get: <K extends keyof M>(prop: K) => M[K]; }
>Data : { get: <K extends keyof T>(prop: K) => T[K]; }
>M : M

View file

@ -0,0 +1,120 @@
tests/cases/conformance/classes/propertyMemberDeclarations/strictPropertyInitialization.ts(4,5): error TS2564: Property 'a' has no initializer and is not definitely assigned in the constructor.
tests/cases/conformance/classes/propertyMemberDeclarations/strictPropertyInitialization.ts(6,5): error TS2564: Property 'c' has no initializer and is not definitely assigned in the constructor.
tests/cases/conformance/classes/propertyMemberDeclarations/strictPropertyInitialization.ts(48,5): error TS2564: Property 'a' has no initializer and is not definitely assigned in the constructor.
tests/cases/conformance/classes/propertyMemberDeclarations/strictPropertyInitialization.ts(71,5): error TS2564: Property 'a' has no initializer and is not definitely assigned in the constructor.
tests/cases/conformance/classes/propertyMemberDeclarations/strictPropertyInitialization.ts(93,22): error TS2565: Property 'a' is used before being assigned.
tests/cases/conformance/classes/propertyMemberDeclarations/strictPropertyInitialization.ts(94,23): error TS2565: Property 'b' is used before being assigned.
==== tests/cases/conformance/classes/propertyMemberDeclarations/strictPropertyInitialization.ts (6 errors) ====
// Properties with non-undefined types require initialization
class C1 {
a: number; // Error
~
!!! error TS2564: Property 'a' has no initializer and is not definitely assigned in the constructor.
b: number | undefined;
c: number | null; // Error
~
!!! error TS2564: Property 'c' has no initializer and is not definitely assigned in the constructor.
d?: number;
}
// No strict initialization checks in ambient contexts
declare class C2 {
a: number;
b: number | undefined;
c: number | null;
d?: number;
}
// No strict initialization checks for static members
class C3 {
static a: number;
static b: number | undefined;
static c: number | null;
static d?: number;
}
// Initializer satisfies strict initialization check
class C4 {
a = 0;
b: number = 0;
c: string = "abc";
}
// Assignment in constructor satisfies strict initialization check
class C5 {
a: number;
constructor() {
this.a = 0;
}
}
// All code paths must contain assignment
class C6 {
a: number; // Error
~
!!! error TS2564: Property 'a' has no initializer and is not definitely assigned in the constructor.
constructor(cond: boolean) {
if (cond) {
return;
}
this.a = 0;
}
}
class C7 {
a: number;
constructor(cond: boolean) {
if (cond) {
this.a = 1;
return;
}
this.a = 0;
}
}
// Properties with string literal names aren't checked
class C8 {
a: number; // Error
~
!!! error TS2564: Property 'a' has no initializer and is not definitely assigned in the constructor.
"b": number;
0: number;
}
// No strict initialization checks for abstract members
abstract class C9 {
abstract a: number;
abstract b: number | undefined;
abstract c: number | null;
abstract d?: number;
}
// Properties with non-undefined types must be assigned before they can be accessed
// within their constructor
class C10 {
a: number;
b: number;
c?: number;
constructor() {
let x = this.a; // Error
~
!!! error TS2565: Property 'a' is used before being assigned.
this.a = this.b; // Error
~
!!! error TS2565: Property 'b' is used before being assigned.
this.b = x;
let y = this.c;
}
}

View file

@ -0,0 +1,229 @@
//// [strictPropertyInitialization.ts]
// Properties with non-undefined types require initialization
class C1 {
a: number; // Error
b: number | undefined;
c: number | null; // Error
d?: number;
}
// No strict initialization checks in ambient contexts
declare class C2 {
a: number;
b: number | undefined;
c: number | null;
d?: number;
}
// No strict initialization checks for static members
class C3 {
static a: number;
static b: number | undefined;
static c: number | null;
static d?: number;
}
// Initializer satisfies strict initialization check
class C4 {
a = 0;
b: number = 0;
c: string = "abc";
}
// Assignment in constructor satisfies strict initialization check
class C5 {
a: number;
constructor() {
this.a = 0;
}
}
// All code paths must contain assignment
class C6 {
a: number; // Error
constructor(cond: boolean) {
if (cond) {
return;
}
this.a = 0;
}
}
class C7 {
a: number;
constructor(cond: boolean) {
if (cond) {
this.a = 1;
return;
}
this.a = 0;
}
}
// Properties with string literal names aren't checked
class C8 {
a: number; // Error
"b": number;
0: number;
}
// No strict initialization checks for abstract members
abstract class C9 {
abstract a: number;
abstract b: number | undefined;
abstract c: number | null;
abstract d?: number;
}
// Properties with non-undefined types must be assigned before they can be accessed
// within their constructor
class C10 {
a: number;
b: number;
c?: number;
constructor() {
let x = this.a; // Error
this.a = this.b; // Error
this.b = x;
let y = this.c;
}
}
//// [strictPropertyInitialization.js]
"use strict";
// Properties with non-undefined types require initialization
var C1 = /** @class */ (function () {
function C1() {
}
return C1;
}());
// No strict initialization checks for static members
var C3 = /** @class */ (function () {
function C3() {
}
return C3;
}());
// Initializer satisfies strict initialization check
var C4 = /** @class */ (function () {
function C4() {
this.a = 0;
this.b = 0;
this.c = "abc";
}
return C4;
}());
// Assignment in constructor satisfies strict initialization check
var C5 = /** @class */ (function () {
function C5() {
this.a = 0;
}
return C5;
}());
// All code paths must contain assignment
var C6 = /** @class */ (function () {
function C6(cond) {
if (cond) {
return;
}
this.a = 0;
}
return C6;
}());
var C7 = /** @class */ (function () {
function C7(cond) {
if (cond) {
this.a = 1;
return;
}
this.a = 0;
}
return C7;
}());
// Properties with string literal names aren't checked
var C8 = /** @class */ (function () {
function C8() {
}
return C8;
}());
// No strict initialization checks for abstract members
var C9 = /** @class */ (function () {
function C9() {
}
return C9;
}());
// Properties with non-undefined types must be assigned before they can be accessed
// within their constructor
var C10 = /** @class */ (function () {
function C10() {
var x = this.a; // Error
this.a = this.b; // Error
this.b = x;
var y = this.c;
}
return C10;
}());
//// [strictPropertyInitialization.d.ts]
declare class C1 {
a: number;
b: number | undefined;
c: number | null;
d?: number;
}
declare class C2 {
a: number;
b: number | undefined;
c: number | null;
d?: number;
}
declare class C3 {
static a: number;
static b: number | undefined;
static c: number | null;
static d?: number;
}
declare class C4 {
a: number;
b: number;
c: string;
}
declare class C5 {
a: number;
constructor();
}
declare class C6 {
a: number;
constructor(cond: boolean);
}
declare class C7 {
a: number;
constructor(cond: boolean);
}
declare class C8 {
a: number;
"b": number;
0: number;
}
declare abstract class C9 {
abstract a: number;
abstract b: number | undefined;
abstract c: number | null;
abstract d?: number;
}
declare class C10 {
a: number;
b: number;
c?: number;
constructor();
}

View file

@ -0,0 +1,209 @@
=== tests/cases/conformance/classes/propertyMemberDeclarations/strictPropertyInitialization.ts ===
// Properties with non-undefined types require initialization
class C1 {
>C1 : Symbol(C1, Decl(strictPropertyInitialization.ts, 0, 0))
a: number; // Error
>a : Symbol(C1.a, Decl(strictPropertyInitialization.ts, 2, 10))
b: number | undefined;
>b : Symbol(C1.b, Decl(strictPropertyInitialization.ts, 3, 14))
c: number | null; // Error
>c : Symbol(C1.c, Decl(strictPropertyInitialization.ts, 4, 26))
d?: number;
>d : Symbol(C1.d, Decl(strictPropertyInitialization.ts, 5, 21))
}
// No strict initialization checks in ambient contexts
declare class C2 {
>C2 : Symbol(C2, Decl(strictPropertyInitialization.ts, 7, 1))
a: number;
>a : Symbol(C2.a, Decl(strictPropertyInitialization.ts, 11, 18))
b: number | undefined;
>b : Symbol(C2.b, Decl(strictPropertyInitialization.ts, 12, 14))
c: number | null;
>c : Symbol(C2.c, Decl(strictPropertyInitialization.ts, 13, 26))
d?: number;
>d : Symbol(C2.d, Decl(strictPropertyInitialization.ts, 14, 21))
}
// No strict initialization checks for static members
class C3 {
>C3 : Symbol(C3, Decl(strictPropertyInitialization.ts, 16, 1))
static a: number;
>a : Symbol(C3.a, Decl(strictPropertyInitialization.ts, 20, 10))
static b: number | undefined;
>b : Symbol(C3.b, Decl(strictPropertyInitialization.ts, 21, 21))
static c: number | null;
>c : Symbol(C3.c, Decl(strictPropertyInitialization.ts, 22, 33))
static d?: number;
>d : Symbol(C3.d, Decl(strictPropertyInitialization.ts, 23, 28))
}
// Initializer satisfies strict initialization check
class C4 {
>C4 : Symbol(C4, Decl(strictPropertyInitialization.ts, 25, 1))
a = 0;
>a : Symbol(C4.a, Decl(strictPropertyInitialization.ts, 29, 10))
b: number = 0;
>b : Symbol(C4.b, Decl(strictPropertyInitialization.ts, 30, 10))
c: string = "abc";
>c : Symbol(C4.c, Decl(strictPropertyInitialization.ts, 31, 18))
}
// Assignment in constructor satisfies strict initialization check
class C5 {
>C5 : Symbol(C5, Decl(strictPropertyInitialization.ts, 33, 1))
a: number;
>a : Symbol(C5.a, Decl(strictPropertyInitialization.ts, 37, 10))
constructor() {
this.a = 0;
>this.a : Symbol(C5.a, Decl(strictPropertyInitialization.ts, 37, 10))
>this : Symbol(C5, Decl(strictPropertyInitialization.ts, 33, 1))
>a : Symbol(C5.a, Decl(strictPropertyInitialization.ts, 37, 10))
}
}
// All code paths must contain assignment
class C6 {
>C6 : Symbol(C6, Decl(strictPropertyInitialization.ts, 42, 1))
a: number; // Error
>a : Symbol(C6.a, Decl(strictPropertyInitialization.ts, 46, 10))
constructor(cond: boolean) {
>cond : Symbol(cond, Decl(strictPropertyInitialization.ts, 48, 16))
if (cond) {
>cond : Symbol(cond, Decl(strictPropertyInitialization.ts, 48, 16))
return;
}
this.a = 0;
>this.a : Symbol(C6.a, Decl(strictPropertyInitialization.ts, 46, 10))
>this : Symbol(C6, Decl(strictPropertyInitialization.ts, 42, 1))
>a : Symbol(C6.a, Decl(strictPropertyInitialization.ts, 46, 10))
}
}
class C7 {
>C7 : Symbol(C7, Decl(strictPropertyInitialization.ts, 54, 1))
a: number;
>a : Symbol(C7.a, Decl(strictPropertyInitialization.ts, 56, 10))
constructor(cond: boolean) {
>cond : Symbol(cond, Decl(strictPropertyInitialization.ts, 58, 16))
if (cond) {
>cond : Symbol(cond, Decl(strictPropertyInitialization.ts, 58, 16))
this.a = 1;
>this.a : Symbol(C7.a, Decl(strictPropertyInitialization.ts, 56, 10))
>this : Symbol(C7, Decl(strictPropertyInitialization.ts, 54, 1))
>a : Symbol(C7.a, Decl(strictPropertyInitialization.ts, 56, 10))
return;
}
this.a = 0;
>this.a : Symbol(C7.a, Decl(strictPropertyInitialization.ts, 56, 10))
>this : Symbol(C7, Decl(strictPropertyInitialization.ts, 54, 1))
>a : Symbol(C7.a, Decl(strictPropertyInitialization.ts, 56, 10))
}
}
// Properties with string literal names aren't checked
class C8 {
>C8 : Symbol(C8, Decl(strictPropertyInitialization.ts, 65, 1))
a: number; // Error
>a : Symbol(C8.a, Decl(strictPropertyInitialization.ts, 69, 10))
"b": number;
0: number;
}
// No strict initialization checks for abstract members
abstract class C9 {
>C9 : Symbol(C9, Decl(strictPropertyInitialization.ts, 73, 1))
abstract a: number;
>a : Symbol(C9.a, Decl(strictPropertyInitialization.ts, 77, 19))
abstract b: number | undefined;
>b : Symbol(C9.b, Decl(strictPropertyInitialization.ts, 78, 23))
abstract c: number | null;
>c : Symbol(C9.c, Decl(strictPropertyInitialization.ts, 79, 35))
abstract d?: number;
>d : Symbol(C9.d, Decl(strictPropertyInitialization.ts, 80, 30))
}
// Properties with non-undefined types must be assigned before they can be accessed
// within their constructor
class C10 {
>C10 : Symbol(C10, Decl(strictPropertyInitialization.ts, 82, 1))
a: number;
>a : Symbol(C10.a, Decl(strictPropertyInitialization.ts, 87, 11))
b: number;
>b : Symbol(C10.b, Decl(strictPropertyInitialization.ts, 88, 14))
c?: number;
>c : Symbol(C10.c, Decl(strictPropertyInitialization.ts, 89, 14))
constructor() {
let x = this.a; // Error
>x : Symbol(x, Decl(strictPropertyInitialization.ts, 92, 11))
>this.a : Symbol(C10.a, Decl(strictPropertyInitialization.ts, 87, 11))
>this : Symbol(C10, Decl(strictPropertyInitialization.ts, 82, 1))
>a : Symbol(C10.a, Decl(strictPropertyInitialization.ts, 87, 11))
this.a = this.b; // Error
>this.a : Symbol(C10.a, Decl(strictPropertyInitialization.ts, 87, 11))
>this : Symbol(C10, Decl(strictPropertyInitialization.ts, 82, 1))
>a : Symbol(C10.a, Decl(strictPropertyInitialization.ts, 87, 11))
>this.b : Symbol(C10.b, Decl(strictPropertyInitialization.ts, 88, 14))
>this : Symbol(C10, Decl(strictPropertyInitialization.ts, 82, 1))
>b : Symbol(C10.b, Decl(strictPropertyInitialization.ts, 88, 14))
this.b = x;
>this.b : Symbol(C10.b, Decl(strictPropertyInitialization.ts, 88, 14))
>this : Symbol(C10, Decl(strictPropertyInitialization.ts, 82, 1))
>b : Symbol(C10.b, Decl(strictPropertyInitialization.ts, 88, 14))
>x : Symbol(x, Decl(strictPropertyInitialization.ts, 92, 11))
let y = this.c;
>y : Symbol(y, Decl(strictPropertyInitialization.ts, 95, 11))
>this.c : Symbol(C10.c, Decl(strictPropertyInitialization.ts, 89, 14))
>this : Symbol(C10, Decl(strictPropertyInitialization.ts, 82, 1))
>c : Symbol(C10.c, Decl(strictPropertyInitialization.ts, 89, 14))
}
}

View file

@ -0,0 +1,226 @@
=== tests/cases/conformance/classes/propertyMemberDeclarations/strictPropertyInitialization.ts ===
// Properties with non-undefined types require initialization
class C1 {
>C1 : C1
a: number; // Error
>a : number
b: number | undefined;
>b : number | undefined
c: number | null; // Error
>c : number | null
>null : null
d?: number;
>d : number | undefined
}
// No strict initialization checks in ambient contexts
declare class C2 {
>C2 : C2
a: number;
>a : number
b: number | undefined;
>b : number | undefined
c: number | null;
>c : number | null
>null : null
d?: number;
>d : number | undefined
}
// No strict initialization checks for static members
class C3 {
>C3 : C3
static a: number;
>a : number
static b: number | undefined;
>b : number | undefined
static c: number | null;
>c : number | null
>null : null
static d?: number;
>d : number | undefined
}
// Initializer satisfies strict initialization check
class C4 {
>C4 : C4
a = 0;
>a : number
>0 : 0
b: number = 0;
>b : number
>0 : 0
c: string = "abc";
>c : string
>"abc" : "abc"
}
// Assignment in constructor satisfies strict initialization check
class C5 {
>C5 : C5
a: number;
>a : number
constructor() {
this.a = 0;
>this.a = 0 : 0
>this.a : number
>this : this
>a : number
>0 : 0
}
}
// All code paths must contain assignment
class C6 {
>C6 : C6
a: number; // Error
>a : number
constructor(cond: boolean) {
>cond : boolean
if (cond) {
>cond : boolean
return;
}
this.a = 0;
>this.a = 0 : 0
>this.a : number
>this : this
>a : number
>0 : 0
}
}
class C7 {
>C7 : C7
a: number;
>a : number
constructor(cond: boolean) {
>cond : boolean
if (cond) {
>cond : boolean
this.a = 1;
>this.a = 1 : 1
>this.a : number
>this : this
>a : number
>1 : 1
return;
}
this.a = 0;
>this.a = 0 : 0
>this.a : number
>this : this
>a : number
>0 : 0
}
}
// Properties with string literal names aren't checked
class C8 {
>C8 : C8
a: number; // Error
>a : number
"b": number;
0: number;
}
// No strict initialization checks for abstract members
abstract class C9 {
>C9 : C9
abstract a: number;
>a : number
abstract b: number | undefined;
>b : number | undefined
abstract c: number | null;
>c : number | null
>null : null
abstract d?: number;
>d : number | undefined
}
// Properties with non-undefined types must be assigned before they can be accessed
// within their constructor
class C10 {
>C10 : C10
a: number;
>a : number
b: number;
>b : number
c?: number;
>c : number | undefined
constructor() {
let x = this.a; // Error
>x : number
>this.a : number
>this : this
>a : number
this.a = this.b; // Error
>this.a = this.b : number
>this.a : number
>this : this
>a : number
>this.b : number
>this : this
>b : number
this.b = x;
>this.b = x : number
>this.b : number
>this : this
>b : number
>x : number
let y = this.c;
>y : number | undefined
>this.c : number | undefined
>this : this
>c : number | undefined
}
}

View file

@ -23,6 +23,7 @@
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */

View file

@ -23,6 +23,7 @@
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */

View file

@ -23,6 +23,7 @@
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */

View file

@ -23,6 +23,7 @@
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */

View file

@ -23,6 +23,7 @@
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */

View file

@ -23,6 +23,7 @@
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */

View file

@ -23,6 +23,7 @@
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */

View file

@ -23,6 +23,7 @@
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */

View file

@ -6,7 +6,7 @@ interface Foo {
}
class A<P extends Partial<Foo>> {
props: Readonly<P>
constructor(public props: Readonly<P>) {}
doSomething() {
this.props.foo && this.props.foo()
}
@ -19,7 +19,7 @@ interface Banana {
}
class Monkey<T extends Banana | undefined> {
a: T;
constructor(public a: T) {}
render() {
if (this.a) {
this.a.color;
@ -96,7 +96,8 @@ var __extends = (this && this.__extends) || (function () {
};
})();
var A = /** @class */ (function () {
function A() {
function A(props) {
this.props = props;
}
A.prototype.doSomething = function () {
this.props.foo && this.props.foo();
@ -104,7 +105,8 @@ var A = /** @class */ (function () {
return A;
}());
var Monkey = /** @class */ (function () {
function Monkey() {
function Monkey(a) {
this.a = a;
}
Monkey.prototype.render = function () {
if (this.a) {

View file

@ -14,24 +14,24 @@ class A<P extends Partial<Foo>> {
>Partial : Symbol(Partial, Decl(lib.d.ts, --, --))
>Foo : Symbol(Foo, Decl(typeVariableTypeGuards.ts, 0, 0))
props: Readonly<P>
>props : Symbol(A.props, Decl(typeVariableTypeGuards.ts, 6, 33))
constructor(public props: Readonly<P>) {}
>props : Symbol(A.props, Decl(typeVariableTypeGuards.ts, 7, 16))
>Readonly : Symbol(Readonly, Decl(lib.d.ts, --, --))
>P : Symbol(P, Decl(typeVariableTypeGuards.ts, 6, 8))
doSomething() {
>doSomething : Symbol(A.doSomething, Decl(typeVariableTypeGuards.ts, 7, 22))
>doSomething : Symbol(A.doSomething, Decl(typeVariableTypeGuards.ts, 7, 45))
this.props.foo && this.props.foo()
>this.props.foo : Symbol(foo, Decl(typeVariableTypeGuards.ts, 2, 15))
>this.props : Symbol(A.props, Decl(typeVariableTypeGuards.ts, 6, 33))
>this.props : Symbol(A.props, Decl(typeVariableTypeGuards.ts, 7, 16))
>this : Symbol(A, Decl(typeVariableTypeGuards.ts, 4, 1))
>props : Symbol(A.props, Decl(typeVariableTypeGuards.ts, 6, 33))
>props : Symbol(A.props, Decl(typeVariableTypeGuards.ts, 7, 16))
>foo : Symbol(foo, Decl(typeVariableTypeGuards.ts, 2, 15))
>this.props.foo : Symbol(foo, Decl(typeVariableTypeGuards.ts, 2, 15))
>this.props : Symbol(A.props, Decl(typeVariableTypeGuards.ts, 6, 33))
>this.props : Symbol(A.props, Decl(typeVariableTypeGuards.ts, 7, 16))
>this : Symbol(A, Decl(typeVariableTypeGuards.ts, 4, 1))
>props : Symbol(A.props, Decl(typeVariableTypeGuards.ts, 6, 33))
>props : Symbol(A.props, Decl(typeVariableTypeGuards.ts, 7, 16))
>foo : Symbol(foo, Decl(typeVariableTypeGuards.ts, 2, 15))
}
}
@ -50,23 +50,23 @@ class Monkey<T extends Banana | undefined> {
>T : Symbol(T, Decl(typeVariableTypeGuards.ts, 19, 13))
>Banana : Symbol(Banana, Decl(typeVariableTypeGuards.ts, 11, 1))
a: T;
>a : Symbol(Monkey.a, Decl(typeVariableTypeGuards.ts, 19, 44))
constructor(public a: T) {}
>a : Symbol(Monkey.a, Decl(typeVariableTypeGuards.ts, 20, 16))
>T : Symbol(T, Decl(typeVariableTypeGuards.ts, 19, 13))
render() {
>render : Symbol(Monkey.render, Decl(typeVariableTypeGuards.ts, 20, 9))
>render : Symbol(Monkey.render, Decl(typeVariableTypeGuards.ts, 20, 31))
if (this.a) {
>this.a : Symbol(Monkey.a, Decl(typeVariableTypeGuards.ts, 19, 44))
>this.a : Symbol(Monkey.a, Decl(typeVariableTypeGuards.ts, 20, 16))
>this : Symbol(Monkey, Decl(typeVariableTypeGuards.ts, 17, 1))
>a : Symbol(Monkey.a, Decl(typeVariableTypeGuards.ts, 19, 44))
>a : Symbol(Monkey.a, Decl(typeVariableTypeGuards.ts, 20, 16))
this.a.color;
>this.a.color : Symbol(Banana.color, Decl(typeVariableTypeGuards.ts, 15, 18))
>this.a : Symbol(Monkey.a, Decl(typeVariableTypeGuards.ts, 19, 44))
>this.a : Symbol(Monkey.a, Decl(typeVariableTypeGuards.ts, 20, 16))
>this : Symbol(Monkey, Decl(typeVariableTypeGuards.ts, 17, 1))
>a : Symbol(Monkey.a, Decl(typeVariableTypeGuards.ts, 19, 44))
>a : Symbol(Monkey.a, Decl(typeVariableTypeGuards.ts, 20, 16))
>color : Symbol(Banana.color, Decl(typeVariableTypeGuards.ts, 15, 18))
}
}
@ -86,15 +86,15 @@ class BigMonkey extends Monkey<BigBanana> {
>render : Symbol(BigMonkey.render, Decl(typeVariableTypeGuards.ts, 31, 43))
if (this.a) {
>this.a : Symbol(Monkey.a, Decl(typeVariableTypeGuards.ts, 19, 44))
>this.a : Symbol(Monkey.a, Decl(typeVariableTypeGuards.ts, 20, 16))
>this : Symbol(BigMonkey, Decl(typeVariableTypeGuards.ts, 29, 1))
>a : Symbol(Monkey.a, Decl(typeVariableTypeGuards.ts, 19, 44))
>a : Symbol(Monkey.a, Decl(typeVariableTypeGuards.ts, 20, 16))
this.a.color;
>this.a.color : Symbol(Banana.color, Decl(typeVariableTypeGuards.ts, 15, 18))
>this.a : Symbol(Monkey.a, Decl(typeVariableTypeGuards.ts, 19, 44))
>this.a : Symbol(Monkey.a, Decl(typeVariableTypeGuards.ts, 20, 16))
>this : Symbol(BigMonkey, Decl(typeVariableTypeGuards.ts, 29, 1))
>a : Symbol(Monkey.a, Decl(typeVariableTypeGuards.ts, 19, 44))
>a : Symbol(Monkey.a, Decl(typeVariableTypeGuards.ts, 20, 16))
>color : Symbol(Banana.color, Decl(typeVariableTypeGuards.ts, 15, 18))
}
}

View file

@ -14,7 +14,7 @@ class A<P extends Partial<Foo>> {
>Partial : Partial<T>
>Foo : Foo
props: Readonly<P>
constructor(public props: Readonly<P>) {}
>props : Readonly<P>
>Readonly : Readonly<T>
>P : P
@ -52,7 +52,7 @@ class Monkey<T extends Banana | undefined> {
>T : T
>Banana : Banana
a: T;
constructor(public a: T) {}
>a : T
>T : T

View file

@ -11,7 +11,7 @@ type Data<T> = {
};
class Parent<M> {
private data: Data<M>;
constructor(private data: Data<M>) {}
getData(): Data<M> {
return this.data;
}

View file

@ -7,7 +7,7 @@ interface Foo {
}
class A<P extends Partial<Foo>> {
props: Readonly<P>
constructor(public props: Readonly<P>) {}
doSomething() {
this.props.foo && this.props.foo()
}
@ -20,7 +20,7 @@ interface Banana {
}
class Monkey<T extends Banana | undefined> {
a: T;
constructor(public a: T) {}
render() {
if (this.a) {
this.a.color;

View file

@ -0,0 +1,101 @@
// @strict: true
// @declaration: true
// Properties with non-undefined types require initialization
class C1 {
a: number; // Error
b: number | undefined;
c: number | null; // Error
d?: number;
}
// No strict initialization checks in ambient contexts
declare class C2 {
a: number;
b: number | undefined;
c: number | null;
d?: number;
}
// No strict initialization checks for static members
class C3 {
static a: number;
static b: number | undefined;
static c: number | null;
static d?: number;
}
// Initializer satisfies strict initialization check
class C4 {
a = 0;
b: number = 0;
c: string = "abc";
}
// Assignment in constructor satisfies strict initialization check
class C5 {
a: number;
constructor() {
this.a = 0;
}
}
// All code paths must contain assignment
class C6 {
a: number; // Error
constructor(cond: boolean) {
if (cond) {
return;
}
this.a = 0;
}
}
class C7 {
a: number;
constructor(cond: boolean) {
if (cond) {
this.a = 1;
return;
}
this.a = 0;
}
}
// Properties with string literal names aren't checked
class C8 {
a: number; // Error
"b": number;
0: number;
}
// No strict initialization checks for abstract members
abstract class C9 {
abstract a: number;
abstract b: number | undefined;
abstract c: number | null;
abstract d?: number;
}
// Properties with non-undefined types must be assigned before they can be accessed
// within their constructor
class C10 {
a: number;
b: number;
c?: number;
constructor() {
let x = this.a; // Error
this.a = this.b; // Error
this.b = x;
let y = this.c;
}
}