type-assurance
Version:
Lightweight type guards and assertions
288 lines • 9.45 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const tap_1 = __importDefault(require("tap"));
const index_js_1 = require("../index.js");
tap_1.default.test("string", async (t) => {
const s = "s";
const isString = (0, index_js_1.is)(s, String);
if (isString) {
s.startsWith("s");
}
t.ok(isString);
});
tap_1.default.test("number", async (t) => {
const s = 42;
const isNumber = (0, index_js_1.is)(s, Number);
if (isNumber) {
s.toFixed(2);
}
t.ok(isNumber);
});
tap_1.default.test("boolean", async (t) => {
const s = true;
t.ok((0, index_js_1.is)(s, Boolean));
});
tap_1.default.test("object", async (t) => {
const obj = { name: "aaa", count: 1 };
const matches = (0, index_js_1.is)(obj, { name: String, count: Number });
if (matches) {
obj.name.charAt(0);
obj.count.toFixed(2);
}
t.ok(matches);
});
tap_1.default.test("null", async (t) => {
const v = null;
t.ok((0, index_js_1.is)(v, null));
t.notOk((0, index_js_1.is)(v, { name: String }));
});
tap_1.default.test("undefined", async (t) => {
const v = undefined;
t.ok((0, index_js_1.is)(v, undefined));
t.notOk((0, index_js_1.is)(v, null));
});
tap_1.default.test("nested object", async (t) => {
const obj = JSON.parse(`{ "post": { "id": 23 } }`);
const matches = (0, index_js_1.is)(obj, { post: { id: Number } });
if (matches) {
obj.post.id.toFixed(2);
}
t.ok(matches);
});
tap_1.default.test("instance", async (t) => {
const d = new Date();
const matches = (0, index_js_1.is)(d, Date);
if (matches) {
d.getTime();
}
t.ok(matches);
});
tap_1.default.test("class instance", async (t) => {
class Foo {
constructor() {
this.bar = 42;
}
}
const foo = new Foo();
const matches = (0, index_js_1.is)(foo, Foo);
if (matches) {
foo.bar;
}
t.ok(matches);
});
tap_1.default.test("array", async (t) => {
const a = [1, 2, 3];
const matches = (0, index_js_1.is)(a, [Number]);
if (matches) {
a.map((n) => n.toFixed(2));
}
t.ok(matches);
});
tap_1.default.test("array item type mismatch", async (t) => {
const a = ["a", "b", "c"];
const matches = (0, index_js_1.is)(a, [Number]);
t.notOk(matches);
});
tap_1.default.test("not an array", async (t) => {
const a = { 0: "a", 1: "b" };
const matches = (0, index_js_1.is)(a, [String]);
t.notOk(matches);
});
tap_1.default.test("mixed array", async (t) => {
const a = ["a", 1, true];
const matches = (0, index_js_1.is)(a, []);
t.ok(matches);
});
tap_1.default.test("tuple", async (t) => {
const a = ["A", 2, new Date()];
const matches = (0, index_js_1.is)(a, [String, Number, Date]);
if (matches) {
a[0].charAt(0);
a[1].toFixed(2);
a[2].getTime();
}
t.ok(matches);
t.notOk((0, index_js_1.is)(a, [String, Number, String]));
});
tap_1.default.test("unsupported schema", async (t) => {
const matches = (0, index_js_1.is)("", Symbol());
t.notOk(matches);
});
tap_1.default.test("arrow function", async (t) => {
t.ok((0, index_js_1.is)("foo", (v) => typeof v === "string"));
t.notOk((0, index_js_1.is)("foo", (v) => typeof v === "number"));
});
tap_1.default.test("function declaration", async (t) => {
function isArray(v) {
return Array.isArray(v);
}
t.ok((0, index_js_1.is)([], isArray));
});
tap_1.default.test("Array", async (t) => {
t.ok((0, index_js_1.is)([], Array));
t.ok((0, index_js_1.is)(["abc", 123], Array));
t.notOk((0, index_js_1.is)({}, Array));
});
tap_1.default.test("Array.isArray", async (t) => {
t.ok((0, index_js_1.is)([], Array.isArray));
t.ok((0, index_js_1.is)(["abc", 123], Array.isArray));
t.notOk((0, index_js_1.is)({}, Array.isArray));
});
tap_1.default.test("unknown", async (t) => {
t.ok((0, index_js_1.is)(23, index_js_1.unknown));
t.ok((0, index_js_1.is)("abc", index_js_1.unknown));
t.ok((0, index_js_1.is)([], index_js_1.unknown));
t.ok((0, index_js_1.is)({}, index_js_1.unknown));
t.ok((0, index_js_1.is)(undefined, index_js_1.unknown));
t.ok((0, index_js_1.is)(null, index_js_1.unknown));
});
tap_1.default.test("union", async (t) => {
const a = { foo: "bar" };
const b = { foo: 42 };
t.ok((0, index_js_1.is)(a, { foo: (0, index_js_1.union)(String, Number) }));
t.ok((0, index_js_1.is)(b, { foo: (0, index_js_1.union)(String, Number) }));
t.notOk((0, index_js_1.is)(a, { foo: (0, index_js_1.union)(Boolean, Number) }));
const isStringOrNumber = (0, index_js_1.union)(String, Number);
t.ok((0, index_js_1.is)(23, isStringOrNumber));
t.ok((0, index_js_1.is)("hello", isStringOrNumber));
t.notOk((0, index_js_1.is)(false, isStringOrNumber));
});
tap_1.default.test("optional", async (t) => {
const a = { foo: "foo" };
const b = { foo: undefined };
const c = {};
t.ok((0, index_js_1.is)(a, { foo: (0, index_js_1.optional)(String) }));
t.ok((0, index_js_1.is)(b, { foo: (0, index_js_1.optional)(String) }));
t.ok((0, index_js_1.is)(c, { foo: (0, index_js_1.optional)(String) }));
const isStringOrUndefined = (0, index_js_1.optional)(String);
t.ok((0, index_js_1.is)("hello", isStringOrUndefined));
t.ok((0, index_js_1.is)(undefined, isStringOrUndefined));
t.notOk((0, index_js_1.is)(23, isStringOrUndefined));
});
tap_1.default.test("literal", async (t) => {
const a = "foo";
t.ok((0, index_js_1.is)(a, "foo"));
t.ok((0, index_js_1.is)(a, (0, index_js_1.union)("foo", "bar")));
t.notOk((0, index_js_1.is)(a, "bar"));
});
tap_1.default.test("object with literal", async (t) => {
const obj = { name: "aaa", count: 1 };
const matches = (0, index_js_1.is)(obj, { name: "aaa", count: Number });
if (matches) {
obj.name.charAt(0);
obj.count.toFixed(2);
}
t.ok(matches);
});
tap_1.default.test("assert", async (t) => {
const obj = { foo: "foo" };
(0, index_js_1.assert)(obj, { foo: String });
t.throws(() => {
(0, index_js_1.assert)(obj, { foo: Number });
}, TypeError);
});
tap_1.default.test("TypeFromSchema", async (t) => {
let s = "foo";
//@ts-expect-error
s = 23;
let i = 23;
//@ts-expect-error
i = "foo";
const isStringOrNumber = (0, index_js_1.union)(String, Number);
let x = 23;
x = "foo";
//@ts-expect-error
x = false;
const fooSchema = {
required: {
value: String,
},
optional: (0, index_js_1.optional)(String),
};
let foo = {
required: {
value: "",
},
};
foo.optional = "foo";
//@ts-expect-error
foo.optional = 23;
});
tap_1.default.test("typeGuard", async (t) => {
const isString = (0, index_js_1.typeGuard)(String);
t.ok(isString("foo"));
t.notOk(isString(23));
let isNumber;
isNumber = (0, index_js_1.typeGuard)(Number);
t.ok(isNumber(23));
//@ts-expect-error
isNumber = isString;
});
tap_1.default.test("diff", async (t) => {
t.match((0, index_js_1.diff)("abc", Number), ["value"]);
t.match((0, index_js_1.diff)({ num: "aaa", val: "bbb" }, { num: Number, val: String }), [
"value.num",
]);
});
tap_1.default.test("deeply nested schemas", async (t) => {
const schema = {
level1: (0, index_js_1.optional)({
level2: {
level3: {
level4: {
level5: {
level6: {
level7: {
level8: {
level9: {
level10: {
value: String,
},
},
},
},
},
},
},
},
},
}),
};
const value = {};
t.ok((0, index_js_1.is)(value, schema));
});
tap_1.default.test("record", async (t) => {
const obj = { foo: "foo" };
(0, index_js_1.assert)(obj, (0, index_js_1.record)(String, String));
t.throws(() => {
(0, index_js_1.assert)(obj, (0, index_js_1.record)(String, Number));
}, TypeError);
});
tap_1.default.test("nested record", async (t) => {
// Define schemas
const recordSchema = (0, index_js_1.record)(String, index_js_1.unknown);
const TestSchema = {
record: recordSchema,
};
// Test empty record
const s = {};
t.ok((0, index_js_1.is)(s, recordSchema), "empty record should be valid");
// Test nested record in object
const test = {
record: s,
};
t.ok((0, index_js_1.is)(test, TestSchema), "object with record should be valid");
// Test with actual data
const populatedTest = {
record: {
key1: "string",
key2: 42,
key3: { nested: true },
},
};
t.ok((0, index_js_1.is)(populatedTest, TestSchema), "object with populated record should be valid");
});
//# sourceMappingURL=index.js.map