Below is a simplified code snippet that encounters an issue inferring Set<number>
for the inner
property:
function test<S>(state: S, fn: (value: { outer: Set<S> }) => void) {}
function numberFn(value: { outer: Set<{ inner: Set<number> }> }) {}
test({ inner: new Set() }, numberFn)
// ^____
// Type 'Set<unknown>' is not assignable to type 'Set<number>'.
// Type 'unknown' is not assignable to type 'number'.ts(2322)
In my actual code, I have numerous calls like test
with various types for the generic parameter of Set
, and currently these types are not exported. Explicitly typing them would be troublesome, so I was hoping TypeScript could automatically infer them, but unfortunately, I have not been able to achieve that.
To gain a better understanding and attempt to solve the issue, I conducted some tests:
function test1<T>(input: { arr: T[] }, value: { val: T }) {}
test1({ arr: [] }, { val: 5 })
// function test1<number>(input: {
// arr: number[];
// }, value: {
// val: number;
// }): void
The above test worked successfully. However, the following test did not yield the desired result:
function test2<T>(input: { arr: Array<T> }, value: { val: T }) {}
test2({ arr: new Array() }, { val: 5 })
// function test2<any>(input: {
// arr: any[];
// }, value: {
// val: any;
// }): void
This discrepancy may be due to special handling of []
in comparison to generic types in general. Interestingly, when attempting the same scenario using Set
instead of Array
with reversed argument order, the correct type inference was achieved:
function test3<T>(value: { val: T }, input: { arr: Set<T> }) {}
test3({ val: 5 }, { arr: new Set() })
// function test3<number>(value: {
// val: number;
// }, input: {
// arr: Set<number>;
// }): void
To work around this issue, explicit type inference can be performed in TypeScript, although it may become convoluted, as shown below:
function test<S>(state: S, fn: (value: { outer: Set<S> }) => void) {}
function numberFn(value: { outer: Set<{ inner: Set<number> }> }) {}
test(
{ inner: new Set() } as Parameters<typeof numberFn>[0] extends { outer: Set<infer S> } ? S : never,
numberFn,
)
Is there a way to rewrite the code so that TypeScript can correctly infer the type for state, similar to what occurs in test3
?