Let's start with some definitions:
type Contravariant<T> = (t: T) => void;
declare let cNum: Contravariant<number>;
declare let cStr: Contravariant<string>;
Now, say I want to make a function which I can pass in an array of Contravariant
union type. First, I used a simple approach:
declare function f1<T>(a: T[]): void;
f1([cNum, cStr]); // T inferred as 'Contravariant<number> | Contravariant<string>'
Looks good! However, the array can take any type currently, I'd like to limit this to only Contravariant
type, so let's make T
extend Contravariant
.
declare function f2<T extends Contravariant<any>>(a: T[]): void;
f2([cNum, cStr]); // T inferred as 'Contravariant<number> | Contravariant<string>'
Seems to work good. However, eslint is now complaining about using any
. unknown
obviously doesn't work since it's contravariant.
What are the potential downsides to using any
in this case? Can I safely use any
? Are there any ways to do this without using any
?
Some other things I tried:
declare function f3<T extends Contravariant<U>, U>(a: T[]): void;
f3([cNum, cStr]);
// Type 'Contravariant<number>' is not assignable to type 'Contravariant<unknown>'.
// Type 'unknown' is not assignable to type 'number'.
declare function f3a<U>(a: Contravariant<U>[]): void;
f3a([cNum, cStr]);
// Type 'Contravariant<string>' is not assignable to type 'Contravariant<number>'.
// Type 'number' is not assignable to type 'string'.
declare function f4<T extends Contravariant<T extends Contravariant<infer I> ? I : never>>(a: T[]): void;
f4([cNum, cStr]);
// Type 'Contravariant<string>' is not assignable to type 'Contravariant<string | number>'.
// Type 'string | number' is not assignable to type 'string'.
// Type 'number' is not assignable to type 'string'.
Why do all these (f3, f3a, f4) result in a different type inferred? They look like they all do the same thing to me.
CodePudding user response:
I don't see any downside of using any
here. After all, it's just used for a constraint. But if you really need a solution without any
, you may be interested in this:
declare function f1<
T extends unknown[]
>(a: [...{ [K in keyof T]: Contravariant<T[K]> }]): void;
f1([cNum, cStr]);
T
will be inferred as [number, string]
in this case.