✅ 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.
This commit is contained in:
tmu committed 2026-09-16 19:29:04 +00:00
1 parent cce030b8e5
commit ed71929365
1 file changed
+53 -13
+53 -13
View File
@@ -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