Typescript here. My User
class:
class User {
private name: string;
private managerOf: User;
constructor(name: string, managerOf: User) {
this.name = name;
this.managerOf = managerOf;
}
getName(): string {
return this.name;
}
isManagerOf(user: User): boolean {
return user.getName() == this.name;
}
}
let jerry = new User("Jerry", null);
let jane = new User("Jane", jerry);
This produces the following error, when passing null
into Jerry's ctor:
Argument of type 'null' is not assignable to parameter of type 'User'
What can I do so that I can pass null
in for Jerry's managerOf
value?
Full reproducible example is here.
CodePudding user response:
So its necessary [to specificy null on] both on the constructor and the field definition?
Yes, but only because of how the managerOf
it's declared. It all flows from the type of the property, and Typescript enforces that the rest must match.
You declared it:
private managerOf: User;
Which means only a User
is a valid type to be assined to managerOf
. null
is not a User
so is not allowed.
Now you go to the constructor:
class User {
private name: string;
private managerOf: User;
constructor(name: string, managerOf: User) {
this.name = name;
this.managerOf = managerOf;
}
}
new User('qwerty', null) // error
Here you can't pass in null
because the constructor doesn't allow null
there. So let's fix that:
class User {
private name: string;
private managerOf: User;
constructor(name: string, managerOf: User | null) {
this.name = name;
this.managerOf = managerOf; // error
}
}
new User('qwerty', null) // fine
Now you can pass null
to the constructor, but null
still isn't an allowed type for the memberOf
property, so you get an error when you try to do that assignment.
So now let's allow the property to null
:
class User {
private name: string;
private managerOf: User | null;
constructor(name: string, managerOf: User | null) {
this.name = name;
this.managerOf = managerOf; // fine
}
}
new User('qwerty', null) // fine
So, if the property memberOf
is allowed to be null
then that needs to part of the type.
And the memberOf
constructor argument is totally separate from the memberOf
property of the class instances. The only thing that links them is that you decide to assign one to the other, and Typescript enforces type safety in that assignment.
How's typescript supposed to know that you don't want to do something like this?
class User {
private name: string,
private managerOf: User
constructor(name: string, managerOf: User | null) {
this.name = name
this.managerOf = managerOf || defaultManager
}
}
const jerry = new User("Jerry", null);
jerry.managerOf.name // returns the default name
Here you accept null
in the constructor, but the proeprty can't be null
and is populated with a default value.
Cases like this are why you have to put it "both" places.
It's worth noting there is a common shorthand for this:
class User {
constructor(
private name: string,
private managerOf: User | null
) {}
}
new User('qwerty', null) // fine
This syntax accepts a constructor argument and assigns it to an instance property only ever declaring it once.