import { expect, test } from "vitest"; import * as t from "../index"; function assertNever<_ extends never>(): void {} test("Kind includes every exported concrete Type class", () => { assertNever<{ [K in Exclude< keyof typeof t, "Type" | "TypeImpl" | "UnmergeableType" | "ConstraintType" | "OpaqueType" >]: (typeof t)[K] extends abstract new(...args: any[]) => infer Instance ? Instance extends t.Type ? Instance extends t.Kind ? never : Instance : never : never; }[Exclude< keyof typeof t, "Type" | "TypeImpl" | "UnmergeableType" | "ConstraintType" | "OpaqueType" >]>(); expect(true).toBe(true); }); test("allows type narrowing for exhaustiveness checking", () => { // Let's write a function that takes a validation, and see if we can use type narrowing with // successive `if` statements to get ourselves to be able to call this function on a `t.Kind`. const takesValidation = (v: t.Validation) => { return v; }; // Now let's write a function that takes a t.Kind, and see if we can narrow it with `if` // statements to a t.Validation, without ever directly checking for `if(u instanceof // t.Validation)` const fn = (u: t.Kind) => { if(u instanceof t.Any) { return "any"; } if(u instanceof t.SetType) { return "set"; } if(u instanceof t.MapType) { return "map"; } if(u instanceof t.Dict) { return "dict"; } if(u instanceof t.Struct) { return "struct"; } if(u instanceof t.Arr) { return "array"; } if(u instanceof t.Value) { return "value"; } if(u instanceof t.InstanceOf) { return "instanceof"; } if(u instanceof t.TypeOf) { return "typeof"; } if(u instanceof t.Either) { return "either"; } if(u instanceof t.Intersection) { return "intersection"; } if(u instanceof t.MergeIntersect) { return "merge-intersect"; } if(u instanceof t.Is) { return "is" } if (u instanceof t.Never) { return "never" } if(u instanceof t.Comment) { return "comment"; } if(u instanceof t.PartialStruct) { return "partial"; } // This should compile even though we never ran `if(u instanceof Validation)`, because we've // narrowed the type to just Validation by checking for everything else that `Kind` could be. // // This also allows us to do exhaustiveness checking: if we haven't enumerated every possible // option before this, this will fail compilation, since `u` could be one of the classes we // haven't enumerated. takesValidation(u); return "validation"; }; expect(fn(t.any)).toEqual("any"); });