Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"typeof this" returns "this" (instance) instead of its type (class) #51265

Closed
clshortfuse opened this issue Oct 21, 2022 · 6 comments
Closed
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@clshortfuse
Copy link

clshortfuse commented Oct 21, 2022

Bug Report

🔎 Search Terms

typeof this
this
constructorOf

🕗 Version & Regression Information

v.4.8.4 will say a property doesn't exist.

v.4.9.0 (nightly) will give the same, but also cannot find this

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

⏯ Playground Link

https://www.typescriptlang.org/play?suppressImplicitAnyIndexErrors=true&ts=4.9.0-dev.20221021#code/MYGwhgzhAECC0G8CwAoa0IBcyYJbGgDMB7Y6AXmgHIAjMAJyoG5VV1hiA7LegV2EzF6ACgCUiAL6s00ABYBTECGJjE0evMy96nagHchIACZVoUlG2gBzTQBEc8sQC57meYkvoNWndE7y9aFdHURYZc0sbTAAVAE8AB3liQmDnTASkwiCHDxl0AHp89U1tXWCw9C8S32F-QNTRUQA6Dm5MPgEhaEhodMTk7LcKs2l0KOjZXAg0yZhkPOKfXUxZ4YiZcYzkiam0rayVqdzKxdLe2ZauHn5Bem6YXk4Aa05iPV0evszzqbXUddaWGgZEodTgYjCqGITSiDRhdjAsQhUPhMX2cNeemRFhQ0PGszETQUShUoRRm36hB202axOUTGghQwsmIvGMRDAuBA5M0cUp1MJJGIYSAA

💻 Code

class A {
  static foo = 'bar';

  constructor() {}

  hello() { return 'world' }

  getDate():Date {
    return new Date();
  }

  getTypeofDate():typeof Date {
    // return Date;
    return (new Date()).constructor as typeof Date;
  }

  getThis():this {
    return this;
  }

  getTypeofThis():typeof this {
    return this.constructor as unknown as typeof this;
  }
}

const o = new A();

o.getDate().getDay();
o.getTypeofDate().now();

o.getThis().hello();
o.getTypeofThis().hello; // should fail
o.getTypeofThis().foo; // should pass

🙁 Actual behavior

(typeof this) returns this

🙂 Expected behavior

(typeof this) should return the static context of an instance.


I am aware that I can do typeof A, but I'm simplifying for the reproducible.

Actual usage is web components: HTMLElement and static observedAttributes. Classes that extend the base class (eg => InputElement extends FormControlElement) will override the static attributes. Therefore, the base class FormControlElement needs to access this.constructor.observedAttributes , not FormControlElement.observedAttributes in order to properly see what attributes are being observed. (ie: subclass is extending superclass static properties).

@RyanCavanaugh RyanCavanaugh added the Working as Intended The behavior described is the intended behavior; this is not a bug label Oct 21, 2022
@fatcerberus
Copy link

fatcerberus commented Oct 21, 2022

This is right, because typeof x in TypeScript means “the type of the value referred to by x” (in other words what you get if you console.log(x)). For this inside a method, that’s the current instance, so you get the instance type.

Basically typeof is giving you the type of a binding that exists in value space. It so happens that this in type space refers to the same type as this in value space. Which isn’t true for classes: MyClass refers to the instance type in typespace but console.log’ging it will log the class itself.

@RyanCavanaugh
Copy link
Member

typeof takes an expression which is a value and produces its type. The value this, at that position, refers to an instance of a class, not its constructor.

Flow chose a different interpretation of it, I can't speak as to why.

@clshortfuse
Copy link
Author

clshortfuse commented Oct 21, 2022

Is there a way we can do ClassOf<T> and get back the class (constructor)? I raked my head for hours trying to figure out a way to do with TS, and all I could come with was a type map, but that somewhat kills it being dynamic. No code I could write could recreate a class "type". It seems internal only.

Ended up with this on my base class:

  /** @return {typeof CustomElement} */
  get static() {
    return /** @type {typeof CustomElement} */ (/** @type {unknown} */ (this.constructor));
  }

And these on the subclasses:

  /** @return {typeof Control} */
  get static() { return /** @type {typeof Control} */ (super.static); }

It's not major, or production blocking, I wish I didn't have to reapply every time. I did see an issue related to polymorphic this, but wasn't sure if that's the same "issue": #5863

@RyanCavanaugh
Copy link
Member

Not today, no. We tried making this.constructor be the class constructor type, but doing this quickly breaks the type system because it makes all class instances invariant whenever their constructors vary contravariantly (which is quite common). See #3841

@clshortfuse
Copy link
Author

@RyanCavanaugh Thanks. I figured I'm not breaking new ground here. Just wasn't sure where to track the conversation. I'll keep an eye on the other filed issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

3 participants