Estou tentando criar uma integração com StompJS no meu projeto que reutiliza conexões e assinaturas para os mesmos tópicos. Estou usando TypeScript e estou tendo um problema em que o TS me dá um erro independentemente do que eu faça. Se eu o suprimir, o código funciona, mas isso parece trapaça.
const RESULT_PARSER_BY_TOPIC = {
'/topic/user': (value: string): { id: string } => JSON.parse(value),
'/topic/game': (value: string): string => value,
'/topic/messages': (value: string): number => parseInt(value, 10),
} as const;
type Topic = keyof typeof RESULT_PARSER_BY_TOPIC;
type ResultParser<_Topic extends Topic> = typeof RESULT_PARSER_BY_TOPIC[_Topic];
type Result<_Topic extends Topic> = ReturnType<ResultParser<_Topic>>;
type Consumer<_Topic extends Topic> = (value: Result<_Topic>) => void;
type Unsubscribable = { unsubscribe: () => void };
type Subscription<_Topic extends Topic> = {
producer: Promise<Unsubscribable>;
consumers: Set<Consumer<_Topic>>;
};
export type Unsubscribe = () => void;
const SUBSCRIPTION_BY_TOPIC: Partial<{
[_Topic in Topic]: Subscription<_Topic> // I think problem is with this type
}> = {};
export function subscribe<_Topic extends Topic>(
topic: _Topic,
callback: Consumer<_Topic>
): Unsubscribe {
if (SUBSCRIPTION_BY_TOPIC[topic] === undefined) {
SUBSCRIPTION_BY_TOPIC[topic] = { // <-- I get the error here
consumers: new Set(),
producer: subscribeToTopic(topic)
} as Subscription<_Topic>;
}
SUBSCRIPTION_BY_TOPIC[topic].consumers.add(callback);
return () => {
// some clean up logic...
};
}
O objetivo de tudo isso é ter um método de assinatura fácil de usar. Ele deve validar se os parâmetros do tópico e do consumidor são compatíveis:
subscribe('/topic/user', (value) => {...}); // should infere that "value" is { id: string }
subscribe('/topic/game', (value) => {...}); // should infere that "value" is string
//if parameter mismatch there should be an error
subscribe('/topic/messages', (value: boolean) => {...}); // <- error: "(value: number) => void" expected
Mas recebo um erro de tipo ao tentar atribuir uma nova Assinatura ao mapeamento de assinaturas (isso ocorre quando alguém assina um novo tópico; caso contrário, reutilizo uma assinatura de tópico existente), que uso para rastrear se o tópico ainda tem assinantes ativos (na lógica de limpeza: se o último assinante cancelar a assinatura, eu cancelo a assinatura do tópico). O erro:
TS2322:
Type Subscription<_Topic> is not assignable to type Partial<{
"/topic/user": Subscription<"/topic/user">;
"/topic/game": Subscription<"/topic/game">;
"/topic/messages": Subscription<"/topic/messages">;
}>[_Topic]
Type Subscription<_Topic> is not assignable to type Subscription<"/topic/user"> & Subscription<"/topic/game"> & Subscription<"/topic/messages">
Type Subscription<_Topic> is not assignable to type Subscription<"/topic/user">
Type _Topic is not assignable to type "/topic/user"
Type "/topic/user" | "/topic/game" | "/topic/messages" is not assignable to type "/topic/user"
Type "/topic/game" is not assignable to type "/topic/user"
Esta linha em particular me confunde:
Type Subscription<_Topic> is not assignable to type Subscription<"/topic/user"> & Subscription<"/topic/game"> & Subscription<"/topic/messages">
É como se o TS não conseguisse resolver que estou tentando atribuir um valor para uma chave específica, e não para qualquer chave no mapeamento.
Tentei converter diferentes partes do código para os tipos correspondentes, mas não obtive resultados significativos. O que "funcionou" foi alterar o tipo "Subscription.consumers" para Set<any>:
type Subscription = {
producer: Promise<Unsubscribable>;
consumers: Set<any>;
};
Mas eu gostaria de evitar isso, o objetivo deste projeto TS é que eu aprenda a usar melhor o TS.