Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
211 views
in Technique[技术] by (71.8m points)

typescript - Is there an function to simplify the following union type TUnitTransform?

Is there an function to simplify the type TUnitTransform as following:

type TUnitTransform = ((...rest: [number]) => [number]) &
  ((...rest: [number, number]) => [number, number]) &
  ((...rest: [number, number, number]) => [number, number, number]) &
  ......

Because there are some common functions that need to accept transformed parameters (unit "px" to "rpx"):

const fn1 = (rpx1: number) => { ... }
const fn2 = (rpx1: number, rpx2: number) => { ... }
const fn3 = (rpx1: number, rpx2: number, rpx3: number) => { ... }
......

And I have to transform them like that:

const px2rpx: TUnitTransform = (...rest: number[]): any => rest.map((i) => i / k)

fn1(...px2rpx(20))
fn2(...px2rpx(20, 30))
fn3(...px2rpx(20, 30, 40))
......
question from:https://stackoverflow.com/questions/65850619/is-there-an-function-to-simplify-the-following-union-type-tunittransform

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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 numbers 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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...