New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add metadata support to Tagged type #723
Conversation
Friendly bump in case you forgot. If you're just busy, feel free to ignore this comment. |
@sindresorhus thanks! I think I can tackle this this week :) |
8f1ea79
to
a134a83
Compare
@sindresorhus Ok, I've updated this PR to rebase it on top of main and to add more docs that better explain the use cases for metadata and cover some of the details around assignability! |
74c3cb1
to
24a1496
Compare
source/opaque.d.ts
Outdated
|
||
@category Type | ||
*/ | ||
export type Tagged<Type, Tag extends PropertyKey, TagMetadata = never> = Type & MultiTagContainer<Tag, TagMetadata>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I ended up making a tag's default metadata never
instead of void
, as never
has the more consistent/sensible (covariant) assignability semantics that we discussed in the other issue.
0ed26ba
to
0133fe8
Compare
type MultiTagContainer<Token extends PropertyKey> = { | ||
readonly [tag]: {[K in Token]: void}; | ||
}; | ||
type Tag<Token extends PropertyKey, TagMetadata> = TagContainer<{[K in Token]: TagMetadata}>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In addition to adding the TagMetadata
param, I decided to rename MultiTagContainer
to Tag
because it makes the IDE popups much clearer for tagged types. E.g. in these types:
type AbsolutePath = Tagged<string, 'AbsolutePath'>;
type NormalizedPath = Tagged<string, 'NormalizedPath'>;
type NormalizedAbsolutePath = AbsolutePath & NormalizedPath;
Hovering over NormalizedAbsolutePath
will now show
string & Tag<'AbsolutePath', never> & Tag<'NormalizedPath', never>
instead of
string & MultiTagContainer<'AbsolutePath', never> & MultiTagContainer<'NormalizedPath', never>
// UnwrapTagged/UnwrapOpaque should work on types with multiple tags. | ||
const unwrapped3 = '' as UnwrapTagged<NormalizedAbsolutePath>; | ||
const unwrapped4 = '' as UnwrapOpaque<NormalizedAbsolutePath>; | ||
expectType<string>(unwrapped3); | ||
expectType<string>(unwrapped4); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these are new tests that i added because i realized that i accidentally broke UnwrapTagged
without them in an earlier version of this PR
c8ac6c3
to
3d641c5
Compare
Thanks for writing this. Looks good 👍 |
Thanks for merging! 💯🎉 |
Addition to support the use case described here #665 (comment)