#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
Редактировать
Обнаружил, что 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. Да, я думаю, что это единственное решение, пока не будут поддерживаться перегрузки.