fix(core): fix assertEvent narrowing for generic event type parameters - #5621
fix(core): fix assertEvent narrowing for generic event type parameters#5621tarann26 wants to merge 2 commits into
Conversation
assertEvent(event, descriptor) failed to narrow event when the event's type was itself a generic type parameter (e.g. inside a function declared as <TEvent extends SomeEventUnion>(event: TEvent)). The call compiled, but every property access on event afterward errored with "Property '...' does not exist", forcing callers to either avoid generics or manually re-annotate the event's type. This came from ExtractEvent's narrowing logic (added in the partial descriptor support), which nests a TEvent['type'] check inside a distributive conditional over TEvent. TypeScript can't eagerly resolve that nesting when TEvent is an unresolved generic parameter, so it leaves the whole narrowed type opaque. assertEvent now uses its own narrowing type that keeps the wildcard normalization and the TEvent distribution as sibling branches instead of nesting them, which TypeScript can resolve independently even for generic callers. The TAssertedDescriptor extends EventDescriptor<TEvent> constraint is untouched, so invalid descriptors are still a compile error for both concrete and generic event types. Fixes statelyai#5448
🦋 Changeset detectedLatest commit: e3613fb The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
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 |
…elds
The local AssertedEvent type matched events with plain assignability
(`TEvent extends { type: Descriptor }`), which discards a member whose
`type` field is itself a union of literals: `{ type: 'a' | 'b' }` is not
assignable to `{ type: 'a' }`, so asserting 'a' collapsed the member to
`never` and property access failed. This regressed a case the original
ExtractEvent handled.
Match `{ type: infer TType }` and test the fresh TType parameter with
`true extends (TType extends Descriptor ? true : false)`, mirroring
ExtractEvent's EventDescriptorMatches. Testing an inferred parameter lets
the check distribute over a union-typed `type` field, and avoids indexing
`TEvent['type']` on an unresolved generic, so narrowing works for both
concrete and generic callers while still rejecting invalid descriptors.
Add regression tests for the union-typed `type` field case (concrete and
generic, plus precision and invalid-descriptor guards).
|
Pushed a follow-up (e3613fb) for the union-typed I reworked it to match on Added regression tests for the concrete and generic union- |
Fixes #5448
assertEvent(event, descriptor)compiled fine whenevent's type was behind a generic type parameter (e.g. inside a function declared as<TEvent extends SomeEventUnion>(event: TEvent)), but every property access oneventafterward failed to type-check with a "Property '...' does not exist" error. Concrete (non-generic) callers were unaffected.Root cause: the narrowing type nests a
TEvent['type']indexed access inside a distributive conditional overTEvent. TypeScript can't eagerly resolve that nesting whenTEventis an unresolved generic parameter, so it leaves the whole narrowed type opaque and property accesses fail.assertEventnow uses its own narrowing type that keeps the wildcard normalization and theTEventdistribution as sibling branches instead of nesting them, which TypeScript resolves independently even for generic callers. TheTAssertedDescriptor extends EventDescriptor<TEvent>constraint is untouched, so invalid descriptors are still a compile-time error for both concrete and generic event types.Added type-level regression tests in
assert.types.test.tscovering the generic narrowing case from the issue, that narrowing still excludes properties from other union members (not just "does it compile"), and that invalid descriptors are still rejected for generic callers.