Kannan Goundan
Kannan Goundan

Reputation: 5232

Issues using Partial<T> on a bounded generic type

function f<T extends {id: string}>(id: T['id']): Partial<T> {
  return {id: id}; // ERROR: Type '{ id: T["id"]; }' is not assignable to type 'Partial<T>'
}

(TypeScript Playground link)

I'm trying to figure out why the above code isn't allowed. Is is fundamentally unsound? Or is it because of a limitation of the TypeScript type system?

Upvotes: 2

Views: 920

Answers (2)

jcalz
jcalz

Reputation: 328362

This is a missing feature; the compiler can't see the assignment as sound and rejects it for the same reason as in Why can't I return a generic 'T' to satisfy a Partial<T>?, even though it would be safe to allow it. There was an issue at microsoft/TypeScript#22229 asking for it to be addressed, but it was closed as "working as intended". From the discussion within it looks like it's just a missing feature or a design limitation.

You could, if you want, use a type assertion:

function fAssert<T extends {id: string}>(id: T['id']): Partial<T> {
  return {id: id} as Partial<T>; // assert
}

Or you could build the return value in stages (although this method is too permissive and allows some unsound things which probably won't be fixed anytime soon either):

function fRoundabout<T extends {id: string}>(id: T['id']): Partial<T> {
  const ret: Partial<T> = {}; // allowed
  ret.id = id; // also works
  return ret;
}

I'm not 100% sure why you want the return type to be Partial<T>, though, since such a type may or may not have values at the keys of T, whereas your returned object definitely has an id property and nothing else. It looks more like Pick<T, "id"> than Partial<T> to me:

function fDifferentReturnType<T extends { id: string }>(id: T['id']): Pick<T, 'id'> {
  return { id: id }; // okay
}

Since the example is just a toy example, it's quite possible that your real use case requires Partial<T> and not {id: T['id']}. But if not, then you might decide to sidestep the whole Partial issue entirely.

Okay, hopefully one of those suggestions helps. Good luck!

Playground link

Upvotes: 1

Romain Deneau
Romain Deneau

Reputation: 3061

jcalz suggest 3 options. Which one fits?

I agree with him in the fact that it's not clear from this "toy" example if there's a true need to want the return type Partial<T>. Pick<T, 'id'> can be enough. In this case, inference just work fine and it may be a simpler solution:

function f<T extends {id: string}>(id: T['id']) {
  return { id }; // type: { id: T['id'] }
}

Upvotes: 1

Related Questions