Emily Silva
Emily Silva28/09/2026 13:48
Compartilhe

6 dicas de TypeScript com React que aprendi na prática (algumas errando)

    Estou na reta final da Formação TypeScript Fullstack Developer da DIO. Nos desafios do DIO Bank, construí componentes em React, escrevi testes e quebrei a cabeça com erros que nenhum tutorial tinha me avisado.

    Reuni aqui as 6 lições que mais mudaram a forma como eu escrevo código. Se você está começando com TypeScript e React, espero que elas te poupem algumas horas.

    1. Toda prop merece uma interface

    No começo, eu criava o componente e ia passando props "no escuro". Com TypeScript, a primeira coisa que faço agora é descrever o que o componente recebe:

    interface ButtonProps {
    label: string;
    onClick: () => void;
    disabled?: boolean;
    }
    
    export const Button = ({ label, onClick, disabled = false }: ButtonProps) => (
    <button onClick={onClick} disabled={disabled}>
      {label}
    </button>
    );
    

    O ? diz que a prop é opcional, e o valor padrão (disabled = false) resolve o caso em que ela não vem.

    Por que isso importa: se alguém esquecer o onClick ou passar um número no label, o erro aparece no editor, antes de rodar. A interface vira a documentação do componente.

    2. Diga ao useState o que ele guarda

    O TypeScript adivinha o tipo pelo valor inicial. Com useState(''), ele sabe que é uma string. O problema aparece quando o valor inicial é null:

    interface Usuario {
    nome: string;
    email: string;
    }
    
    const [usuario, setUsuario] = useState<Usuario | null>(null);
    

    Sem o <Usuario | null>, o TypeScript entende que aquele estado só pode ser null para sempre, e reclama quando você tenta salvar o usuário logado.

    Por que isso importa: o | null obriga você a tratar o caso "ainda não tem usuário" antes de acessar usuario.nome, que é justamente onde muita tela quebra.

    3. Tipe os eventos, não use any

    Quando o editor reclamava do event, minha primeira vontade era colocar any e seguir em frente. Não faça isso. O React já tem os tipos prontos:

    const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setEmail(event.target.value);
    };
    
    <input type="email" value={email} onChange={handleChange} />
    

    Por que isso importa: com o tipo certo, o editor passa a sugerir event.target.value sozinho. Com any, você desliga o TypeScript exatamente onde ele mais ajuda.

    4. Context API com um hook que avisa quando algo está errado

    Estudando o desafio de login do DIO Bank, vi que é preciso compartilhar o estado "usuário logado" entre várias telas. O padrão que mais me deu segurança foi este:

    interface AuthContextType {
    isLoggedIn: boolean;
    setIsLoggedIn: (value: boolean) => void;
    }
    
    const AuthContext = createContext<AuthContextType | undefined>(undefined);
    
    export function useAuth() {
    const context = useContext(AuthContext);
    if (!context) {
      throw new Error('useAuth precisa ser usado dentro de <AuthProvider>');
    }
    return context;
    }
    

    Em vez de cada tela chamar useContext(AuthContext) e lidar com undefined, todas usam useAuth().

    Por que isso importa: se alguém esquecer de envolver a aplicação no Provider, o erro aparece na hora com uma mensagem clara, e não como um "cannot read properties of undefined" perdido no console.

    5. Nos testes, crie o espião dentro de cada teste

    Essa me custou um bom tempo. Eu testava uma função que mostra um alert de boas-vindas e criei o jest.spyOn uma vez só, lá no topo do describe. O primeiro teste passava e os seguintes falhavam sem motivo aparente.

    O motivo: projetos criados com Create React App vêm com resetMocks: true, que "zera" os mocks depois de cada teste. O espião criado lá em cima simplesmente deixava de funcionar.

    A solução foi criar o espião dentro de cada teste e restaurar tudo no final:

    afterEach(() => {
    jest.restoreAllMocks();
    });
    
    it('mostra a mensagem de boas-vindas', () => {
    const alertSpy = jest.spyOn(window, 'alert').mockImplementation(() => {});
    welcome();
    expect(alertSpy).toHaveBeenCalled();
    });
    

    Por que isso importa: cada teste fica independente dos outros. Se um falhar, você sabe que o problema está nele, e não na ordem em que os testes rodaram.

    6. Arrow function como propriedade de classe não funciona com super

    Essa aconteceu no primeiro desafio, o banco em TypeScript puro, e vale para qualquer projeto com classes. Eu escrevi os métodos da classe base como arrow functions:

    class Conta {
    depositar = (valor: number) => {
      // ...
    };
    }
    
    class ContaBonus extends Conta {
    depositar = (valor: number) => {
      super.depositar(valor + 10); // não funciona
    };
    }
    

    O super.depositar não existe, porque uma arrow function declarada assim vira propriedade de cada objeto, e não método da classe. O super só enxerga métodos da classe. A correção foi usar a sintaxe de método:

    class Conta {
    depositar(valor: number) {
      // ...
    }
    }
    
    class ContaBonus extends Conta {
    depositar(valor: number) {
      super.depositar(valor + 10); // agora funciona
    }
    }
    

    Por que isso importa: o código parecia certo e compilava na minha cabeça. Entender a diferença entre propriedade e método me fez finalmente entender como a herança funciona por baixo.

    O que fica

    Nenhuma dessas dicas é sobre decorar sintaxe. Todas são sobre a mesma ideia: deixar o TypeScript apontar o erro antes do usuário encontrar. Cada tipo que você escreve é uma pergunta a menos lá na frente.

    Os desafios estão no meu GitHub: github.com/rainhadamidia

    Compartilhe
    Comentários (0)