✅ Assert handler params in the compat fixture
Give the primitive-union handlers a parameter and assert its member type, and assert the tagged-union widening handler's parameter too. A matcher without handler-parameter assertions cannot catch a wrong or `any` handler parameter: the matcher's own `(shape: T) => R` type is built from `T` and the handler return, so it stays correct even when `Member<T, K>` inversion is broken.
This commit is contained in:
1 parent
75807c4bd8
commit
00ed6bee1e
1 file changed
+29
-5
+29
-5
@@ -7,7 +7,11 @@
|
|||||||
// Why the `expectTypeOf` assertions: a compile that merely succeeds is a weak
|
// Why the `expectTypeOf` assertions: a compile that merely succeeds is a weak
|
||||||
// oracle. An `any`-typed declaration would compile, but `expect-type`'s exact
|
// oracle. An `any`-typed declaration would compile, but `expect-type`'s exact
|
||||||
// equality and `.not.toBeAny()` reject `any`, so the assertions prove the
|
// equality and `.not.toBeAny()` reject `any`, so the assertions prove the
|
||||||
// emitted types are real. See development/ci.md § TypeScript compatibility.
|
// emitted types are real. Every handler *parameter* is asserted too, not just
|
||||||
|
// the matcher: the handler's member type comes from the `Member` inversion in
|
||||||
|
// the declarations, and a wrong or `any` parameter would otherwise slip
|
||||||
|
// through, since the matcher's own `(shape: T) => R` type is built from `T` and
|
||||||
|
// the handler returns. See development/ci.md § TypeScript compatibility.
|
||||||
|
|
||||||
import { expectTypeOf } from "expect-type";
|
import { expectTypeOf } from "expect-type";
|
||||||
import {
|
import {
|
||||||
@@ -20,8 +24,16 @@ import {
|
|||||||
type ResultCode = "ok" | "created";
|
type ResultCode = "ok" | "created";
|
||||||
|
|
||||||
const toStatus = getPrimitiveUnionMatcher<ResultCode>()({
|
const toStatus = getPrimitiveUnionMatcher<ResultCode>()({
|
||||||
ok: () => "OK",
|
ok: (s) => {
|
||||||
created: () => "CREATED",
|
expectTypeOf(s).not.toBeAny();
|
||||||
|
expectTypeOf(s).toEqualTypeOf<"ok">();
|
||||||
|
return "OK";
|
||||||
|
},
|
||||||
|
created: (s) => {
|
||||||
|
expectTypeOf(s).not.toBeAny();
|
||||||
|
expectTypeOf(s).toEqualTypeOf<"created">();
|
||||||
|
return "CREATED";
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
expectTypeOf(toStatus).not.toBeAny();
|
expectTypeOf(toStatus).not.toBeAny();
|
||||||
@@ -29,7 +41,13 @@ expectTypeOf(toStatus).toEqualTypeOf<(shape: ResultCode) => string>();
|
|||||||
|
|
||||||
// The widening twin keeps each handler's own return type in the union.
|
// The widening twin keeps each handler's own return type in the union.
|
||||||
const toStatusW = getPrimitiveUnionMatcherW<ResultCode>()(
|
const toStatusW = getPrimitiveUnionMatcherW<ResultCode>()(
|
||||||
{ ok: () => "OK" as const },
|
{
|
||||||
|
ok: (s) => {
|
||||||
|
expectTypeOf(s).not.toBeAny();
|
||||||
|
expectTypeOf(s).toEqualTypeOf<"ok">();
|
||||||
|
return "OK" as const;
|
||||||
|
},
|
||||||
|
},
|
||||||
(rest) => {
|
(rest) => {
|
||||||
expectTypeOf(rest).not.toBeAny();
|
expectTypeOf(rest).not.toBeAny();
|
||||||
expectTypeOf(rest).toEqualTypeOf<"created">();
|
expectTypeOf(rest).toEqualTypeOf<"created">();
|
||||||
@@ -63,7 +81,13 @@ expectTypeOf(format).not.toBeAny();
|
|||||||
expectTypeOf(format).toEqualTypeOf<(shape: Contact) => string>();
|
expectTypeOf(format).toEqualTypeOf<(shape: Contact) => string>();
|
||||||
|
|
||||||
const formatW = getTaggedUnionMatcherW<Contact>()("kind")(
|
const formatW = getTaggedUnionMatcherW<Contact>()("kind")(
|
||||||
{ email: (e) => e.address },
|
{
|
||||||
|
email: (e) => {
|
||||||
|
expectTypeOf(e).not.toBeAny();
|
||||||
|
expectTypeOf(e).toEqualTypeOf<{ kind: "email"; address: string }>();
|
||||||
|
return e.address;
|
||||||
|
},
|
||||||
|
},
|
||||||
(rest) => {
|
(rest) => {
|
||||||
expectTypeOf(rest).not.toBeAny();
|
expectTypeOf(rest).not.toBeAny();
|
||||||
expectTypeOf(rest).toEqualTypeOf<{ kind: "phone"; number: string }>();
|
expectTypeOf(rest).toEqualTypeOf<{ kind: "phone"; number: string }>();
|
||||||
|
|||||||
Reference in new issue
Block a user