Skip to content

Use NoInfer utility type where applicable#1403

Draft
whatfontisthis wants to merge 1 commit into
sindresorhus:mainfrom
whatfontisthis:feat/use-noinfer
Draft

Use NoInfer utility type where applicable#1403
whatfontisthis wants to merge 1 commit into
sindresorhus:mainfrom
whatfontisthis:feat/use-noinfer

Conversation

@whatfontisthis

Copy link
Copy Markdown

Summary

Addresses #848 by applying TypeScript 5.4's NoInfer utility type where it genuinely improves inference behavior.

Changes

GetTagMetadata in source/tagged.d.ts

Before:

export type GetTagMetadata<Type extends Tag<TagName, unknown>, TagName extends PropertyKey> = ...

After:

export type GetTagMetadata<Type extends Tag<NoInfer<TagName>, unknown>, TagName extends PropertyKey> = ...

Why NoInfer helps here:

The constraint Type extends Tag<TagName, unknown> creates two potential inference sites for TagName:

  1. The structural shape of Type — since Type must extend Tag<TagName, unknown>, TypeScript can infer TagName by inspecting which tags Type carries.
  2. The explicit TagName argument itself.

When GetTagMetadata is used in a generic function that accepts both a tagged value and a tag name:

declare function readTag<T extends Tag<K, unknown>, K extends PropertyKey>(
  tagged: T,
  tagName: K
): GetTagMetadata<T, K>;

Without NoInfer, TypeScript may use the structure of T as an inference site for K, potentially widening K when T carries multiple tags. With NoInfer<TagName> in the constraint, K is inferred only from the explicit tagName argument — which is the intended semantics. The tagged value's structure should validate the lookup, not determine the tag name.

What was reviewed

All source types in source/ were reviewed for NoInfer applicability. Most type-fest types are pure type-level utilities that don't involve competing inference sites for the same type parameter — NoInfer is primarily beneficial in function generics, and most type-fest types are used as explicit type arguments rather than inferred from multiple argument positions simultaneously.

GetTagMetadata is the clearest case: it has both a constraint-based inference site and an explicit parameter for the same TagName variable, making NoInfer semantically correct.

Tests

All existing tests pass:

  • test-d/opaque.ts — covers Tagged, GetTagMetadata, UnwrapTagged, etc.
  • TypeScript compiler (tsc --noEmit) passes with no errors.

Closes #848

…'s structure

The constraint `Type extends Tag<TagName, unknown>` creates an additional
inference site for `TagName` — TypeScript can infer it from the structural
shape of `Type` (e.g., which tags it carries) rather than solely from the
explicit `TagName` argument.

Wrapping with `NoInfer<TagName>` in the constraint position ensures that
when `GetTagMetadata` is used in generic function signatures, `TagName` is
inferred only from the explicit type argument, not from the tag structure
embedded in `Type`. This matches the intended semantics: the caller should
supply the tag name they want to look up; the shape of the tagged value
should validate that lookup, not drive it.

Closes sindresorhus#848

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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.

Use NoInfer?

1 participant