I have a function which takes a tuple and returns a tuple where the elements are related one-to-one through generic types. I fail to write an implementation that uses that tuple. Here is a contrived example:
export type WrapA<T extends readonly unknown[]> = {
[K in keyof T]: {x: T[K]};
}
export type WrapB<T extends readonly unknown[]> = {
[K in keyof T]: {y: T[K]};
}
function a<T extends readonly unknown[]>(t: WrapA<T>) : WrapB<T> {
// Question 2
if(t.length == 0) {
return [] // Type 'never[]' is not assignable to type 'WrapB<T>'.ts(2322)
}
// Question 1
return t.map(i => {y: i.x}) // Type 'void[]' is not assignable to type 'WrapB<T>'.ts(2322)
}
// Question 3
// Type parameters are required, for b and c to be 'narrowed'
const [b, c] = a<[number, string]>([{x: 1}, {x: "s"}])
Question 1: How can I relate the parameter type to the return type in the implementation (w/o any
obviously)? In this example I'm using map
but it could also have side effects, as long as the elements of t
map to the elements of the return value.
Question 2: Can I "type narrow" t
to any empty list? This is useful if I want to skip expensive computations. I tried checking the length and using a type guard:
function isEmptyTuple<T extends unknown[]>(t: T) : t is [] {
return t.length == 0
}
// A type predicate's type must be assignable to its parameter's type.
// Type '[]' is not assignable to type 'T'.
// '[]' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'unknown[]'.ts(2677)
Question 3: If I omit the type parameters when I call a
the return type is less specific (string | number
). Is there a better way to define the types such that the implied type argument is specific?