Uma configuração escrita como objeto pode obedecer ao tipo geral e ainda perder informação útil para quem vai consumi-la. O caminho inverso também é ruim: uma asserção pode silenciar uma chave errada antes que o código rode.
O operador satisfies resolve esse ponto para valores definidos no próprio código. Ele confere se a expressão atende a um contrato, mas mantém a forma específica inferida pela expressão. Isso torna o operador útil para configurações, mapas de rotas, feature flags e registros de tools.
Neste artigo, a ideia é aplicada a um registro pequeno de tools. A fixture local foi compilada com TypeScript 5.9.3: a versão correta passa, e a troca deliberada de search por seach gera erro de compilação. Não há benchmark nem experiência de produção sendo atribuída ao exemplo.

Resposta curta
- Use satisfies quando um valor literal precisa cumprir um contrato sem perder suas chaves e valores inferidos.
- Combine as const satisfies quando o registro também deve preservar literais e ficar somente leitura.
- Use uma anotação quando o tipo declarado, mais amplo, é o contrato que o restante do programa deve enxergar.
- Não use satisfies como validação de JSON, variáveis de ambiente ou argumentos que chegaram em runtime.
O que satisfies verifica?
satisfies é uma verificação em tempo de compilação. Dado um valor e um tipo à direita, o TypeScript verifica a compatibilidade entre eles e continua usando o tipo inferido do valor no restante do código.
O operador foi introduzido no TypeScript 4.9. A documentação oficial usa o mesmo princípio para detectar uma chave escrita incorretamente em um objeto e, ao mesmo tempo, manter propriedades específicas acessíveis. A fonte é a documentação de lançamento do TypeScript 4.9, consultada em 12 de outubro de 2026.
Considere um contrato de configuração:
type ToolDefinition = {
description: string;
run(input: { query: string }): Promise<unknown>;
};
type ToolName = "search" | "read";
const tools = {
search: {
description: "Search the indexed documents.",
run: async (input: { query: string }) => ({ query: input.query }),
},
read: {
description: "Read one indexed document.",
run: async (input: { query: string }) => ({ query: input.query }),
},
} as const satisfies Record<ToolName, ToolDefinition>;
type AvailableTool = keyof typeof tools;
const selected: AvailableTool = "search";
const selectedTool = tools[selected];
O contrato exige as duas chaves de ToolName e verifica a forma de cada definição. Ao mesmo tempo, keyof typeof tools produz "search" | "read" a partir do valor escrito. O tipo do registro e o tipo específico do valor trabalham juntos, em vez de um substituir o outro.
satisfies não é o mesmo que anotação ou as
As três formas parecem semelhantes, mas comunicam intenções diferentes.
Anotação: o consumidor vê o tipo declarado
const tools: Record<ToolName, ToolDefinition> = {
search: {
description: "Search the indexed documents.",
run: async (input) => ({ query: input.query }),
},
read: {
description: "Read one indexed document.",
run: async (input) => ({ query: input.query }),
},
};
Essa forma é boa quando o tipo amplo é justamente a interface que você quer expor. O consumidor passa a trabalhar com Record<ToolName, ToolDefinition>. Literais mais específicos da expressão deixam de ser o centro da API.
Asserção: o código pede confiança
const tools = {
seach: {
description: "Search the indexed documents.",
run: async (input: { query: string }) => ({ query: input.query }),
},
} as Record<ToolName, ToolDefinition>;
as diz ao compilador para tratar a expressão como outro tipo. Ele não executa uma validação em runtime. A documentação de Everyday Types, consultada em 12 de outubro de 2026, também deixa claro que asserções são removidas durante a compilação.
Neste exemplo, as é uma escolha ruim porque pode esconder a diferença entre seach e search. Uma asserção pode ser necessária em uma fronteira que o compilador não consegue provar, mas não deve ser o atalho padrão para conferir um objeto literal.
satisfies: confira sem trocar o tipo do valor
const tools = {
seach: {
description: "Search the indexed documents.",
run: async (input: { query: string }) => ({ query: input.query }),
},
} as const satisfies Record<ToolName, ToolDefinition>;
Esse trecho falha porque seach não está em ToolName e search ficou ausente. A fixture usada neste post produziu exatamente esse tipo de diagnóstico depois da troca deliberada da chave.
Como usar as const satisfies em um registro
O padrão é mais valioso quando o objeto tem duas responsabilidades:
- cumprir uma forma mínima compartilhada;
- continuar sendo uma fonte de tipos específicos para o programa.
Isso aparece em registros de comandos, rotas, eventos, feature flags e tools de agentes. Um registro de tools pode validar sua própria documentação e assinatura durante o build. Para entender a parte que descreve a seleção de uma tool para um agente, veja como escrever descrições de tools para agentes de IA. O satisfies cobre o contrato do registro escrito no código; ele não decide se o modelo escolheu a tool correta.
O mesmo limite aparece na etapa seguinte. Depois que uma tool foi escolhida, os argumentos ainda precisam ser conferidos antes da execução. Esse é um problema de runtime, tratado em como validar tool calls em TypeScript. Separar os dois contratos evita acreditar que uma verificação estática validou uma entrada externa.
Se o registro for indexado por uma chave dinâmica, a configuração do compilador também participa da segurança. noUncheckedIndexedAccess torna visível que uma leitura como tools[name] pode não encontrar uma entrada. O operador satisfies ajuda a construir o registro; ele não prova que a chave recebida é válida.
Como verificar o exemplo localmente
Salve o primeiro exemplo em registry.ts e crie um tsconfig.json mínimo:
{
"compilerOptions": {
"strict": true,
"noEmit": true,
"target": "ES2022",
"types": []
},
"files": ["registry.ts"]
}
Depois execute:
npx -y -p typescript@5.9.3 tsc -p tsconfig.json --pretty false
O comando deve terminar sem diagnóstico quando o registro contém search e read. Para verificar o contrato, troque temporariamente search por seach. O compilador deve reclamar da propriedade desconhecida e também da chave esperada que desapareceu. Corrija a chave e execute o comando novamente.
Esse teste prova o comportamento do compilador para um valor literal. Não prova que um arquivo JSON recebido por HTTP, uma variável de ambiente ou os argumentos de uma tool são válidos. Para essa fronteira, use um parser ou uma validação explícita antes de transformar a entrada em um tipo confiável. Validar JSON externo antes de usar em TypeScript detalha esse outro problema.
Quando não usar satisfies
Não coloque satisfies em todo objeto automaticamente. Há três sinais de que outra escolha é mais clara:
- o consumidor deve enxergar somente uma interface pública ampla, então uma anotação comunica melhor a intenção;
- o valor chega em runtime, então primeiro é preciso validar os dados e só depois usá-los com segurança;
- a diferença entre as chaves permitidas e as chaves reais é parte da regra de negócio, então um tipo Record rígido pode ser o contrato errado.
Também vale evitar a combinação com uma asserção que desfaz a checagem, como as unknown as Record<ToolName, ToolDefinition>. Se o contrato não pode ser satisfeito, o erro deve levar você a revisar o dado, o tipo ou a fronteira de validação.
Fontes e limites
As regras sobre satisfies, asserções e preservação da inferência foram verificadas na documentação de lançamento do TypeScript 4.9 e no Handbook sobre tipos do dia a dia. O suporte a @satisfies em comentários JSDoc também aparece nas notas do TypeScript 5.0, mas não é necessário para o exemplo deste artigo.
A fixture foi compilada localmente com TypeScript 5.9.3, em 12 de outubro de 2026, usando strict, noEmit, target: ES2022 e types: []. A imagem é uma ilustração explicativa gerada para mostrar o fluxo do contrato. A pesquisa, a redação e a revisão tiveram assistência de IA; a assistência não executou o compilador nem substitui as fontes ou a responsabilidade editorial do autor.