Property has no initializer and is not definitely assigned in the constructor

Viewed 59916

I am getting an error when I give

export class UserComponent implements OnInit {
    user: User;

    constructor() { }

    ngOnInit() {
        this.user = {
            firstName : "test",
            lastName : "test",
            age : 40,
            address: {
                street : "Test",
                city : "test",
                state: "test"
            }
        }
    }
}

I am getting this error (see image 1):

Property 'user' has no initializer and is not definitely assigned in the constructor.

user is instantiated from User which is declared in User.ts by this code:

export interface User {
    firstName: string,
    lastName: string,
    age?: number,
    address?: {
        street?: string,
        city?: string,
        state?: string
    }
}

If I add a question mark (?) to user, another error appears in some other file (see image 2):

Error occurs in the template of component UserComponent.
src/app/components/users/users.component.html:2:44 -
    error TS2532: Object is possibly 'undefined'.

2 <ul class="list-unstyled" *ngIf="loaded && users?.length > 0">
                                             ~~~~~~~~~~~~~~~~~
4 Answers

This is caused by TypeScript's Strict Class Initialization.

https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#strict-class-initialization

To resolve this issue you must either declare that the type can be both Foo|undefined, or utilize the '!' symbol to indicate definite assignment as per the above documentation:

Case 1:

@Component({...})
export class Component {
  @Input() myInput: string|undefined;
}

We indicate that the type may be either a string or undefined.

Case 2:

@Component({...})
export class Component {
  @Input() myInput!: string;
}

In this case we utilize the ! symbol to indicate that we are aware that myInput is not initialized in the constructor and we will handle it elsewhere.

Alternate Solution

If you do not care about strict class initialization, you can disable the feature completely in your tsconfig.json by adding a flag to your compiler options

{
 "compilerOptions": {
    "strictPropertyInitialization": false
  }
}

Note: You will have to restart the TypeScript server for the compilerOptions to be reapplied. The easiest way to achieve this is to simply restart your IDE.

If none of this works, you can try it

  "angularCompilerOptions": {
    ....
    "strictPropertyInitialization": false,
    ...

  }

I think your course was correct. BUT, I think it is mostly fine to move things from ngOnInit if they are not related to rendering.

IMO, Angular makes a quick rule to avoid some rendering issue but it is not a black and white one. The rule I think it is just trying to make thing simple for new users. And this is a more advance topic.

quote from https://github.com/angular/angular/issues/24571#issuecomment-404606595

For angular components, use the following rules in deciding between:
a) adding initializer
b) make the field optional
c) leave the '!'

If the field is annotated with @input - Make the field optional b) or add an initializer a).
If the input is required for the component user - add an assertion in ngOnInit and apply c.
If the field is annotated @ViewChild, @ContentChild - Make the field optional b).
If the field is annotated with @ViewChildren or @ContentChildren - Add back '!' - c).
Fields that have an initializer, but it lives in ngOnInit. - Move the initializer to the constructor.
Fields that have an initializer, but it lives in ngOnInit and cannot be moved because it depends on other @input fields - Add back '!' - c).

I think this is a good guideline

Related