Awesome
<div align="center"> <h1>React+TypeScript Cheatsheets em Português</h1> <a href="https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/81"> <img height="80" width="80" alt="react + ts logo" src="https://user-images.githubusercontent.com/6764957/53868378-2b51fc80-3fb3-11e9-9cee-0277efe8a927.png" align="left" /> </a> <p>Cheatsheets para desenvolvedores com experiência em React que estão iniciando com TypeScript</p>Web docs | 中文翻译 | Español | Contribute! | Ask!
</div><div align="center">
:wave: Este repositório é mantido por @giseladifini e @swyx. Estamos muito felizes que você quer experimentar React com Typescript! Se você perceber algo de errado ou faltando, por favor abra uma issue! :+1:
</div>Todas as dicas de React + TypeScript
- Cheatsheet Básico (
/README.md
) é focado em ajudar desenvolvedores React a começar a usar TS com React apps- Foco nas melhores práticas com exemplos para copiar e colar.
- Explica alguns tipos básicos de uso de TS e configuração ao longo do caminho.
- Responde às perguntas mais frequentes.
- Não cobre a lógica de tipo genérico em detalhes. Em vez disso, preferimos ensinar técnicas de solução de problemas simples para iniciantes.
- O objetivo é se familiarizar com TS sem precisar aprender muito sobre TS.
- Cheatsheet Avançado (
/AVANÇADO.md
) ajuda a mostrar e explicar o uso avançado de tipos genéricos para pessoas que escrevem utilitários/funções/props de renderização/componentes de ordem superior (HOCs) reutilizáveis e bibliotecas TS+React.- Possui dicas e truques diversos para usuários profissionais.
- Conselhos para contribuir com DefinitelyTyped.
- O Objetivo é tirar total vantagem sobre o TypeScript.
- Cheatsheet de migração (
/MIGRANDO.md
) ajuda a reunir conselhos para a migração incremental de grandes bases de código de JS ou Flow, de pessoas que já fizeram isso.- Nós não tentamos convencer as pessoas a mudar, apenas ajudar as pessoas que já decidiram isso.
- ⚠️ Esta é uma nova cheatsheet, toda ajuda é bem-vinda.
- Cheatsheet de HOCs (
/HOC.md
) especificamente ensina as pessoas a escrever HOCs com a ajuda de exemplos.- Familiaridade com Genéricos é necessário.
- ⚠️ Esta é uma nova cheatsheet, toda a assistência é bem-vinda.
Tabela de conteúdos da Cheatsheet básica
<details> <summary><b>Expandir Tabela de Conteúdo</b></summary> <!--START-SECTION:setup-toc--> <!--END-SECTION:setup-toc-->- Seção 2: Primeiros Passos
- Componente de Função
- Hooks
- useState
- useReducer
- useEffect
- useRef
- useImperativeHandle
- Hooks Customizados
- Componentes de Classe
- Talvez você não precise do
defaultProps
- "Tipando"
defaultProps
- Consumindo Props de um Componente com defaultProps
- Discussões e Conhecimentos Diversos
- Tipos ou Interfaces?
- Exemplos básicos do tipo Prop
- Exemplos úteis do tipo React Prop
- getDerivedStateFromProps
- Formulários e Eventos
- Context
- Exemplo Básico
- Exemplo Extendido
- forwardRef/createRef
- Portais
- Limites de erros
- Concurrent React/React Suspense
- Manual de resolução de problemas: Tipos
- Tipos de União e Tipos de Proteção
- Tipos Opcionais
- Tipos de Enum
- Tipos de Asserção
- Simulando Tipos Nominais
- Tipos de Interseção
- Tipos de União
- Sobrecarregando Tipos de Função
- Usando Tipos Inferidos
- Usando Tipos Parciais
- Os Tipos de que preciso não foram exportados!
- Os Tipos de que preciso não existem! <!--END-SECTION:types-toc-->
- Manual de resolução de problemas: Operadores
- Manual de resolução de problemas: Utilitários
- Manual de resolução de problemas: tsconfig.json
- Manual de resolução de problemas: Erros en tipos oficiais
- Bases de código de React + TypeScript recomendadas para aprender
- Ferramentas e integração em editores
- Linting
- Outros recursos sobre React + TypeScript
- Discussões recomendadas sobre React + TypeScript
- Hora de realmente aprender TypeScript
- Aplicação de Exemplo
- Minha pergunta não foi respondida aqui!
Seção 1: Configuração
Pré-requisitos
- Uma boa compreensão de React.
- Familiaridade com os tipos básicos de TypeScript ( O guia de 2ality é de grande ajuda. Se você é completamente novato em TypeScript, dê uma olhada no tutorial de chibicode ).
- Ter lido a seção de TypeScript na documentação oficial do React.
- Ter lido a seção do React do novo playground de TypeScript ( Opcional: também acompanhar os mais de 40 exemplos na seção de exemplos do playground ).
Este guia sempre assumirá que você está usando a última versão de Typescript. Notas para versões mais antigas usarão a etiqueta <details>
.
Ferramentas iniciais de React + TypeScript
Configurações na nuvem:
- Playground do TypeScript com React apenas se você estiver depurando tipos (e relatando problemas), não para executar código.
- CodeSandbox - IDE na nuvem, inicializa super rápido.
- Stackblitz - IDE na nuvem, inicializa super rápido.
Configurações de desenvolvimento local:
- Next.js:
npx create-next-app -e with-typescript
irá criar no seu diretório atual. - Create React App:
npx create-react-app name-of-app --template typescript
irá criar em um novo diretório. - Meteor:
meteor create --typescript name-of-my-new-typescript-app
- Ignite para React Native:
ignite new myapp
- TSDX:
npx tsdx create mylib
para Creating React+TS libraries
Ferramentas menos maduras mas que vale a pena conferir:
- Vite:
npm init vite-app my-react-project --template react-ts
(nota - ainda não está na versão v1.0, mas é muito rápida). - Snowpack:
npx create-snowpack-app my-app --template app-template-react-typescript
- Docusaurus v2 com suporte a TypeScript
- Parcel
- JP Morgan's
modular
: CRA + TS + Yarn Workspaces toolkit.yarn create modular-react-app <project-name>
Manual de configuração:
- O guia de Basarat para uma configuração manual de React + TypeScript + Webpack + Babel.
- Em particular, certifique-se de ter instalado
@types/react
e@types/react-dom
.( Leia mais sobre o projeto DefinitelyTyped caso você não esteja familiarizado ). - Existem também muitos boilerplates de React + Typescript. Por favor consulte nossa lista de outros recursos.
Import React
import * as React from 'react';
import * as ReactDOM from 'react-dom';
Este é o caminho mais seguro no futuro para importar React. Se você definir --allowSyntheticDefaultImports
(ou adicionar "allowSyntheticDefaultImports": true
) em seu tsconfig.json
, você poderá importar como se faz normalmente em jsx:
import React from 'react';
import ReactDOM from 'react-dom';
<details>
<summary>Explicação</summary>
Por que usar allowSyntheticDefaultImports
ao invés de esModuleInterop
? Daniel Rosenwasser comentou que é melhor para webpack/parcel. Para consultar mais argumentos dessa discussão https://github.com/wmonk/create-react-app-typescript/issues/214
Você também deveria verificar a nova documentação do TypeScript para descrições oficiais entre cada flag do compilador!
</details> <!--END-SECTION:setup--> <!--START-SECTION:function-components-->Seção 2: Primeiros Passos
Componente de Função
Podem ser escritos como funções normais que recebem props
como argumento e retornam um elemento JSX.
type AppProps = { message: string }; /* também se pode usar uma interface */
const App = ({ message }: AppProps) => <div>{message}</div>;
<details>
<summary><b>Por que `React.FC` é desencorajado? E sobre `React.FunctionComponent` / `React.VoidFunctionComponent`?</b></summary>
Você pode ver isso em muitas bases de código React + TypeScript:
const App: React.FunctionComponent<{ message: string }> = ({ message }) => (
<div>{message}</div>
);
No entanto, o consenso geral hoje é que o uso de React.FunctionComponent
(ou a abreviação React.FC
) é [desencorajado] (https://github.com/facebook/create-react-app/pull/8177). Isto é um ponto de vista, é claro, mas se você concorda e deseja remover React.FC
da sua base de código, você pode usar [este jscodeshift codemod] (https://github.com/gndelia/codemod-replace-react- fc-typescript).
Algumas diferenças da versão de "função normal":
-
React.FunctionComponent
é explícito sobre o tipo de retorno, enquanto a versão normal da função é implícita (ou então precisa de anotações adicionais). -
Fornece verificação de tipos e preenchimento automático para propriedades estáticas como
displayName
,propTypes
edefaultProps
.- Observe que existem alguns problemas conhecidos usando
defaultProps
comReact.FunctionComponent
. Consulte [este problema para obter detalhes] (https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/87). Nós mantemos uma seçãodefaultProps
separada para que você também possa consultar.
- Observe que existem alguns problemas conhecidos usando
-
Fornece uma definição implícita de
children
(veja abaixo) - no entanto, há alguns problemas com o tipochildren
implícito (por exemplo, DefinitelyTyped#33006), e é melhor ser explícito sobre os componentes que consomemchildren
, de qualquer maneira.
const Title: React.FunctionComponent<{ title: string }> = ({
children,
title,
}) => <div title={title}>{children}</div>;
<details>
<summary>
Usando `React.VoidFunctionComponent` ou` React.VFC` como alternativa
</summary>
A partir da versão [@types/react 16.9.48] (https://github.com/DefinitelyTyped/DefinitelyTyped/pull/46643), você também poderá usar o tipo React.VoidFunctionComponent
ou React.VFC
se quiser tipar children
explicitamente. Esta é uma solução provisória até que FunctionComponent
não aceite nenhum children
por padrão (planejado para @types/react@^18.0.0
).
type Props = { foo: string };
// OK agora mas futuramente causará erro
const FunctionComponent: React.FunctionComponent<Props> = ({
foo,
children,
}: Props) => {
return (
<div>
{foo} {children}
</div>
); // OK
};
// OK agora mas futuramente se tornará obsoleto
const VoidFunctionComponent: React.VoidFunctionComponent<Props> = ({
foo,
children,
}) => {
return (
<div>
{foo}
{children}
</div>
);
};
</details>
- _No futuro_, ele poderá marcar automaticamente os `props` como `readonly` (somente leitura), embora isso seja um ponto discutível se o objeto `props` for desestruturado na lista de parâmetros.
Na maioria dos casos, faz pouca diferença qual sintaxe é usada, mas você pode preferir a natureza mais explícita de React.FunctionComponent
.
Esses padrões não são suportados:
** Renderização condicional **
const MyConditionalComponent = ({ shouldRender = false }) =>
shouldRender ? <div /> : false; // tampouco faça isso em JS
const el = <MyConditionalComponent />; // gera um erro
Isso ocorre porque, devido às limitações do compilador, os componentes de função não podem retornar nada além de uma expressão JSX ou null
, caso contrário, ele reclama com uma mensagem de erro enigmática dizendo que outro tipo não pode ser atribuído ao Elemento.
const MyArrayComponent = () => Array(5).fill(<div />);
const el2 = <MyArrayComponent />; // gera um erro
Array.fill
Infelizmente, apenas anotar o tipo de função não vai ajudar, então se você realmente precisar retornar outros tipos exóticos que o React suporta, será necessário executar uma declaração de tipo:
const MyArrayComponent = () => (Array(5).fill(<div />) as any) as JSX.Element;
[Veja o comentário de @ferdaber aqui] (https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/57).
</details> <!--END-SECTION:function-components--> <!--START-SECTION:hooks-->Hooks
Há suporte para Hooks em @types/react
a partir da versão v16.8.
useState
Inferência automática de tipos funciona bem com valores simples
const [val, toggle] = React.useState(false);
// infere-se que `val` é do tipo boolean
// `toggle` aceita apenas booleans
Veja também no artigo em inglês (utilizando Using Inferred Types se precisar usar um tipo complexo para o qual você depende da inferência.
No entanto, muitos hooks são inicializados com valores nulos e você pode se perguntar como deve fazer para definir o tipo. Declare explicitamente o tipo e use um tipo de união (union type):
const [user, setUser] = React.useState<IUser | null>(null);
// mais adiante...
setUser(newUser);
Você também pode usar asserções de tipo (type assertions) se um estado for inicializado logo após o setup e sempre tiver um valor definido após o setup:
const [user, setUser] = React.useState<IUser>({} as IUser);
// mais adiante...
setUser(newUser);
"Mentimos" temporariamente para o compilador de Typescript que {}
é do tipo IUser
. Você deve então configurar o estado de user
— se não o fizer, o resto do seu código pode depender do fato de que user
é do tipo IUser
e isso pode levar a erros em tempo de execução (runtime errors).
useReducer
Você pode utilizar Uniões de tipos com propriedades definidas (Discriminated Unions) para actions da função reducer. Não esqueça de definir o tipo de retorno, caso contário, o compilador irá inferir o tipo.
const initialState = { count: 0 };
type ACTIONTYPE =
| { type: "increment"; payload: number }
| { type: "decrement"; payload: string };
function reducer(state: typeof initialState, action: ACTIONTYPE) {
switch (action.type) {
case "increment":
return { count: state.count + action.payload };
case "decrement":
return { count: state.count - Number(action.payload) };
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = React.useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({ type: "decrement", payload: "5" })}>
-
</button>
<button onClick={() => dispatch({ type: "increment", payload: 5 })}>
+
</button>
</>
);
}
<details>
<summary><b>Uso do tipo `Reducer` da biblioteca `redux`</b></summary>
Caso você use a biblioteca redux para escrever a reducer function, ela fornece um helper conveniente do formato Reducer<State, Action>
que cuida do tipo do retorno para você.
Assim, o exemplo de reducer acima se torna:
import { Reducer } from 'redux';
export function reducer: Reducer<AppState, Action>() {}
</details>
useEffect / useLayoutEffect
Ambos useEffect
e useLayoutEffect
são usados para executar <b>efeitos colaterais</b> e retornam uma função de limpeza opcional, o que significa que se eles não lidam com retorno de valores, nenhum tipo é necessário. Ao usar useEffect
, tome cuidado para não retornar nada além de uma função ou undefined
, caso contrário, tanto o TypeScript quanto o React apresentarão error. Isso pode ser sutil ao usar arrow functions:
function DelayedEffect(props: { timerMs: number }) {
const { timerMs } = props;
useEffect(
() =>
setTimeout(() => {
/* faça coisas aqui */
}, timerMs),
[timerMs]
);
// um exemplo ruim! setTimeout implicitamente retorna número (tipo number)
// porque o corpo da arrow function não está entre chaves
return null;
}
<details>
<summary>
Solução para o exemplo acima
</summary>
function DelayedEffect(props: { timerMs: number }) {
const { timerMs } = props;
useEffect(() => {
setTimeout(() => {
/* faça coisas aqui */
}, timerMs);
}, [timerMs]);
// melhor; utilize a keyword void para ter certeza de que retornará undefined
return null;
}
</details>
useRef
Em TypeScript, useRef
retorna uma referência que pode ser somente leitura ou mutável, a depender se o tipo fornecido cobre totalmente o valor inicial ou não. Escolha um que se adapte ao seu caso de uso.
Opção 1: ref de um elemento da DOM
Para acessar um elemento da DOM: forneça apenas o tipo de elemento como argumento e use null
como valor inicial. Neste caso, a referência retornada terá um .current
somente leitura que é gerenciado pelo React. O TypeScript espera que você dê esta referência à prop ref
de um elemento:
function Foo() {
// - Se possível, seja o mais específico possível. Por exemplo, HTMLDivElement
// é melhor que HTMLElement e muito melhor que Element.
// - Em termos técnicos, isso retorna RefObject<HTMLDivElement>
const divRef = useRef<HTMLDivElement>(null);
useEffect(() => {
// Observe que ref.current pode ser null. Isso é esperado, porque você pode
// renderizar condicionalmente o elemento da ref, ou você poderia esquecer de atribuí-lo a um elemento
if (!divRef.current) throw Error("divRef is not assigned");
// Agora você tem certeza que divRef.current é um HTMLDivElement
doSomethingWith(divRef.current);
});
// Atribua a ref a um elemento para que o React possa gerenciar-lo pra você
return <div ref={divRef}>etc</div>;
}
Se você tem certeza de que divRef.current
nunca será nulo, também é possível usar o operador de asserção não nulo !
:
const divRef = useRef<HTMLDivElement>(null!);
// Mais tarde... não precisa checar se o elemento é nulo
doSomethingWith(divRef.current);
Observe que você está desativando a segurança de tipo aqui - você terá um erro de tempo de execução se esquecer de atribuir a referência a um elemento na renderização ou se o elemento com ref for renderizado condicionalmente.
<details> <summary> Dica: Escolhendo qual `HTMLElement` usar </summary>Refs demandam especificidade - não é suficiente apenas especificar qualquer HTMLElement
antigo. Se você não souber o nome do tipo de elemento necessário, verifique [lib.dom.ts](https://github.com/microsoft/TypeScript/blob/v3.9.5/lib/lib.dom. d.ts#L19224-L19343) ou cometa um erro de tipo intencional e deixe o compilador lhe dizer o tipo correto:
Opção 2: Valor ref mutável
Para ter um valor mutável: forneça o tipo desejado e verifique se o valor inicial pertence totalmente a esse tipo:
function Foo() {
// Tecnicamente, isto retorna MutableRefObject<number | null>
const intervalRef = useRef<number | null>(null);
// Você mesmo gerência a ref (por isso se chama MutableRefObject!)
useEffect(() => {
intervalRef.current = setInterval(...);
return () => clearInterval(intervalRef.current);
}, []);
// A ref (intervalRef) não é passado para a prop "ref" de nenhum elemento
return <button onClick={/* clearInterval the ref */}>Cancel timer</button>;
}
Veja também (conteúdo em inglês)
useImperativeHandle
Não temos muito ainda sobre esse tema, há uma discussão nas issues do repositório original. Por favor, contribua se puder!
type ListProps<ItemType> = {
items: ItemType[];
innerRef?: React.Ref<{ scrollToItem(item: ItemType): void }>;
};
function List<ItemType>(props: ListProps<ItemType>) {
useImperativeHandle(props.innerRef, () => ({
scrollToItem() {},
}));
return null;
}
Custom Hooks
Se você estiver retornando um array em seu Custom Hook (hooks customizados), você vai querer evitar a inferência de tipo, pois o TypeScript irá inferir um tipo de união (quando, na verdade, você quer tipos diferentes em cada posição do array). Em vez disso, use const assertions do TypeScript 3.4:
export function useLoading() {
const [isLoading, setState] = React.useState(false);
const load = (aPromise: Promise<any>) => {
setState(true);
return aPromise.finally(() => setState(false));
};
return [isLoading, load] as const; // infere [boolean, typeof load] ao invés de (boolean | typeof load)[]
}
Dessa forma, quando você desestrutura (desctructure), você obtém os tipos certos com base na posição de desestruturação.
<detalhes> <summary><b>Alternativa: definir um tipo de retorno de tupla (tuple)</b></summary>Se você está tendo problemas com const assertions, você também pode declarar ou definir os tipos do retorno da função:
export function useLoading() {
const [isLoading, setState] = React.useState(false);
const load = (aPromise: Promise<any>) => {
setState(true);
return aPromise.finally(() => setState(false));
};
return [isLoading, load] as [
boolean,
(aPromise: Promise<any>) => Promise<any>
];
}
Uma função auxiliar que define o tipe de tuplas automaticamente também pode ser útil se você escrever muitos custom hooks:
function tuplify<T extends any[]>(...elements: T) {
return elements;
}
function useArray() {
const numberValue = useRef(3).current;
const functionValue = useRef(() => {}).current;
return [numberValue, functionValue]; // o tipo fica (number | (() => void))[]
}
function useTuple() {
const numberValue = useRef(3).current;
const functionValue = useRef(() => {}).current;
return tuplify(numberValue, functionValue); // o tipo fica [number, () => void]
}
</details>
Saiba que a equipe do React recomenda que custom hooks que retornam mais de dois valores usem objetos em vez de tuplas.
Leituras sobre Hooks + TypeScript (em inglês):
- https://medium.com/@jrwebdev/react-hooks-in-typescript-88fce7001d0d
- https://fettblog.eu/typescript-react/hooks/#useref
Se você estiver escrevendo uma biblioteca de Hooks, não esqueça que você também deve expor os tipos para os usuários utilizarem.