Mikhail Kostiuchenko
Mikhail Kostiuchenko

Reputation: 10541

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

in my Angular app i have a component:

import { MakeService } from './../../services/make.service';
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-vehicle-form',
  templateUrl: './vehicle-form.component.html',
  styleUrls: ['./vehicle-form.component.css']
})
export class VehicleFormComponent implements OnInit {
  makes: any[];
  vehicle = {};

  constructor(private makeService: MakeService) { }

  ngOnInit() {
    this.makeService.getMakes().subscribe(makes => { this.makes = makes
      console.log("MAKES", this.makes);
    });
  }

  onMakeChange(){
    console.log("VEHICLE", this.vehicle);
  }
}

but in the "makes" property I have a mistake. I dont know what to do with it...

mistake

Upvotes: 1004

Views: 1387656

Answers (30)

Rahul Kumar Tiwari
Rahul Kumar Tiwari

Reputation: 129

input is not always required, you can declare it as optional using the? syntax.

makes?: any;

OR

makes?: string;

Upvotes: 3

Ooker
Ooker

Reputation: 3054

Strict property initialization checks only apply to properties that are declared with proper identifiers

According to microsoft/TypeScript#20075, the pull request that implemented the --strictPropertyInitialization compiler option:

Strict property initialization checks only apply to properties that are declared with proper identifiers. It does not apply to properties with computed names or properties with names specified as string or numeric literals.

// Compile with --strict  
class Test {
   a: number;  // Error, not initialized
   "hello world": number;  // No check
}

In the type {a: string} or the value {a: ""}, the a is an identifier, whereas in the type {"a": string} or the value {"a": ""}, the "a" is a string literal. So the former receives checking while the latter does not.

This answer is copied from the answer of jcalz in the question An object key of a class isn't initialized or assigned in the constructor. Why does quoting it mute the error?

Upvotes: 0

Brijesh Ray
Brijesh Ray

Reputation: 1303

For angular 17 , i did strict:false in tsconfig.json and it worked for me

enter image description here

Upvotes: 7

Alexandre Annic
Alexandre Annic

Reputation: 10768

The error is legitimate and may prevent your app from crashing. You typed makes as an array but it can also be undefined.

You have 2 options (instead of disabling the typescript's reason for existing...):

1. In your case the best is to type makes as possibily undefined.

makes?: any[]
// or
makes: any[] | undefined

So the compiler will inform you whenever you try to access to makes that it could be undefined. Otherwise, if the // <-- Not ok lines below were executed before getMakes finished or if getMakes failed, your app would crash and a runtime error would be thrown. That's definitely not what you want.

makes[0] // <-- Not ok
makes.map(...) // <-- Not ok

if (makes) makes[0] // <-- Ok
makes?.[0] // <-- Ok
(makes ?? []).map(...) // <-- Ok

2. You can assume that it will never fail and that you will never try to access it before initialization by writing the code below (risky!). So the compiler won't take care about it.

makes!: any[] 


More specifically,

Your code's design warrants improvement. Best practices discourage the use of locally defined and mutable variables. A more effective approach involves managing data storage within a service:

  • To ensure null/undefined safety,
  • To factorise code (including typing, loading state and errors),
  • To control unnecessary re-fetch across components.

Here's an illustrative example of the concept I'm proposing. Please note that I have not tested it, and there is room for further improvement.

type Make = any // Type it

class MakeService {

  private readonly source = new BehaviorSubject<Make[] | undefined>(undefined);
  loading = false;

  private readonly getMakes = (): Observable<Make[]> => {
    /* ... your current implementation */
  };

  readonly getMakes2 = () => {
    if (this.source.value) {
      return this.source.asObservable();
    }
    return new Observable(_ => _.next()).pipe(
      tap(_ => {
        this.loading = true;
      }),
      mergeMap(this.getMakes),
      mergeMap(data => {
        this.source.next(data);
        return data;
      }),
      tap(_ => {
        this.loading = false;
      }),
      catchError((err: any) => {
        this.loading = false;
        return throwError(err);
      }),
    );
  };
}

@Component({
  selector: 'app-vehicle-form',
  template: `
    <div *ngIf="makeService.loading">Loading...</div>
    <div *ngFor="let vehicule of vehicules | async">
      {{vehicle.name}}
    </div>
  `,
  styleUrls: ['./vehicle-form.component.css']
})
export class VehicleFormComponent implements OnInit {
  constructor(public makeService: MakeService) {}

  readonly makes = this.makeService.getMakes2().pipe(
    tap(makes => console.log('MAKES', makes))
  );

  readonly vehicules = this.makes.pipe(
    map(make => make/* some transformation */),
    tap(vehicule => console.log('VEHICLE', vehicule)),
  );

  ngOnInit() {
    this.makes.subscribe(makes => {
      console.log('MAKES', makes);
    });
  }
}

Upvotes: 92

maxisam
maxisam

Reputation: 22745

This has been discussed in Angular Github at https://github.com/angular/angular/issues/24571

I think this recommendation from angular gives good advice:

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 a).
  • Fields that have an initializer, but it lives in ngOnInit and cannot be moved because it depends on other @input fields - Add back '!' - c).

