I am working on a TypeScript definition file for the knockout validation plugin. One of the things I'm stuck on has to do with how new validation rules are defined in that library.
The way to create a basic synchronous validator for ko.validation is like so:
ko.validation.rules['myrulename'] = {
message: 'default message to display when validation fails',
validator: function (value, params) {
// use value and params to evaluate rule,
// then return true if valid, false if invalid
}
}
This would be straightforward, however there is a slightly different way to create an asynchronous validator:
ko.validation.rules['myrulename'] = {
async: true,
message: 'default message to display when validation fails',
validator: function (value, params, callback) {
// use value and params to evaluate rule,
// then invoke callback to indicate pass / fail
// this function does not return a bool (it is void)
}
}
The asynchronous callback can take either a bool (for validation pass/fail) or an object literal (isValid for pass/fail, and message to change the validation message). For this, I have created the following:
interface KnockoutValidationAsyncCallbackArgs {
isValid: bool;
message: string;
}
interface KnockoutValidationAsyncCallback {
(result: bool): void;
(result: KnockoutValidationAsyncCallbackArgs): void;
}
Here is a trimmed down version of my KnockoutValidationStatic which exposes the rules array. I have also create a special interface for the rules array to specify string indexing:
interface KnockoutValidationRulesArray extends Array {
[index: string]: KnockoutValidationRule;
}
interface KnockoutValidationStatic {
rules: KnockoutValidationRulesArray;
... other members defined on this interface
}
interface KnockoutStatic {
validation: KnockoutValidationStatic;
}
What I am stuck on is the KnockoutValidationRule. I have tried the following:
interface KnockoutValidationRule {
async?: bool;
message: string;
validator(value: any, params: any): bool;
validator(value: any, params: any, callback: KnockoutValidationAsyncCallback): void;
}
This works fine for synchronous validator declarations. However when I create an asynchronous one, I get the following error(s):
Cannot convert '{ message: string; validator: (value: any,params: any,callback: KnockoutValidationAsyncCallback) => void }' to 'KnockoutValidationRule': Types of property 'validator' of types '{ message: string; validator: (value: any,params: any,callback: KnockoutValidationAsyncCallback) => void; }' and 'KnockoutValidationRule' are incompatible:
Call signatures of type '(value: any,params: any,callback: KnockoutValidationAsyncCallback) => void' and '{ (value: any,params: any): bool; (value: any, params: any,callback: KnockoutValidationAsyncCallback): void; } are incompatible:
Call signature expects 2 or fewer parameters
I have also considered making the callback an optional parameter, but that doesn't work because the function returns different values depending on the signature (bool without 3rd arg, void if 3rd arg is present).
What am I doing wrong here? Is there something wrong with my string-indexed array for the rules? Is it possible to create a TypeScript interface based on how this library works?
Update 1
It looks like I was on the right track with the overloads in my KnockoutValidationRule interface. I just discovered that if I change ko.validation.rules from a KnockoutValidationRulesArray to a KnockoutValidationRule[], my test code compiles fine for both sync and async validators...
interface KnockoutValidationStatic {
//rules: KnockoutValidationRulesArray; // this breaks it
rules: KnockoutValidationRule[] // this fixes it
... other members defined on this interface
}
Am I declaring the array wrong?
Update 2
Here is some example code I am using to test the definitions:
/// <reference path="../../ko/knockout-2.2.d.ts" />
/// <reference path="../../ko/knockout.validation.d.ts" />
module TestKoVal {
ko.validation.rules['test1'] = {
message: '',
validator: (value: any, params: any): bool => {
return false;
}
}
ko.validation.rules['test2'] = {
async: true,
message: '',
validator: function (value: any, params: any, callback: KnockoutValidationAsyncCallback): void => {
callback(false);
callback(true);
callback({ isValid: false, message: 'custom' });
}
}
}
Like I said, the above compiles fine when ko.validation.rules is a KnockoutValidationRule[]. It fails when I change it to a KnockoutValidationRulesArray.
KnockoutValidationAsyncCallbackis that it would prevent code from being able to execute both overloads of the callback. Within a validation function, I might want to invoke eithercallback(true)orcallback({isValid: false, message: 'custom message'}). - danludwig