Как я могу аннотировать рекурсивные типы в TypeScript?
Если у меня есть такая функция:
function say(message: string) {
alert(message);
return say;
}
у него есть интересное свойство, что я могу связать ему вызовы:
say("Hello,")("how")("are")("you?");
Компилятор будет генерировать предупреждение, если я передам число в первый вызов, но он позволит мне помещать числа в последующие вызовы.
say("Hello")(1)(2)(3)(4)
Какую аннотацию типа мне нужно добавить в функцию say
, чтобы заставить компилятор генерировать предупреждения, когда я передаю недопустимые типы цепочечным вызовам?
Ответы
Ответ 1
Тип, который ссылается на себя, должен иметь имя. Например,
interface OmegaString {
(message: string): OmegaString;
}
тогда вы можете аннотировать say
как OmegaString
,
function say(message: string): OmegaString {
alert(message);
return say;
}
тогда следующий код будет проверять тип.
say("Hello,")("how")("are")("you?");
но следующее не будет,
say("Hello")(1)(2)(3)(4)
Ответ 2
Цепной метод
Когда вы используете класс вместо функции, вы можете использовать тип this
, чтобы выразить тот факт, что метод возвращает экземпляр, на который он был вызван (методы цепочки).
Без this
:
class StatusLogger {
log(message: string): StatusLogger { ... }
}
// this works
new ErrorLogger().log('oh no!').log('something broke!').log(':-(');
class PrettyLogger extends StatusLogger {
color(color: string): PrettyLogger { ... }
}
// this works
new PrettyLogger().color('green').log('status: ').log('ok');
// this does not!
new PrettyLogger().log('status: ').color('red').log('failed');
С this
:
class StatusLogger {
log(message: string): this { ... }
}
class PrettyLogger extends StatusLogger {
color(color: string): this { ... }
}
// this works now!
new PrettyLogger().log('status:').color('green').log('works').log('yay');
Цепная функция
Когда функция цепляется, вы можете ввести ее с помощью интерфейса:
function say(text: string): ChainableType { ... }
interface ChainableType {
(text: string): ChainableType;
}
say('Hello')('World');
Цепная функция со свойствами/методами
Если функция имеет другие свойства или методы (например, jQuery(str)
vs jQuery.data(el)
), вы можете ввести ее как интерфейс:
interface SayWithVolume {
(message: string): this;
loud(): this;
quiet(): this;
}
const say: SayWithVolume = ((message: string) => { ... }) as SayWithVolume;
say.loud = () => { ... };
say.quiet = () => { ... };
say('hello').quiet()('can you hear me?').loud()('hello from the other side');