2017-11-27 6 views
1

Ich mag würde Dekorateure verwenden, um meine Instanzen zu bestätigen:ES/TS decoractors zum Beispiel Validierung

function min(minVal) { 
    return (target, key) => { 
     let value = target[key]; 

     const getter =() => value; 
     const setter = (val) => { 
      if (val < minVal) { 
       throw new Error(`Value smaller than ${minVal}`); 
      } 
      value = val; 
     } 
     Reflect.deleteProperty[key]; 
     Reflect.defineProperty(target, key, { 
      get: getter, 
      set: setter 
     }); 
    } 
} 

function max, String, Number ... 

class User { 

    @String 
    name: String 

    @Number 
    @max(100) 
    @min(0) 
    age: Number 

    static validate(obj) { 
     ??? 
    } 
} 

Dies funktioniert, wenn ich die Instanz direkt verwenden, aber ich möchte in der Lage sein, die Validate-Methode aufrufen und passiert in einem einfachen Objekt:

User.validate({ name: 'Jon', age: 23}) // ok 
User.validate({ name: 'Jon', age: -5}) // error 

gibt es eine Möglichkeit, die Dekorateure auf das übergebene Objekt anzuwenden, ohne eine Benutzerinstanz?

Antwort

1

Zunächst gibt es ein Problem mit der Implementierung des Hintergrundsfelds. Da Sie die lokale Variable value verwenden, die sich in einer Funktion befindet, die einmal für die Eigenschaft class + aufgerufen wird, für die der Decorator verwendet wird, verwenden alle Instanzen dasselbe Sicherungsfeld für eine Eigenschaft. Der folgende Code funktioniert beispielsweise nicht erwartungsgemäß :

class User { 
    @min(0) 
    age: Number 
} 

var u = new User(); 
u.age = 1; 
var u2 = new User(); 
u2.age = 2; 

console.log(u.age); // outputs 2 instead of 1 
console.log(u2.age); // outputs 2 

die Validierungen auf einem Objekt auszuführen wörtlichen Sie eine statische Liste aller Validatoren halten konnte, und haben die Dekorateur auf diese statische Liste hinzufügen, wenn aufgerufen. Eine Implementierung hierfür könnte folgendermaßen aussehen (auch der Code behebt das Problem, das ich oben erwähnt):

function validation(validator) : PropertyDecorator & MethodDecorator{ 
    return (target, key: PropertyKey, propDesc?: PropertyDescriptor) => { 
     let privateKey = "_" + key.toString(); 
     propDesc = propDesc || { 
      configurable: true, 
      enumerable: true, 
     }; 
     propDesc.get = propDesc.get || (function() { return this[privateKey] }); 

     const originalSetter = propDesc.set || (function (val) { this[privateKey] = val }); 
     propDesc.set = function (val) { 
      validator(val); 
      originalSetter.call(this, val); 
     } 

     const validators: Array<(target: object) => void> = target.constructor['validators'] || (target.constructor['validators'] = []); 
     validators.push((target) => { 
      validator(target[key]); 
     }) 

     return propDesc; 
    } 
} 
function min(minVal) { 
    return validation((val) => { 
     if (val < minVal) { 
      throw new Error(`Value smaller than ${minVal}`); 
     } 
    }); 
} 

function max(maxValue) { 
    return validation((val) => { 
     if (val > maxValue) { 
      throw new Error(`Value greater than ${maxValue}`); 
     } 
    }); 
} 

class User { 
    static validators: Array<(target: object) => void> = []; 
    static validate(obj: Partial<User>) { 
     User.validators.forEach(fn => fn(obj)); 
    } 
    @min(0) @max(100) 
    age: number 

    private _ageField : number; 
    @min(0) @max(100) 
    get ageProp(): number { 
     return this._ageField; 
    } 
    set ageProp(value: number) { 
     this._ageField = value; 
    } 

} 
User.validate({ 
    age: 10, 
    ageProp: 10 
}); 

var u = new User(); 
u.age = 10; 
u.ageProp = 10; 

Edit: Aktualisiert Antwort mit Eigenschaften Felder nicht nur zu arbeiten.

Verwandte Themen