Skip to content

Allow overloading/overriding method signatures in subclasses (i.e. narrowing declarations) #21176

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
baygeldin opened this issue Jan 13, 2018 · 2 comments
Labels
Question An issue which isn't directly actionable in code

Comments

@baygeldin
Copy link

baygeldin commented Jan 13, 2018

TypeScript Version: 2.6.2

When deriving from a class which methods are too generic it's usually desirable to restrict method signatures in the subclass without changing the implementation. It would be great to have an ability to narrow declarations.

Code

import { EventEmitter } from 'events'

class MyClass extends EventEmitter {
  on(event: 'foo', fn: (d: number) => void): this
  on(event: 'bar', fn: (d: string) => void): this
}

const instance = new MyClass()

instance.on('foo', (n) => { console.log(n + 42) }) // Ok
instance.on('xyz', (d) => { console.log(d) }) // Error

Expected behavior:
Compiles correctly if there are no type errors like on the last line.

Actual behavior:
Function implementation is missing or not immediately following the declaration.
(method) MyClass.on(event: "bar", fn: (d: string) => void): this (+1 overload)

Related:
I use the following workaround, because it has the least boilerplate, but it is not ideal since it has a runtime dependency (i.e. additional function call):

import { EventEmitter } from 'events'

class MyClass extends EventEmitter {
  on(event: 'foo', fn: (d: number) => void): this
  on(event: 'bar', fn: (d: string) => void): this
  on(event: any, fn: (...args: any[]) => void) {
      return super.on(event, fn)
  }
}

const instance = new MyClass()

instance.on('foo', (n) => { console.log(n + 42) }) // OK
instance.on('xyz', (d) => { console.log(d) }) // Error

Also, is this issue somehow related to the problem I've described?

@baygeldin baygeldin changed the title Allow overloading/overriding method signatues in subclasses (i.e. narrowing declarations) Allow overloading/overriding method signatures in subclasses (i.e. narrowing declarations) Jan 15, 2018
@mhegazy
Copy link
Contributor

mhegazy commented Jan 16, 2018

If you do not want to implement the function, just change the signature, put it in an interface with the same name. e.g.:

interface MyClass { 
    on(event: 'foo', fn: (d: number) => void): this;
    on(event: 'bar', fn: (d: string) => void): this;
}

class MyClass extends EventEmitter {
  
}

One thing to note, that it does not matter whether you put it in a class or in an interface, the new definitions of on override those of the base. in other words, MyClass.on only has two overloads here one that accepts foo and another for bar, but the base signature that accepts string is not there any longer as it was overwritten.

@mhegazy mhegazy added the Question An issue which isn't directly actionable in code label Jan 16, 2018
@baygeldin
Copy link
Author

@mhegazy interesting, so it works because class generates interface declarations (as pointed out in docs) and because interface declarations of the same module are merged. I guess I need to revisit what interfaces in TypeScript really are... Thank you, it works.

@microsoft microsoft locked and limited conversation to collaborators Jul 3, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Question An issue which isn't directly actionable in code
Projects
None yet
Development

No branches or pull requests

2 participants