DEV Community

Jennifer Wadella for Bitovi

Posted on

Non-null Assertion Operator in Angular

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
  }
}
Enter fullscreen mode Exit fullscreen mode

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";
  }
}
Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode

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";
  }
}
Enter fullscreen mode Exit fullscreen mode

The above example is an appropriate use-case, this does NOT mean:

Alt Text

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)