#Static property access in abstract and subclasses results in errors

64 messages · Page 1 of 1 (latest)

ivory patrol
#

Hi,

I was just playing around with some class-based syntax, and quickly ran into a number of different unexpected errors (even more if you've got a strict ESLint setup). Please see the attached code sample. There are a few attempts there commented out to remedy the situation but I didn't find them satisfying. There's also an open issue (from 2015!) https://github.com/Microsoft/TypeScript/issues/3841

Does anyone here have ideas around best practices for using static properties? I'd appreciate any help, especially as I find it difficult to believe that there are no straightforward solutions to these very basic use cases. Thanks! ✌️

GitHub

Given class Example { } The current type of Example.constructor is Function, but I feel that it should be typeof Example instead. The use case for this is as follows: I'd like to reference the ...

honest barnBOT
#
slashrug#0

Preview:```ts
export abstract class Renderer {
// declare ['constructor']: typeof Renderer

public constructor(protected readonly component: Component) {}
public abstract initialize(): void
}
// export interface Renderer {
// constructor: { [Prop in keyof typeof Renderer]: (typeof Renderer)[Prop] }
...```

dusk storm
#

I'm not sure how common what you are asking for (static inheritance) is done in JS, but quite some OOP languages simply don't have it. C# for example existed without it for a decade and is only added recently.

honest barnBOT
#
nonspicyburrito#0

Preview:```ts
abstract class Renderer {
public constructor(
protected readonly component: Component
) {}
public abstract initialize(): void
}

abstract class Component {
protected readonly renderer: Renderer

public constructor() {
this.renderer = this.createRenderer()
...```

dusk storm
#

For example your Component code could be rewritten like this and eliminate the use of static completely.

ivory patrol
dusk storm
#

Instance methods and properties are on the prototype, they don't get a copy of everything on every instance if you are worried about memory usage.

ivory patrol
dusk storm
#

Not if you use a getter.

#
class Foo {
    private get template() {
        return 'the template'
    }
}

No instance of Foo will get a template property, it exists only on the prototype.

ivory patrol
#

I appreciate the brainstorming but I'd like to know how to use a language feature, not how to avoid it. (I'm already considering not using class syntax at all to get around all the weirdness...)

dusk storm
#

I'm just pointing out there are alternative ways to go about it, as seen in many other OOP languages where this isn't possible at all and they live just fine.

undone frost
#

nice I didn't know declare ['constructor']: typeof MyComponent was possible

#

and I remembered why you shouldn't use typeof Class unless you're wanting the construct signature

honest barnBOT
#
webstrand#0

Preview:```ts
class Foo {
declare ["constructor"]: typeof Foo
constructor(a: string) {}
}

class Bar extends Foo {
declare ["constructor"]: typeof Bar
constructor(b: number) {
super("")
}
}```

#
class Foo {
  declare ["constructor"]: typeof Foo;
  constructor(a: string) {}
}

class Bar extends Foo {
  declare ["constructor"]: typeof Bar;
//        ^^^^^^^^^^^^^^^
// Property '["constructor"]' in type 'Bar' is not assignable to the same property in base type 'Foo'.
//   Type 'typeof Bar' is not assignable to type 'typeof Foo'.
//     Types of construct signatures are incompatible.
//       Type 'new (b: number) => Bar' is not assignable to type 'new (a: string) => Foo'.
//         Types of parameters 'b' and 'a' are incompatible.
//           Type 'string' is not assignable to type 'number'.
  constructor(b: number) {super("")}
}```
undone frost
#

or at least one of the reasons why this.constructor isn't typed by default

undone frost
#

that's what I do when I want constants on the prototype anyway

#

it's all consistently typed without needing "magic" to keep inheritance working

#

you just need a little magic to set the prototype in subclasses that need to modify it

ivory patrol
undone frost
#

declare ["constructor"] appears to be equivalent to declaration merging an interface and far less cumbersome

ivory patrol
undone frost
#

might want to use Object.defineProperty instead of = so that you can mark the property as non-enumerable

ivory patrol
#

Yep.

undone frost
#

it only ever happens once at runtime, so the cost is not terrible

#

finally, you can also set the htmlTemplate using a class decorator

ivory patrol
#

That was my thought process, but you know at that point all the class sugar seems like unnecessary noise..

undone frost
#

might be more ergonomic than using static { }? I'm not sure

undone frost
#

super, #private, target.new, etc all break

#

it's no longer just sugar, sadly

ivory patrol
#

I mean afaik #private and decorators don't mix anyway.

undone frost
#

I mean like, in plain old javascript

class Foo {}
function Bar() {
    Foo.call(this);
}
Bar.prototype = Object.create(Foo.prototype);
new Bar();

you can't use ES6 classes with ES5 classes

#

it's a runtime error

#

so just get rid of the idea that the old prototype-style classes still exist. They do not, not really

ivory patrol
#

Meh who needs constructors anyway

undone frost
#

well, building objects dynamically and filling them with methods is much slower

#

Object.create is pretty slow, too last I measured it

ivory patrol
#

Prototypical inheritance is all I need I mean.

undone frost
#

delete Object.prototype.__proto__ is essential for security

ivory patrol
undone frost
#

Object.setPrototypeOf is rather dangerous for performance

undone frost
#

oh I keep forgetting, but { __proto__: whatever, ... } is faster than Object.create

#

note that this is special syntax and the __proto__ there has no relation to Object.prototype.__proto__

#

but even that is slow compared to new whatever()

#

(but probably only because few people use it)

ivory patrol
#

Ugh you know what I'd like? If basic stuff like this had already been figured out, but apparently that's too much to ask for on the web.... -.- /rant

undone frost
#

yep. I'm still hoping that we get a "use a better language"; pragma in the future

#

that jettisons all of these bad ideas

#

for new bad ideas, probably 😮‍💨

#

but I can hope they're not as bad

ivory patrol
#

I mean we just the DOM APIs hooked up to wasm and I'm never looking back.

undone frost
ivory patrol
#

Well yeah sth at Mozilla used to be working on this stuff until she left for some other company...

#

Very disappointing, don't know why there's no progress on that front.

#

Anyway, thanks for the ideas @undone frost

dusk storm
#

All this work to fight against a pattern the language doesn't support 😅

ivory patrol
#

JS supports this pattern just fine, question is why TS devs don't care about basic stuff being broken.

dusk storm
#

Can't speak for others, for me personally static inheritance is not something required for OOP to function, and as said that's the case for plenty other OOP languages.

#

This is far from the only JS pattern that TS doesn't support.

ivory patrol
#

Well I'm pretty this is as good as it's gonna get until issue #3841 is resolved so