From that information I'd be inclined to rephrase TUnitTransform
as:
type TUnitTransform =
<T extends [number, ...number[]]>(...rest: [...T]) => { [K in keyof T]: number; }
The parameter list of this function is constrained to be of type [number, ...number[]]
; that's a tuple with a rest element and means that the list must start with a number
and then contain zero or more number
s after that. This has the effect of requiring at least one parameter.
The output type of the function is a mapped array/tuple type which has the same length as the parameter list, and whose output type is all number
. This transformation prevents the compiler from putting numeric literal types in the output. If I were to just say [...T]
, then a call to px2rx(20, 30, 40)
would have the output of type [20, 30, 40]
, whereas we want [number, number, number]
.
And px2rx
could be implemented like this:
const px2rpx: TUnitTransform = <T extends number[]>(...rest: T) =>
rest.map(i => i / k) as { [K in keyof T]: number };
Note that TypeScript's standard library's type signature for Array.prototype.map()
doesn't capture the preservation of array length, so I need the type assertion (with as
) to avoid a compiler warning.
Let's see if it works:
const n1 = px2rpx(20);
// const n1: [number]
const n5 = px2rpx(1, 2, 3, 4, 5);
// const n5: [number, number, number, number, number]
// errors:
px2rpx(); // error!
// Expected at least 1 arguments, but got 0
px2rpx(1, "two", 3); // error!
// -----> ~~~~~
// string is not assignable to number
That all looks correct to me.
Playground link to code
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…