Универсальная функция TypeScript может работать только при перегрузке функции с несколькими сигнатурами - PullRequest
0 голосов
/ 19 июня 2019

Я определяю интерфейс с универсальной функцией, такой как:

export interface IState {
  send: <I, E>(message: I, callback?: (e: E) => void) => IState;
}

Он отлично работает для классов с несколькими сигнатурами:

class Left implements IState {
  send(m: 'go-on', cb?: (e: never) => void): Left;
  send(m: 'turn-right', cb?: (e: never) => void): Right;
  send(m: 'go-on' | 'turn-right', cb?: any) {
    return m === 'go-on' ? new Left() : new Right();
  }
}

class Right implements IState {
  send(m: 'go-on', cb?: (e: never) => void): Right;
  send(m: 'turn-left', cb?: (e: never) => void): Left;
  send(m: 'go-on' | 'turn-left', cb?: any) {
    return m === 'go-on' ? new Right() : new Left();
  }
}

type Both = Left | Right;

function test(l: Both) {
  if (l instanceof Left) {
    l.send('turn-right')
      .send('turn-left')
      .send('turn-right')
      .send('turn-left');
  }
  const l2 = new Left();
  l2.send('go-on')
    .send('turn-right')
    .send('turn-left');
  l2.send('turn-right').send('turn-left');
}

Однако, когда я хочу определитьIСостоящий только с одной подписью отправки, я получил ошибки компиляции:

class CountState implements IState {
  constructor(public readonly data: number) {}
  // send(m: 'inc', cb?: (e: number) => void): CountState;
  // send(m: 'inc', cb?: (e: number) => void): CountState;
  send(m: 'inc', cb?: (e: number) => void): CountState {
    const result = this.data + 1;
    if (cb !== undefined) {
      cb(result);
    }
    return new CountState(this.data + 1);
  }
}

Ошибка при методе отправки:

Свойство 'send' в типе 'CountState' не может быть назначено одному и тому жесвойство в базовом типе 'IState'.Тип '(m: "inc", cb ?: ((e: number) => void) | undefined) => CountState' нельзя назначить типу '(сообщение: I, обратный вызов ?: ((e: E) => void) | undefined) => IState '.Типы параметров 'm' и 'message' несовместимы.Тип 'I' нельзя назначить типу '' inc ''. Ts (2416)

Если я добавлю эти две строки комментариев, например,

class CountState implements IState {
  constructor(public readonly data: number) {}
  send(m: 'inc', cb?: (e: number) => void): CountState;
  send(m: 'inc', cb?: (e: number) => void): CountState;
  send(m: 'inc', cb?: (e: number) => void): CountState {
    const result = this.data + 1;
    if (cb !== undefined) {
      cb(result);
    }
    return new CountState(this.data + 1);
  }
}

Он компилируется нормально, но это выглядит действительно странно.Как я могу это исправить?

1 Ответ

2 голосов
/ 19 июня 2019

Я согласен с Тицианом Черниковой-Драгомиром, что это похоже на ошибку компилятора. Определение IState в основном гласит, что свойство «send» - это функция, которую можно вызывать с любыми типами «message», а параметр обратного вызова: «e» также может иметь любой тип.

export interface IState {
    send: <I, E>(message: I, callback?: (e: E) => void) => IState;
}

Между тем, в вашем примере использования вы явно перечисляете возможные типы, что противоречит определению интерфейсов. Странно, если это проходит компиляцию.

class Left implements IState {
    send(m: 'go-on', cb?: (e: never) => void): Left;
    send(m: 'turn-right', cb?: (e: never) => void): Right;
    send(m: 'go-on' | 'turn-right', cb?: any) {
        return m === 'go-on' ? new Left() : new Right();
    }
}

Глядя на код теста, который вы включили, вы в любом случае проверяете точный тип неизвестного типа «Оба», поэтому может показаться, что функциональность не будет потеряна, даже если вы просто определите отдельные методы в классах. и право на каждое действие. э:

class Left {
    turnRight(...) {
        return new Right();
    }
    keepGoin(...) {
        return new Left();
    }
}

вместо использования общего метода send для каждого действия.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...