From 707296e9f3aaf8c34383f5f1e539fdff6ef06c46 Mon Sep 17 00:00:00 2001 From: DanilF Date: Sun, 25 Oct 2015 22:39:52 -0400 Subject: [PATCH] DanilF - Added typings for decorum library. --- decorum/decorum-tests.ts | 102 ++++++++++ decorum/decorum-tests.ts.tscparams | 2 + decorum/decorum.d.ts | 292 +++++++++++++++++++++++++++++ 3 files changed, 396 insertions(+) create mode 100644 decorum/decorum-tests.ts create mode 100644 decorum/decorum-tests.ts.tscparams create mode 100644 decorum/decorum.d.ts diff --git a/decorum/decorum-tests.ts b/decorum/decorum-tests.ts new file mode 100644 index 0000000000..dd1743b5e7 --- /dev/null +++ b/decorum/decorum-tests.ts @@ -0,0 +1,102 @@ +/// + +import {Required} from 'decorum'; +import {Email} from 'decorum'; +import {MinLength} from 'decorum'; +import {MaxLength} from 'decorum'; +import {Length} from 'decorum'; +import {FieldName} from 'decorum'; +import {Validation} from 'decorum'; +import {Pattern} from 'decorum'; +import {Validator} from 'decorum'; +import {BaseValidator} from 'decorum'; + +class MyModel { + @FieldName('User name') + @Required() + @MaxLength(50) + username = ''; + + @FieldName('Email address') + @Email() + @Required('Your email address will be used to send you a confirmation email. You must fill it out') + emailAddress = ''; + + @Required() + @MinLength(10) + @MaxLength(30) + password = ''; + + @FieldName('Confirm password') + @Validation( + 'The passwords do not match.', + (pwd, model) => model.password === pwd + ) + confirmPassword = ''; + + @Pattern(/^[a-z0-9-]+$/i, 'Must be a valid slug tag') + slug = 'foo'; + + @Length(6, 'Alias must be 6 characters long') + alias: string; +} + +// ES6-style +class MyController { + model = new MyModel(); + validator = Validator.new(this.model); + + doStuff(): void { + var opts = this.validator.getValidationOptions('alias'); + var fieldName = opts.getFieldName(); + var errs = opts.validateValue('foo', this.model); + opts.setFieldName('Foo'); + opts.addValidator(null); + var validators = opts.getValidators(); + } + + validate(): void { + var result = this.validator.validate(); + if (!result.isValid) { + for(var i = 0; i < result.errors.length; i++) { + var current = result.errors[i]; + console.error(current.fieldName, current.errors); + } + } + } +} + +// ES5-style +function MyOtherModel() { + this.foo = ''; + this.bar = ''; +} + +Validator.decorate(MyOtherModel, { + foo: [ + Required() + ], + bar: [ + Pattern(/^[a-z][0-9]$/i) + ] +}); + +var otherValidator = Validator.new(new MyOtherModel()); +otherValidator.validateField('foo', ''); + +// Custom validator +class MyValidator extends BaseValidator { + + validatesEmptyValue(): boolean { + return false; + } + + getMessage(fieldName: string, fieldValue: any): string { + return 'No!'; + } + + isValid(value: any, model: any): boolean { + return false; + } + +} diff --git a/decorum/decorum-tests.ts.tscparams b/decorum/decorum-tests.ts.tscparams new file mode 100644 index 0000000000..5300ee7222 --- /dev/null +++ b/decorum/decorum-tests.ts.tscparams @@ -0,0 +1,2 @@ +--experimentalDecorators +--target ES5 diff --git a/decorum/decorum.d.ts b/decorum/decorum.d.ts new file mode 100644 index 0000000000..858b1e2dd5 --- /dev/null +++ b/decorum/decorum.d.ts @@ -0,0 +1,292 @@ +// Type definitions for Decorum JS v0.1.2 +// Project: https://github.com/dflor003/decorum +// Definitions by: Danil Flores +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +declare module 'decorum' { + /** + * A generic custom validation. Takes a predicate that will receive the proposed value as the first parameter and the + * current model state as the second. + * @param message The message to display when the predicate fails. + * @param predicate A lambda expression/function that determines if the value is valid. If it returns a falsy value, the + * field will be considered invalid and will return the passed error message upon validation. + * @returns {function(Object, string): void} A field validation decorator. + */ + export function Validation(message: string, predicate: (value: any, model: TModel) => boolean): PropertyDecorator; + + /** + * Validate's that the field is a valid email address. The format used is the same as the webkit browser's internal + * email validation format. For looser or stricter formats, use your own validation based on the @Pattern decorator. + * @param message [Optional] Overrides the default validation error message. + * @returns {function(Object, string): void} A field validation decorator. + */ + export function Email(message?: string): PropertyDecorator; + + /** + * Sets the field's "friendly" name in validation error messages. + * @param name The field's friendly name + * @returns {function(Object, string): void} A field validation decorator. + */ + export function FieldName(name: string): PropertyDecorator; + + /** + * Validate's a field's EXACT length. Validation fails if the field is not EXACTLY the length passed. + * @param length The exact length the field must be. + * @param message [Optional] Overrides the default validation error message. + * @returns {function(Object, string): void} A field validation decorator. + */ + export function Length(length: number, message?: string): PropertyDecorator; + + /** + * Validates a field's maximum length. + * @param maxLength The field's maximum length. Must be a positive integer greater than 1. + * @param message [Optional] Overrides the default validation error message. + * @returns {function(Object, string): void} A field validation decorator. + */ + export function MaxLength(maxLength: number, message?: string): PropertyDecorator; + + /** + * Validates the field's minimum length. + * @param minLength The field's minimum length. Must be a positive integer greater than 0 + * @param message [Optional] Overrides the default validation error message. + * @returns {function(Object, string): void} A field validation decorator. + */ + export function MinLength(minLength: number, message?: string): PropertyDecorator; + + /** + * Validates the field against a regular expression pattern. + * @param regex The regex to validate against. Should be a valid JavaScript {RegExp} instance. + * @param message [Optional] Overrides the default validation error message. + * @returns {function(Object, string): void} A field validation decorator. + */ + export function Pattern(regex: RegExp, message?: string): PropertyDecorator; + + /** + * Marks the field as required. + * @param message [Optional] Overrides the default validation error message. + * @returns {function(Object, string): void} A field validation decorator. + */ + export function Required(message?: string): PropertyDecorator; + + /** + * A map from field name to array of field validation decorators. + */ + export type ValidationDefinitions = { + [field: string]: PropertyDecorator[]; + }; + + /** + * Static container for convenience methods related to field validation. + */ + export class Validator { + /** + * Creates a new model validator for the given model. Model should be a valid class that has a valid constructor + * and a prototype. + * @param model The model to create the validator for. + * @returns {ModelValidator} An instance of {ModelValidator} + */ + static new(model: any): ModelValidator; + + /** + * Decorates the passed class with model validations. Use this when you do not have access to ES7 decorators. + * The object passed should be a valid class (ES6 class or ES5 function constructor). + * @param objectType The class to decorate. + * @param definitions One or more field validation definitions of the form { "fieldName": [ decorators ] }. + */ + static decorate(objectType: any, definitions: ValidationDefinitions): void; + + /** + * Creates an anonymous validator, immediately validates the model, and returns any validation errors on the model + * as a result. + * @param model The model to validate. + */ + static validate(model: any): IValidationResult; + } + + /** + * Details about validation errors on a field. + */ + export interface IFieldValidationError { + /** + * The property name of the field on the model. + */ + field: string; + + /** + * The "friendly" name of the field. If not set on the model via @FieldName(...), it will default to "Field". + */ + fieldName: string; + + /** + * One or more field validation errors. Empty if no errors. + */ + errors: string[]; + } + + /** + * Result returned when a model is validated. + */ + export interface IValidationResult { + /** + * Whether or not the model is valid. + */ + isValid: boolean; + + /** + * A map of field name to validation errors. + */ + errors: IFieldValidationError[]; + } + + /** + * Wraps a model to allow the consuming class to call validation methods. + */ + export class ModelValidator { + /** + * Creates a new model validator. + * @param model The model to validate. Should be a class that has a valid constructor function and prototype. + */ + constructor(model: any); + + /** + * Gets the validation options for the given field name. + * @param fieldKey The name of the field to get options for. + * @returns {FieldOptions} The field options associated with that field or null if no validations defined + * for the field. + */ + getValidationOptions(fieldKey: string): FieldOptions; + + /** + * Validates the given field on this {ModelValidator}'s model. If a proposed value is passed, validate + * against that passed value; otherwise, use the field's current value on the model. + * @param fieldKey The name of the field to validate. + * @param proposedValue [Optional] The proposed value to set on the field. + * @returns {string[]} An array of field validation error messages if the field is invalid; otherwise, + * an empty array. + */ + validateField(fieldKey: string, proposedValue?: any): string[]; + + /** + * Validate the entire model and return a result that indicates whether the model is valid or not and any errors + * that have occurred in an object indexed by field name on the model. + * @returns {IValidationResult} An object that contains whether the model is valid or not and errors by field name. + */ + validate(): IValidationResult; + } + + /** + * Callback invoked when a validation needs to return an error. Parameters include field name, + * field value, and any other properties relating to the field validation itself. + */ + export type MessageHandler = (fieldName: string, fieldValue: any, ...args: any[]) => string; + + /** + * A map of validation "key" (unique name for a given type of validation) to message handler callback. + */ + export interface IMessageHandlerMap { + [key: string]: MessageHandler; + } + + /** + * Mechanism for overriding validation errors to provide for custom or localized error messages. + * @type {{IMessageHandlerMap}} + */ + let MessageHandlers: IMessageHandlerMap; + + /** + * Validation options for a given field including actual validators and meta data such as the field name. + */ + export class FieldOptions { + /** + * Gets the "friendly" name of the field for use in validation error messages. Defaults to just "Field". + * @returns {string} + */ + getFieldName(): string; + + /** + * Sets the "friendly" name of the field for use in validation error messages. This name will be used in the text + * of validation errors. + * @param name The new name to set. + */ + setFieldName(name: string): void; + + /** + * Add a validator to the list of validators for this field. + * @param validator The validator to add. Should be a class that extends from {BaseValidator}. + */ + addValidator(validator: BaseValidator): void; + + /** + * Gets the validators assigned to this field. + * @returns {BaseValidator[]} The validators for this field. + */ + getValidators(): BaseValidator[]; + + /** + * Runs through all of the validators for the field given a particular value and returns any validation errors that + * may have occurred. + * @param value The value to validate. + * @param model The rest of the model. Used in custom cross-field validations. + * @returns {string[]} Any validation errors that may have occurred or an empty array if the value passed is valid + * for the field. + */ + validateValue(value: any, model: any): string[]; + } + + /** + * Base abstract class for all validators. Methods that must be overridden: + * getMessage(...) - Get error message to return when field is invalid. + * isValid(...) - Check validity of field given proposed value and the rest of the model. + */ + abstract class BaseValidator { + /** + * Initializes the {BaseValidator} + * @param validatorKey A unique "key" by which to identify this field validator i.e. length, maxlength, required. + * Should be a valid JS property name. + * @param message A custom error message to return. Should be passed down from concrete class' constructors to enable + * customizing error messages. + */ + constructor(validatorKey: string, message: string); + + /** + * Returns true if the validator instance was passed a custom error message. + */ + hasCustomMessage: boolean; + + /** + * Check whether this validator should process an "empty" value (i.e. null, undefined, empty string). Override + * this in derived classes to skip validators if the field value hasn't been set. Things like email, min/max length, + * and pattern should return false for this to ensure they don't get fired when the model is initially empty + * before a user has had a chance to input a value. Things like required should override this to true so that + * they are fired for empty values. Base implementation defaults to false + * @returns {boolean} + */ + validatesEmptyValue(): boolean; + + /** + * Gets the custom error message set on this validator. + * @returns {string} The custom error message or null if none has been set. + */ + getCustomMessage(): string; + + /** + * Gets the unique name for this validator. + * @returns {string} The unique name for this validator. + */ + getKey(): string; + + /** + * [Abstract] Gets the error message to display when a field fails validation by this validator. + * @param fieldName The "friendly" name set for the field. + * @param fieldValue The field's current value. + */ + abstract getMessage(fieldName: string, fieldValue: any): string; + + /** + * [Abstract] Checks the passed value for validity. + * @param value The field's proposed value. + * @param model The rest of the model if cross-field validity checks are necessary. + */ + abstract isValid(value: any, model: any): boolean; + } +}