#Add read-only property to class's prototype

27 messages · Page 1 of 1 (latest)

fleet dove
#

How can I do something like this without a type error?

export interface Foo { readonly bar: string; }
class Foo {}
Foo.prototype.bar = '';
naive stream
#

If you're strongly tied to it being a readonly prototype property, not sure you can. You may just need to bypass the typechecker there somehow.

#

e.g.

interface Foo { readonly bar: string }
class Foo {}
Object.assign(Foo.prototype, {bar: ""})
thorn marsh
#

or just // @ts-ignore

#

worth noting that neither of those two solutions are all that type-safe though

#

you could maybe do something like this

sonic solsticeBOT
#
n_n#2622

Preview:ts interface Foo { readonly bar: string } class Foo {} ;(Foo.prototype as {bar: Foo["bar"]}).bar = "a"

thorn marsh
#

which is a tiny bit better

naive stream
#

// @ts-ignore is meant to be something of a last resort, so I'd leave it as one.

thorn marsh
#

sure, but afaict Object.assign(Foo.prototype, {bar: ""}) completely bypasses the type system (in this specific context at least), so it's about as bad as (Foo.prototype as any).bar = 'a' i think

faint jasper
#

I've never done it but I made a quick function which kinda works like your solutions just I think it looks nicer using it

function assignProto<T>(constructable: new (...args: any[]) => T, data: Partial<T>) {
    Object.assign(constructable.prototype, data)
}

interface Foo { readonly bar: string }
class Foo {}
assignProto(Foo,
  { bar: "" } // this is typed
)
fleet dove
#

Thanks both. Basically it's a scripting context object (think jQuery's $) with lots of objects hanging off it but one or two instance objects. I want to keep the object small by having all the static stuff on the prototype.

naive stream
#

Sure these are all "bypassing the type system" solutions, but if you're going to bypass the type-system, it's better to do it in a more fine-grained way than // @ts-ignore which will ignore everything wrong with the line.

fleet dove
#

Maybe I'm going about this the wrong way. I just wanna attach stuff to the prototype like class methods are under the hood.

naive stream
#

I'd probably consider a static, which would be accessed like Foo.bar not fooInstance.bar, but that is a different approach.

#

But yeah, I think what you're doing largely makes sense, TS just isn't completely geared towards some forms of "low-level" prototype stuff.

thorn marsh
#

i guess there isn't anything wrong with Object.assign, just seems a little arbitrary

fleet dove
#

Yer, I thought so. Our game engine has a hacky/unsafe but very powerful runtime scripting system, new Function('$', script)(new ScriptApi(args, data)) so the easy of use of the API object wins out above all, ruling out static.

thorn marsh
#

other than specifying the type of MyClass.prototype

sonic solsticeBOT
#
n_n#2622

Preview:ts type DeepMutable<T> = { -readonly [K in keyof T]: DeepMutable<T[K]> } function unsafeMutable<T>(it: T): DeepMutable<T> { return it as T } interface Foo { readonly bar: string } class Foo {} unsafeMutable(Foo.prototype).bar = "a"

thorn marsh
#

you may want to do something like this if you're going to be doing this kinda stuff often

#

as an alternative to jakob's solution

fleet dove
#

That look good to me. We're basically side-stepping TS anyway by running unknown JS at runtime so happy to go with this. Thanks again.

thorn marsh
# sonic solstice

alternatively:

type Mutable<T> = { [K in keyof T]: T[K]; }
``` instead of `DeepMutable` - because if you're not careful and end up with very complex types (e.g. recursive types), this would never finish processing those types
turbid falcon
#

!resolved