SonMooSans
SonMooSans

Reputation: 95

Typescript array of generic types

I want to do something like this:

type Item<T> = T & {
  func: (v: T) => void
}

function a<What>(...items: Item<What>[]) {}

a({
  name: "",
  func: (v: {name: string}) => {}
}, {
  count: 0,
  func: (v: {count: number}) => {}
})

However above code will cause an error, how to do the similar thing with typscript?

Upvotes: 2

Views: 177

Answers (3)

Tobias S.
Tobias S.

Reputation: 23825

You can utilize a syntax like this:

function a<T extends any[]>(...items: { 
  [K in keyof T]: T[K] & { func: (v: Omit<T[K], 'func'>) => void } 
}) {}

A mapped type can be used to map over each element in items. T[K] always represents the current element, so we can use it to intersect itself with the function object.

// ok
a({
  name: "",
  func: (v: { name: string }) => {}
}, {
  count: 0,
  func: (v: { count: number }) => {}
})

// error
a({
  name: 0,
  func: (v: { name: string }) => {} /*
  ~~~~ Types of property 'name' are incompatible 
*/ 
}, {
  count: 0,
  func: (v: { count: number }) => {}
})

For the implementation of the function, I would recommend to put all the complex generic stuff in an overload and to use a simpler signature for the implementing function.

function b<T extends any[]>(...items: { 
  [K in keyof T]: T[K] & { func: (v: Omit<T[K], 'func'>) => void } 
}): void
function b(...items: ({ func: (arg: object) => void } & Record<string, any>)[]) {
  for (const item of items) {
    item.func({})
  }
}

Playground

Upvotes: 2

dsalex1
dsalex1

Reputation: 464

I almost have it, however i cant get function to infer the generic on its own, but im sharing this anyway, in case someone else might solve it, or in case this helps you nontheless.

The trick i used to is to recursively infer each Item of What, that way What is properly handled as tuple and the Item constraint is applied to each element in What seperately.

type Item<T> =
    T & {func(v:T):void}

type Items<What> =
    What extends [Item<infer A>, ...infer Rest]
    ? [Item<A>,...Items<[...Rest]>]
    : What extends [] ? [] : never

function testFunction<What>(...items: Items<What>) {}

//this works, with an explicit generic
testFunction<[Item<{
    name: string;
}>, Item<{
    count: number;
}>]>({
  name: "",
  func: (v: {name: string}) => {}
}, {
  count: 0,
  func: (v: {count: number}) => {}
})

Playground

Upvotes: 2

Dimava
Dimava

Reputation: 10841

type Item<T> = T & {
  func: (v: T) => void
}

function a<A extends Item<any>[]>(...items: A) {}

a({
  name: "",
  func: (v: {name: string}) => {}
}, {
  count: 0,
  func: (v: {count: number}) => {}
})
// ^?
// function a<[{
//     name: string;
//     func: (v: {
//         name: string;
//     }) => void;
// }, {
//     count: number;
//     func: (v: {
//         count: number;
//     }) => void;
// }]>(items_0: {
//     name: string;
//     func: (v: {
//         name: string;
//     }) => void;
// }, items_1: {
//     count: number;
//     func: (v: {
//         count: number;
//     }) => void;
// }): void

Upvotes: -1

Related Questions