React Typescript Cheatsheet in Portuguese 🇧🇷
Cheatsheets para desenvolvedores com experiência em React que estão iniciando com TypeScript
Web docs | 中文翻译 | Español | Contribute! | Ask!
: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:
/README.md
) é focado em ajudar desenvolvedores React a começar a usar TS com React apps
/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.
/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.
/HOC.md
) especificamente ensina as pessoas a escrever HOCs com a ajuda de exemplos.
defaultProps
defaultProps
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:
Configurações de desenvolvimento local:
npx create-next-app -e with-typescript
irá criar no seu diretório atual.npx create-react-app name-of-app --template typescript
irá criar em um novo diretório.meteor create --typescript name-of-my-new-typescript-app
ignite new myapp
npx tsdx create mylib
para Creating React+TS libraries
Ferramentas menos maduras mas que vale a pena conferir:
npm init vite-app my-react-project --template react-ts
(nota - ainda não está na versão v1.0, mas é muito rápida).npx create-snowpack-app my-app --template app-template-react-typescript
modular
: CRA + TS + Yarn Workspaces toolkit. yarn create modular-react-app <project-name>
Manual de configuração:
@types/react
e @types/react-dom
.( Leia mais sobre o projeto DefinitelyTyped caso você não esteja familiarizado ).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';
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!
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>;
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
e defaultProps
.
defaultProps
com React.FunctionComponent
. Consulte [este problema para obter detalhes] (https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/87). Nós mantemos uma seção defaultProps
separada para que você também possa consultar.Fornece uma definição implícita de children
(veja abaixo) - no entanto, há alguns problemas com o tipo children
implícito (por exemplo, DefinitelyTyped#33006), e é melhor ser explícito sobre os componentes que consomem children
, de qualquer maneira.
const Title: React.FunctionComponent<{ title: string }> = ({
children,
title,
}) => <div title={title}>{children}</div>;
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>
);
};
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).
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>
</>
);
}
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;
}
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.
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.
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.