#Combine two partial sets into whole type in a type safe way

20 messages · Page 1 of 1 (latest)

next apex
#

I struggled to come up with a meaningful title, but the example should make it really clear what I want:

interface Animal {
  legs: number;
  name: string;
  color: string;
}

function animalBuilder(defaults: Partial<Animal>) {
  return (rest: Partial<Animal>): Animal => ({...defaults, ...rest});
}

const builder = animalBuilder({color: 'black'});

builder({name: 'bear', legs: 4}); // Should error without 'name' and 'legs'

Is this possible? I know it would require a generic, but I'm not sure how to construct it. The defaults parameter should be a subset of Animal, and then rest would be all the properties that weren't provided in defaults.

bright lagoon
#

could make animalBuilder generic over defaults, then use that as rest: Partial<Animal> & Omit<Animal, keyof T>
that's not fully type safe though i think. not sure

#

maybe the generic could be over keys and defaults could be a mapped type

#

on mobile so can't give a solid answer right now, sorry

next apex
analog jayBOT
#
xibread#0

Preview:```ts
interface Animal {
legs: number
name: string
color: string
}

function animalBuilder<T extends Partial<Animal>>(
defaults: T
) {
return <U extends Omit<Animal, keyof T>>(rest: U) =>
({...defaults, ...rest} as Animal)
}

const builder = animalBuilder({color: "black"})
...```

ripe yew
#

i was looking for a way to do this without an assertion, but if assertions are in play i think this is slightly safer than using Partial:

function animalBuilder<K extends keyof Animal>(defaults: Pick<Animal, K>) {
  return (rest: Omit<Animal, K>): Animal => ({...defaults, ...rest}) as Animal;
}
#

especially without exactOptionalPropertyTypes enabled

next apex
#

The assertion is fine, I have to use it anyway for other reasons

#

This should have been obvious now that I'm looking at the solution 😅

#

Thanks @ripe yew

#

Darn, I wish partial type argument inference was a thing. In my actual use case, Animal is also a generic, so the builder is kind of awkward to call into. You have to explicitly list the keys.

bright lagoon
ripe yew
#

that wasn't what their requirements said

#

rest would be all the properties that weren't provided in defaults.

ripe yew
#

e.g. you can often split up argument lists to simulate partial type argument inference (like const f = <A>() => <B>(a: A, b: B) => ...)

next apex
#

Yep, I was just about to try exactly that

#

That worked. Here's the example

analog jayBOT
#
sonuvacar#0

Preview:```ts
interface Animal {
legs: number
name: string
color: string
}

interface Dog extends Animal {
furLength: number
}

function createBuilder<T extends Animal>() {
return <K extends keyof T>(defaults: Pick<T, K>) => {
return (rest: Omit<T, K>): T =>
({...defaults, ...rest} as T)
...```