Merge pull request #20075 from Microsoft/strictPropertyInitialization
Strict property initialization checks in classes
This commit is contained in:
commit
148dc4e013
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -2287,6 +2287,7 @@ declare namespace ts {
|
|||
strict?: boolean;
|
||||
strictFunctionTypes?: boolean;
|
||||
strictNullChecks?: boolean;
|
||||
strictPropertyInitialization?: boolean;
|
||||
suppressExcessPropertyErrors?: boolean;
|
||||
suppressImplicitAnyIndexErrors?: boolean;
|
||||
target?: ScriptTarget;
|
||||
|
|
|
@ -2287,6 +2287,7 @@ declare namespace ts {
|
|||
strict?: boolean;
|
||||
strictFunctionTypes?: boolean;
|
||||
strictNullChecks?: boolean;
|
||||
strictPropertyInitialization?: boolean;
|
||||
suppressExcessPropertyErrors?: boolean;
|
||||
suppressImplicitAnyIndexErrors?: boolean;
|
||||
target?: ScriptTarget;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
229
tests/baselines/reference/strictPropertyInitialization.js
Normal file
229
tests/baselines/reference/strictPropertyInitialization.js
Normal 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();
|
||||
}
|
209
tests/baselines/reference/strictPropertyInitialization.symbols
Normal file
209
tests/baselines/reference/strictPropertyInitialization.symbols
Normal 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))
|
||||
}
|
||||
}
|
||||
|
226
tests/baselines/reference/strictPropertyInitialization.types
Normal file
226
tests/baselines/reference/strictPropertyInitialization.types
Normal 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
|
||||
}
|
||||
}
|
||||
|
|
@ -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. */
|
||||
|
||||
|
|
|
@ -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. */
|
||||
|
||||
|
|
|
@ -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. */
|
||||
|
||||
|
|
|
@ -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. */
|
||||
|
||||
|
|
|
@ -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. */
|
||||
|
||||
|
|
|
@ -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. */
|
||||
|
||||
|
|
|
@ -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. */
|
||||
|
||||
|
|
|
@ -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. */
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue