Don't make Tagged
type compatibility dependent on type fest version
#875
+6
−1
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Since removing
Opaque
/UnwrapOpaque
is obviously gonna require a new major version, I wanted to put this PR up now, in case it makes sense to land both changes in the same major version.This PR switches the
tag
key used by theTagged
type to be a string rather than a symbol, undoing this commit. The rationale for that, which I've summarized from this comment is:It's easy for two different copies of
type-fest
to end up installed in a project. Consider a dependency tree like:Depending on the version of
type-fest
installed at the root (i.e., by the@my-company/app
package) and the version of the@my-company/types
package used the chosen version of@my-company/some-internal-library
, npm may be forced to install multipletype-fest
copies.When multiple type-fest copies are present, a
Tagged
type produced by one copy is not assignable to aTagged
type produced by another copy even if the types have the same tag names, tag metadata, and base/untagged type, because Typescript sees thetag
symbol from each copy oftype-fest
as distinct.This is weird behavior, as it ends up making assignability dependent on exactly how/when npm chooses to dedupe packages, which isn't even necessarily stable as new packages are installed or as npm is updated or as installed packages are updated.
In the example dependency tree above, it means that: if
@my-company/some-internal-library
returns a tagged type from its copy of the@my-company/types
package, that type may not be assignable to the copy of that same type exported by the copy of@my-company/types
that@my-company/app
is depending on directly — even if the tagged type hasn't changed (in terms of its tag names/tag metadata/base type).This also happens to make
Record<string, T>
taggable (solving #708), at the expense ofRecord<symbol, T>
not being taggable (which seems like a good tradeoff).Given that type-fest used a string-based key for many years, I don't think this poses any big issues, but there would be some small user-facing changes. For example,
keyof Tagged<{ foo: string }, 'SomeTag'>
would become'foo' | '__type-fest-tag__'
instead of'foo' | typeof tag
; that's not fundamentally different, but it's maybe a tad less clear. It would probably be helpful too, at that point, fortype-fest
to export a string literal type with the name of the magic tag string (i.e.,"__type-fest-tag__"
), so that users can doExclude<keyof Tagged, TypeFestTagKey>
, in much the same way they might've had to doExclude<keyof Tagged, symbol>
before.