Created
March 15, 2021 18:48
-
-
Save mrnerdhair/b4f1a5cebd825fc52c9dbdf5de3efb1a to your computer and use it in GitHub Desktop.
Demonstrate issues raised in ForbesLindesay/funtypes#48
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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