Home > Back-end >  Accepting null arguments for class instances in Typescript constructors
Accepting null arguments for class instances in Typescript constructors

Time:11-30

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.

See playground

  • Related