Как выразить смешанную специальную и параметрическую полиморфность в машинописи?
Я не уверен, что я сейчас описываю квесты в заголовке. То, что я пытаюсь спросить, вытекает из следующего требования.
Я пытаюсь сделать реферат для состояний конечных автоматов и приходит со следующим определением (в машинописи)
interface IState {
send<T, E>(message: T, callback?:(event: E)=>void): IState;
}
Я пытаюсь выразить, что состояние конечного автомата должно быть в состоянии принимать сообщения и возвращать новое состояние с необязательным обратным вызовом для обработки событий s во время перехода.
При реализации этого интерфейса в конкретных состояниях, есть проблема.
Например, я пытаюсь создать простой конечный автомат только с двумя состояниями, ВЛЕВО и ВПРАВО, с тремя возможными сообщениями: включение, поворот налево, поворот направо. Следующая таблица показывает их отношения.
Ключевым моментом является то, что я хочу ограничить состояние LEFT, только принимать сообщения о включении и повороте вправо, в то время как отправка поворота влево в LEFT, как ожидается, будет ошибкой компиляции.
Я попытался реализовать как показано в машинописи 3.4.5.
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();
}
}
Реализация не имеет ошибки компиляции, и автозаполнение делает работу, как ожидалось. Но так как это выглядит странно, я задал вопрос: универсальная функция TypeScript может работать только при перегрузке функции более чем с одной подписью.
Спасибо за добрые ответы на этот вопрос, я понимаю, что присваивать функции перегрузки универсальным функциям неправильно. Но тогда как я могу выразить общий интерфейс состояния, сохраняя при этом определенное состояние, принимая только нужные типы сообщений?
Еще одна абстракция, которую я могу придумать,
interface IState<T, E, R extends IState<?, ?, ?>> {
send(message: T, callback?:(event: E)=>void): R;
}
Но тип возврата рекурсивный, и я не знаю, что заполнить для этих трех квестовых отметок выше.
Более простая версия может быть
interface IState<T, E> {
send(message: T, callback?:(event: E)=>void): IState<any, any>;
}
Кажется, он ведет себя как-то, кроме раздражающего любого в типе возврата.
interface IState {
send<T, E>(message: T, callback?:(event: E)=>void): IState;
}
Я нашел, возможно, связанную проблему в GitHub по поводу общей стоимости.
Этот вопрос хорошо определен?
Если это правда, есть ли правильное решение в списке методов выше?
Если неверно, какое правильное решение?
1 ответ
Я думаю, что лучший вариант это interface IState<T, E, R extends IState<?, ?, ?>>
, Вопросительные знаки можно заменить на any
нас не волнует, что это за государство, только то, что это какое-то государство.
interface IState<T, E, R extends IState<any, any, any>> {
send(message: T, callback?: (event: E) => void): R;
}
class Left implements IState<'go-on', never, Left>, IState<'turn-right', never, Right>{
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<'go-on', never, Right>, IState<'turn-left', never, Left> {
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();
}
}
let left = new Left();
let left_go_on: Left = left.send("go-on")
let left_turn_right: Right = left.send("turn-right")
left.send("turn-left") // error
let right = new Right();
let right_go_on: Right = right.send("go-on")
let right_turn_right: Left = right.send("turn-left")
right.send("turn-right") // error
Или если вы хотите иметь только на интерфейсе в implements
пункт это также работает:
interface IState<T extends [any, any, IState<[any, any, any]>]> {
send: T extends T ? ((message: T[0], callback?: (event: T[1]) => void) => T[2]) : never
}
class Left implements IState<['go-on', never, Left] | ['turn-right', never, Right]>{
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<['go-on', never, Right] | ['turn-left', never, Left]> {
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();
}
}
let left = new Left();
let left_go_on: Left = left.send("go-on")
let left_turn_right: Right = left.send("turn-right")
left.send("turn-left") // error
let right = new Right();
let right_go_on: Right = right.send("go-on")
let right_turn_right: Left = right.send("turn-left")
right.send("turn-right") // error