Menu

Operador satisfies em TypeScript: vs anotação de tipo e as

O operador satisfies verifica se um valor corresponde a um tipo sem mudar o tipo inferido do valor. Veja o que ele faz, como se compara a uma anotação de tipo e ao as (o mesmo objeto escrito de três jeitos), como se combina com as const e por que combina com objetos de configuração.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

value satisfies Type verifica em tempo de compilação se value corresponde a Type, e depois deixa em paz o tipo próprio do valor, que é mais preciso. Uma anotação substituiria esse tipo preciso por Type; o satisfies valida sem alargar.

O satisfies continua fazendo a verificação: uma cor faltando, uma chave digitada errado como bleu ou um valor como true gera erro de compilação naquela linha. Ele existe desde o TypeScript 4.9 e, como toda anotação de tipo, é removido do JavaScript gerado.

O problema que o satisfies resolve

Com uma anotação de tipo, o tipo da variável é a anotação. O compilador esquece o que viu no literal. Aqui a mesma paleta é anotada em vez disso, e agora o TypeScript não sabe mais que green é uma string:

O compilador mostra:

index.ts(11,27): error TS2339: Property 'toUpperCase' does not exist on type 'Color'.
  Property 'toUpperCase' does not exist on type '[number, number, number]'.

Antes do TypeScript 4.9 as opções eram: anotar e fazer narrowing à mão em todo lugar (typeof palette.green === "string"), ou dispensar a anotação e perder a verificação. O satisfies dá as duas coisas. Troque : Record<ColorName, Color> por satisfies Record<ColorName, Color> depois da chave de fechamento e o código roda.

satisfies vs anotação de tipo vs as

O mesmo objeto de configurações, escrito de três jeitos:

O as deixou passar o lang que faltava, e asserted.lang é undefined em tempo de execução enquanto o tipo dele diz string. Remova lang das outras duas linhas e as duas falham com TS2741, Property 'lang' is missing in type ....

Anotação const x: T = vAssertion v as Tv satisfies T
Propriedades faltandoerropermitidoerro
Propriedades extras (objeto literal)erropermitidoerro
Tipo errado de propriedadeerrosó se os tipos não se sobrepuseremerro
Tipo de x depoisTTo tipo inferido de v
Literal types ("dark", 8080)alargados para Talargados para Tmantidos onde T os permite
Chaves de um Record<string, ...>qualquer string (erros de digitação compilam)qualquer stringexatamente as chaves escritas
Efeito em tempo de execuçãonenhumnenhumnenhum

Regra prática: anote quando você quer que a variável tenha o tipo declarado (um valor que você vai reatribuir, uma API pública), e use satisfies quando você quer uma verificação, mas o tipo próprio do valor é mais útil.

Pegando erros em objetos literais

O satisfies faz a verificação completa de compatibilidade, incluindo a verificação de propriedades extras, então erros de digitação em chaves são erros:

type Route = { path: string; method: "GET" | "POST" };

const home = { path: "/", metod: "GET" } satisfies Route;
// error TS2561: Object literal may only specify known properties, but 'metod' does not exist in type 'Route'. Did you mean to write 'method'?

A verificação também dá ao literal um tipo contextual, como faz uma anotação. Isso importa de dois jeitos. Literais de string são mantidos como literal types quando o tipo de destino os espera: { path: "/", method: "GET" } satisfies Route tem method: "GET", enquanto o mesmo objeto sem anotação inferiria method: string. E os parâmetros de callbacks são inferidos a partir do tipo de destino:

As chaves de um Record continuam conhecidas

Um uso comum é uma tabela de consulta. Anotada como Record<string, T>, toda string é uma chave válida e um erro de digitação compila, retornando undefined em tempo de execução. Com satisfies, os valores continuam sendo verificados contra T, mas o tipo da variável lista exatamente as chaves que você escreveu:

keyof typeof endpoints só é útil porque as chaves sobreviveram. Com a anotação seria apenas string.

Para exigir um conjunto fixo de chaves, use satisfies com um Record sobre uma union: satisfies Record<"dev" | "prod", string> aponta um prod faltando com TS2741 e um staging desconhecido com TS2353.

as const satisfies

as const e satisfies se combinam. Escreva o as const primeiro: ele deixa o valor readonly em todos os níveis com literal types, e depois o satisfies verifica esse valor exato.

Cada rota é verificada contra Route (um method: "PUT" seria erro), e a tupla de literal types continua disponível, então Path é uma union dos caminhos reais. Use readonly Route[] (ou ReadonlyArray<Route>) como destino, já que um array as const é readonly.

Objetos de configuração

Configuração é onde o satisfies mostra seu valor: o formato precisa estar certo, e o código em outros lugares quer os valores precisos.

Esqueça a entrada production, erre a grafia de logLevel ou escreva logLevel: "verbose" e o compilador aponta a linha exata. O mesmo padrão serve para arquivos *.config.ts: export default { ... } satisfies SomeConfig verifica o arquivo inteiro enquanto o objeto exportado mantém os valores literais.

Quando não usar satisfies

  • A variável vai ser reatribuída. let cfg = { port: 3000 } satisfies { port: number | string } dá a cfg o tipo { port: number }, então um cfg = { port: "80" } depois falha (TS2322). Anote as variáveis que você pretende mudar.
  • Você quer o tipo declarado de propósito. Para o valor de retorno de uma função ou uma constante exportada que faz parte de uma API, o tipo da anotação é o contrato, e expor o literal type exato pode tornar mudanças futuras incompatíveis.
  • O valor não é um literal. O satisfies brilha em objetos e arrays literais. Em uma variável ou no resultado de uma chamada, ele é uma simples verificação de compatibilidade, que uma anotação já oferece.

Perguntas frequentes

O que o satisfies faz no TypeScript?

expression satisfies Type verifica em tempo de compilação que a expressão pode ser atribuída a Type, apontando propriedades faltando, propriedades extras e tipos de valor errados, e depois deixa inalterado o tipo inferido da própria expressão. Você tem a segurança de uma anotação e a precisão da inferência. Ele é apagado da saída JavaScript.

Qual é a diferença entre satisfies e uma anotação de tipo?

Os dois verificam o valor. Uma anotação (const x: T = ...) então dá à variável o tipo T, esquecendo o que o compilador sabia sobre o valor (literal types, qual membro da union é cada propriedade, quais chaves existem). satisfies T mantém o tipo inferido, então se sabe que x.someKey existe e uma propriedade string | number que guarda uma string tem o tipo string.

Qual é a diferença entre satisfies e as no TypeScript?

O as é uma assertion: sobrescreve o tipo e verifica quase nada, então propriedades faltando passam despercebidas. O satisfies é uma verificação: o valor precisa realmente corresponder ao tipo, e o tipo inferido dele é mantido. Quando os dois compilariam, o satisfies é a escolha mais segura.

O que significa as const satisfies?

Aplica os dois: as const deixa o valor readonly em todos os níveis com literal types, e depois o satisfies verifica esse resultado contra um tipo. Escreva o as const primeiro: const routes = [...] as const satisfies readonly Route[];. A variável mantém os literal types exatos para uso posterior, e uma entrada errada continua sendo erro de compilação.

Qual versão do TypeScript adicionou o satisfies?

O TypeScript 4.9, lançado em novembro de 2022. É sintaxe simples e apagável, então também roda com o type stripping nativo do Node, e todas as versões atuais do TypeScript (incluindo a 7) o suportam.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR