Let's begin by clarifying some terms:
type Contravariant<T> = (t: T) => void;
declare let cNum: Contravariant<number>;
declare let cStr: Contravariant<string>;
Now, suppose I wish to create a function that accepts an array of the Contravariant
union type. Initially, I took a straightforward approach:
declare function f1<T>(a: T[]): void;
f1([cNum, cStr]); // 'T' is inferred as 'Contravariant<number> | Contravariant<string>'
This seems fine! Yet, the array can currently accept any type, but I intend to restrict it solely to the Contravariant
type. Hence, I decided to have T
extend from Contravariant
.
declare function f2<T extends Contravariant<any>>(a: T[]): void;
f2([cNum, cStr]); // 'T' is inferred as 'Contravariant<number> | Contravariant<string>'
It appears to be functioning correctly. Nonetheless, eslint now disapproves of using any
. Using unknown
would not work since it is contravariant.
What potential drawbacks might arise from employing any
in this situation? Can I safely utilize any
? Are there alternative methods that do not involve using any
?
I attempted some other approaches as well:
declare function f3<T extends Contravariant<U>, U>(a: T[]): void;
f3([cNum, cStr]);
// Error: Type 'Contravariant<number>' cannot be assigned to type 'Contravariant<unknown>'.
// Type 'unknown' cannot be assigned to type 'number'.
declare function f3a<U>(a: Contravariant<U>[]): void;
f3a([cNum, cStr]);
// Error: Type 'Contravariant<string>' cannot be assigned to type 'Contravariant<number>'.
// Type 'number' cannot be assigned to type 'string'.
declare function f4<T extends Contravariant<T extends Contravariant<infer I> ? I : never>>(a: T[]): void;
f4([cNum, cStr]);
// Error: Type 'Contravariant<string>' cannot be assigned to type 'Contravariant<string | number>'.
// Type 'string | number' cannot be assigned to type 'string'.
// Type 'number' cannot be assigned to type 'string'.
Why does each of these functions (f3, f3a, f4) result in a different inferred type? They seem identical to me.