2015-08-17 3 views
1

Итак, я хочу создать функцию negate, которая принимает некоторую функцию, которая возвращает логическое значение для некоторого списка аргументов и возвращает функция, которая принимает те же аргументы и выдает абсолютно противоположный логический результат.Функция, которая выполняет функцию предиката (возвращает логическое значение) и возвращает функцию предиката с теми же параметрами

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

function negate(predicate: Function): Function { 
    return function() { 
     return !predicate.apply(this, arguments); 
    } 
} 

Мы можем даже указать, что результирующая функция возвращает логическое значение, используя () => boolean в качестве возвращаемого типа. Но эта сигнатура указывает, что возвращаемая функция не принимает аргументов - на самом деле она должна иметь точно такие же аргументы, как и в функции predicate.

Что бы я хотел - это указать это. Если ограничить predicate ровно один параметр, я могу:

function negate<A>(predicate: (a: A) => boolean): (a: A) => boolean { 
    return function (a: A) { 
     return !predicate.apply(this, arguments); 
    } 
} 

И я мог бы использовать перегрузку, чтобы указать это на ноль, один, два, и так далее параметров, вручную определяя каждую версию. В какой-то момент я смогу принять столько параметров, сколько должна разумно принимать любая функция. И расширение его с еще большим количеством параметров, если мое определение «разумное» расширяется, не ужасно много работы.

В качестве альтернативы, this answer предлагает другую тактику:

export function negate<Predicate extends Function>(p: Predicate): Predicate { 
    return <any> function() { 
     !p.apply(this, arguments); 
    } 
} 

Который является типобезопасным так долго, как предикат фактически делает возвращает логический но не имеет никакого способа ограничения параметров для вещей, которые возвращают логическое значение (и, что еще хуже, это беззвучно произнесет любой небулевой результат, который вы получите в логическом порядке, чтобы применить !, и это не будет указано в сигнатуре возвращаемой функции).

Так что я действительно хотел бы быть как полностью типобезопасный и DRY.

Есть ли в машинописном машиностроении какие-либо функции, которые сделают это возможным?

+0

@basarat Хотя подобное, я не думаю, что это * копия * вопрос, из-за требования здесь, что переданное в функцию возврата 'boolean'. Отредактировал заголовок, чтобы сосредоточиться на этом. – KRyan

ответ

0

Построенный путь.

interface IPredicate { 
    (...args: any[]): boolean; 
} 
function negate<Predicate extends IPredicate>(p: Predicate): Predicate { 
    return <any> function(): boolean { 
     return !p.apply(this, arguments); 
    } 
} 

Интерфейс IPredicate позволяет определить, что все аргументы, которые мы принимаем, мы ожидаем, что логический результата, а затем с помощью <Predicate extends IPredicate> позволяют определить, что аргумент p и возвращенная функция должны принимать те же параметры, так как они должны быть как такими жеPredicate что extends IPredicate.

Внутренняя функция negate, мы используем apply и поэтому информация о типе теряется. Таким образом, <any> используется, чтобы в основном обеспечить компилятор TypeScript, что внутренняя функция верна. Это делает интерьер небезопасным, но он небольшой и не требует обслуживания после его написания.

Это также можно расширить, чтобы обрабатывать другие логические функции в предикатах, например.union:

export function union<Predicate extends IPredicate>(
    p: Predicate, 
    ...rest: Predicate[] 
): Predicate { 
    if (rest.length > 0) { 
     return <any> function() { 
      return p.apply(this, arguments) || union.apply(this, rest).apply(this, arguments); 
     } 
    } 
    else { 
     return p; 
    } 
} 
Смежные вопросы