type-fest icon indicating copy to clipboard operation
type-fest copied to clipboard

Proposal: AtMostOne

Open anton-johansson opened this issue 4 years ago • 2 comments

I'm looking for something similar to RequireAtleastOne, however I don't want it to be required.

Say I have two fields that should be mutually exclusive, but at the same time I want them to be optional. I tried wrapping them around SetOptional, but that "removed" the mutually exclusiveness.

Ideas? :)

anton-johansson avatar May 31 '21 08:05 anton-johansson

It sounds closer to RequireExactlyOne except being optional. Sounds useful. I'm not sure about the name though. It will need some bike shedding.

sindresorhus avatar Jun 01 '21 10:06 sindresorhus

I'm looking for this as well. I've gotten it to work by adding a strategic | Partial<{[Key in KeysType]: Pick<ObjectType, Key>}> to the existing RequireExactlyOne definition, though I'm not sure this is the simplest solution.

type RequireAtMostOne<ObjectType, KeysType extends keyof ObjectType = keyof ObjectType> =
  ({[Key in KeysType]: (
    Required<Pick<ObjectType, Key>> &
    Partial<Record<Exclude<KeysType, Key>, never>>
  )}[KeysType] | Partial<{[Key in KeysType]: Pick<ObjectType, Key>}>) & _Omit<ObjectType, KeysType>);

type Test = RequireAtMostOne<{
  a: number,
  b: number,
  c: number,
}, 'a' | 'b'>;

const pass1: Test = {a: 1, c: 3};
const pass2: Test = {b: 2, c: 3};
const pass3: Test = {c: 3};

const fail1: Test = {a: 1, b: 2, c: 3};
const fail2: Test = {};

As for name, I'd suggest RequireAtMostOne to complement the existing RequireExactlyOne and RequireAtLeastOne.

eritbh avatar Jun 15 '21 00:06 eritbh

@sindresorhus done in #654 with RequireOneOrNone. Should we add an alias to the readme for this?

tommy-mitchell avatar Aug 14 '23 00:08 tommy-mitchell