Below I have class E
which extends W
. Both have a property meow, which is an object of functions. I'd like to take the functions from W
and spread them over E
's. However I would also like to access the original functions on W from within the classes on E. I assumed that I could do this through calling super directly but it's not working.
class W {
meow = {
woof: () => {
'2'
}
}
}
class E extends W {
meow = {
...super.meow,
hello: () => {
return super.meow.woof()
}
}
}
const x = new E()
console.log(x.meow.hello())
Is there any way this syntax could work?
Real world example of what I'm trying to do:
class Reads {
one() {
return '2'
}
byId() {
return '2'
}
}
class ReadsProp {
read = new Reads()
}
class UserReads extends Reads {
byEmail() {
return this.one()
}
}
class UserReadsProp extends ReadsProp {
read = new UserReads()
}
class UserRepo {
read = new UserReadsProp().read
}
const userRepo = new UserRepo()
console.log(userRepo.read.byEmail())
I wouldn't do it that way (though I think I've found a way to force it to work). After the version forcing it to work, I've shown how I think I'd do it instead.
(You seem to prefer to rely on ASI avoid writing semicolons, so I've done that below.)
The reason what you have isn't working is that E
's meow
replaces W
's meow
. Properties are created with "define" semantics, and so when E
's properties are created, W
's meow
is thrown away (before the initializer is evaluated) and E
's meow
is created in its place on the object being created. super.meow
is the same as this.meow
in the initializer, and has the value undefined
. Spreading undefined
is a no-op.
Here's the fairly ugly way I've forced it to work:
type PropType<TObj, TProp extends keyof TObj> = TObj[TProp]
class W {
meow = {
woof: () => '2'
}
}
class E extends W {
// @ts-ignore 2564 (not definitely assigned in ctor -- it is, in the super)
meow: PropType<W, "meow"> & {
hello: () => string
}
constructor() {
super()
// @ts-ignore 2565 (not assigned -- it is, in the super)
this.meow.hello = () => {
return this.meow.woof()
}
}
}
const x = new E()
console.log(x.meow.hello())
As you can see, it involves suppressing a couple of TypeScript errors. Here's what that does:
meow
for TypeScript only: meow: PropType & { hello: () => string } Doing that requires extracting the type of W
's meow
(so E
's has woof
), which I did by using PropType
from this answer :
type PropType<TObj, TProp extends keyof TObj> = TObj[TProp]
Then, since we know that W
creates meow
, I've extended the one W
creates in E
's constructor:
this.meow.hello = () => { return this.meow.woof() }
If you had multiple functions to add, you could assign them via Object.assign
.
woof
so it actually returns the '2'
, so we could see that hello
successfully calls it.I'd create classes for meow
:
class WMeow {
woof() {
return '2'
}
}
class W {
meow = new WMeow()
}
class EMeow extends WMeow {
hello() {
return this.woof()
}
}
class E extends W {
meow = new EMeow()
}
const x = new E()
console.log(x.meow.hello())
Now, that doesn't give meow
's methods access to the W
or E
instance. The fact you were using arrow function ssuggests to me that you wanted them to have access to this
(the W
or E
instance).
If you wanted that access, WMeow
and EMeow
could both accept a parent instance:
class WMeow<T extends W> {
constructor(protected parent: T) {
}
woof() {
this.parent.wmethod()
return '2'
}
}
class W {
meow = new WMeow<W>(this)
wmethod() {
console.log("wmethod")
}
}
class EMeow<T extends E> extends WMeow<T> {
hello() {
this.parent.emethod()
return this.woof()
}
}
class E extends W {
meow = new EMeow(this)
emethod() {
console.log("emethod")
}
}
const x = new E()
console.log(x.meow.hello())
The main issue is that you ask about "functions", but meow
is a member variable. super
is about calling functions.
The super keyword is used to access and call functions on an object's parent.
Super property accesses are used to access base class member functions from derived classes and are permitted in contexts where
this
(section 4.2) references a derived class instance or a derived class constructor function.
So it's not like Java for example.
A thing what you can do is making it a function, like a getter. That would work for the one level of inheritance shown in the question:
class W { _meow = { woof: () => { return '2' } } get meow() { return this._meow; } } class E extends W { meow = { ...super.meow, hello: () => { return super.meow.woof() } } } const x = new E() console.log(x.meow.hello()) console.log(x.meow.woof())
Added the second log
to show that the spread syntax is operational here (if you comment the ...super.meow
, x.meow.woof()
will die).
Side note: () => {'2'}
is wrong. It either has to be () => '2'
, or () => {return '2'}
.
Side note2: it works as a snippet, because it (and the question too) is JavaScript, not TypeScript. meow
is a completely ad-hoc object. TypeScript is what TJ Crowder shows, and it involves a lot more typing.
meow
.
This code uses Object.assign()
to extend the already existing meow
, and works both in JS and TS:
class W { _meow = { woof: () => { return '2' } } get meow() { return this._meow; } set meow(m) { Object.assign(this._meow, m); } } class E extends W { meow = { ...super.meow, hello: () => { return super.meow.woof() } } } const x = new E() console.log(x.meow.hello()) console.log(x.meow.woof())
Fun fact: as this variant extends the already existing meow
"property", it does not actually depend on the ...super.meow
part, but without it TS starts complaining that the hello
-only object is not assignable to the (implicit) type of meow
in W
,
Property 'woof' is missing in type '{ hello: () => string; }' but required in type '{ woof: () => string; }'.
The compiled code works nevertheless, as JS actually does not care.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.