Tipar getServerSideProps no Next.js 2026
getServerSideProps sem tipagem vira um campo minado de any. Veja como tipar props, context, redirect e notFound corretamente no Pages Router do Next.js.
Por que isso é importante
Tipar getServerSideProps no Next.js 2026. getServerSideProps sem tipagem vira um campo minado de any. Veja como tipar props, context, redirect e notFound corretamente no Pages Router do Next.js.
O problema: props sem tipo
Quando você exporta getServerSideProps sem tipagem, o Next.js passa as props pro componente como any. Você acessa props.user.nome (errado, é props.user.name) e o TypeScript não reclama. O erro só aparece no browser.
// SEM tipagem - perigoso
export async function getServerSideProps(context) {
// context é any
const data = await fetch('...');
return { props: { user: data } };
}
export default function Page(props) {
// props é any
return <h1>{props.user.nome}</h1>; // typo! deveria ser .name
// Sem erro em compile time
}
Solução 1: GetServerSideProps generic
A forma mais direta é usar o tipo GetServerSideProps do Next.js como generic, passando a interface das props. Isso tipa o retorno e garante que o componente receba os campos certos.
import { GetServerSideProps } from 'next';
// Define a interface das props
interface UserPageProps {
user: {
id: string;
name: string;
email: string;
};
lastUpdated: string;
}
// Tipa o getServerSideProps com o generic
export const getServerSideProps: GetServerSideProps<UserPageProps> = async (context) => {
// context agora é GetServerSidePropsContext - tipado!
const { params, query, req, res } = context;
const response = await fetch(`https://api.example.com/users/${params?.id}`);
const user = await response.json();
return {
props: {
user: {
id: user.id,
name: user.name,
email: user.email,
},
lastUpdated: new Date().toISOString(),
},
};
};
// Componente com props tipadas
export default function UserPage({ user, lastUpdated }: UserPageProps) {
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
<small>Atualizado em: {lastUpdated}</small>
</div>
);
}
Com GetServerSideProps
Solução 2: InferGetServerSidePropsType (zero duplicação)
Se você não quer manter uma interface separada, dá pra usar InferGetServerSidePropsType pra inferir o tipo das props direto da função. Assim o tipo fica sincronizado automaticamente.
import { GetServerSideProps, InferGetServerSidePropsType } from 'next';
export const getServerSideProps = (async (context) => {
const res = await fetch('https://api.example.com/posts');
const posts = (await res.json()) as Array<{
id: string;
title: string;
content: string;
}>;
return {
props: {
posts,
totalCount: posts.length,
fetchedAt: new Date().toISOString(),
},
};
}) satisfies GetServerSideProps;
// Tipo inferido automaticamente!
export default function PostsPage(
props: InferGetServerSidePropsType<typeof getServerSideProps>
) {
// props.posts é Array<{ id: string; title: string; content: string }>
// props.totalCount é number
// props.fetchedAt é string
return (
<div>
<h1>{props.totalCount} posts</h1>
{props.posts.map(post => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>{post.content}</p>
</article>
))}
</div>
);
}
O satisfies GetServerSideProps garante que a função tem o formato certo, e InferGetServerSidePropsType extrai o tipo das props automaticamente. Zero duplicação, zero chance de sair de sincronia.
Tipando o context: params, query e req
O context do getServerSideProps tem vários campos úteis. Dá pra tipar os params e query pra ter controle sobre o que vem da URL.
import { GetServerSideProps, GetServerSidePropsContext } from 'next';
import { ParsedUrlQuery } from 'querystring';
// Tipo customizado pros params da URL
interface PostParams extends ParsedUrlQuery {
slug: string;
}
// Passa o tipo dos params como segundo generic
export const getServerSideProps: GetServerSideProps<PostProps, PostParams> = async (context) => {
const { slug } = context.params!; // slug é string
const { page } = context.query; // page é string | string[] | undefined
// Headers e cookies do request
const authToken = context.req.headers.authorization;
const cookies = context.req.cookies;
const userAgent = context.req.headers['user-agent'];
// Locale e resolvedUrl
const locale = context.locale; // string | undefined
const url = context.resolvedUrl; // string
const post = await getPostBySlug(slug);
return {
props: {
post,
currentPage: Number(page) || 1,
},
};
};
Redirect e notFound tipados
O getServerSideProps pode retornar redirect ou notFound em vez de props. O TypeScript lida com isso via union type no retorno. A tipagem garante que você retorne a estrutura correta.
import { GetServerSideProps } from 'next';
interface DashboardProps {
user: { id: string; name: string; role: string };
stats: { totalOrders: number; revenue: number };
}
export const getServerSideProps: GetServerSideProps<DashboardProps> = async (context) => {
const token = context.req.cookies['auth-token'];
// Sem token: redireciona pro login
if (!token) {
return {
redirect: {
destination: '/login?callbackUrl=/dashboard',
permanent: false, // 307 temporário
},
};
}
const user = await getUserFromToken(token);
// Usuário não encontrado: 404
if (!user) {
return { notFound: true };
}
// Sem permissão: redireciona
if (user.role !== 'admin') {
return {
redirect: {
destination: '/unauthorized',
permanent: false,
},
};
}
const stats = await getDashboardStats(user.id);
return {
props: {
user: { id: user.id, name: user.name, role: user.role },
stats,
},
};
};
Galera, o redirect usa permanent: false pra 307 (temporário) e permanent: true pra 308 (permanente). Escolha certo pro SEO: se o redirecionamento é definitivo, use true.
Padrão avançado: helper tipado reutilizável
Se várias páginas compartilham lógica no getServerSideProps (autenticação, por exemplo), crie um wrapper tipado que encapsula o padrão.
import { GetServerSideProps, GetServerSidePropsContext, GetServerSidePropsResult } from 'next';
interface AuthUser {
id: string;
name: string;
role: string;
}
type AuthenticatedHandler<P extends Record<string, unknown>> = (
context: GetServerSidePropsContext,
user: AuthUser
) => Promise<GetServerSidePropsResult<P>>;
// Wrapper que garante autenticação
function withAuth<P extends Record<string, unknown>>(
handler: AuthenticatedHandler<P>
): GetServerSideProps<P> {
return async (context) => {
const token = context.req.cookies['auth-token'];
if (!token) {
return {
redirect: { destination: '/login', permanent: false },
};
}
const user = await verifyToken(token);
if (!user) {
return {
redirect: { destination: '/login', permanent: false },
};
}
return handler(context, user);
};
}
// Uso: limpo e tipado
interface ProfileProps {
user: AuthUser;
orders: Array<{ id: string; total: number }>;
}
export const getServerSideProps = withAuth<ProfileProps>(async (context, user) => {
const orders = await getOrders(user.id);
return {
props: { user, orders },
};
});
Erros comuns ao tipar getServerSideProps
Atenção
Erro 1: Retornar Date ou funções nas props. getServerSideProps serializa pra JSON. Date vira string, funções desaparecem. Converta datas pra ISO string antes de retornar.
Erro 2: Esquecer o ! em context.params quando a rota é dinâmica. O params pode ser undefined em rotas não-dinâmicas, mas numa rota [slug] ele sempre existe. Use non-null assertion ou tipagem de params.
Erro 3: Duplicar tipos entre getServerSideProps e componente. Use InferGetServerSidePropsType ou uma interface compartilhada. Nunca copie/cole a interface.
Erro 4: Não tratar erros da API no getServerSideProps. Se o fetch falha, o servidor retorna 500 genérico. Sempre envolva em try/catch e retorne notFound ou redirect em caso de erro.
Passo a passo: getServerSideProps tipado
- Defina a interface das props que o componente vai receber
- Tipe getServerSideProps com GetServerSideProps<Props>
- Se tiver params dinâmicos, passe a interface no segundo generic
- Tipe o componente com a mesma interface ou use InferGetServerSidePropsType
- Trate todos os caminhos: props, redirect e notFound
- Envolva chamadas externas em try/catch
- Converta Date pra string antes de retornar nas props
Checklist: getServerSideProps tipado
Checklist: SSR + TypeScript
- GetServerSideProps tipado com generic das props
- Params tipados com interface que extends ParsedUrlQuery
- InferGetServerSidePropsType usado no componente (ou interface compartilhada)
- Redirect e notFound retornados quando necessário
- Datas convertidas pra string (JSON serializable)
- Try/catch em todas as chamadas externas
- Wrapper withAuth criado se autenticação é compartilhada
- Context (req, query, locale) tipado e usado corretamente
TypeScript Profissional: Projeto Completo
No CrazyStack você constrói um projeto completo com TypeScript
Node.js e React. SSR tipado
APIs seguras e deploy otimizado. Acesse:
/comprar?src=blog-como-tipar-getserversideprops-nextjs-typescript
]
}
]
}
]
}