Skip to content

Instantly share code, notes, and snippets.

@mrnerdhair
Created March 15, 2021 18:48
Show Gist options
  • Save mrnerdhair/b4f1a5cebd825fc52c9dbdf5de3efb1a to your computer and use it in GitHub Desktop.
Save mrnerdhair/b4f1a5cebd825fc52c9dbdf5de3efb1a to your computer and use it in GitHub Desktop.
Demonstrate issues raised in ForbesLindesay/funtypes#48
import { Object as Obj, Literal, Unknown, String as Str, Record as Rec, Result, Static, Runtype, InstanceOf, Never, Function as Func } from "funtypes";
[
{description: "as-is", fn: (rt: any) => rt},
{description: "without placeholders", fn: <T extends Runtype<unknown>>(rt: T) => {
return Unknown.withParser({
parse(value: any): Result<Static<T>> {
return (rt.test(value) ? {success: true, value} : rt.safeParse(value)) as Result<Static<T>>;
}
})
}},
].forEach(({description, fn}) => {
describe(description, () => {
describe("Object w/ Literal", () => {
const Type = fn(Obj({
foo: Literal("bar"),
}))
it("should strict equal", () => {
const initial = {foo: "bar"}
expect(Type.parse(initial)).toStrictEqual(initial)
})
it("should be identical", () => {
const initial = {foo: "bar"}
expect(Type.parse(initial)).toBe(initial)
})
})
describe("Object w/ String", () => {
const Type = fn(Obj({
foo: Str,
}))
it("should strict equal", () => {
const initial = {foo: "bar"}
expect(Type.parse(initial)).toStrictEqual(initial)
})
it("should be identical", () => {
const initial = {foo: "bar"}
expect(Type.parse(initial)).toBe(initial)
})
})
describe("Object {}", () => {
const Type = fn(Obj({}))
it("should strict equal", () => {
const initial = {foo: "bar"}
expect(Type.parse(initial)).toStrictEqual(initial)
})
it("should be identical", () => {
const initial = {foo: "bar"}
expect(Type.parse(initial)).toBe(initial)
})
})
describe("Unknown", () => {
const Type = fn(Unknown)
it("should strict equal", () => {
const initial = {foo: "bar"}
expect(Type.parse(initial)).toStrictEqual(initial)
})
it("should be identical", () => {
const initial = {foo: "bar"}
expect(Type.parse(initial)).toBe(initial)
})
})
describe("Record", () => {
const Type = fn(Rec(Literal("foo"), Literal("bar")))
it("should strict equal", () => {
const initial = {foo: "bar"}
expect(Type.parse(initial)).toStrictEqual(initial)
})
it("should be identical", () => {
const initial = {foo: "bar"}
expect(Type.parse(initial)).toBe(initial)
})
})
describe("fn", () => {
const Type = fn(Obj({
foo: Literal("bar"),
}))
it("should strict equal", () => {
const initial = {foo: "bar"}
expect(Type.parse(initial)).toStrictEqual(initial)
})
it("should be identical", () => {
const initial = {foo: "bar"}
expect(Type.parse(initial)).toBe(initial)
})
})
describe("fixed-length Uint8Array", () => {
const Type = fn(InstanceOf(Uint8Array).And(Obj({
length: Literal(32),
})))
it("should strict equal", () => {
const initial = Buffer.alloc(32)
initial.write("foobar", "utf8")
expect(Type.parse(initial)).toStrictEqual(initial)
})
it("should be identical", () => {
const initial = Buffer.alloc(32)
initial.write("foobar", "utf8")
expect(Type.parse(initial)).toBe(initial)
})
})
describe("InstanceOf(Uint8Array)", () => {
const Type = fn(Unknown.And(InstanceOf(Uint8Array)))
it("should strict equal", () => {
const initial = Buffer.alloc(32)
initial.write("foobar", "utf8")
expect(Type.parse(initial)).toStrictEqual(initial)
})
it("should be identical", () => {
const initial = Buffer.alloc(32)
initial.write("foobar", "utf8")
expect(Type.parse(initial)).toBe(initial)
})
})
describe("InstanceOf(Foo)", () => {
class Foo {
foo: string
constructor(foo: string) {
this.foo = foo;
}
}
describe("by itself", () => {
const Type = fn(InstanceOf(Foo))
const initial = new Foo("bar")
const final = Type.parse(initial)
it("should strict equal", () => {
expect(final).toStrictEqual(initial)
})
it("should be identical", () => {
expect(final).toBe(initial)
})
it("should be a Foo", () => {
expect(final).toBeInstanceOf(Foo)
})
})
describe("with Never.Or()", () => {
const Type = fn(Never.Or(InstanceOf(Foo)))
const initial = new Foo("bar")
const final = Type.parse(initial)
it("should strict equal", () => {
expect(final).toStrictEqual(initial)
})
it("should be identical", () => {
expect(final).toBe(initial)
})
it("should be a Foo", () => {
expect(final).toBeInstanceOf(Foo)
})
})
describe("with Unknown.And()", () => {
const Type = fn(Unknown.And(InstanceOf(Foo)))
const initial = new Foo("bar")
const final = Type.parse(initial)
it("should strict equal", () => {
expect(final).toStrictEqual(initial)
})
it("should be identical", () => {
expect(final).toBe(initial)
})
it("should be a Foo", () => {
expect(final).toBeInstanceOf(Foo)
})
})
describe("with And(Obj({foo: Literal('bar')}))", () => {
// This throws in parse(), placeholders or not, but it shouldn't! Otherwise you can't represent stuff like:
// interface Type {
// (...args: any[]): any,
// foo: "bar",
// }
const Type = fn(InstanceOf(Foo).And(Obj({foo: Literal("bar")})))
const initial = new Foo("bar")
const final = Type.parse(initial)
it("should strict equal", () => {
expect(final).toStrictEqual(initial)
})
it("should be identical", () => {
expect(final).toBe(initial)
})
it("should be a Foo", () => {
expect(final).toBeInstanceOf(Foo)
})
})
})
describe("symbol properties", () => {
const foo = Symbol("Foo")
const Type = fn(Obj({
[foo]: "bar",
}))
const initial = {[foo]: "bar"}
const final = Type.parse(initial)
it("should strict equal", () => {
expect(final).toStrictEqual(initial)
})
it("should be identical", () => {
expect(final).toBe(initial)
})
it("should have its symbol property set", () => {
expect(final[foo]).toEqual("bar")
})
})
describe("Function", () => {
describe("by itself", () => {
const Type = fn(Func);
type Type = Static<typeof Type>
const initial = (foo: string) => `${foo}bar`;
const final = Type.parse(initial)
it("should strict equal", () => {
expect(final).toStrictEqual(initial)
})
it("should be identical", () => {
expect(final).toBe(initial)
})
it("should be callable", () => {
expect(final("foo")).toEqual("foobar")
})
})
describe(".And(Unknown)", () => {
const Type = fn(Func.And(Unknown));
type Type = Static<typeof Type>
const initial = (foo: string) => `${foo}bar`;
const final = Type.parse(initial)
it("should strict equal", () => {
expect(final).toStrictEqual(initial)
})
it("should be identical", () => {
expect(final).toBe(initial)
})
it("should be callable", () => {
expect(final("foo")).toEqual("foobar")
})
})
describe(".And(Object)", () => {
const Type = fn(Func.And(Obj({})));
type Type = Static<typeof Type>
const initial = (foo: string) => `${foo}bar`;
const final = Type.parse(initial)
it("should strict equal", () => {
expect(final).toStrictEqual(initial)
})
it("should be identical", () => {
expect(final).toBe(initial)
})
it("should be callable", () => {
expect(final("foo")).toEqual("foobar")
})
})
})
describe("Object({foo: String})", () => {
const Type = fn(Obj({foo: Str}));
describe("with an enumerable property", () => {
const initial = {foo: "bar"};
const final = Type.parse(initial)
it("should strict equal", () => {
expect(final).toStrictEqual(initial)
})
it("should be identical", () => {
expect(final).toBe(initial)
})
})
describe("with an non-enumerable property", () => {
const initial = Object.create(Object.prototype, {
foo: {
value: "bar",
enumerable: false,
}
})
const final = Type.parse(initial)
it("should strict equal", () => {
expect(final).toStrictEqual(initial)
})
it("should be identical", () => {
expect(final).toBe(initial)
})
})
describe("with an read-only property", () => {
const initial = Object.create(Object.prototype, {
foo: {
value: "bar",
readonly: true,
}
})
const final = Type.parse(initial)
it("should strict equal", () => {
expect(final).toStrictEqual(initial)
})
it("should be identical", () => {
expect(final).toBe(initial)
})
it("should be readonly", () => {
expect(() => {
final.foo = "baz"
}).toThrow()
})
})
describe("with an getter-based property", () => {
let i = 0;
const initial = Object.create(Object.prototype, {
foo: {
get: () => (i++).toString(),
readonly: true,
}
})
const final = Type.parse(initial)
it("should strict equal", () => {
expect(final).toStrictEqual(initial)
})
it("should be identical", () => {
expect(final).toBe(initial)
})
it("should use the getter", () => {
i = 0;
expect(final.foo).toBe("0");
expect(final.foo).toBe("1");
})
})
})
})
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment