Как создать определение интерфейса TypeScript для перегруженной функции со строковым индексированным массивом?

Я работаю над файлом определения TypeScript для плагина проверки нокаута. Одна из вещей, на которой я застрял, связана с тем, как в этой библиотеке определены новые правила проверки.

Способ создания базового синхронного валидатора для ko.validation выглядит так:

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
    }
}

Это было бы просто, однако есть несколько другой способ создать асинхронный валидатор:

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)
    }
}

Асинхронный обратный вызов может принимать либо логическое значение (для проверки пройден / не пройден), либо литерал объекта (isValid для прохождения / неудачи и message для изменения сообщения проверки). Для этого я создал следующее:

interface KnockoutValidationAsyncCallbackArgs {
    isValid: bool;
    message: string;
}

interface KnockoutValidationAsyncCallback {
    (result: bool): void;
    (result: KnockoutValidationAsyncCallbackArgs): void;
}

Вот урезанная версия моего KnockoutValidationStatic, которая предоставляет массив правил. Я также создал специальный интерфейс для массива правил, чтобы указать индексирование строк:

interface KnockoutValidationRulesArray extends Array {
    [index: string]: KnockoutValidationRule;
}

interface KnockoutValidationStatic {
    rules: KnockoutValidationRulesArray;
    ... other members defined on this interface
}

interface KnockoutStatic {
    validation: KnockoutValidationStatic;
}

Я застрял на KnockoutValidationRule. Я пробовал следующее:

interface KnockoutValidationRule {
    async?: bool;
    message: string;
    validator(value: any, params: any): bool;
    validator(value: any, params: any, callback: KnockoutValidationAsyncCallback): void;
}

Это отлично работает для объявлений синхронных валидаторов. Однако, когда я создаю асинхронный, я получаю следующие ошибки:

Невозможно преобразовать '{message: string; validator: (value: any, params: any, callback: KnockoutValidationAsyncCallback) => void} 'to' KnockoutValidationRule ': типы свойства' validator 'типов' {message: string; валидатор: (значение: любое, параметры: любое, обратный вызов: KnockoutValidationAsyncCallback) => void; } 'и' KnockoutValidationRule 'несовместимы:

Сигнатуры вызовов типа '(значение: любое, параметры: любое, обратный вызов: KnockoutValidationAsyncCallback) => void' и '{(значение: любое, параметры: любое): bool; (значение: любые, параметры: любые, обратный вызов: KnockoutValidationAsyncCallback): void; } несовместимы:

Сигнатура вызова требует 2 или меньше параметров

Я также рассматривал возможность сделать обратный вызов необязательным параметром, но это не работает, потому что функция возвращает разные значения в зависимости от подписи (bool без 3-го аргумента, void, если присутствует 3-й аргумент).

Что я здесь делаю не так? Что-то не так с моим строковым индексированным массивом для правил? Можно ли создать интерфейс TypeScript на основе того, как работает эта библиотека?

Обновление 1

Похоже, я был на правильном пути с перегрузками в моем KnockoutValidationRule интерфейсе. Я только что обнаружил, что если я изменю ko.validation.rules с KnockoutValidationRulesArray на KnockoutValidationRule[], мой тестовый код будет нормально компилироваться как для синхронизирующих, так и для асинхронных валидаторов ...

interface KnockoutValidationStatic {
    //rules: KnockoutValidationRulesArray; // this breaks it
    rules: KnockoutValidationRule[] // this fixes it
    ... other members defined on this interface
}

Я неправильно объявляю массив?

Обновление 2

Вот пример кода, который я использую для проверки определений:

/// <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' });
        }
    }
}

Как я уже сказал, вышеупомянутая компилируется нормально, когда ko.validation.rules равно KnockoutValidationRule[]. Он не работает, когда я меняю его на KnockoutValidationRulesArray.


