#typescript #typescript-generics
#typescript #typescript-дженерики
Вопрос:
Моя цель здесь — сгруппировать запросы и ответы по действию, поэтому, если у меня есть (как в приведенном ниже коде) два действия: Play
и Stay
, у меня будет пара запрос / ответ, представляющая запрос / ответ Play
действия и то же самое для Stay
действия.
Я хочу выразить ограничение, заключающееся в том, что могут быть связаны только запрос и ответ на одно и то же действие (см. Playground)
type ActionName = "Play" | "Stay";
type RequestD<A extends ActionName, B extends Record<string, unknown>> = {
_tag: "Request";
_action: A;
} amp; B;
type ResponseD<A extends ActionName, B extends Record<string, unknown>> = {
_tag: "Response";
_action: A;
} amp; B;
type PlayRequest = RequestD<"Play", { a: number }>;
type PlayResponse = ResponseD<"Play", { b: number }>;
type StayRequest = RequestD<"Stay", { c: number }>;
type StayResponse = ResponseD<"Stay", { d: number }>;
type ActionOf<X> = X extends RequestD<infer A, any>
? A
: X extends ResponseD<infer A, any>
? A
: never;
// Problem:
// X is "Play" | "Stay"`
// Is there a way to get "Play"?
type X = ActionOf<PlayRequest>;
type TagOf<X> = X extends RequestD<any, any>
? "Request"
: X extends ResponseD<any, any>
? "Response"
: never;
// Problem:
// Y1 is "Request" and that's expected
// Y2 is "Request" as well and that's unexpected, how is this possible? How can I fix it?
type Y1 = TagOf<PlayRequest>;
type Y2 = TagOf<PlayResponse>;
type RoundTrip<
A extends ActionName,
Req extends RequestD<A, any>,
Res extends ResponseD<A, any>
> = {
_action: A;
request: Req;
response: Res;
};
type PlayRT = RoundTrip<"Play", PlayRequest, PlayResponse>;
// Problem:
// The following should be wrong because the first request is related to action "Stay", not "Play"
// Is there a way to enforce that?
type WrongRT = RoundTrip<"Play", StayRequest, PlayResponse>;
Мои вопросы, изложенные в приведенном выше коде:
- Как я могу связать действие «Воспроизвести» с запросом / ответом, чтобы использовать его на уровне типа?
- Почему я не могу различать запрос / ответ, даже если эти типы соответствующим образом помечены (
_tag
поле)? - Как я могу ограничить действие, чтобы оно было тем же действием в запросе / ответе по
RoundTrip
типу?
Ответ №1:
Проблема на самом деле довольно проста, вы используете any
в качестве второго параметра в Request
и Response
.
Если вы посмотрите на свое определение:
type RequestD<A extends ActionName, B extends Record<string, unknown>> = {
_tag: "Request";
_action: A;
} amp; B; // note that you are extending the entire object.
Затем, когда вы затем определите дженерик как таковой:
type TagOf<X> = X extends RequestD<any, any>
? "Request"
: X extends ResponseD<any, any>
? "Response"
: never;
Это приведет к сбою, поскольку any
включает объект, например, { _tag: "Request" }
для ответа или { _tag: "Response" }
для запроса.
Это в основном не дает typescript ничего для работы. Чтобы исправить это, вам просто нужно изменить any
на что-то вроде пустого объекта {}
.
Комментарии:
1. Спасибо! Это ответ на вопрос номер 2, есть какие-либо подсказки по другим?
2. Подождите, это также устраняет проблему № 3, остается только номер 1, какие-либо предложения?
3. Неважно, вы правы во всех отношениях, используя
RequestD<any, {}>
иResponseD<any, {}>
,ActionOf
я получаю правильный ответ. Спасибо!