1

I have defined a custom type AtLeastOne which ensures that the passed-in object contains at least one valid key. This works well when the generic is instantiated but requires casting when another generic type is passed into it. I am unable to understand why this casting is required.

Typescript playground link

// based off of https://stackoverflow.com/a/48244432/14905094
type AtLeastOne<T, U = { [K in keyof T]: Pick<Required<T>, K> }> = Partial<T> & U[keyof U];

type DbMetaData = {
  id?: number;
};

const correctlyWorks: AtLeastOne<DbMetaData> = { id: 123 };
const correctlyFails: AtLeastOne<DbMetaData> = {};

const genericsExample = <T extends DbMetaData>() => {
  // why does this not work?
  const doesNotWork: AtLeastOne<T> = { id: 123 };

  // can clear the error by using an "as"
  const requiresAsAssertion: AtLeastOne<T> = { id: 123 } as AtLeastOne<T>;
};

The following error is generated:

Type '{ id: 123; }' is not assignable to type 'AtLeastOne<T, { [K in keyof T]: Pick<Required<T>, K>; }>'.
  Type '{ id: 123; }' is not assignable to type 'Partial<T>'

1 Answers1

0

Your generic constraint says that T extends DbMetaData. So, for example, {} would satisfy that constraint, but const correctlyWorks: AtLeastOne<{}> = { id: 123 }; would be an error.

In other words, TS is showing an error because there are some values of T for which AtLeastOne<T> is not compatible with the assigned value.

sam256
  • 1,291
  • 5
  • 29