Why doesn't this work?
const x: unknown[] = ['x', 32, true]; // OK
const y: (...args: unknown[]) => unknown = (xx: number) => {}; // ERROR
// Type '(xx: number) => void' is not assignable to type '(...args: unknown[]) => unknown'.
// Types of parameters 'xx' and 'args' are incompatible.
// Type 'unknown' is not assignable to type 'number'. ts(2322)
My goal is to make sure that y
is any runnable function. I was trying not to use any
.
Hope to improve my understanding of how unknown
works in this case.
CodePudding user response:
Function types are contravariant in their parameter types; see Difference between Variance, Covariance, Contravariance and Bivariance in TypeScript for more details. Contravariance means the direction of assignability flips; if T
is assignable to U
, then (...u: U) => void
is assignable to (...t: T) => void
and not vice versa. This is necessary for type safety. Picture the direction of data flow: if you want fruit then I can give you an apple, but if you want something that will eat all your fruit I can't give you something that eats only apples.
The function type (xx: number) => void
is equivalent to (...args: [number]) => void
, and you cannot assign that to (...args: unknown[]) => void
. Yes, [number]
is assignable to unknown[]
, but that's not the direction we care about. Your assignment is therefore unsafe. If this worked:
const y: (...args: unknown[]) => unknown =
(xx: number) => xx.toFixed(); // should this be allowed?
Then you'd be able to call y()
with any set of arguments you wanted without a compiler error, but hit a runtime error:
y("x", 32, true); // no compiler error
//