When I declare a variable in typescript like below:
id: number
I get the following error:
Property 'id' has no initializer and is not definitely assigned in the constructor.ts(2564)
If I change the variable to one of the following, the error goes away:
id?: number
id!: number
What are the differences between those and what situations I should use the above?
CodePudding user response:
When you write this:
class Optional {
id?: number;
}
you have declared that id
is an optional property. That means it may or may not be present in an instance of Optional
. If it is present, the value should be a number
(or possibly undefined
unless you are using the --exactOptionalPropertyTypes
compiler option). If it is not present, then if you read the id
property, the value will be undefined
.
The above resolves the compiler warning because you are not required to initialize a property that's optional; it will just be undefined
when you read it.
Anyway that means you cannot just use the id
property as a number
:
const o = new Optional();
o.id.toFixed(2) // compiler error!
//~~ <-- Object is possibly undefined
That warning is good, right? Because you don't want to accidentally read the toFixed()
method of undefined
. You need to check for undefined
first, possibly by using the optional chaining operator (?.
):
o.id?.toFixed(2) // okay
Optional properties are a reasonable approach when you are not sure if the properties will be assigned by the time users want to access them, and you want to protect these users from potential runtime errors.
On the other hand, when you write this:
class Asserted {
id!: number
}
you have asserted that the id
property has definitely been assigned. That assertion is you telling the compiler something it can't verify for itself. This is useful in situations where you actually do assign the property but the compiler cannot follow the logic:
class ActuallyAssigned {
id!: number
constructor() {
Object.assign(this, { id: 123 });
}
}
An instance of ActuallyAssigned
will have the id
property set in the constructor, because the Object.assign()
function copies properties into the first parameter from all subsequent parameters. But the compiler cannot understand this, and so without the definite assignment assertion, you'd get a warning. Silencing that warning is reasonable here.
But in Asserted
above, it's not reasonable. You have lied to the compiler. It can't see that id
has be assigned because it actually has not been assigned. And so you will not get a compiler warning if you go ahead and treat the id
property like a number
, even though it might be undefined
:
a.id.toFixed(2) // no compiler error, but
//