1

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.

9
  • 1
    constructor(name: string, managerOf: User | null) Commented Nov 29, 2022 at 20:10
  • but then I get Type 'User | null' is not assignable to type 'User'. Commented Nov 29, 2022 at 20:15
  • Then make managerOf nullable as well? I'm not sure what the problem is. Commented Nov 29, 2022 at 20:47
  • The problem is that I've posted a succinct, reproducible code example and no one has (yet) shown me what is needed to make the code compile. How would I make managerOf nullable as well, and why would I need to do that if I specify it being nullable in the constructor. Commented Nov 29, 2022 at 20:51
  • private managerOf: User | null; Commented Nov 29, 2022 at 20:51

1 Answer 1

3

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

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.