Cheatsheets para desenvolvedores com experiĂȘncia em React que estĂŁo iniciando com TypeScript
Web docs | äžæçż»èŻ | Español | Contribute! | Ask!
đ 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! đ
- 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.
Expandir Tabela de ConteĂșdo
- 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!
- 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!
- 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>
.
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
Outras ferramentas
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 * 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';
Explicação
Por que usar allowSyntheticDefaultImports
ao invés de esModuleInterop
? Daniel Rosenwasser comentou que Ă© melhor para webpack/parcel. Para consultar mais argumentos dessa discussĂŁo wmonk/create-react-app-typescript#214
VocĂȘ tambĂ©m deveria verificar a nova documentação do TypeScript para descriçÔes oficiais entre cada flag do compilador!
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>;
Por que `React.FC` Ă© desencorajado? E sobre `React.FunctionComponent` / `React.VoidFunctionComponent`?
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] (facebook/create-react-app#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] (typescript-cheatsheets/react#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>;
Usando `React.VoidFunctionComponent` ou` React.VFC` como alternativa
A partir da versĂŁo [@types/react 16.9.48] (DefinitelyTyped/DefinitelyTyped#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>
);
};
Na maioria dos casos, faz pouca diferença qual sintaxe Ă© usada, mas vocĂȘ pode preferir a natureza mais explĂcita de React.FunctionComponent
.
Problemas menores
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] (typescript-cheatsheets/react#57).
HĂĄ suporte para Hooks em @types/react
a partir da versĂŁo v16.8.
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).
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>
</>
);
}
Uso do tipo `Reducer` da biblioteca `redux`
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>() {}
Ambos useEffect
e useLayoutEffect
são usados para executar efeitos colaterais 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;
}
Solução para o exemplo acima
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;
}
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.
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.
Dica: Escolhendo qual `HTMLElement` usar
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:
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>;
}
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;
}
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.
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]
}
Saiba que a equipe do React recomenda que custom hooks que retornam mais de dois valores usem objetos em vez de tuplas.
- 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.
- https://github.com/mweststrate/use-st8
- https://github.com/palmerhq/the-platform
- https://github.com/sw-yx/hooks
Tem algo a acrescentar? - link para o repositĂłrio original.