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 = v | Assertion v as T | v satisfies T | |
|---|---|---|---|
| Propriedades faltando | erro | permitido | erro |
| Propriedades extras (objeto literal) | erro | permitido | erro |
| Tipo errado de propriedade | erro | só se os tipos não se sobrepuserem | erro |
Tipo de x depois | T | T | o tipo inferido de v |
Literal types ("dark", 8080) | alargados para T | alargados para T | mantidos onde T os permite |
Chaves de um Record<string, ...> | qualquer string (erros de digitação compilam) | qualquer string | exatamente as chaves escritas |
| Efeito em tempo de execução | nenhum | nenhum | nenhum |
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á acfgo tipo{ port: number }, então umcfg = { 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
satisfiesbrilha 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.