1

I'm trying to create a derived interface that adds a new overload to an existing method. But TypeScript won't let me.

Consider the following interface that allows adding two numbers:

interface SimpleAdder {
  add(a: number, b: number): number;
}

Let's say I want to create a derived interface that -- in addition to numbers -- also allows adding bigints. I would expect the following code to work:

interface ExtendedAdder extends SimpleAdder {
  add(a: bigint, b: bigint): bigint;
}

This code gives the following error:

Interface 'ExtendedAdder' incorrectly extends interface 'SimpleAdder'.
  Types of property 'add' are incompatible.
    Type '(a: bigint, b: bigint) => bigint' is not assignable to type '(a: number, b: number) => number'.
      Types of parameters 'a' and 'a' are incompatible.
        Type 'number' is not assignable to type 'bigint'.(2430)

TypeScript doesn't seem to understand that I'm trying to add an overload, not replace the method signature.

It does work if I explicitly repeat the inherited overload. But this seems redundant to me (especially in more complex read-world scenarios):

interface ExtendedAdder extends SimpleAdder {
  add(a: number, b: number): number; // Copied from SimpleAdder
  add(a: bigint, b: bigint): bigint;
}

Another workaround I found was to define ExtendedAdder as a type alias rather than an interface. But this approach lacks the clarity of a derived interface:

type ExtendedAdder = SimpleAdder & {
  add(a: bigint, b: bigint): bigint;
}

Edit: My actual code has nothing to do with adding numbers, and my actual function signatures are more complex. This question is strictly about adding an overload to an existing method through a derived interface, not about this particular use case.

Daniel Wolf
  • 12,855
  • 13
  • 54
  • 80

1 Answers1

0

Here you have generic solution:

//https://stackoverflow.com/questions/50374908/transform-union-type-to-intersection-type/50375286#50375286
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
  k: infer I
) => void
  ? I
  : never;

type A = (a: number, b: number) => number;
type B = (a: bigint, b: bigint) => bigint;
type C = (a: number[], b: string, c: string[]) => 42

type Fn = (...args: any[]) => any;
type AllFn = [A, B, C]

type Overloading<T extends ReadonlyArray<Fn>> = UnionToIntersection<T[number]>

type OverloadingParams<T extends ReadonlyArray<Fn>> = Parameters<T[number]>

const x: Overloading<AllFn> = (...arg: OverloadingParams<AllFn>) => null as any;

const result1 = x(2n, 2n) // bigint
const result2 = x(2, 2)// number
const result3 = x([1, 2], 'sd', ['sdf']) //42
const result4 = x(1, 'sd', ['sdf']) //expected error
const result5 = x([1], 2n, ['sdf']) //expected error
const result6 = x([1], 2, ['sdf']) //expected error
const result7 = x(1, 2,2) //expected error