Skip to content

Instantly share code, notes, and snippets.

@dfrankland
Created March 25, 2024 04:55
Show Gist options
  • Save dfrankland/ff186e15d0aca094a6c2832a11c81577 to your computer and use it in GitHub Desktop.
Save dfrankland/ff186e15d0aca094a6c2832a11c81577 to your computer and use it in GitHub Desktop.
Convert TypeScript optional properties to required with `undefined` unions
/*
* [Try on the TypeScript Playground](https://www.typescriptlang.org/play?#code/PTAEDkHkBUFEC5QBcAWBLAzsgngBwKagYoD2ArgDYAmoAdiUqAEaEDGJAtriRvlQDR0SdfH1AAzEgCdQaRkmEtQAQ1qzaSfFPHLW+AFBI8hALLKpAay2gAvKADeoKfmVUStCtlAB9b2+-0SN4KZKwo3hzmVlTeuFIkBFJGvohktGgAjmSEGNgcTCQUoAC+ANz6hsagAEr4WWjOANL42BhmlpC4SGjuyhQACvGJ3fgYADzQAHy2DvqgoCAQMAigAKq8oJGW1iysymQbAAYAtAD8h074HCQAbqOgh2lU+OJotHwXCiRzoADa-epQFZsCRxKBoCosP0ALpnVK0Z6vd40fAAD00CKw0H+0NAp3BONAAB9QO0rDJENiYeUyhUjAQavhcBRdPgyVpBgktCNxtBBAA1GZPF5vPjTOz2H7-QHA0HgyGgGGIdkyNEYqhYwn42Co1gUMjPCY4wQq6YkwWUnE08qVBmdbq9AZDbloUbQEirBEi5ETcWM5mslWc4au8a1epNFptKL2nq0PrBl2jX2TG30wjQUaMOyxx2JpKh92exGiqhjRwJB3xiinRAYJBSN4AcxKqZ+iwAeqcgA)
*/
// NOTE: this type should not be composed, no need for it to be an interface
type Marker = { readonly __do_not_touch_marked_property__: unique symbol };
type RequireKeysMarkOptionalProperties<T> = {
// NOTE: Use marker because `-?` removes `undefined` too
[P in keyof T as P]-?: undefined extends T[P] ? T[P] | Marker : T[P];
};
type ReplaceMarkerProperties<T, V = undefined> = {
[P in keyof T as P]: Marker extends T[P] ? Exclude<T[P], Marker> | V : T[P];
};
type OptionalPropertiesToUndefined<T> = ReplaceMarkerProperties<RequireKeysMarkOptionalProperties<T>>;
@dfrankland
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment