Skip to content

fix(Data): preserve generic A in TaggedEnum $match arms#6250

Merged
tim-smart merged 1 commit into
Effect-TS:mainfrom
milkyskies:fix/taggedenum-generic-match-inference
Jun 5, 2026
Merged

fix(Data): preserve generic A in TaggedEnum $match arms#6250
tim-smart merged 1 commit into
Effect-TS:mainfrom
milkyskies:fix/taggedenum-generic-match-inference

Conversation

@milkyskies

@milkyskies milkyskies commented May 27, 2026

Copy link
Copy Markdown
Contributor

Type

  • Refactor
  • Feature
  • Bug Fix
  • Optimization
  • Documentation Update

Description

The direct-form $match(self, cases) overload on Data.TaggedEnum's GenericMatchers had A, B, C, D as explicit type parameters, with cases constrained against TaggedEnum.Kind<Z, A, B, C, D>. TypeScript infers Cases and the type params in a single pass; when the caller is itself a generic function (e.g. function f<A>(node: Tree<A>)), A is unbound at inference time and defaults to its constraint, so arms receive unknown instead of A:

function collectValues<A>(node: Tree<A>): ReadonlyArray<A> {
  return Tree.$match(node, {
    Leaf: ({ value }) => [value],
    //     ^^^^^ inferred as `unknown`, expected `A`
    Branch: ({ children }) => children.flatMap(collectValues),
  });
  // Type 'unknown[]' is not assignable to type 'readonly A[]'.
}

Match.value(node).pipe(Match.tag(...)) works because Match.value<const I>(i: I) captures I as the concrete Tree<A> instance before any cases are typed.

Fix

Reorder the two $match overloads so the direct form comes first, and rewrite the direct form to infer a single const Self extends Kind<Z, any, any, any, any> from self, then derive Cases from Self. The generic A flows in through Self via const inference, and arms get the expected concrete arg type.

The curried form ($match(cases)(self)) is unchanged — it still works for pipe + concrete types as before.

Related

Closes Effect-TS#6249.

The direct-form $match(self, cases) overload had A, B, C, D as explicit
type parameters, with cases constrained against TaggedEnum.Kind<Z, A, B,
C, D>. TypeScript infers Cases and the type params in one pass; when the
caller is a generic function (e.g. function f<A>(node: Tree<A>)), A is
unbound at inference time and defaults to its constraint, so arms receive
unknown instead of A.

Reorder the overloads (direct form first) and rewrite the direct form to
infer a single const Self extends Kind<Z, any, any, any, any> from self,
then derive Cases from Self. The generic A flows in through Self via
const inference, and arms get the expected concrete arg type.

The curried form (cases-first) is unchanged.
@milkyskies milkyskies requested a review from mikearnaldi as a code owner May 27, 2026 05:18
@github-project-automation github-project-automation Bot moved this to Discussion Ongoing in PR Backlog May 27, 2026
@changeset-bot

changeset-bot Bot commented May 27, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 7d05d86

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
effect Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@mikearnaldi mikearnaldi requested a review from tim-smart June 4, 2026 16:31
@tim-smart tim-smart merged commit e2126bc into Effect-TS:main Jun 5, 2026
11 checks passed
@github-project-automation github-project-automation Bot moved this from Discussion Ongoing to Done in PR Backlog Jun 5, 2026
@github-actions github-actions Bot mentioned this pull request Jun 5, 2026
Hoishin added a commit to Hoishin/effect that referenced this pull request Jun 8, 2026
* upstream/main:
  Version Packages (Effect-TS#6259)
  fix(ai): support empty tool parameters (Effect-TS#6260)
  fix(Data): preserve generic A in TaggedEnum $match arms (Effect-TS#6250)
  Emit `additionalProperties: false` for records with string keys and `Schema.Never` values (Effect-TS#6257)
  Version Packages (Effect-TS#6227)
  Backport cluster shard group fixes (Effect-TS#6255)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Data.taggedEnum<WithGenerics<1>>().$match loses generic A inside arms

2 participants