Estou tentando implementar uma função minimalista de tipo seguro que faz requisições HTTP contra uma API de servidor tipada, mas estou tendo dificuldades com os tipos. Especificamente, como tornar o terceiro argumento (o payload da requisição HTTP) opcional para as requisições GET
and DELETE
, enquanto o verifica e não é opcional para POST
and PUT
.
Um exemplo simplificado ( TypeScript playground ):
export type Method = 'GET' | 'DELETE' | 'POST' | 'PUT'
// attempt 1:
const httpRequest1 = <M, D>(method: M, url: string, data?: D) =>
'not implemented yet' as any
// attempt 2:
type HttpRequest <M extends Method, D, R=unknown> = M extends 'GET' | 'DELETE'
? (method: M, url: string) => Promise<R>
: (method: M, url: string, data: D) => Promise<R>
const httpRequest2: HttpRequest = (method, url, data) =>
'not implemented yet' as any
// expected usage:
interface Data {
title: string;
}
httpRequest<'POST', Data>('POST', '/', { title: 'test' }) // should typecheck
httpRequest<'POST', Data>('POST', '/', { titleoops: 'test' }) // should NOT typecheck
httpRequest<'POST', Data>('POST', '/') // should NOT typecheck
httpRequest<'GET', undefined>('GET', '/') // should typecheck
Nunca consigo que todas as quatro condições sejam verificadas como esperado ao mesmo tempo. Pelo menos uma delas está sempre quebrada.
Edição: Se você estiver se perguntando, o caso de uso completo, atualizado com a solução funcional .
Você pode usar sobrecargas de função.
Você cria uma função que pode lidar com qualquer caso em tempo de execução e, então, atribui a ela uma série de declarações de tipo que atuam como diferentes sobrecargas.
No local da chamada, o TS garantirá que seus argumentos correspondam a uma das sobrecargas.
Em vez de sobrecargas de funções, você pode usar uma sintaxe de propagação:
Parque infantil