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:
undefinedis too narrow of a type, the actual type should beunknown. This presents a particular problem when intersecting with another type, becauseunknown & T=>Tbutundefined & {}=>never.- 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.