/** * Parte 7 — Tipar código assíncrono. * * npx tsx src/08-tipagem.ts * npm run check # é aqui que esta parte é verificada de verdade * * Os blocos marcados com `@ts-expect-error` documentam erros que o compilador * DEVE apontar. Se algum deles parar de ser erro, o `npm run check` falha — * o arquivo é, ao mesmo tempo, exemplo e teste. */ import { buscarLivro, type Livro } from './biblioteca.js'; // --------------------------------------------------------------------------- // 1. `Awaited` modela o desembrulhamento recursivo realizado por `await`. // --------------------------------------------------------------------------- async function anoDoLivro(id: number): Promise { const livro: Livro = await buscarLivro(id); // Promise -> Livro return livro.ano; } type RetornoBruto = ReturnType; // Promise type RetornoUtil = Awaited; // number const conferencia: RetornoUtil = 1956; // compila porque RetornoUtil é number // @ts-expect-error uma função async nunca devolve o valor cru const erroDeTipo: number = anoDoLivro(1); // --------------------------------------------------------------------------- // 2. Genéricas assíncronas: o tipo atravessa a espera. // --------------------------------------------------------------------------- async function primeiroQueResponder(tarefas: Array>): Promise { return Promise.any(tarefas); } async function usoDaGenerica(): Promise { const livro = await primeiroQueResponder([buscarLivro(1), buscarLivro(2)]); return livro.titulo; // o compilador sabe que é Livro, não any } // --------------------------------------------------------------------------- // 3. `catch` recebe `unknown` — e isso é uma boa notícia. // --------------------------------------------------------------------------- /** * Qualquer valor pode ser lançado em JavaScript: `throw 'texto'` é legal. * Por isso `useUnknownInCatchVariables` (parte do `strict`) tipa o parâmetro * do catch como `unknown`, obrigando a provar o que ele é. */ export function mensagemDeErro(erro: unknown): string { if (erro instanceof Error) return erro.message; if (typeof erro === 'string') return erro; return 'erro desconhecido'; } async function comCatchTipado(id: number): Promise { try { return (await buscarLivro(id)).titulo; } catch (erro) { // @ts-expect-error `erro` é unknown: não dá para ler .message direto console.log(erro.message); return mensagemDeErro(erro); } } // --------------------------------------------------------------------------- // 4. Dado que veio de fora não tem tipo — tem uma promessa de tipo. // --------------------------------------------------------------------------- /** * `resposta.json()` devolve `any` na biblioteca padrão. Aceitar esse `any` * apaga a verificação de tipos justamente na fronteira em que ela é mais * necessária: o único ponto do programa em que o dado não foi escrito por você. * * O caminho é receber como `unknown` e validar. */ function ehLivro(valor: unknown): valor is Livro { if (typeof valor !== 'object' || valor === null) return false; const candidato = valor as Record; return ( typeof candidato.id === 'number' && typeof candidato.titulo === 'string' && typeof candidato.autor === 'string' && typeof candidato.ano === 'number' && typeof candidato.isbn === 'string' ); } /** Faz o papel de `await fetch(...).then((r) => r.json())`, sem rede. */ async function receberDaApi(carga: string): Promise { return JSON.parse(carga) as unknown; } async function lerLivroDaApi(carga: string): Promise { const dado = await receberDaApi(carga); if (!ehLivro(dado)) { throw new TypeError('resposta fora do contrato de Livro'); } return dado; // a partir daqui, e só a partir daqui, é Livro } // --------------------------------------------------------------------------- async function principal(): Promise { console.log('conferência Awaited :', conferencia); console.log('genérica :', await usoDaGenerica()); console.log('catch tipado :', await comCatchTipado(99)); const valido = '{"id":3,"titulo":"Vidas Secas","autor":"Graciliano Ramos","ano":1938,"isbn":"9788501069665"}'; console.log('validado :', (await lerLivroDaApi(valido)).titulo); try { await lerLivroDaApi('{"id":3,"titulo":"Vidas Secas"}'); } catch (erro) { console.log('fora do contrato :', mensagemDeErro(erro)); } console.log('erro de tipo (só compilação):', typeof erroDeTipo); } void principal();