Skip to content

Instantly share code, notes, and snippets.

@viglioni
Last active November 12, 2021 02:10
Show Gist options
  • Save viglioni/c47568a51a5b17ff2a8b7c4b71b175b0 to your computer and use it in GitHub Desktop.
Save viglioni/c47568a51a5b17ff2a8b7c4b71b175b0 to your computer and use it in GitHub Desktop.
A NonEmpty<T> type for both objects and lists
// Typescript playground of this gist: https://www.typescriptlang.org/play?#code/PTAEBUE8AcFMGcCwAoFAXGtQDkD2A7AUQFtoMB5AIwCsAecAPlAF5QBvFUL7nrgbQDSoAJb5QAa1iRcAMwgBdAFygACsIDG4+gBpQAhp15cAvn0nS54eShvIMcHARJlIAQQBO7gIaR6TVnzgugB0oeB88vIA3Lb2WHhEpBh+LI6JLh7evoygAD5pzhQ0frYgoIQAHl6kADYIoLGYoADCBGhe6mipHMg8sMRewjXK8GjuogDm2oZc0AAWBLAjY5PTyMalwLg0oLjiKOoEo6BeoMoJhb6t+O2d-lxs84vKAERe8AAmLxvIh-jHlDOBSSVzaHTQ9zY-UGw1Ab0+3wORy66iBFxBtGutwh3Se+CWcPeX100KGryJiNQyBA22ooFgnlw7kUSP+XQ+aKcGKx4MhPz+xyw5y5LkxYLu3UoNS85IRPxQZRqwmOewav2RoDkwvSyXwAFdiJQGfc+KAAIygXQAJlA1nVbNAE05Ot8o3G+AmJrhZpelrhVt9doVYCVo1ODPcTJZ9uOc2dl1o+sNxtSprtAq6wnjGLdky9FvkQA
// My two references:
// for lists: https://stackoverflow.com/questions/56006111/is-it-possible-to-define-a-non-empty-array-type-in-typescript
// for objs: https://stackoverflow.com/questions/40510611/typescript-interface-require-one-of-two-properties-to-exist
// Special thanks to @CaioCSdev https://github.com/CaioCSdev
/*
* Creating non empty types for both objects and lists
*/
// Types
type AnyArray = unknown[];
type AnyObj = Record<string | number, unknown>;
type NonEmptyObj<T extends AnyObj> = {
[K in keyof T]: Pick<T, K> & Partial<T>;
}[keyof T];
type NonEmptyArray<T> = [T, ...T[]];
type NonEmpty<T extends AnyArray | AnyObj> = T extends (infer U)[]
? NonEmptyArray<U>
: T extends AnyObj
? NonEmptyObj<T>
: never;
// Examples
type Contact = {
email: string;
phone: string;
};
//obj: ok
const a: NonEmpty<Contact> = { phone: 'asd' };
const b: NonEmpty<Contact> = { email: 'asd' };
const c: NonEmpty<Contact> = { phone: 'asd', email: 'asd' };
// obj: type error:
const d: NonEmpty<Contact> = {};
const e: NonEmpty<Contact> = { bla: 'asd' };
// list: ok
const f: NonEmpty<number> = [1, 2];
const g: NonEmpty<string> = ['1', '2'];
// list: type error:
const h: NonEmpty<number> = [];
const i: NonEmpty<string> = [1];
// Destructuring:
const fasd = ({email}: NonEmptyObj<Contact>) => ({email})
fasd({phone: "asd"}) // valid call, returns { email: undefined }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment