1

TypeScript is complaining about an uninitialized class property, even though it appears to be properly assigned.

Here's a minimum reproducible example:

type Config = {
    color: string;
}

class Toto {
    color: string;

    constructor(config: Config) {
        this.setConfig(config);
    }

    setConfig(config: Config) {
        this.color = config.color;
    }
}

TypeScript playground

The compiler underlines the color property definition and throws the following error:

Property 'color' has no initializer and is not definitely assigned in the constructor.

The issue appears to be with using the setConfig method to set the value of color, since the following code produces no compiler errors:

type Config = {
    color: string;
}

class Toto {
    color: string;

    constructor(config: Config) {
        this.color = config.color;
    }
}

This problem is I want to be able to use the setConfig method to set class properties to different values during the lifetime of a class instance. I don't want to have to repeated the same code within both my constructor and setConfig method just to get around a TS compiler issue.

Does anyone know why the compiler can't see that the class variable is properly being set in the constructor?

4
  • 2
    What you're describing isn't allowed in TypeScript because setConfig could be overridden in a subclass and that override could choose to not set this.color. The only way TypeScript can guarantee that this.color is set is by requiring it to be set directly in the constructor. Commented Jul 14, 2020 at 5:56
  • You can always initialise it to null or empty string or a similar default value. Commented Jul 14, 2020 at 6:00
  • Ah, so you could subclass Toto and overide it with a different setConfig method and then that new method would be used by the Toto class constructor? @Dai Commented Jul 14, 2020 at 6:02
  • @RobertCooper Correct. Commented Jul 14, 2020 at 6:03

3 Answers 3

3

If you as the developer are certain that color will not go uninitialized in the constructor you can just tell the compiler that this is the case:

type Config = {
    color: string;
}

class Toto {
    color!: string;

    constructor(config: Config) {
        this.setConfig(config);
    }

    setConfig(config: Config) {
        this.color = config.color;
    }
}

The ! tells the compiler that this variable will not be null at the point when ! is used.

Of course in an ideal world the compiler would be able to detect the indirect setting but I don't think this is currently possible.

Playground Link

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

Comments

1

TypeScript does not allow this since the setConfig method could be overwritten if even the Toto class is subclassed:

class SubToto extends Toto {
    setConfig() {
        // Do nothing
    }
}

Therefore, the two possible approaches are:

  1. Set the values directly in the constructor, and repeat the code within setConfig:
class Toto {
    color: string;

    constructor(config: Config) {
        this.color = config.color;
    }

    setConfig(config: Config) {
        this.color = config.color;
    }
}

This has the downside of repeating the code.

  1. Setting an initial empty value to the property.
class Toto {
    color: string | null = null;

    constructor(config: Config) {
        this.setConfig(config)
    }

    setConfig(config: Config) {
        this.color = config.color;
    }
}

This has the downside of having to now check if the property is empty before using it elsewhere in your class methods.

Comments

0

You can do it like this, because initially color is undefined. Or initialise it to empty string like private color: string = '';.

type Config = {
    color: string;
}

class Toto {

    private color: string|undefined;
    
    constructor(config: Config) {
        this.setConfig(config);
    }

    setConfig(config: Config) {
        this.color = config.color;
    }
}

2 Comments

I think private color: string | null = null; is better than using undefined, imo.
Ya, I was hoping to avoid having to set an initial empty value, but it appears as though it might be the best workaround.

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.