Upvotes: 8

Vinicius Ribeiro
Vinicius Ribeiro

Reputation: 124

A little late for the party. but the most voted answer includes changing the security level as a whole abandoning type safety, to work around this use the typing mechanism of the typescript itself

@ViewChild(MatPaginator) paginator: MatPaginator | undefined;

ngAfterViewInit() { 
  this.dataSource.paginator = this.paginator as MatPaginator;
}

I believe the angular material documentation should be updated, but until then I believe this is the best way to do it

Upvotes: 1

  1. Go to tsconfig.json

  2. add "strictPropertyInitialization": false, in "compilerOptions":

enter image description here

Upvotes: 43

Rajat Biswas
Rajat Biswas

Reputation: 23

Just declare your variable as type any

@Input() headerName: any;

Upvotes: -3

Alvin Kimata
Alvin Kimata

Reputation: 338

It is because typescript 2.7.2 included a strict class checking where all properties should be declared in constructor. So to work around that, just add an exclamation mark (!) like:

name!:string;

Upvotes: 9

Shubham Chauhan
Shubham Chauhan

Reputation: 31

Instead of turning off the strict mode, you can assign a initial value to the variable. For example:

  1. makes: any[] = [null];

  2. private year: number = 0;

Upvotes: 0

Martin Čuka
Martin Čuka

Reputation: 18518

Just go to tsconfig.json and set

"compilerOptions": {
    "strictPropertyInitialization": false,
    ...
}

to get rid of the compilation error.

Otherwise you need to initialize all your variables which is a little bit annoying

Upvotes: 1661

mitesh keswani
mitesh keswani

Reputation: 173

Follow 2 steps to solve this::

  1. "strictPropertyInitialization": false entry in tsconfig.json
  2. ctrl+c stop your server on Terminal(check for terminal, where it is running) and run it again with the command you use like ng serve These 2 steps, should solve your issue.. as it solved mine.. cheers.. ;)

Upvotes: 0

samivic
samivic

Reputation: 1310

if you don't want to change your tsconfig.json, you can define your class like this:

class Address{
  street: string = ''
}

or, you may proceed like this as well:

class Address{
  street!: string
}

by adding an exclamation mark "!" after your variable name, Typescript will be sure that this variable is not null or undefined.

Upvotes: 14

Alexey Ryzhkov
Alexey Ryzhkov

Reputation: 441

In my case it works with different declaration according new typescript strict features:

@ViewChild(MatSort, {static: true}) sort!: MatSort;

If disable typescript new strict feature in tsonfig.json with

"compilerOptions": {
  ///
  ,
  "strictPropertyInitialization":false
}

the old Angular's guide code works well

@ViewChild(MatSort) sort: MatSort;

Here is 4 ways to solve the issue thanks to Arunkumar Gudelli (2022) https://www.angularjswiki.com/angular/property-has-no-initializer-and-is-not-definitely-assigned-in-the-constructor/

Upvotes: 6

Onur Dikmen
Onur Dikmen

Reputation: 377

1) You can apply it like the code below. When you do this, the system will not give an error.

"Definite Assignment Assertion" (!) to tell TypeScript that we know this value

Detail info

@Injectable()
export class Contact {
  public name !:string;
  public address!: Address;
  public digital!: Digital[];
  public phone!: Phone[];
}

2) The second method is to create a constructor and define values here.

export class Contact {
  public name :string;
  public address: Address;
  public digital: Digital[];
  public phone: Phone[];

  constructor( _name :string,
     _address: Address,
     _digital: Digital[],
     _phone: Phone[])
  {
    this.name=_name;
    this.address=_address;
    this.digital=_digital;
    this.phone=_phone;
  }
}

3) The third choice will be to create a get property and assign a value as follows

  export class Contact {
      public name :string="";
      public address: Address=this._address;
    
      get _address(): Address {
        return new Address();
      }
     
    }

Upvotes: 6

Holger Rattenscheid
Holger Rattenscheid

Reputation: 197

You can also add @ts-ignore to silence the compiler only for this case:

//@ts-ignore
makes: any[];

Upvotes: -2

xgqfrms
xgqfrms

Reputation: 12206

there are many solutions

demo code

class Plant {
  name: string;
  // ❌ Property 'name' has no initializer and is not definitely assigned in the constructor.ts(2564)
}

solutions

  1. add 'undefined' type
class Plant {
  name: string | undefined;
}
  1. add definite assignment assertion
class Plant {
  name!: string;
}
  1. declare with init value 👍
class Plant {
  name: string = '';
}
  1. use the constructor with an init value 👍
class Plant {
  name: string;
  constructor() {
    this.name = '';
  }
}
  1. use the constructor and init value by params 👍
