Type tuple so that function chain has valid parameter and return types

柔情痞子 提交于 2020-01-25 06:39:36

问题


I'd like to create a type for this tuple / array below.

Where this is valid:

const funcs = [(a: string) => 1, (a: number) => 'A', (a: string) => 2]

And this is invalid:

const funcs = [(a: string) => 1, (a: number) => 2, (a: string) => 3]

The difference being the return type for the middle function changed from string to number.

Is it possible?

type SubtractOne<T extends number> = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62][T];
type AddOne<T extends number> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62][T];

const funcs = [(a: string) => 1, (a: number) => 'A', (a: string) => 2]

type CheckFuncs<T extends any[]> = { [(K extends number) in keyof T]: T[AddOne<K>] }

type funcsType = typeof funcs
type funcsType2 = CheckFuncs<funcsType>

During my research I came across a way to index with a mapping. Is it possible to use this to add or subtract AddOne to K? then I can access the ReturnType of T[K] and the Parameter<T[k+1]>[0]?

Playground


回答1:


Indexing into the next or previous value of a tuple given an index type K is something I'd do with the support for tuple rest/spread. Consider Tail<T>, which takes a tuple type T and returns a new tuple type with the first element removed:

// Tail<[1,2,3]> is [2,3]
type Tail<T extends readonly any[]> =
    ((...a: T) => void) extends ((h: any, ...r: infer R) => void) ? R : never;

Then "T[K+1]" can be expressed as Tail<T>[K] instead.

That's probably the answer to your question as asked, but I'll continue a little to show how to use it:


Here's how I might start writing your CheckFuncs type:

type CheckFuncs<T extends readonly ((x: any) => any)[]> = { [K in keyof T]:
    K extends keyof Tail<T> ? (
        [T[K], Tail<T>[K]] extends [(x: infer A) => infer R, (x: infer S) => any] ? (
            [R] extends [S] ? T[K] : (x: A) => S
        ) : never
    ) : T[K]
}

That walks through T and compares T[K] with Tail<T>[K], and converts T[K] into a version of itself that works. Then these (note that you need const assertions for the tuple type to be preserved):

const pass = [(a: string) => 1, (a: number) => 'A', (a: string) => 2] as const
const fail = [(a: string) => 1, (a: number) => 3, (a: string) => 2] as const

produce these:

type passType = CheckFuncs<typeof pass>
// readonly [(a: string) => number, (a: number) => string, (a: string) => number]

type failType = CheckFuncs<typeof fail>
// readonly [(a: string) => number, (x: number) => string, (a: string) => number]

And you can make a function that works with CheckFuncs like this:

function useFuncs<T extends readonly ((x: any) => any)[]>(...t: CheckFuncs<T>) { }

useFuncs((a: string) => 1, (a: number) => 'A', (a: string) => 2); // okay
useFuncs((a: string) => 1, (a: number) => 3, (a: string) => 2); // error!
// ----------------------> ~~~~~~~~~~~~~~~~
// Argument of type '(a: number) => number' is not assignable to 
// parameter of type '(x: number) => string'.

Of course the exact way you want to write or use CheckFuncs might be different; this is just an illustration. Hope that helps; good luck!

Link to code



来源:https://stackoverflow.com/questions/58910250/type-tuple-so-that-function-chain-has-valid-parameter-and-return-types

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!