user1543574
user1543574

Reputation: 863

Rest operator with generics in Typescript

Consider the following code...

I would expect the spread operator to be valid code, but it ends up not being so. I'm hoping somebody with expertise in Typescript can advise on why this is...

export default function convertToGlobalFragment<
  T extends basicFragmentRecipe_T
>(
  fragmentFactory: (recipe: T) => configFragment_I,
  recipe: T & { matchingRules: globalMatchingRules_T }
): globalConfigFragment_I {

  let {
    matchingRules,
    ...restOfRecipe
  }: { matchingRules: globalMatchingRules_T, restOfRecipe: T } = recipe;

}

The error code

Property 'restOfRecipe' is missing in type 'basicFragmentRecipe_T & { matchingRules: globalMatchingRules_T; }' but required in type '{ matchingRules: globalMatchingRules_T; restOfRecipe: T; }'.ts(2741)

Upvotes: 0

Views: 54

Answers (2)

mbdavis
mbdavis

Reputation: 4010

let {
    matchingRules,
    ...restOfRecipe
  }: { matchingRules: globalMatchingRules_T, restOfRecipe: T } = recipe;

This part is wrong because recipe doesn't contain a property called restOfRecipe - it's { matchingRules: globalMatchingRules_T } & T - where T is assignable to basicFragmentRecipe_T

You can pull out restOfRecipe without this code - let TS do the work:

  let { matchingRules, ...restOfRecipe } = recipe;

Now restOfRecipe is of type:

enter image description here

Pick<T & { matchingRules: globalMatchingRules_T; }, Exclude<keyof T, "matchingRules">>

Let's break that down.

The type of recipe is the combination of T and { matchingRules: globalMatchingRules_T; }

To get from this type the properties except 'matchingRules' - we need to exclude matchingRules from T and { matchingRules: globalMatchingRules_T; }. As the latter part will always contain matchingRules, we can skip checking the combination and just look at Exclude<keyof T, "matchingRules" to get the keys of T without 'matchingRules'.

Finally we then use Pick<T, K> to pick the keys of T (without matchingRules) from the type of Recipe, giving us the result above.

Upvotes: 1

Anatoly
Anatoly

Reputation: 22758

recipe is basicFragmentRecipe_T plus { matchingRules: globalMatchingRules_T } and it seems that basicFragmentRecipe_T does not have restOfRecipe prop at all.

You need to indicate the same target type to assign recipe:

let {
    matchingRules,
    ...restOfRecipe
  }: { matchingRules: globalMatchingRules_T } & T = recipe;

Upvotes: 1

Related Questions