1

Below is my code snippet:

class Hello {
    abc = {
        a: 'l',
        b: 1,
    }
    k = (prop: keyof typeof Hello.prototype.abc) => {
        console.log(prop)
    }
}

And the code works fine in TypeScript official playground.

So my question is: abc is a instance member of class Hello, Why can I access the abc property from Hello.prototype?

2
  • I thought I knew the answer. But tried it as well and it feels like incorrect behavior. Good find @L_K. Might be worth mentioning to the dev team Commented Mar 20, 2019 at 12:51
  • @OzLodriguez I'll open a issue in the github :-) Commented Mar 21, 2019 at 3:31

2 Answers 2

4

The issue is that for class X {...}, The type of X.prototype is represented in TypeScript as X, the type of an instance of a class. From the (outdated but still relevant for this question) TypeScript Language Specification:

The type of the constructor function introduced by a class declaration is called the constructor function type. The constructor function type has the following members:

...[snip]...

  • A property named 'prototype', the type of which is an instantiation of the class type with type Any supplied as a type argument for each type parameter.

So TypeScript thinks that the type of Hello.prototype is Hello. This is, of course, not true in general. It's true for methods, but not for instance properties, as you have discovered.

I searched through the issues in Github but didn't find anything mentioning this. It might be worth opening a new issue suggesting that the type of a class prototype be widened to only include methods.

EDIT: Whoopsie, I guess there are several existing issues about this, which were essentially declined suggestions because it would hurt more than it would help.

I know for a fact that doing so would mess up some stuff (currently an x instanceof Class check will narrow x's type to Class['prototype'], which, if widened to only include methods, would break everyone's code everywhere), so I doubt that they'd take such a suggestion. But it would be nice to have somewhere to point as official word on this issue.

Anyway, backing up to your problem... the runtime code is good, right? And you want this to work:

new Hello().k("a");

and would like a compiler warning on

new Hello().k("oopsie"); // error

? If so, then I'd suggest taking prototype out of the equation entirely and use a lookup type instead on the instance type Hello instead:

class Hello {
    abc = {
        a: 'l',
        b: 1,
    }
    k = (prop: keyof Hello['abc']) => {
        console.log(prop)
    }
}

This now behaves as desired and is not exposing the weirdness around instance properties and class prototypes in TypeScript.

Okay, hope that helps. Good luck!

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

1 Comment

Assuming you created this issue, I see @RyanCavanaugh had better luck than I did finding existing issues. I've edit the above answer to include them.
-1

The class syntax is basically just sugar coating. JavaScript still utilizes prototypes under the hood for instance methods. So eventhough you are creating a scoped member inside of a class. Hello is a global object that contains abc and even k won't be on it's prototype chain.

There is a nice article about the information here https://medium.com/javascript-scene/javascript-factory-functions-with-es6-4d224591a8b1

More insane yet, you can actually do this

class Hello {
    ...
}

 Hello.prototype.hiThere = 'oh Hello' 

 const test = new Hello()

 test.hiThere => oh Hello

2 Comments

I don't think abc is on Hello.prototype, because it's actually initialized in the constructor.
Good observation. Even if you add another class stating Hello2.prototype TypeScript type will still work as well. I am confused by this as to why it's working. Might even be a bug! Might be worth mentioning to The TS github project?

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.