person danludwig    schedule 23.12.2012    source источник
comment
Я не эксперт в этом, но попробуйте разделить KnockoutValidationAsyncCallback на два (по одному для каждой подписи) и создать дополнительную перегрузку для KnockoutValidationRule. Поскольку у вас есть два метода в интерфейсе, я думаю, что TypeScript ожидает объект с двумя методами, а не с одной функцией сопоставления.   -  person Morten Mertner    schedule 23.12.2012
comment
@MortenMertner благодарит за предложение. Проблема с разделением KnockoutValidationAsyncCallback заключается в том, что это не позволит коду выполнить обе перегрузки обратного вызова. В функции проверки я могу вызвать либо callback(true), либо callback({isValid: false, message: 'custom message'}).   -  person danludwig    schedule 23.12.2012
comment
Не могли бы вы опубликовать уроки, связанные с этим? Я мог бы немного повозиться с этим и посмотреть, смогу ли я что-нибудь придумать.   -  person Morten Mertner    schedule 23.12.2012
comment
PS: Не забудьте отправить свой файл .d.ts для этого парню, поддерживающему это репо: github.com/borisyankov/ ОпределенноTyped   -  person Morten Mertner    schedule 23.12.2012
comment
@MortenMertner, да, я действительно собирался отправить Борису еще один запрос на перенос после того, как я немного уточню этот файл определения. github.com/borisyankov/DefinentyTyped/pull/134   -  person danludwig    schedule 23.12.2012
comment
Я разместил образец тестового кода, который использую для проверки компиляции при внесении изменений в файл определения.   -  person danludwig    schedule 23.12.2012


Ответы (2)


Думаю, я понял, почему вышеперечисленное не сработает. В javascript, когда вы используете синтаксис массива [] со строкой в ​​качестве индексатора, вы не создаете массив в обычном смысле. Вместо этого этот синтаксис просто добавляет свойства к объекту. Следующие элементы функционально эквивалентны:

ko.validation['test1'] = { ... };
ko.validation.test1 = { ... };
person danludwig    schedule 23.12.2012
comment
Вы правы - так работает JavaScript - включая именованные элементы в DOM. - person Fenton; 24.12.2012

Вы можете обойти проблему, изменив KnockoutValidationRule:

export interface KnockoutValidationRule {
    async?: bool;
    message: string;
    validator: (value: any, params: any, callback?: KnockoutValidationAsyncCallback): bool;
}

И внесите соответствующее изменение там, где это используется:

ko.validation.rules['test2'] = {
    async: true,
    message: '',
    validator: (value: any, params: any, callback?: KnockoutValidationAsyncCallback): bool => {
        callback(false);
        callback(true);
        callback({ isValid: false, message: 'custom' });
    }
}

Это объединяет две сигнатуры методов. Метод проверки на самом деле не должен возвращать bool, поэтому тот факт, что метод объявлен как возвращающий bool, в основном является косметической / эстетической проблемой.

Возможно, стоит начать обсуждение этого вопроса на сайте codeplex, поскольку я сомневаюсь, что это редко используется в библиотеках JavaScript. .

person Morten Mertner    schedule 23.12.2012
comment
Косметическая проблема? Действительно? Значит, приведенный выше код действительно компилируется, хотя объявляет возвращаемый тип bool, но ничего не возвращает? - person danludwig; 23.12.2012
comment
Я согласен с тем, что это некрасиво, может ввести в заблуждение людей, читающих определение, и работает только в тех случаях, когда вы действительно можете объединить подписи. Но компилируется :) - person Morten Mertner; 23.12.2012
comment
К вашему сведению, я отказался от попытки индексировать определение правил в виде массива и использовал очень близкое соответствие моему коду в вопросе. Я отправил файл определения в проект Бориса Янкова «Определенно типизированный», и он был загружен в мастер: github.com/borisyankov/DefinentyTyped/blob/master/ - person danludwig; 27.12.2012
comment
Спасибо! Надеюсь, скоро я буду наслаждаться плодами твоего труда :-) - person Morten Mertner; 28.12.2012