Pular para o conteúdo principal

Deep Linking e Typed Routes

Última atualização: 15 de julho de 2026

Visão Geral

O app tem um scheme de deep link configurado, mas só um deep link é de fato tratado no código: o callback de login. E embora o projeto tenha rotas tipadas habilitadas, boa parte do código as ignora na prática. Este documento cobre os dois pontos.

O scheme do app é definido em app.json:

{
"expo": {
"scheme": "com.vidyacode.force"
}
}

Não existe um bloco linking customizado no código, o Expo Router usa o linking automático baseado nesse scheme. E o único lugar do app que de fato trata uma URL de entrada vinda de fora é o efeito de deep link do AuthProvider, documentado em Login e Callback OAuth:

// auth.provider.tsx
const linkingUrl = useLinkingURL();

useEffect(() => {
if (!linkingUrl) return;
if (!linkingUrl.includes("code=")) return;
// ...
}, [dismissBrowserIfOpen, linkingUrl]);

O redirect URI configurado para o Keycloak (com.vidyacode.force:///, visto em Login e Callback OAuth) usa exatamente esse mesmo scheme. Fora desse caso, todo outro uso de Linking no app é para sair do app, não para receber um link de entrada: abrir o aplicativo de mapas nativo (documentado em Seleção e Navegação), abrir um link de contato de cliente, ou abrir uma URL externa a partir do modal de permissões. Não existe, hoje, nenhum deep link de recuperação de senha, nenhum callback de SSO adicional, nem qualquer outro ponto de entrada externo tratado pelo app.

Rotas tipadas estão ligadas, mas amplamente contornadas

app.json habilita o experimento de rotas tipadas do Expo Router:

"experiments": {
"typedRoutes": true,
"reactCompiler": true
}

Com isso ativo, o Expo Router gera tipos (em expo-env.d.ts, um arquivo gerado e fora do controle de versão) que deveriam permitir ao TypeScript verificar se um pathname passado para router.push corresponde a uma rota que de fato existe. Na prática, boa parte do código não se beneficia disso: 38 chamadas de router.push, espalhadas por 18 arquivos diferentes, terminam com as any ou as never, contornando exatamente essa verificação:

router.push({
pathname: "/home/indicadores",
// ...
} as any);

Isso é visível em praticamente todos os fluxos já documentados, não é uma prática isolada de uma feature só. Um efeito prático dessa inconsistência é que renomear ou remover uma rota não é necessariamente pego pelo TypeScript nos pontos que fazem esse cast, mesmo com rotas tipadas habilitadas no projeto: o as any/as never silencia exatamente o erro que o recurso existe para capturar.

Armadilhas conhecidas

Não assuma que uma chamada de router.push sem as any está de fato tipada e validada, e não assuma o oposto, que toda chamada com as any está incorreta. O cast é usado tanto para contornar limitações reais de tipos (parâmetros dinâmicos, rotas com nomes gerados) quanto por hábito em lugares onde o tipo já bateria sem o cast. Antes de remover um as any de uma chamada existente, rode o typecheck do projeto para confirmar se a rota e os parâmetros realmente batem com o que o Expo Router espera.