Skip to content

Passing this from a constructor before all fields are set introduces undefineds in some cases #62917

@Taureon

Description

@Taureon

🔎 Search Terms

"field uninitialized",
"field initialized",
"constructor this",
"constructor passes this before field initialization",
"this passed when field uninitialized",
"use this before field initialization",
"constructor before initialization pass this",
"constructor use before initialization"

🕗 Version & Regression Information

  • This changed between versions ______ and _______
  • This changed in commit or PR _______
  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about constructor.
  • I was unable to test this on prior versions because I am using the latest version so I do not have those, and it does not appear to be fixed in the nightly version.

⏯ Playground Link

https://www.typescriptlang.org/play/?#code/GYVwdgxgLglg9mABDMYCmAnAFHEVMBciA8npgJSIDeAUIvYgPSOJQAWMAzoqJLAogBEAazBwA7p0Gs2AQyiIABrnwYAdADdZAGxBpFybrKVgQAWwBGmAyk75ZAEzoNmiOMBPmrGRAB9E4A5owChoDooANIgAnmgK4miIAOZxSoHBoeE8GHBmyFBqas70EAiccNpoatpwSThk6lq6aOQA3DQAvjQ0ENqynNykqtTFiAAOIBbaMBCITXpEppaY7QyIpWB2GCDQcNiUtGtrruAQsiBJbAqYORhEiijo+wb9nOZo3OzySuxcBvU+WxQWSQNCjY4sdxKIbWShyIzabQ8GBobQObhjOATPr4BxRCx4GTfLiIABWIDsiDECnYYKOLhYZ04iTYmDQ+LQZwpiQSiFkGESzKgsDASR+HE4mh0ehewFU4n56KK9Poj0wWF+nDa4IYmqlzUQAF5EAAWACsqwYXS6NHQ4hIDSwbSAA

💻 Code

function inner(outer: Outer) {
    // this function assumes that `outer.value` is a `number` instead
    // of `number | undefined`, yet we get `undefined` from it..
    console.log(outer.value);
}

class Outer {
    public value: number;
    constructor() {
        // uncaught error: `inner()` assumes that `this` (our instance
        // of `Outer`) has all fields populated, but that is just not the
        // case here, because we are setting `this.value` afterwards.
        inner(this);
        this.value = 45;
    }
}

new Outer();

🙁 Actual behavior

If you write a constructor that passes this before you initialize all fields, Typescript does not warn you that "you are passing this before all fields are initialized!"

This leads to rogue undefineds slipping past the type system, without even having to cast to any different types.

🙂 Expected behavior

In this situation, an error should be appearing at inner(this);, because

  • inner assumes every field of Outer to be initialized, and
  • Outer's constructor does not initialize every field before passing this to inner, which means
  • inner now deals with undefined fields without knowing,

and all entirely without any erroneous casting.

Additional information about the issue

A potentially related problem may be inner writing to outer.

It could also be a source of issues, like if it set a field's which then gets overwritten later.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions