I'm using Typescript and creating a form validation library with chained methods, but I'm stuck trying to reuse functions because of the this
return, I will exemplify in a simpler way:
const schema = {
computer() {
return {
turnOn() {
console.log('turn on')
return this
},
openVscode() {
console.log('open vscode')
return this
},
work() {
console.log('work')
return this
},
turnOff() {
console.log('turn off')
return this
}
}
},
phone() {
return {
turnOn() {
console.log('turn on')
return this
},
takeSelfies() {
console.log('take selfies')
return this
},
callToMom() {
console.log('call to mom')
return this
},
turnOff() {
console.log('turn off')
return this
}
}
}
}
everything works fine.
const devices = {
iMac: schema.computer().turnOn().openVscode().work().turnOff(),
iPhone: schema.phone().turnOn().takeSelfies().callToMom().turnOff()
}
but when I try to separate the repeated functions in another file, for example
const mixed = {
turnOn() {
console.log('turn on')
return this
},
turnOff() {
console.log('turn off')
return this
}
}
to reuse
const newSchema = {
computer() {
return {
...mixed,
openVscode() {
console.log('open vscode')
return this
},
work() {
console.log('work')
return this
}
}
},
phone() {
return {
...mixed,
takeSelfies() {
console.log('take selfies')
return this
},
callToMom() {
console.log('call to mom')
return this
}
}
}
}
I'm stuck
I know this is because the this
of the mixed
object returns only the content itself, but I don't know how to solve this problem.
I thank you all!
The points made by captain-yossarian and Charlie about mixins are good and worth investigating if you want use them. However, here's a solution that doesn't require much refactoring of the original code:
const mixed = { /* same as before */ }
type AnyFn = (...args: never[]) => unknown
type SetReturnTypes<T extends Record<string, AnyFn>> = {
[K in keyof T]: (...args: Parameters<T[K]>) => SetReturnTypes<T>
}
const makeMixed = <T>(obj: T & ThisType<T & typeof mixed>): SetReturnTypes<T & typeof mixed> => ({
...mixed,
...obj
}) as unknown as SetReturnTypes<T & typeof mixed>
const newSchema = {
computer() {
return makeMixed({
openVscode() {
console.log('open vscode')
return this
},
work() {
console.log('work')
return this
}
})
},
phone() {
return makeMixed({
takeSelfies() {
console.log('take selfies')
return this
},
callToMom() {
console.log('call to mom')
return this
}
})
}
}
newSchema.computer().turnOn().openVscode()
SetReturnTypes
makes all methods of T
return the correct this
type, assuming all these methods return this
.makeMixed
is a helper to create an object with mixed
with the correct types. It uses ThisType
so that the methods in obj
have the correct this
type:makeMixed({
doSomething() {
this.turnOn() // would be a compile error without the ThisType
return this
}
})
An issue with this is that methods not returning this
are incorrectly typed. If you want to ensure that all the methods return this
, let me know and I may be able to come up with a solution for that.