✅ Spec mixed string and numeric union keys
This commit is contained in:
1 parent
ed71929365
commit
a0f0894339
1 file changed
+128
@@ -64,6 +64,40 @@ test("getPrimitiveUnionMatcherW dispatches on numeric literal keys", () => {
|
|||||||
assert.equal(matcher(2), 20);
|
assert.equal(matcher(2), 20);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("getPrimitiveUnionMatcherW dispatches on mixed string and numeric keys", () => {
|
||||||
|
// Arrange
|
||||||
|
const factory = getPrimitiveUnionMatcherW<"a" | "b" | 1 | 2>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const matcher = factory({
|
||||||
|
a: (s) => {
|
||||||
|
expectTypeOf(s).toEqualTypeOf<"a">();
|
||||||
|
return "A" as const;
|
||||||
|
},
|
||||||
|
b: (s) => {
|
||||||
|
expectTypeOf(s).toEqualTypeOf<"b">();
|
||||||
|
return "B" as const;
|
||||||
|
},
|
||||||
|
1: (n) => {
|
||||||
|
expectTypeOf(n).toEqualTypeOf<1>();
|
||||||
|
return 10 as const;
|
||||||
|
},
|
||||||
|
2: (n) => {
|
||||||
|
expectTypeOf(n).toEqualTypeOf<2>();
|
||||||
|
return 20 as const;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expectTypeOf(matcher).toEqualTypeOf<
|
||||||
|
(shape: "a" | "b" | 1 | 2) => "A" | "B" | 10 | 20
|
||||||
|
>();
|
||||||
|
assert.equal(matcher("a"), "A");
|
||||||
|
assert.equal(matcher("b"), "B");
|
||||||
|
assert.equal(matcher(1), 10);
|
||||||
|
assert.equal(matcher(2), 20);
|
||||||
|
});
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// API: getPrimitiveUnionMatcher — ✔️ Exhaustive / ✔️ ReturnsStrict
|
// API: getPrimitiveUnionMatcher — ✔️ Exhaustive / ✔️ ReturnsStrict
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -113,6 +147,39 @@ test("getPrimitiveUnionMatcher handlers receive the matched literal", () => {
|
|||||||
assert.equal(matcher("off"), "handler off");
|
assert.equal(matcher("off"), "handler off");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("getPrimitiveUnionMatcher infers one return type across mixed string and numeric keys", () => {
|
||||||
|
// Arrange
|
||||||
|
const factory = getPrimitiveUnionMatcher<"a" | "b" | 1 | 2>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const matcher = factory({
|
||||||
|
a: (s): number => {
|
||||||
|
expectTypeOf(s).toEqualTypeOf<"a">();
|
||||||
|
return 1;
|
||||||
|
},
|
||||||
|
b: (s): 1 | 2 => {
|
||||||
|
expectTypeOf(s).toEqualTypeOf<"b">();
|
||||||
|
return 2;
|
||||||
|
},
|
||||||
|
1: (n): number => {
|
||||||
|
expectTypeOf(n).toEqualTypeOf<1>();
|
||||||
|
return 10;
|
||||||
|
},
|
||||||
|
2: (n): number => {
|
||||||
|
expectTypeOf(n).toEqualTypeOf<2>();
|
||||||
|
return 20;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
// ✔️ ReturnsStrict: R is the best common return type, not a widening union.
|
||||||
|
expectTypeOf(matcher).toEqualTypeOf<(shape: "a" | "b" | 1 | 2) => number>();
|
||||||
|
assert.equal(matcher("a"), 1);
|
||||||
|
assert.equal(matcher("b"), 2);
|
||||||
|
assert.equal(matcher(1), 10);
|
||||||
|
assert.equal(matcher(2), 20);
|
||||||
|
});
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// API: getPrimitiveUnionMatcherPartial — ❌ Exhaustive / ✔️ ReturnsStrict
|
// API: getPrimitiveUnionMatcherPartial — ❌ Exhaustive / ✔️ ReturnsStrict
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -169,6 +236,35 @@ test("getPrimitiveUnionMatcherPartial also accepts an exhaustive pattern", () =>
|
|||||||
assert.equal(matcher("b"), "B");
|
assert.equal(matcher("b"), "B");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("getPrimitiveUnionMatcherPartial routes mixed string and numeric keys, gaps go to _", () => {
|
||||||
|
// Arrange
|
||||||
|
const factory = getPrimitiveUnionMatcherPartial<"a" | "b" | 1 | 2>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const matcher = factory({
|
||||||
|
a: (s): string => {
|
||||||
|
expectTypeOf(s).toEqualTypeOf<"a">();
|
||||||
|
return "A";
|
||||||
|
},
|
||||||
|
1: (n): string => {
|
||||||
|
expectTypeOf(n).toEqualTypeOf<1>();
|
||||||
|
return "one";
|
||||||
|
},
|
||||||
|
_: (s): string => {
|
||||||
|
// The fallback sees the whole union, not a single literal.
|
||||||
|
expectTypeOf(s).toEqualTypeOf<"a" | "b" | 1 | 2>();
|
||||||
|
return "fallback";
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expectTypeOf(matcher).toEqualTypeOf<(shape: "a" | "b" | 1 | 2) => string>();
|
||||||
|
assert.equal(matcher("a"), "A");
|
||||||
|
assert.equal(matcher("b"), "fallback");
|
||||||
|
assert.equal(matcher(1), "one");
|
||||||
|
assert.equal(matcher(2), "fallback");
|
||||||
|
});
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// API: getPrimitiveUnionMatcherPartialW — ❌ Exhaustive / ❌ ReturnsStrict
|
// API: getPrimitiveUnionMatcherPartialW — ❌ Exhaustive / ❌ ReturnsStrict
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -205,6 +301,38 @@ test("getPrimitiveUnionMatcherPartialW allows gaps and widens to the union of ha
|
|||||||
assert.equal(matcher("z"), "fallback");
|
assert.equal(matcher("z"), "fallback");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("getPrimitiveUnionMatcherPartialW widens mixed string and numeric key returns to their union", () => {
|
||||||
|
// Arrange
|
||||||
|
const factory = getPrimitiveUnionMatcherPartialW<"a" | "b" | 1 | 2>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const matcher = factory({
|
||||||
|
a: (s) => {
|
||||||
|
expectTypeOf(s).toEqualTypeOf<"a">();
|
||||||
|
return "A" as const;
|
||||||
|
},
|
||||||
|
1: (n) => {
|
||||||
|
expectTypeOf(n).toEqualTypeOf<1>();
|
||||||
|
return 10 as const;
|
||||||
|
},
|
||||||
|
_: (s) => {
|
||||||
|
// The fallback sees the whole union, not a single literal.
|
||||||
|
expectTypeOf(s).toEqualTypeOf<"a" | "b" | 1 | 2>();
|
||||||
|
return "fallback" as const;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
// ❌ ReturnsStrict: mixed handler returns widen to their union.
|
||||||
|
expectTypeOf(matcher).toEqualTypeOf<
|
||||||
|
(shape: "a" | "b" | 1 | 2) => "A" | 10 | "fallback"
|
||||||
|
>();
|
||||||
|
assert.equal(matcher("a"), "A");
|
||||||
|
assert.equal(matcher("b"), "fallback");
|
||||||
|
assert.equal(matcher(1), 10);
|
||||||
|
assert.equal(matcher(2), "fallback");
|
||||||
|
});
|
||||||
|
|
||||||
test("getPrimitiveUnionMatcherPartialW also accepts an exhaustive pattern", () => {
|
test("getPrimitiveUnionMatcherPartialW also accepts an exhaustive pattern", () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
const factory = getPrimitiveUnionMatcherPartialW<"x" | "y">();
|
const factory = getPrimitiveUnionMatcherPartialW<"x" | "y">();
|
||||||
|
|||||||
Reference in new issue
Block a user