Skip to content

Instantly share code, notes, and snippets.

@worc
Created June 2, 2023 21:44
Show Gist options
  • Save worc/55824c6d910f8a2739b15aa20203abb0 to your computer and use it in GitHub Desktop.
Save worc/55824c6d910f8a2739b15aa20203abb0 to your computer and use it in GitHub Desktop.
Type algebra to detect empty objects
/**
* This came out of some work where we were sending an encoded JSON object as a query param in a network call.
* But, we can't send nothing, `param=`, or we crash the server, that's taken care of by restricting the type
* to at least `Record<string, string>`, but it left open the possibility of empty objects. Encoded as `'%7B%7D'`.
*
* So I went looking and it's definitely possible to restrict empty objects, but the type algebra is wild. And
* it makes use of something people are calling F-bounded quantification. And the error message isn't every helpful.
*
* See:
* - https://stackoverflow.com/a/72554458/769780
* - https://stackoverflow.com/a/72420133/769780
*/
// One of many "canonical" ways to describe an empty object:
type EmptyObject = Record<string, never>;
type NonEmptyObject<T extends Record<string, unknown>> = T extends EmptyObject
? never
: T;
export default function encodeQueryObject<T extends Record<string, unknown>>(
queryObject: NonEmptyObject<T>,
) {
return encodeURIComponent(JSON.stringify(queryObject));
}
// This is I think why they say the generic is self-referential, when
// calling the function we don't provide a generic, but let the argument's
// own implicit type determine the type algebra:
const empty = {};
encodeQueryObject(empty) // TS2345: Argument of type '{}' is not assignable to parameter of type 'never'.
const notEmpty = {
jquery: '123',
};
encodeQueryObject(notEmpty) // No error
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment