Guilherme Oderdenge
Guilherme Oderdenge

Reputation: 5001

Why is my typing failing when array contains multiple elements but not only one?

Given the following code:

function SumSystem() {
    return {
        sum(x: number, y: number) {
            return x + y
        }
    }
}

function MultiplySystem() {
    return {
        multiply(x: number, y: number) {
            return x * y
        }
    }
}

function emit<S extends () => Record<string, unknown>>(systems: S[]): { [K in keyof ReturnType<S>]: ReturnType<S>[K] } {
    // whatever
    return {}
}

emit([SumSystem]).sum(1, 2) // works
emit([MultiplySystem]).multiply(2, 2) // works

emit([SumSystem, MultiplySystem]).sum(1, 2) // does not work
emit([SumSystem, MultiplySystem]).multiply(2, 2) // does not work

As per comments, the last two emits are not working, and that's occurring because I am passing two values into the argument's array: [SumSystem, MultiplySystem].

Questions:

  1. What's the problem?
  2. Is it possible to fix it?
  3. If the second question is true, how?

Playground

Upvotes: 1

Views: 24

Answers (1)

tenshi
tenshi

Reputation: 26374

Use the generic to infer the type of the entire array instead, get the return type of all the functions, then turn that union into an intersection for the result.

type UnionToIntersection<U> = 
  (U extends any ? (k: U) => void : never) extends ((k: infer I)=>void) ? I : never

function emit<S extends (() => Record<string, unknown>)[]>(systems: S): UnionToIntersection<ReturnType<S[number]>> { ... }

Playground


Your solution does not work as intended because S is inferred as (() => { sum... }) | (() => { multiply... }) instead of an array.

Upvotes: 2

Related Questions