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…
23 Answers
Just go to tsconfig.json
and set
"strictPropertyInitialization": false
to get rid of the compilation error.
Otherwise you need to initialize all your variables which is a little bit annoying
10
Just make sure you add that after “strict”: true else the transpiler seems to turn it on again (although VS seems to know it’s off).
– montyIn this way you will disable the strict checking property initialization for all the project. It’s better to add the
!
postfix operator to the variable name, to just ignore this case, or initialize the variable inside the constructor.You are suggesting to ignore potential problems that the compiler is telling about, this is not really safe. So downvote.
– OlgaStrictPropertyInitialzer is a flag introduced in typescript 2.7. It’s up to everybody to choose if you want to enable all these flags and strictly follow all rules or turn off some validation. It makes completely no sense to follow all the rules every time. If your code gets too verbose and downsides and are bigger than advantages you should definetly turn it off. I am not telling that this is the best practice in all cases but its definetly viable option in most cases…
As I mentioned in my answer you have two options either you disable validation OR initialize all variables. Its up to everybody to choose what will you benefit more from project to project.
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 = [];
}
6
sorry, I am new in typescript. Can you say where is my mistake?
–as the error says you need to initialize the variable to some value makes: any[] = [];
You must use ‘Definite Assignment Assertion’ to tell typescript that this variable will have a value at runtime as follows:
makes!: any[];
HI @Sajeetharan, how to chnage vscode ts version
Initializing variables in the constructor is an Angular anti-pattern. Don’t do it.
It is because TypeScript 2.7 includes a strict class checking where all the properties should be initialized in the constructor. A workaround is to add
the !
as a postfix to the variable name:
makes!: any[];
6
The “!” syntax exists for those common-ish cases where you can’t guarantee that the value will be defined immediately. It’s an escape hatch, and shouldn’t be relied on, as it can make your code less safe. A default value is usually preferred. Good to know it exists, though
– kingdaro@kingdaro is right. While this can generally be used, it can also lead to code that flat doesn’t work. As an example, in the basic webapp generated by VS2017, change the assignment for the forecasts in fetchdata.components.ts by adding the ‘!’ (public forecasts!: WeatherForecast[];) and it will cause it to completely error out
– IronRodThis is the best solution, since it is directly after the @Input() decorator (in the new angular), when reading any given component, it reads naturally and culls out any dev mistakes.
– ELI7VHincidentally what is the difference from using ! or ? to handle nulls or undefined?
I needed to define a property that uses an Enum in a service on an app where I can’t assume the user’s choices, not even a default. It is defined in the first step and not read until the second, so this was very useful escape, good to know it exists, thanks very much.
We may get the message Property has no initializer and is not definitely assigned in the constructor
when adding some configuration in the tsconfig.json
file so as to have an Angular project compiled in strict mode:
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"noImplicitThis": true,
"alwaysStrict": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictPropertyInitialization": true,
Indeed the compiler then complains that a member variable is not defined before being used.
For an example of a member variable that is not defined at compile time, a member variable having an @Input
directive:
@Input() userId: string;
We could silence the compiler by stating the variable may be optional:
@Input() userId?: string;
But then, we would have to deal with the case of the variable not being defined, and clutter the source code with some such statements:
if (this.userId) {
} else {
}
Instead, knowing the value of this member variable would be defined in time, that is, it would be defined before being used, we can tell the compiler not to worry about it not being defined.
The way to tell this to the compiler is to add the ! definite assignment assertion
operator, as in:
@Input() userId!: string;
Now, the compiler understands that this variable, although not defined at compile time, shall be defined at run-time, and in time, before it is being used.
It is now up to the application to ensure this variable is defined before being used.
As an an added protection, we can assert the variable is being defined, before we use it.
We can assert the variable is defined, that is, the required input binding was actually provided by the calling context:
private assertInputsProvided(): void {
if (!this.userId) {
throw (new Error("The required input [userId] was not provided"));
}
}
public ngOnInit(): void {
// Ensure the input bindings are actually provided at run-time
this.assertInputsProvided();
}
Knowing the variable was defined, the variable can now be used:
ngOnChanges() {
this.userService.get(this.userId)
.subscribe(user => {
this.update(user.confirmedEmail);
});
}
Note that the ngOnInit
method is called after the input bindings attempt, this, even if no actual input was provided to the bindings.
Whereas the ngOnChanges
method is called after the input bindings attempt, and only if there was actual input provided to the bindings.
4
this is the correct answer when using ‘strict’ mode
– RnDrxA lot of this makes sense to me apart from one thing… What would the assert give you? Would be fine for debugging, but not for using the app. Throwing errors that will not be caught is not a good idea. You should provide some fallback that will make things work no matter what.
– NuxThis should be upvoted as the correct answer, as it solves the problem, rather than just turning of part of the linting.
Using “definite assignment assertion” in strict mode just to avoid the condition checks doesn’t make great sense. You are undermining the whole logic of having a strict typed system. If avoiding those checks is more important to you, then you probably shouldn’t use strict mode.
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
3
IMO, it is kinda going backward.
– maxisamFirstly, thanks for the solution, it worked. Secondly, can anyone explain what just happened?
It worked , it is necessary when creating directives in angular.
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[]
2
This seems like the only correct solution. I’m horrified by the other answers.
I also consider that this error message is useful. So deactivate it in tsconfig.json is not the appropriate solution. And adding ‘!’ can allows the compiler to let go, but it makes the code less sure, unless you’re really sure of what is going on. A better solution seems to understand that the property can be undedinfed, and therefore the developer has to write code accordingly. This is the purpose of the answer to me.
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.
2
Easy and quick way to fix
– AshokWorking perfectly.
You either need to disable the --strictPropertyInitialization
that
Sajeetharan referred to, or do something like this to satisfy the initialization requirement:
makes: any[] = [];
You can also do the following if you really don’t want to initialise it.
makes?: any[];
0
If you want to initialize an object based on an interface you can initialize it empty with following statement.
myObj: IMyObject = {} as IMyObject;
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.
As of TypeScript 2.7.2, you are required to initialise a property in the constructor if it was not assigned to at the point of declaration.
If you are coming from Vue, you can try the following:
Add
"strictPropertyInitialization": true
to your tsconfig.jsonIf you are unhappy with disabling it you could also try this
makes: any[] | undefined
. Doing this requires that you access the properties with null check (?.
) operator i.e.this.makes?.length
- You could as well try
makes!: any[];
, this tells TS that the value will be assigned at runtime.
Get this error at the time of adding Node in my Angular project –
TSError: ? Unable to compile TypeScript:
(path)/base.api.ts:19:13 – error TS2564: Property ‘apiRoot
Path’ has no initializer and is not definitely assigned in the constructor.private apiRootPath: string;
Solution –
Added "strictPropertyInitialization": false
in ‘compilerOptions’ of tsconfig.json.
my package.json –
"dependencies": {
...
"@angular/common": "~10.1.3",
"@types/express": "^4.17.9",
"express": "^4.17.1",
...
}
0
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;
}
When you upgrade using [email protected] , its compiler strict the rules follows for array type declare inside the component class constructor.
For fix this issue either change the code where are declared in the code or avoid to compiler to add property “strictPropertyInitialization”: false in the “tsconfig.json” file and run again npm start .
Angular web and mobile Application Development you can go to http://www.jtechweb.in
0
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.
2
Worked for me when initializing the @ViewChild variable.. see for reference: stackblitz.com/edit/…
Writing
@Input() availableData: HierarchyItem[] | undefined;
is the same as writing@Input() availableData?: HierarchyItem[]
😉
Can’t you just use a Definite Assignment Assertion? (See https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#definite-assignment-assertions)
i.e. declaring the property as makes!: any[];
The ! assures typescript that there definitely will be a value at runtime.
Sorry I haven’t tried this in angular but it worked great for me when I was having the exact same problem in React.
1
Working with all versions since it is in the official documentation and I tried it. THANKS!!
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
},
1
What is the difference between ‘!’ and ‘?’ ?
– pouyada
change the
fieldname?: any[];
to this:
fieldname?: any;
Comment the //"strict": true
line in tsconfig.json file.
This has been discussed in Angular Github at https://github.com/angular/angular/issues/24571
I think this is what everyone will move to
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).
You can declare property in constructor like this:
export class Test {
constructor(myText:string) {
this.myText= myText;
}
myText: string ;
}
Next to variables “?” You can fix it by putting it.
Example:
———>id?:number
———>name?:string