Skip to content

In target: ES5, Base class constructor's primitive return value should be ignored #51738

Open
@Josh-Cena

Description

@Josh-Cena

Bug Report

🔎 Search Terms

class, extends, constructor, return primitive

🕗 Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about class constructors

⏯ Playground Link

Playground link with relevant code

💻 Code

new class extends class {
  constructor() {
    return 1;
  }
} {
  constructor() {
    super();
    console.log(typeof this);
  }
}

🙁 Actual behavior

Logs "number"

🙂 Expected behavior

Logs "object"

Activity

MartinJohns

MartinJohns commented on Dec 3, 2022

@MartinJohns
Contributor

This is working as intended. It's legit JavaScript. For TypeScript to change this behaviour would violate the language design goals.

Related: #38519 / #27594 / #13819

Josh-Cena

Josh-Cena commented on Dec 3, 2022

@Josh-Cena
ContributorAuthor

@MartinJohns The change can be trivially done in the downleveling—it requires no type information.

new /** @class */ (function (_super) {
    __extends(class_1, _super);
    function class_1() {
-       var _this = _super.call(this) || this;
+       var _this = _super.call(this);
+       if (_this === null || (typeof _this !== "object" && typeof _this !== "function"))
+           _this = this;
        console.log(typeof _this);
        return _this;
    }
    return class_1;
}(/** @class */ (function () {
    function class_2() {
        return 1;
    }
    return class_2;
}())));

FWIW, anything that Babel does right but tsc does wrong looks like a bug to me.

MartinJohns

MartinJohns commented on Dec 3, 2022

@MartinJohns
Contributor

Nevermind my earlier comment. I wrongly assumed the JavaScript will behave the same.

fatcerberus

fatcerberus commented on Dec 3, 2022

@fatcerberus

You probably should have specified that this only happens with ES5-downleveled code. From the OP it seemed like you meant this happened regardless of target (which would indicate it was just normal JS behavior).

changed the title [-]Base class constructor's primitive return value should be ignored[/-] [+]ES5 output: Base class constructor's primitive return value should be ignored[/+] on Dec 3, 2022
changed the title [-]ES5 output: Base class constructor's primitive return value should be ignored[/-] [+]In target: ES5, Base class constructor's primitive return value should be ignored[/+] on Dec 3, 2022
RyanCavanaugh

RyanCavanaugh commented on Dec 5, 2022

@RyanCavanaugh
Member

FWIW, anything that Babel does right but tsc does wrong looks like a bug to me.

You should use Babel in that case. The TS emit takes some complexity/compliance tradeoffs and is much less configurable.

I don't think we want to risk breaking any ES5 programs at this point given how old of a target it is.

added
SuggestionAn idea for TypeScript
Awaiting More FeedbackThis means we'd like to hear from more people who would be helped by this feature
on Dec 5, 2022
Josh-Cena

Josh-Cena commented on Dec 6, 2022

@Josh-Cena
ContributorAuthor

I can guess the historical context here (tsc implementing classes before the latter getting standardized), but IMO the current behavior doesn't benefit anyone and is unlikely to be relied on. If this is a primitive, it means you can't mutate it whatsoever. Unless you do something really weird like:

function SomethingThatReturnsEitherObjectOrNumberIDK() {
  if (somethingNotGoingWell) return 1;
  return {
    the: "actual object that I want to construct",
  };
}

declare class SomethingThatReturnsEitherObjectOrNumberIDK {
  the?: string;
}

new class extends SomethingThatReturnsEitherObjectOrNumberIDK {
  constructor() {
    super();
    if (typeof this === "number")
      return { the: `number wrapped into an object: ${this}` }; // type: { the: never }
  }
}

But that doesn't even get correct types! Why would someone use tsc, choose its bizarre subclassing behavior, but also disregard the types?

Built-in subclassing not working is a missed but understandable tradeoff, but this one seems niche and not as breaking-change-prone, while increasing tsc's conformance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Awaiting More FeedbackThis means we'd like to hear from more people who would be helped by this featureSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @fatcerberus@MartinJohns@RyanCavanaugh@Josh-Cena

        Issue actions

          In target: ES5, Base class constructor's primitive return value should be ignored · Issue #51738 · microsoft/TypeScript