Skip to content
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

Merged
merged 1 commit into from Mar 4, 2024

Conversation

ethanresnick
Copy link
Contributor

Addition to support the use case described here #665 (comment)

source/opaque.d.ts Outdated Show resolved Hide resolved
source/opaque.d.ts Show resolved Hide resolved
@sindresorhus
Copy link
Owner

Friendly bump in case you forgot. If you're just busy, feel free to ignore this comment.

@ethanresnick
Copy link
Contributor Author

@sindresorhus thanks! I think I can tackle this this week :)

@ethanresnick
Copy link
Contributor Author

@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!


@category Type
*/
export type Tagged<Type, Tag extends PropertyKey, TagMetadata = never> = Type & MultiTagContainer<Tag, TagMetadata>;
Copy link
Contributor Author

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.

type MultiTagContainer<Token extends PropertyKey> = {
readonly [tag]: {[K in Token]: void};
};
type Tag<Token extends PropertyKey, TagMetadata> = TagContainer<{[K in Token]: TagMetadata}>;
Copy link
Contributor Author

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>

Comment on lines +116 to +120
// 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);
Copy link
Contributor Author

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

@sindresorhus sindresorhus merged commit 3ec8dba into sindresorhus:main Mar 4, 2024
6 checks passed
@sindresorhus
Copy link
Owner

Thanks for writing this. Looks good 👍

@ethanresnick
Copy link
Contributor Author

Thanks for merging! 💯🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants