Typescript fluent api builder несколько параметров (переопределение) невозможно

#typescript #typescript-typings

#typescript #typescript-типизации

Вопрос:

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

 declare class Foo<T> {
    methodA(foo: string): this 
    methodB(): this;
    methodD(): this;
    methodD(type: string): this;
}
  

Вышеупомянутый класс является свободным API, который может вызывать сам себя. Требование заключается в том, что methodB оно никогда не должно быть вызываемым. Поэтому я создал следующий тип, который будет использоваться поверх Foo<T>

 type FooWithoutB<T> = Omit<Foo<T>, 'methodB'>
  

Но это все же позволяет мне сделать следующее:

 let typeWithoutB!: WithoutB<any>;

typeWithoutB.methodA('hello').methodB() // should not be possible
  

Итак, я подумал, хорошо, давайте переопределим возвращаемый тип, поэтому я создал этот тип:

 type Override<T> = {
    [key in keyof T]: T[key] extends (...param: any[]) => T ? (...params: Parameters<T[key]>) => WithoutB<T> : T[key]
}
  

Приведенное выше почти работает, но нарушает типы параметров:

 foo.methodD() // expected one argument but got 0
foo.methodD('ok') // this works, but above should also work
  

См . playground

Редактировать

Обнаружил, что type F = Parameters<Foo<any>['methodD']> возвращает [type: string] . Есть ли возможность разрешить оба типа параметров?

Комментарии:

1. Я думаю, у вас могут возникнуть большие проблемы, если существуют функции, которые никогда не следует вызывать.

2. согласен на 100%… К сожалению, используется сторонняя библиотека, которую мы не можем легко заменить чем-то другим.

3. рассматривали ли вы возможность использования частной функции для methodB? ваш виртуальный methodD в typescript определяется следующим образом: methodD(type?: string): this

4. Похоже, перегрузки делают это практически невозможным…

5. Кстати, вот открытая проблема, касающаяся перегрузок .

Ответ №1:

Я не думаю, что здесь есть простой ответ. Одна из возможностей — вернуться к классическому ООП и ввести фасад. Поэтому мы бы заключили экземпляр существующего класса в новый класс и перенаправляли вызовы, где это уместно. Что-то вроде приведенного ниже кода. Обратите внимание, что это предполагает, что наши методы и методы возвращают ‘this’.

 class FooFacade<T>
{
    private _foo: Foo<T> = new Foo<T>();
    methodA(foo: string): this { this._foo.methodA(foo); return this; }
    
    methodD(): this;
    methodD(type: string): this;
    methodD(type?: string): this {
        if (type !== undefined) {
            this._foo.methodD(type);
        } else {
            this._foo.methodD();
        }
        return this;
    }
}
  

Очевидным недостатком этого является то, что он немного неуклюжий, и в зависимости от того, насколько сложным на самом деле является Foo, может потребоваться много работы. Преимущество в том, что он дает вам полный контроль над тем, как вызывается стороннее программное обеспечение.

Комментарии:

1. Да, я думаю, что это единственное решение, пока не будут поддерживаться перегрузки.