If your team has been following best practices guidelines put out by the Angular team (& adopted by the community as a whole), you may have made the decision to implement Strict Mode for TypeScript in your codebase.
//tsconfig.json
{
"compileOnSave": false,
"compilerOptions": {
...
"strict": true, //woooo strict mode
},
"angularCompilerOptions": {
"enableIvy": true,
"fullTemplateTypeCheck": true,
"strictInjectionParameters": true
}
}
You may have encountered a new error in Angular Property 'myClassMember' has no initializer and is not definitely assigned in the constructor.ts
If you weren't using a lot of (or any) of the flags for configuring stricter TypeScript parsing previously, you may see new "errors" like this as you code.
*Note, these are not "new errors caused by TypeScript" but instead "already existing potential errors in your codebase that the stricter TypeScript flags are now catching" - this is an IMPORTANT mental shift for your team to make. TypeScript is here to help us write better code and catch potential errors, not to make our lives as developers harder.
The "has no initializer and is not definitely assigned" error is due to the strictPropertyInitialization
flag being set to true as a part of Strict mode, and is checking to ensure any class property declared is not null. In Angular, we are typically declaring properties on our classes when we encounter this error. The TypeScript compiler is reviewing our class members, and if it does not see our class member being given a value when it's declared or if our member is not given a value in the constructor function, it thinks that member is null, and rightfully so!
This is a common occurrence in Angular where we often initialize our members in the component's lifecycle hooks - which TypeScript is unaware of. TypeScript doesn't know that our ngOnInit
function will run after the component is initialized.
import { Component, OnInit } from "@angular/core";
@Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent implements OnInit {
// defined when initialized
public myInitiallyDefinedMember = "beep";
// defined in the constructor method
public myConstructorDefinedMember: string;
// defined in the ngOnInit Lifecycle hook
// will throw TypeScript error
public myLifecycleDefinedMember: string;
constructor() {
this.myConstructorDefinedMember = "bop";
}
ngOnInit() {
this.myLifecycleDefinedMember = "boop";
}
}
Will throw the error:
Error in src/app/app.component.ts (16:10)
Property 'myLifecycleDefinedMember' has no initializer and is not definitely assigned in the constructor.
To let TypeScript know we're going to initialize myLifecycleDefinedMember
in our component, we can use the Non-null Assertion Operator, or a !
.
import { Component, OnInit } from "@angular/core";
@Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent implements OnInit {
...
// defined in the ngOnInit Lifecycle hook,
// now using the Non-null Assertion Operator
public myLifecycleDefinedMember!: string;
ngOnInit() {
this.myLifecycleDefinedMember = "boop";
}
}
The above example is an appropriate use-case, this does NOT mean:
To see how strict mode affects typings & assertions, open this StackBlitz example, and in the tsconfig.json
file remove line 14 "strict": true
.
Key Take Away
You should ONLY use the Non-null Assertion Operator when you know you will be setting a value for a component member. If there are instances where you aren't sure if a member has a value, it is better practice to use the optional operator.
Top comments (0)