'Typescript: Override superclass method without needing to know superclass method signature
Here's how I would reliably override a method in vanilla JS without caring about names or number of arguments, or the return value:
import EventEmitter from 'events'
// console.log event + arguments every time this emitter emits anything.
// Just an example.
class LogEmitter extends EventEmitter {
emit(...args) {
console.log('emit', ...args)
return super.emit(...args)
}
}
But in TypeScript, I hear complaints:
import { EventEmitter } from 'events'
class LogEmitter extends EventEmitter {
emit(...args) { // Rest Parameter 'args' implicitly has an any[] type
console.log('emit', ...args)
return super.emit(...args) // Expected at least 1 arguments, but got 0 or more.
}
}
I'm not sure how to tell TypeScript that this is ok. I don't want to know anything about the signature of the method I'm overriding, just print whatever arguments you're passed. Means I don't have to update my signature if/when the superclass signature changes, and ideally I would have one way to type this that would work for all methods I override, with maybe an exception of telling it which method name to copy the signature from.
Something like:
The type of
...argsshould be whatever theParametersare tosuper.emit
But my newbie attempt at this isn't valid syntax:
import { EventEmitter } from 'events'
class LogEmitter extends EventEmitter {
emit(...args: Parameters<super.emit>) { // 'super' can only be referenced in members of derived classes or object literal expressions.
console.log('emit', ...args)
return super.emit(...args)
}
}
My workaround is to any the required first argument:
import { EventEmitter } from 'events'
class LogEmitter extends EventEmitter {
emit(type: any, ...args: any[]) {
console.log('emit', type, ...args)
return super.emit(type, ...args)
}
}
But I think this is more like patching symptoms, since now this class reports a less accurate signature. I feel like there's a better solution that gives the correct signature for emit automatically without me having to copy it from super.emit.
How can I type this perfectly without having to know anything about the signature of super.emit?
Solution 1:[1]
Unfortunately, Typescript does not (as of TS4.1) contextually type subclass members by the analogous members in super classes (or implemented interfaces). There are a bunch of similar GitHub issues, but I think the canonical one for this particular situation is microsoft/TypeScript#23911. For now, all you can do is "patch symptoms":
As a workaround, if you want to refer to the super class, you will need to do so explicitly by name (EventEmitter) and not by super, and you can use a lookup type to get the emit method:
class LogEmitter extends EventEmitter {
emit(...args: Parameters<EventEmitter['emit']>) {
console.log('emit', ...args)
return super.emit(...args)
}
}
Solution 2:[2]
typeof fn lets you get the type of a function, so Parameters<typeof EventEmitter.prototype.emit> should get things working.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | jcalz |
| Solution 2 | willis |
