Issue
Initially, I wanted to type redux' mapDispatchToProps-like function and got stuck with handling an array of functions (action creators) as an argument. There is a hacky but working example. I wonder if it can be improved.
The problem is that return type does not preserve types per item, it gets generalized to a union.
Update: the problem is in preserving parameter types per function in the resulting array.
In short, code below should be with no errors:
type F = (...x: any[]) => any
type Wrap<T extends F> = (...x: Parameters<T>) => void
const wrap = (fn: any) => (...a: any) => {fn(...a)}
// currently working solution
// the problem is K being too wide (number | string | symbol) for array index thus silenced
// // @ts-expect-error
// function main<Fs extends readonly F[]>(fs: Fs): {[K in keyof Fs]: Wrap<Fs[K]>}
// TODO: desired solution but not finished: every item in `fs` should be wrapped with `Wrap`
function main<Fs extends readonly F[]>(fs: Fs): [...Fs]
function main(fs: any) {return fs.map(wrap)}
const n = (x: number) => x
const s = (x: string) => x
const fs = main([n, s] as const)
// TEST PARAMETERS TYPES
fs[0](1)
fs[1]('1')
// @ts-expect-error
fs[0]('1')
// @ts-expect-error
fs[1](1)
// TEST RETURN TYPES
const _1: void = fs[0](1)
const _2: void = fs[1]('1')
// @ts-expect-error
const _3: number = fs[0](1)
// @ts-expect-error
const _4: string = fs[1]('1')
P.S: there is an open (as for 25-aug-2020) github issue related to problem of my solution #1, so it is not about variadic tuple types but about keyof ArrayType being too wide for an array index type
Solution
You actually don't need TS4 variadic tuples at all. just mapped (tuple) types. The following works in TypeScript 3.5 (though // @ts-expect-error is a 3.9 feature):
function main<Fs extends readonly F[]>(fs: Fs):
{ [K in keyof Fs]: Fs[K] extends F ? Wrap<Fs[K]> : never }
Update: On array (rather than tuple) types, the above will return an array type where the individual indices are no longer known. Once the tuple indices are lost there is no way to recover this information. You can use as const to prevent the initial loss of the tuple information:
const test3 = [
console.log,
(x: number) => x,
] as const;
const fs3 = main(test3);
// const fs3: readonly [Wrap<(...data: any[]) => void>, Wrap<(x: number) => number>]
If you want to be sure you aren't losing the indices, it is possible to reject array types and only allow tuples using a bit of a hack:
function main<Fs extends readonly F[]>(
fs: number extends Fs['length'] ? never : Fs):
{ [K in keyof Fs]: Fs[K] extends F ? Wrap<Fs[K]> : never }
(edit: K in Exclude<keyof Fs, keyof []> -> K in keyof Fs like above, not sure why I had that)
Like above, all of this works in TS3.
Answered By - Mingwei Samuel
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.