Last active
July 7, 2018 11:31
-
-
Save spion/ccd6b3da1d06e3eee8d618f537f6af45 to your computer and use it in GitHub Desktop.
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 { validator, validate } from './experiment'; | |
class U { | |
@validator() name!: string; | |
} | |
class C { | |
@validator() id!: string; | |
@validator() u!: U; | |
} | |
class Test { | |
@validate | |
myMethod(params: C) { | |
console.log('Got params', params); | |
} | |
} | |
it('works', () => { | |
console.log('Experiment'); | |
let t = new Test(); | |
t.myMethod({ id: '1', u: { name: 'ok' } }); | |
expect(() => { | |
t.myMethod({ id: '1', u: { name: (1 as any) as string } }); | |
}).toThrowError('Invalid property u : Invalid property name : not a string'); | |
}); |
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 'reflect-metadata'; | |
let meta = new WeakMap<any, { [k: string]: { [k: string]: any } }>(); | |
type Validator = { key: string; validator: (o: any) => string }; | |
export function updateMeta<T, Key extends keyof T>(o: T, field: Key, obj: { [key: string]: any }) { | |
let m = meta.get(o); | |
if (m == null) { | |
m = {}; | |
meta.set(o, m); | |
} | |
m[field as string] = m[field as string] || {}; | |
Object.assign(m[field as string], obj); | |
} | |
export function getMeta<T>(o: T) { | |
return meta.get(o); | |
} | |
let defaultValidators = new Map<any, (o: any) => string | undefined>(); | |
defaultValidators.set(String, (s: any) => (typeof s === 'string' ? undefined : 'not a string')); | |
defaultValidators.set(Number, (s: any) => (typeof s === 'number' ? undefined : 'not a number')); | |
defaultValidators.set(Boolean, (s: any) => (typeof s === 'boolean' ? undefined : 'not a boolean')); | |
function getValidators(o: any): Validator[] { | |
let fields = Reflect.getMetadata('validated', o) || new Set(); | |
return Array.from(fields).map(f => ({ | |
key: f as string, | |
validator: Reflect.getMetadata('validator', o, f as any) | |
})); | |
} | |
function applyValidators(o: any, validators: Validator[]): string | undefined { | |
for (let v of validators) { | |
let err = v.validator(o[v.key]); | |
if (err) return 'Invalid property ' + v.key + ' : ' + err; | |
} | |
return undefined; | |
} | |
export function validator(validatorFn?: (t: any) => string | undefined) { | |
return function setValidator<T, P extends keyof T>(o: T, p: P) { | |
if (!validatorFn) { | |
let propType = Reflect.getMetadata('design:type', o, p as any); | |
validatorFn = defaultValidators.get(propType); | |
if (!validatorFn) { | |
let validators = getValidators(propType.prototype); | |
validatorFn = (o: any) => applyValidators(o, validators); | |
} | |
} | |
let validated: Set<P> = Reflect.getMetadata('validated', o) || new Set<P>(); | |
validated.add(p); | |
Reflect.defineMetadata('validated', validated, o); | |
Reflect.defineMetadata('validator', validatorFn, o, p as string); | |
}; | |
} | |
export function validate<T, K extends keyof T>(o: T, k: K, prop: PropertyDescriptor) { | |
let types = Reflect.getMetadata('design:paramtypes', o, k as string); | |
let validators = Array.from(types).map((t: any) => getValidators(t.prototype)); | |
let oldValue = prop.value; | |
prop.value = function(...args: any[]) { | |
validators.forEach((v, ix) => { | |
let arg = args[ix]; | |
let err = applyValidators(arg, v); | |
if (err) throw new Error(err); | |
}); | |
oldValue.call(this, ...args); | |
}; | |
} | |
export function strValidator(s: any) { | |
if (typeof s !== 'string') return 'not a string'; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment