From ed71929365b4d9aed0e5f6a54359c6f88bda97f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Wed, 16 Sep 2026 19:29:04 +0000 Subject: [PATCH] :white_check_mark: Check matcher handler params The primitive union matchers invoke every handler with the matched literal, but the specs wrote their handlers with zero parameters, so the passed value and its inferred key-literal type went untested. Declare the parameter on each such handler and pin it with expectTypeOf, matching the style the two already-correct handlers used. The `_` fallbacks assert the whole union rather than a single literal. Return-type expectations are unchanged, so the W / non-W widening distinctions are still covered. --- src/primitive.test.ts | 66 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 53 insertions(+), 13 deletions(-) diff --git a/src/primitive.test.ts b/src/primitive.test.ts index 382cda0..1182311 100644 --- a/src/primitive.test.ts +++ b/src/primitive.test.ts @@ -21,8 +21,14 @@ test("getPrimitiveUnionMatcherW requires every literal key", () => { // Act const matcher = factory({ - a: () => 1 as const, - b: () => "two" as const, + a: (s) => { + expectTypeOf(s).toEqualTypeOf<"a">(); + return 1 as const; + }, + b: (s) => { + expectTypeOf(s).toEqualTypeOf<"b">(); + return "two" as const; + }, }); // Assert @@ -42,8 +48,14 @@ test("getPrimitiveUnionMatcherW dispatches on numeric literal keys", () => { // Act const matcher = factory({ - 1: (n) => n + 1, - 2: (n) => n * 10, + 1: (n) => { + expectTypeOf(n).toEqualTypeOf<1>(); + return n + 1; + }, + 2: (n) => { + expectTypeOf(n).toEqualTypeOf<2>(); + return n * 10; + }, }); // Assert @@ -62,8 +74,14 @@ test("getPrimitiveUnionMatcher infers a single return type shared by all handler // Act const matcher = factory({ - a: (): number => 1, - b: (): 1 | 2 => 2, + a: (s): number => { + expectTypeOf(s).toEqualTypeOf<"a">(); + return 1; + }, + b: (s): 1 | 2 => { + expectTypeOf(s).toEqualTypeOf<"b">(); + return 2; + }, }); // Assert @@ -107,7 +125,10 @@ test("getPrimitiveUnionMatcherPartial routes shapes without a handler to _", () // Act const matcher = factory({ - a: (): 1 | 2 => 1, + a: (s): 1 | 2 => { + expectTypeOf(s).toEqualTypeOf<"a">(); + return 1; + }, _: (s): 1 | 2 => { // The fallback sees the whole union, not a single literal. expectTypeOf(s).toEqualTypeOf<"a" | "b" | "c">(); @@ -132,8 +153,14 @@ test("getPrimitiveUnionMatcherPartial also accepts an exhaustive pattern", () => // Act const matcher = factory({ - a: () => "A", - b: () => "B", + a: (s) => { + expectTypeOf(s).toEqualTypeOf<"a">(); + return "A"; + }, + b: (s) => { + expectTypeOf(s).toEqualTypeOf<"b">(); + return "B"; + }, }); // Assert @@ -154,8 +181,15 @@ test("getPrimitiveUnionMatcherPartialW allows gaps and widens to the union of ha // Act const matcher = factory({ - x: () => 1 as const, - _: () => "fallback" as const, + x: (s) => { + expectTypeOf(s).toEqualTypeOf<"x">(); + return 1 as const; + }, + _: (s) => { + // The fallback sees the whole union, not a single literal. + expectTypeOf(s).toEqualTypeOf<"x" | "y" | "z">(); + return "fallback" as const; + }, }); // Assert @@ -177,8 +211,14 @@ test("getPrimitiveUnionMatcherPartialW also accepts an exhaustive pattern", () = // Act const matcher = factory({ - x: () => 1 as const, - y: () => "two" as const, + x: (s) => { + expectTypeOf(s).toEqualTypeOf<"x">(); + return 1 as const; + }, + y: (s) => { + expectTypeOf(s).toEqualTypeOf<"y">(); + return "two" as const; + }, }); // Assert