UNPKG

type-definitions

Version:
147 lines (134 loc) 4.08 kB
import nativeTypeMap from './nativeTypeMap'; export class BaseType { static isOfType(val) { console.error(`An isOfType function needs to be defined for "${this.name}"`); return false; } static get optional() { return optional(this); } static get defaultValue() { return undefined; } static create(val = this.defaultValue) { // console.log(val, typeof val) if (!this.isOfType(val)) { throw `The provided default value ${JSON.stringify(val)} does not match the type definition`; } return val; } static withDefault(val) { return defaultValue(this, val); } } export class UnionType extends BaseType { static get types() { console.error('This type is meant to be extended with an overrided static `types` property. It\'s suggested to use `Union.ofTypes(...types)` to generate a new union'); return []; } static ofTypes(...types) { return unionOf(...types); } static isOfType(val) { return this.types.some((type) => type.isOfType(val)); } } export class AnyType extends BaseType { static get defaultValue() { return null; } static isOfType(val) { return val !== undefined; } } export function optional(type) { return class OptionalType extends coerce(type) { static isOfType(val) { return super.isOfType(val) || val === undefined || val === null; } static get defaultValue() { return super.definedDefaultValue === undefined ? null : super.defaultValue; } }; } export function coerce(type) { if (typeof type.isOfType === 'function') { if (type.prototype instanceof BaseType) { return type; } else { return class CoercedType extends BaseType { static isOfType(val) { return type.isOfType(val); } static get defaultValue() { return Object.create(type); } static create(...params) { console.warn('.create() doesn\'t work well with custom classes, this probably isn\'t doing what you expect'); return super.create(...params) } } } } let derivedType = nativeTypeMap.get(type); if (!derivedType) { if (Array.isArray(type)) { derivedType = nativeTypeMap.get(Array); // Arrays of length 1, all items of the array should be of that type if (type.length === 1) { derivedType = derivedType.ofType(type[0]); // Array of greater lengths, type should be a union of all types } else if (type.length > 1) { derivedType = derivedType.ofType(unionOf(...type)); } } else if (type.constructor === Object) { derivedType = nativeTypeMap.get(Object); // If there are keys, add a definition to the object if (Object.keys(type).length) { derivedType = derivedType.withDefinition(type); } } else { derivedType = class InsatanceOfType extends BaseType { static isOfType(val) { return val instanceof type; } static get defaultValue() { return Object.create(type); } static create(...params) { console.warn('.create() doesn\'t work well with custom classes, this probably isn\'t doing what you expect'); return super.create(...params) } }; } } return derivedType; } export function unionOf(...unionTypes) { const coercedTypes = unionTypes.map(coerce); return class DefinedUnionType extends UnionType { static get types() { return coercedTypes; } static get defaultValue() { let val = super.defaultValue; if (val === undefined) { val = this.types.map((t) => t.defaultValue).find((tv) => tv !== undefined); } return val; } }; } export function defaultValue(type, val) { if (!type.isOfType(val)) { throw `Supplied default ${JSON.stringify(val)} is not a valid value for type` } return class TypeWithDefault extends type { static get definedDefaultValue() { return val; } static get defaultValue() { return this.definedDefaultValue; } } }