The code example below cannot compile with noUncheckedIndexedAccess. While literalValue
is known to be a number, the value
and spreadValue
variables have 'undefined' in their type union.
Is there any way to annotate the squareTuple and squareSpreadTuple functions, so that passing a tuple of a specific arity will return a result which can be destructured like a literal tuple, without a spurious undefined arriving in its type union?
The Mapped type in the example code was an attempt do do this (by passing on the length attribute of the tuple to its return). It seems to have no effect.
Is there anything else which can be done to transfer the arity of the argument tuple to its returns in a way that destructuring would respect?
function square(num:number){
return num*num;
}
type Mapped<NumList extends ReadonlyArray<number>> = ReadonlyArray<number> & {length:NumList["length"]}
function squareTuple<NumList extends ReadonlyArray<number>>(nums:NumList): Mapped<NumList> {
return nums.map(square);
}
function squareSpreadTuple<NumList extends ReadonlyArray<number>>(...numList:NumList) : Mapped<NumList> {
return squareTuple(numList)
}
const [literalValue] = [9,16,25] as const;
const [value] = squareTuple([3,4,5] as const) ;
const [spreadValue] = squareSpreadTuple(3,4,5) ;
console.log(`Sum of values is: ${literalValue + value + spreadValue }`)
The example code (with the visible compile errors) is at this playground
UPDATE
I managed to get the red lines to go using the code at this playground. This is by no means elegant for something that's a built-in type where I would expect this to be inferred. Probably there is a better way.
UPDATE 2
If I extend support only to the spread case, then the more minimal approach at this playground works. I find it hard to explain why the non-spread case won't compile using this simpler approach, though.