class Plant {
  name: string;
  constructor(name: string) {
    this.name = name ?? '';
  }
}

  1. shorthand of the 5 👍
class Plant {
  constructor(public name: string = name ?? '') {
    // 
  }
}

tsconfig.json

not recommended

{
  "compilerOptions": {
+   "strictPropertyInitialization": false,
-   "strictPropertyInitialization": true,
  }
}

refs

https://www.typescriptlang.org/docs/handbook/2/classes.html#--strictpropertyinitialization

Upvotes: 49

Mohammad Komaei
Mohammad Komaei

Reputation: 9656

in tsconfig.json file , inside "compilerOptions" add :

"strictPropertyInitialization": false,

enter image description here

Upvotes: 10

Tabish Zaman
Tabish Zaman

Reputation: 292

JUST go to the tsconfig.ts and add "strictPropertyInitialization": false, into the compilerOptions Object .

  • if it doesn't solve yet, kindly re-open your Code Editor.

EXAMPLE:

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

Upvotes: 2

Sydney_dev
Sydney_dev

Reputation: 1380

Add these two line on the tsconfig.json

"noImplicitReturns": true,
"strictPropertyInitialization": false,

and make sure strict is set to true

Upvotes: 2

Takesha  Kaluarachchi
Takesha Kaluarachchi

Reputation: 439

a new version of typescript has introduced strick class Initialization, that is means by all of the properties in your class you need to initialize in the constructor body, or by a property initializer. check it in typescript doccumntation to avoid this you can add (! or ?) with property

make!: any[] or make? : any[] 

otherwise, if you wish to remove strick class checking permanently in your project you can set strictPropertyInitialization": false in tsconfig.json file

"compilerOptions": { .... "noImplicitReturns": false, .... "strictPropertyInitialization": false },

Upvotes: 8

Sajeetharan
Sajeetharan

Reputation: 222712

I think you are using the latest version of TypeScript. Please see the section "Strict Class Initialization" in the link.

There are two ways to fix this:

A. If you are using VSCode you need to change the TS version that the editor use.

B. Just initialize the array when you declare it

makes: any[] = [];

or inside the constructor:

constructor(private makeService: MakeService) { 
   // Initialization inside the constructor
   this.makes = [];
}

Upvotes: 388

crispengari
crispengari

Reputation: 9333

Go to your tsconfig.json file and change the property:

 "noImplicitReturns": false

and then add

 "strictPropertyInitialization": false

under "compilerOptions" property.

Your tsconfig.json file should looks like:


{
      ...
      "compilerOptions": {
            ....
            "noImplicitReturns": false,
            ....
            "strictPropertyInitialization": false
      },
      "angularCompilerOptions": {
         ......
      }  
 }

Hope this will help !!

Good Luck

Upvotes: 131

TheMysterious
TheMysterious

Reputation: 299

A batter approach would be to add the exclamation mark to the end of the variable for which you are sure that it shall not be undefined or null, for instance you are using an ElementRef which needs to be loaded from the template and can't be defined in the constructor, do something like below

class Component {
 ViewChild('idname') variable! : ElementRef;
}

Upvotes: 16

Suresh
Suresh

Reputation: 565

Comment the //"strict": true line in tsconfig.json file.

Upvotes: -3

Md Wahid
Md Wahid

Reputation: 460

Put a question (?) mark after makes variable.

  makes?: any[];
  vehicle = {};

  constructor(private makeService: MakeService) { }

It should now works. I'm using angular 12 and it works on my code.

Upvotes: 14

keivan kashani
keivan kashani

Reputation: 1359

You can declare property in constructor like this:

export class Test {
constructor(myText:string) {
this.myText= myText;
} 

myText: string ;
}

Upvotes: 1

Alexei - check Codidact
Alexei - check Codidact

Reputation: 23088

Another way to fix in the case when the variable must remain uninitialized (and it is dealt with at the run time) is to add undefined to the type (this is actually suggested by VC Code). Example:

@Input() availableData: HierarchyItem[] | undefined;
@Input() checkableSettings: CheckableSettings | undefined;

Depending on actual usage, this might lead to other issues, so I think the best approach is to initialize the properties whenever possible.

Upvotes: 6

OrkunPinarkaya
OrkunPinarkaya

Reputation: 39

Next to variables "?" You can fix it by putting it.

Example:

--------->id?:number --------->name?:string

Upvotes: -4

Sumit
Sumit

Reputation: 644

Update for 2021 :

there is property like "strictPropertyInitialization"

Just go to tsconfig.json and set

"strict": false

to get rid of the compilation error.

Otherwise you need to initialize all your variables which is a little bit annoying.

reason behind this error is :

  • typescript is a kind of more Secure lang as compare to javascript.
  • although this security is enhance by enabling strict feature .So every time when you initialize a variable typescript wants them to assign a value.

Upvotes: 27

Related Questions