Parameters allows wrapping generic function, but infer does not

Viewed 56

Typescript throws an error when using inference for a function parameter when it doesn't for the utility type Parameters. Take the following example: [playground]

interface Filter<T> {
    (inp: T): boolean;
}

// get parameter using Parameters
type InpParam<F extends Filter<never>> = Parameters<F>[0];
// get parameter using infer
type InpInfer<F extends Filter<never>> = F extends Filter<infer T> ? T : never;

// compiles fine
function wrapParam<F extends Filter<never>>(filt: F): Filter<InpParam<F>> {
    const wrapper = (inp: InpParam<F>): boolean => filt(inp);
    return wrapper;
}

function wrapInfer<F extends Filter<never>>(filt: F): Filter<InpInfer<F>> {
    const wrapper = (inp: InpInfer<F>): boolean => filt(inp);
    //                                                  ^^^
    // argument of type InpInfer<F> is not assignable to type never
    return wrapper;
}

I'm not exactly sure why InpParam is assignable to never, e.g. is inferred as never, when InpInfer isn't and is instead inferred as unknown. I could have to do with how they handle empty Parameters, but here Parameters actually returns the wrong thing, it returns undefined instead of unknown. (by wrong, I mean unknown is a more generic input parameter since function parameters are contravariant).

I'm curious about how to solve this for two reasons:

  1. undefined is too narrow of a type, the actual type should be unknown. This presents a particular problem when intersecting with another type, because unknown & T => T but undefined & {} => never.
  2. For complicated inputs types, writing the expression to extract a nested type can be complicated, hard to read, and brittle to changing object definitions. Infer, however, always works as long as the generics have the same semantics.

Notes

  • I know that the specific wrappers in question can be written by making the wrapper generic in the input parameter, however that's not the case if I want to also preserve the type of the filter itself (these examples just don't illustrate that for clarity).
  • I also know that the specific wrappers here don't do anything and so could be omitted.
0 Answers
Related