The question in the title pretty much says it all. The catch is that T cannot be restricted.
Here is what I have tried:
class ArrayWrapper<T> {
constructor(private arr: T[]) {}
toMap<K, V>() {
return new Map(<T extends [K, V] ? [K, V][] : never>this.arr);
}
}
I have also tried this:
class ArrayWrapper<T> {
constructor(private arr: T[]) {}
toMap() {
return new Map(<T extends [infer K, infer V] ? [K, V][] : never>this.arr);
}
}
In both of the above cases the compiler is giving me a "Conversion of type 'T[]' to ... may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first" error.
So I tried something different and instead of never, I changed the false branch of the conditional type to never[]. And this got rid of the compilation error but now when I do something like:
class ArrayWrapper<T> {
constructor(private arr: T[]) {}
toMap() {
return new Map(<T extends [infer K, infer V] ? [K, V][] : never[]>this.arr);
}
}
const notConvertibleToMap = [1, 2, 3];
const convertibleToMap: [number, string][] = [[1, 'one'], [2, 'two'], [3, 'three']];
const arr1 = new ArrayWrapper(notConvertibleToMap);
const map1 = arr1.toMap();
const arr2 = new ArrayWrapper(convertibleToMap);
const map2 = arr2.toMap();
The compiler infers Map<unknown, unknown> for both map1 and map2.
What I would like is for the compiler to:
- in the case of
map1, when trying to calltoMap()whenTis not[K,V]either give an error or at the very least maybe infer a return type ofneversince this function call is going to throw an error becauseTis not a tuple and the Map constructor is going to throw. - in the case of
map2, whereTIS a[K, V], I want the compiler to correctly infer a type ofMap<K, V>formap2.
Like I said at the beginning, obviously restricting the type of T in the class like this
class ArrayWrapper<T extends [K, V], K, V> {
constructor(private arr: T[]) {}
toMap() {
return new Map(this.arr);
}
}
would allow for proper inference of K and V, but I don't want to restrict T. I need this class to accept arrays of any type of contained value T.
Additional attempt was made with this:
class ArrayWrapper<T> {
constructor(private arr: T[]) {}
toMap() {
if (this.isArrayOfTuple(this.arr)) {
return new Map(this.arr);
}
throw new Error("called toMap() on ArrayWrapper that is not an array of tuples");
}
isArrayOfTuple<K, V>(arr: T[]): arr is [K, V][] {
return arr[0] instanceof Array && arr[0].length === 2;
}
}
but unfortunately the compiler is complaining over the return type of isArrayOfTuple, saying that
A type predicate's type must be assignable to its parameter's type.
Type '[K, V][]' is not assignable to type 'T[]'.
Type '[K, V]' is not assignable to type 'T'.
'T' could be instantiated with an arbitrary type which could be unrelated to '[K, V]'