2

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?

3
  • What should be What in your case? Commented Dec 11, 2022 at 13:09
  • ah, it's different for every item? Commented Dec 11, 2022 at 13:09
  • Yes, as you see, What is a generic parameter Commented Dec 11, 2022 at 14:10

3 Answers 3

2

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

Sign up to request clarification or add additional context in comments.

2 Comments

funnily enough, your solution allows to pass an additional property func in v, func: (v: { name: string, func: any }) => {} but prohibits all other properties, also inside the function a elements of items get inferred as any due to the index signature, but thats at least better than my solution which infers them as never 😅
Yeah, we should probably Omit the function from T[K].
2

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

Comments

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

1 Comment

This is wrong, it doenst ensure that each argument satisfies the Item constraint, Item<any> collapses to any so youre essentially saying, function a<A extends any[]>(...items: A){}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.