TypedDict no Python: dicionários tipados

Publicado em: 28/08/2026
Tempo de leitura: 5 minutos
Close-up of hands typing on a laptop keyboard, Python book in sight, coding in progress.

Dicionários são flexíveis, mas essa flexibilidade pode esconder erros. Um payload pode exigir id, nome e ativo, enquanto o código trata tudo como dict[str, object]. Nesse tipo genérico, o analisador não sabe quais chaves existem, quais são opcionais nem qual tipo corresponde a cada campo. typing.TypedDict resolve esse problema ao descrever a estrutura esperada de um dicionário sem alterar seu comportamento em runtime.

Neste guia, você aprenderá a criar TypedDict, definir campos obrigatórios e opcionais, usar NotRequired e Required, compor esquemas, representar respostas de APIs, refinar tipos e entender os limites entre validação estática e validação real de dados.

O que é TypedDict?

Um TypedDict é uma declaração de tipo para dicionários com chaves conhecidas. Em execução, o valor continua sendo um dict normal.

from typing import TypedDict

class Usuario(TypedDict):
    id: int
    nome: str
    ativo: bool

usuario: Usuario = {
    "id": 10,
    "nome": "Ana",
    "ativo": True,
}

Mypy ou Pyright pode detectar uma chave ausente, um nome digitado incorretamente ou um valor com tipo incompatível.

usuario_errado: Usuario = {
    "id": "10",       # esperado int
    "nome": "Ana",
    "ativo": True,
}

O Python não impede essa atribuição durante a execução. A proteção aparece quando o projeto executa um verificador estático.

Por que não usar apenas dict[str, object]?

dict[str, object] informa apenas que as chaves são strings e os valores podem ser qualquer objeto. Ele não expressa quais chaves são válidas nem relaciona cada chave ao seu tipo.

def mostrar(usuario: dict[str, object]) -> str:
    return usuario["nome"].upper()  # object não garante upper()

Com TypedDict, o analisador sabe que usuario["nome"] é str.

def mostrar(usuario: Usuario) -> str:
    return usuario["nome"].upper()

Esse nível de precisão complementa os conceitos do guia sobre type hints em Python.

Campos opcionais com total=False

Por padrão, todas as chaves são obrigatórias. Use total=False quando todas puderem faltar.

class AtualizacaoUsuario(TypedDict, total=False):
    nome: str
    ativo: bool
    email: str

Esse formato é útil para operações PATCH, nas quais o cliente envia somente os campos que deseja alterar.

def atualizar(id: int, dados: AtualizacaoUsuario) -> None:
    if "nome" in dados:
        print(dados["nome"])

Uma chave opcional não significa que o valor aceita None. São conceitos diferentes: a chave pode não existir; se existir, deve conter o tipo declarado.

NotRequired e Required

Quando apenas alguns campos são opcionais, use NotRequired. Quando a classe usa total=False e uma chave precisa continuar obrigatória, use Required.

from typing import NotRequired, Required, TypedDict

class Perfil(TypedDict):
    id: int
    nome: str
    apelido: NotRequired[str]
    foto: NotRequired[str]

class EventoParcial(TypedDict, total=False):
    tipo: Required[str]
    payload: object
    origem: str

Esses marcadores deixam o contrato legível e evitam criar várias classes apenas para controlar obrigatoriedade.

Herança e composição

TypedDict pode herdar de outro TypedDict.

class Entidade(TypedDict):
    id: int

class Produto(Entidade):
    nome: str
    preco: float
    estoque: int

A herança ajuda a reutilizar campos comuns, mas não deve virar uma hierarquia complexa. Em APIs, pequenos esquemas específicos costumam ser mais claros do que um modelo gigante usado para criação, leitura, atualização e persistência.

TypedDict funcional

A sintaxe funcional é útil quando as chaves não são identificadores Python válidos.

Cabecalhos = TypedDict(
    "Cabecalhos",
    {
        "content-type": str,
        "x-request-id": str,
    },
)

Em código novo, a sintaxe de classe é normalmente mais fácil de ler e documentar.

Representando respostas de APIs

TypedDict funciona bem para payloads internos ou respostas externas já validadas.

class Endereco(TypedDict):
    cidade: str
    estado: str
    cep: str

class ClienteAPI(TypedDict):
    id: int
    nome: str
    endereco: Endereco
    tags: list[str]

def nome_cidade(cliente: ClienteAPI) -> str:
    return cliente["endereco"]["cidade"]

Não anote diretamente o resultado de response.json() como se ele já fosse confiável. Dados externos precisam de validação antes de receber um tipo mais específico.

TypedDict não valida em runtime

Esta declaração não converte valores, não rejeita chaves extras e não gera mensagens de validação. Se o programa lê JSON, formulários ou filas, use validação explícita, dataclasses com parsing, Pydantic, attrs ou outra ferramenta apropriada.

import json

texto = '{"id": "dez", "nome": 99, "ativo": true}'
dados = json.loads(texto)
# dados existe em runtime, mesmo incompatível com Usuario

Uma abordagem segura valida primeiro e só depois trata o resultado como Usuario.

Refinando chaves opcionais

Quando uma chave usa NotRequired, verifique sua presença antes de acessar.

class Resultado(TypedDict):
    valor: int
    aviso: NotRequired[str]

def imprimir(resultado: Resultado) -> None:
    print(resultado["valor"])
    if "aviso" in resultado:
        print(resultado["aviso"])

