Универсальная функция TypeScript может работать только при перегрузке функции с несколькими сигнатурами
Я определяю интерфейс с общей функцией, такой как:
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');
}
Однако, когда я хочу определить IState только с одной подписью отправки, я получил ошибки компиляции:
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 ответ
Я согласен с Тицианом Черниковой-Драгомиром, что это похоже на ошибку компилятора. Определение 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();
}
}
Глядя на код теста, который вы включили, вы в любом случае проверяете точный тип неизвестного типа "Оба", поэтому может показаться, что функциональность не теряется, даже если вы просто определите отдельные методы в классах Left и Right для каждое действие. э:
class Left {
turnRight(...) {
return new Right();
}
keepGoin(...) {
return new Left();
}
}
в отличие от использования общего метода "send" для каждого действия.