Как я могу аннотировать рекурсивные типы в TypeScript?

если у меня есть такая функция:

function say(message: string) {
    alert(message);
    return say;
}

у него есть интересное свойство, которое я могу связывать с ним вызовы:

say("Hello,")("how")("are")("you?");

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

say("Hello")(1)(2)(3)(4)

какую аннотацию типа мне нужно добавить в say функция, чтобы компилятор генерировал предупреждения, когда я передаю недопустимые типы цепным вызовам?

2 ответов


тип, который ссылается на себя, должен иметь имя. Например,

interface OmegaString {
    (message: string): OmegaString;
}

затем вы можете аннотировать say как OmegaString,

function say(message: string): OmegaString {
    alert(message);
    return say;
}

затем следующий код будет вводить-check.

say("Hello,")("how")("are")("you?");

но следующее не будет,

say("Hello")(1)(2)(3)(4)

цепной метод

когда вы используете класс вместо функции, вы можете использовать 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');