3

Why is the following raising : Type '(...args: T) => void' is not assignable to type 'Fn<T>'.(2322) :

type Fn<T extends unknown[]|unknown> = T extends unknown[] ? (...args: T)=>void : T

const func = <T extends unknown[]>()=>{
    // why does this error and how does one resolve it?
    // ideal Fn must handle cases outside of unknown[] including `any` and `unknown`
    const fn : Fn<T> = (...args: T)=> console.log(args)
    return fn
}

// some tests by me to try and understand what is going on:
type ValidFn1 = Fn<[value:number]>
type ValidFn2 = Fn<any>
type ValidFn3 = Fn<never>
type ValidFn4 = Fn<unknown>
type ValidFn5 = Fn<unknown[]>
type ValidFn6 = Fn<void>

const validFn1: ValidFn1 = (...args)=>console.log(args) // extends unknown[]
const validFn2: ValidFn2 = (...args:any)=>console.log(args) // any extends unknown[]
const validFn3: ValidFn3 = (...args:never)=>console.log(args) // never doesn't extend unknown[]
const validFn4: ValidFn4 = (...args:unknown)=>console.log(args) // unknown doesn't extend unknown[]
const validFn5: ValidFn5 = (...args:unknown[])=>console.log(args) // extends unknown[]
const validFn6: ValidFn6 = (...args:void)=>console.log(args) // void doesn't extend unknown[]

code

TrevTheDev
  • 2,616
  • 2
  • 18
  • 36

1 Answers1

2

I believe what is going on here is a combination of the fact that (1) Typescript does not resolve conditional types inside functions until it is presented with a concrete instantiation (see this SO answer) and (2) function parameters are contravariant (see this SO answer).

So, what you have here is basically TS seeing this line:

    const fn : Fn<T> = (...args: T)=> console.log(args)

And:

  • Resolving Fn<T> inside func to Fn<unknown[]> , i.e., (...args: unknown[]) => void,(because of point 1 above)

  • Realizing that T on the right side could be something narrower than unknown[] (e.g., number[]).

So it then thinks, well, that might not work because, that could be saying, for example:

   const fn:(args:unknown[])=>void = (args:number[])=>console.log(args)

And that is an error because args is contravariant (see point 2 above).

How do you fix it? I think the easiest way to resolve issues under the point 1 category is with an explicit return type on the function and then just an assertion inside the function itsel, e.g.:

const func = <T extends unknown[]>():Fn<T>=>{
    const fn = (...args: T)=> console.log(args)
    return fn as Fn<T>
}
sam256
  • 1,291
  • 5
  • 29