Typ
by JeyDotC
JavaScript
const ValueKind = {
Parameter: 0,
ReturnValue: 1,
}
class TypeCheckError extends Error {
constructor({
kind,
index,
expectedTypeName
}) {
super(`${kind === ValueKind.Parameter ? `Parameter ${index}` : 'Return value'} must be an instance of ${expectedTypeName}`);
}
}
class TypeCheck {
constructor(name, implementation) {
this.implementation = implementation;
this.name = name;
}
perform(value) {
this.implementation(value);
}
}
const Any = new TypeCheck("Any", () => {});
const Void = Any;
Void.name = 'Void';
function Optional(Type) {
return new TypeCheck(`Optional<${Type.name}>`, (entry) => {
if (entry.value === undefined || entry.value === null) {
return;
}
typeCheckFactory(Type).perform(entry);
});
}
function OneOf(...types) {
const expectedTypeName = `OneOf<${types.map(t => t.name).join(', ')}>`;
return new TypeCheck(expectedTypeName, (entry) => {
const didAnyValidate = types.some((T) => {
try {
typeCheckFactory(T).perform(entry);
return true;
} catch (e) {
return false;
}
});
if (!didAnyValidate) {
const {
index,
kind,
} = entry;
throw new TypeCheckError({
index,
kind,
expectedTypeName
});
}
});
}
function PromiseOf(Type) {
return new TypeCheck(`Promise<${Type.name}>`, (entry) => {
if (entry.value instanceof Promise) {
entry.value.then((value) => {
typeCheckFactory(Type).perform({
...entry,
value
});
return value;
});
}
InstanceOf(Promise).perform(entry);
});
}
function InstanceOf(Type) {
return new TypeCheck(Type.name, ({
index,
value,
kind,
}) => {
if (!(value instanceof Type)) {
throw new TypeCheckError({
index,
kind,
expectedTypeName: Type.name
});
}
})
}
const InstanceOfStringCheck = new TypeCheck('string', ({
index,
...