dict.get() também pode ser usado, mas o tipo normalmente inclui None, exigindo tratamento adequado.

Chaves extras e compatibilidade

TypedDict segue regras estruturais. Um dicionário tipado com campos adicionais pode ser aceito em alguns contextos quando contém tudo que o consumidor exige. Porém, atribuições, mutabilidade e obrigatoriedade tornam as regras mais rigorosas do que uma simples comparação de conjuntos.

Evite depender de detalhes surpreendentes de compatibilidade. Defina contratos pequenos e use o verificador escolhido no CI para manter comportamento consistente.

TypedDict como parâmetro e retorno

class NovoPedido(TypedDict):
    cliente_id: int
    itens: list[int]
    cupom: NotRequired[str]

class PedidoCriado(TypedDict):
    id: int
    status: str
    total: float

def criar_pedido(dados: NovoPedido) -> PedidoCriado:
    return {
        "id": 501,
        "status": "criado",
        "total": 149.90,
    }

A assinatura documenta a fronteira da função e melhora autocomplete, revisão e refatoração.

Usando Literal para discriminar variantes

TypedDict combina bem com Literal para representar mensagens diferentes.

from typing import Literal

class Sucesso(TypedDict):
    tipo: Literal["sucesso"]
    valor: int

class Falha(TypedDict):
    tipo: Literal["falha"]
    erro: str

Resposta = Sucesso | Falha

def processar(resposta: Resposta) -> str:
    if resposta["tipo"] == "sucesso":
        return str(resposta["valor"])
    return resposta["erro"]

O campo discriminador permite que o analisador refine automaticamente a variante.

Readonly e evolução de esquemas

Versões recentes do ecossistema de typing também oferecem recursos para indicar campos somente leitura em verificadores compatíveis. A disponibilidade depende da versão do Python e da ferramenta. Antes de adotar um recurso novo, confirme suporte no mypy ou Pyright usado pelo projeto.

Para evoluir APIs, acrescente campos opcionais de forma compatível e evite remover ou mudar tipos sem versionamento. TypedDict ajuda a localizar consumidores afetados durante a refatoração.

Erros comuns

  • Confiar em TypedDict para validar JSON: ele é apenas tipagem estática.
  • Confundir chave opcional com valor Optional: ausência e None são diferentes.
  • Usar um esquema para todas as operações: separe criação, atualização e resposta.
  • Aplicar cast sem validar: o cast silencia o analisador, não corrige dados.
  • Declarar object em todos os campos: isso elimina grande parte do benefício.
  • Ignorar chaves extras e mutabilidade: contratos estruturais ainda possuem regras de compatibilidade.

TypedDict, dataclass ou Pydantic?

Use TypedDict quando o valor deve continuar sendo um dicionário e a principal necessidade é análise estática. Use dataclass quando quiser objetos com atributos, métodos e construção explícita. Use Pydantic quando precisar validar e converter dados externos, gerar schemas ou integrar com frameworks de API.

Essas ferramentas podem coexistir. Uma camada de entrada valida dados com Pydantic e uma camada interna usa TypedDict para estruturas leves ou interoperabilidade com bibliotecas baseadas em dicionários.

Conclusão

typing.TypedDict transforma dicionários informais em contratos verificáveis. Ele descreve chaves obrigatórias, opcionais, tipos aninhados e variantes discriminadas sem mudar o objeto em runtime.

A documentação oficial de TypedDict no módulo typing detalha totalidade, herança e marcadores de obrigatoriedade. Use TypedDict para estruturas conhecidas, execute um verificador estático regularmente e mantenha validação real nas fronteiras onde os dados não são confiáveis.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    A developer typing code on a laptop with a Python book beside in an office.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    Protocol no Python: tipagem estrutural

    Aprenda typing.Protocol no Python para tipagem estrutural, contratos genéricos, callbacks, runtime_checkable, testes e baixo acoplamento.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    A close-up shot showcasing the intricate scales of a snake, highlighting texture and color.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    memoryview no Python: buffers sem cópia

    Aprenda memoryview no Python para acessar buffers sem cópia, criar slices, editar bytearray, usar cast, mmap, struct e sockets com

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    A person typing on a laptop with a Python programming book visible, capturing technology and learning.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    tracemalloc no Python: rastreie memória

    Aprenda tracemalloc no Python para medir picos, criar e comparar snapshots, filtrar alocações e diagnosticar crescimento de memória.

    Ler mais

    Tempo de leitura: 6 minutos
    28/08/2026
    Detailed shot of a Jungle Carpet Python (Morelia spilota cheynei) in its natural habitat.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    queue no Python: comunique threads

    Aprenda queue no Python para comunicar threads com FIFO, LIFO, prioridade, backpressure, task_done, join, sentinelas e shutdown seguro.

    Ler mais

    Tempo de leitura: 6 minutos
    28/08/2026
    Flat lay of a complete toolset neatly organized in a workshop setting, essential for auto repair tasks.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    selectors no Python: vários sockets

    Aprenda selectors no Python para multiplexar sockets, controlar leitura e escrita parcial, buffers, timeouts, wakeup e backpressure.

    Ler mais

    Tempo de leitura: 6 minutos
    28/08/2026
    Close-up view of a computer screen displaying code in a software development environment.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    contextvars no Python: contexto assíncrono

    Aprenda contextvars no Python para contexto por task, request IDs, logging, copy_context, propagação a threads e restauração segura com tokens.

    Ler mais

    Tempo de leitura: 6 minutos
    28/08/2026