Class Employee {
firstName: string;
lastName!: string;
middleName?: string;
}
What is the difference in these 3 different fields of Employee
class?
Class Employee {
firstName: string;
lastName!: string;
middleName?: string;
}
What is the difference in these 3 different fields of Employee
class?
When having compiler option strictNullChecks: false
If you have strictNullChecks: false
in your tsconfig.json
then they are exactly the same since having strictNullChecks
disabled means that all fields can have null or undefined as valid values.
When having compiler option strictNullChecks: true
class Employee {
firstName: string;
lastName!: string;
middleName?: string;
}
firstName: string
means that firstName
must be a string
. null
or undefined
are not valid values for firstName.
All uninitialized fields will have a default value of undefined
, so this will result in an error Property 'firstName' has no initializer and is not definitely assigned in constructor
To silence the error you need to either change the declaration to firstName: string = 'Some default value'
or add a constructor and assign a value for it in the constructor.
constructor() {
this.firstName = 'some default value';
}
Now for the ! syntax. The lastName!: string
syntax is similar to lastName: string
in that it basically says that string
is the only allowed type. null
and undefined
are not allowed. But it will silence the compiler about definite assignment error. Let's say you have the following code.
class Employee {
firstName: string;
lastName!: string;
middleName?: string;
constructor() {
// This will silence the compiler about first name not initialized
this.firstName = 'some default value';
// The compiler cannot tell that lastName is assigned in init() function
this.init();
}
private init(): void {
this.lastName = 'some default value';
}
}
In the previous code, lastName
is definitely assigned in the constructor
via the this.init()
call. However, the compiler cannot know that. So adding the ! is basically telling the compiler "shut up, I know what I am doing". It is up to you then to ensure the correctness of your code.
About the middleName?: string
syntax. This is similar to middleName: string | undefined
; Since all values has a default value of undefined
, the compiler won't complain that middleName
is not assigned.
The ?
in that position marks the property optional.
The !
in that position is the definite assignment assertion. It's sort of a declaration-level version of the non-null assertion operator, but used on a property (can also be used on variables) rather than on an expression.
There are two — or arguably three — errors in that example:
Class
should be class
; JavaScript and TypeScript are case-sensitive.
You need an initializer on firstName
(or a constructor that assigns to it unconditionally).
The !
on lastName
tells TypeScript that lastName
will definitely be assigned, suppressing the kind of error you're getting for firstName
, but nothing (in the example) actually does the assignment that using !
there promises TypeScript you know for sure you're doing.
Edit: The code you linked later deals with #1 and #2 above, but not #3. TypeScript won't warn that lastName
is never assigned and assumes its value is a string, when in fact it's not there and so reading its value will result in undefined
.
They are well hidden in the documentation of TypeScript.
?
is described on interfaces, it marks an optional property.
!
is the definite assertion operator. It tells the compiler that the property is set (not null
or undefined
) even if TypeScript's analyses cannot detect so.
Btw, Class
is not a TypeScript or JavaScript keyword and produces an error in that position. They keyword for declaring a class is class
. TypeScript and JavaScript identifiers and keywords are case sensitive (Class
and class
are different things).