sindresorhus / type-fest

A collection of essential TypeScript types
Creative Commons Zero v1.0 Universal
14.31k stars 543 forks source link

Proposal Inferred Partial type #463

Open Hideman85 opened 2 years ago

Hideman85 commented 2 years ago

I have really no idea how to call this helper, but let me describe the use case.

// Lets imagine I have an options type
type Options {
  readOnly?: boolean;
  required?: boolean;
  filters?: Record<string, string>;
  // ...
}

// Now lets see how I can use in a function
const buildOptions(required: boolean, name: string, team?: string) => {
  const options: Options = {
     required,
     filters: {
       name,
     },
   };

   if (team) {
     // Issue is that filters even if defined above remains optional/partial
     options.filters.team = team;
   }
}

// Today we could manuall build the correct type as
const options: Options & Pick<Required<NonNullable<Options>>, 'required' | 'filters'> = {...}

// Issue is when adding more property you always need to add the property in the list
// I tried the following without success to infer the type without explicitely giving the keys
export const makeInferredPartial = <Base, Real extends Required<NonNullable<Base>>>(
  // partial: {[Key in keyof Base]: Required<NonNullable<Base>>[Key]} extends infer Return ? Partial<Base> & Return : never,
  partial: Real,
) => partial;

const options = makeInferredPartial<Options>({
  filters: { name: 'Hello world' },
})
// Would expect the type to be like
{
  readOnly?: boolean;
  required?: boolean;
  filters: Record<string, string>; // Note it become defined
  // ...
}
// Then the following works
options.filters.team = 'admin';

Please let me know if that make sense and if you could help me to build this helper. I think this become pretty useful when the type has plenty of props and you just want to define them and infer the correct typing.

Thanks in advance for your help.

Upvote & Fund

Fund with Polar

papb commented 2 years ago

Can't you just let options be typed automatically, and enforce the return type of your buildOptions function instead?

function buildOptions(required: boolean, name: string, team?: string): Options {
  const options = {
     required,
     filters: {
       name,
     },
   };

   if (team) {
     options.filters.team = team;
   }

   return options;
}
Hideman85 commented 2 years ago

In this way you do not get the other properties neither the real type of them 'foo' | 'bar' !== string.