Cheatsheets para desarrolladores expertos en React que comienzan con TypeScript
Básico | Avanzado | Migrando | HOC | Inglés | 中文翻译 | Contribuir | Preguntas
👋 Este repositorio es mantenido por @laurosilvacom, @aromanarguello, @rainerxmf , @dantehemerson, y @hanslgarcia . ¡Estamos tan contentos de que quieras probar TypeScript con React! Si ves que algo esta mal, ¡abre un Issue! 👍
- La cheatsheet básica (
/README.md
) se enfoca en ayudar a los desarrolladores de React a comenzar a usar TS en aplicaciones de React- Enfoque en buenas prácticas (según nuestra opinión) y ejemplos que se puedan copiar y pegar.
- Explica en la marcha el uso y configuración de algunos tipos básicos de TS.
- Responde las preguntas más frecuentes.
- El objetivo es lograr volverse efectivo con TS sin aprender demasiado TS.
- La cheatsheet avanzada (
/AVANZADO.md
) ayuda a mostar y explicar usos avanzados de los tipos genéricos para personas que escriben tipos reutilizables en utilidades/funciones/render props/components de orden superior y bibliotecas de TS+React.- También tiene consejos y trucos variados para usuarios avanzados.
- Consejos para contribuir a DefinitelyTyped
- El objetivo es aprovechar al máximo TypeScript.
- La cheatsheet de migración (
/MIGRATING.md
) ayuda a agrupar consejos para migrar incrementalmente una base de código grande de JS o Flow, de personas que lo han hecho.- No intentamos convencer a las personas a hacer el cambio, solo ayudar a aquellos que ya se han decidido.
⚠️ Esta es una nueva cheatsheet, toda ayuda es bienvenida.
- La cheatsheet HOC (
/HOC.md
) enseña específicamente a las personas a escribir componentes de orden superior (HOC por sus siglas en inglés) con ejemplos.- Es necesario estar familiarizado con la Genericidad.
⚠️ Esta es una nueva cheatsheet, toda ayuda es bienvenida.
Expandir tabla de contenidos
- Sección 1: Configuración
- Sección 2: Comienzo
- Componentes de función
- Hooks
- Componentes de clase
- Tipos de defaultProps
- ¿Tipos o interfaces?
- Ejemplos básicos de Prop Types
- Ejemplos útiles de tipos de props en React
- getDerivedStateFromProps
- Formularios y Eventos
- Contexto
- forwardRef/createRef
- Portales
- Barreras de error
- Concurrent React/React Suspense
- Manual básico de resolución de problemas: Tipos
- Manual de resolución de problemas: Imágenes y otros archivos que no son TS/TSX
- Manual de resolución de problemas: Operadores
- Manual de resolución de problemas: Utilitarios
- Manual de resolución de problemas: ESLint
- Manual de resolución de problemas: tsconfig.json
- Manual de resolución de problemas: Errores en tipos oficiales
- Bases de código de React + TypeScript recomendadas para aprender
- Herramientas e integración en editores
- Linting
- Otros recursos sobre React + TypeScript
- Charlas recomendadas sobre React + TypeScript
- Hora de realmente aprender TypeScript
- Aplicación de ejemplo
- ¡Mi pregunta no está respondida aquí!
- Buena comprensión de React
- Estar familiarizado con con los tipos de TypeScript básicos (La guía de 2ality es de ayuda)
- Haber leído la sección sobre TypeScript en la documentación oficial de React.
- Haber leído la sección de React del nuevo Typescript playground (opcional: además haga los 40+ ejemplos debajo de la sección de ejemplos del playground)
Esta guía siempre asumirá que estás iniciando con la última versión de Typescript. Las notas para versiones anteriores se encontrarán en etiquetas expandibles <details>
.
- Create React App v2.1+ con Typescript:
npx create-react-app my-app --template typescript
- Solíamos recomendar
create-react-app-typescript
pero ahora está desaconsejado. consulta las instrucciones para la migración
- La guía de Basarat para una configuración manual de React + TypeScript + Webpack + Babel
- En particular, asegúrate de tener
@types/react
y@types/react-dom
instalados (Lee más acerca del proyecto DefinitelyTyped si no estás familiarizado). - También hay muchos boilerplates de React + Typescript, por favor consulta nuestra lista de recursos debajo.
import * as React from "react";
import * as ReactDOM from "react-dom";
En TypeScript 2.7+, puedes correr TypeScript con --allowSyntheticDefaultImports
(o añadir "allowSyntheticDefaultImports": true
a tsconfig) para importar como se hace normalmente en jsx:
import React from "react";
import ReactDOM from "react-dom";
Explicación
¿Por qué allowSyntheticDefaultImports
sobre esModuleInterop
? Daniel Rosenwasser ha dicho que es mejor para webpack/parcel. Para consultar más argumentos en el debate visita wmonk/create-react-app-typescript#214
¡Por favor haz un PR o abre un issue con tus sugerencias!
Pueden escribirse como funciones normales que pueden tomar un argumento props
y retornar un elemento JSX.
type AppProps = { message: string }; /* también se puede usar una interfaz */
const App = ({ message }: AppProps) => <div>{message}</div>;
¿Y dónde queda `React.FC`/`React.FunctionComponent`?
También puedes escribir componentes con React.FunctionComponent
(o la forma abreviada React.FC
):
const App: React.FC<{ message: string }> = ({ message }) => (
<div>{message}</div>
);
Algunas diferencias con la versión "normal de función":
-
Proporciona chequeo de tipos y autocompletamiento para propiedades estáticas como
displayName
,propTypes
, ydefaultProps
- Sin embargo, actualmente existen problemas conocidos cuando se usadefaultProps
conReact.FunctionComponent
. Consulta este issue para los detalles (navega hacia nuestra sección sobredefaultProps
para las recomendaciones sobre la declaración de tipos). -
Proporciona una definición implícita de
children
(ver debajo); sin embargo existen algunos problemas con el tipo implícitochildren
(p.ej. DefinitelyTyped#33006), y podría considerarse de todas formas ser explícito en los componentes que consumenchildren
.
const Title: React.FunctionComponent<{ title: string }> = ({
children,
title
}) => <div title={title}>{children}</div>;
-
En el futuro, podría marcar props automáticamente como
readonly
, aunque es discutible si el objeto props se desestructura en la lista de parámetros. -
React.FunctionComponent
es explícito sobre el tipo de retorno, mientras la forma normal de función es implícita (o de lo contrario necesita anotaciones adicionales).
En la mayoría de los casos no importa mucho qué sintaxis se use, pero la sintaxis de React.FC
es ligeramente más verbosa sin proporcionar una clara ventaja, por lo que se le da precedencia a la sintaxis "normal de función".
Problemas menores
Estos patrones no se permiten:
Renderizado condicional
const MyConditionalComponent = ({ shouldRender = false }) =>
shouldRender ? <div /> : false; // no hagas esto tampoco en JS
const el = <MyConditionalComponent />; // lanza un error
Esto ocurre por limitaciones en el compilador, los componentes de función no pueden retornar otra cosa que no sea una expresión JSX o null
, de lo contrario se queja con un mensaje de error críptico qeu dice que el otro tipo no es asignable a Element
.
const MyArrayComponent = () => Array(5).fill(<div />);
const el2 = <MyArrayComponent />; // lanza un error
Array.fill
Desafortunadamente con simplemente anotar el tipo de la función no se resolverá, por lo que si realmente necesitas retornar otros tipos exóticos con los que React es compatible, necesitarás realizar una aserción de tipo:
const MyArrayComponent = () => (Array(5).fill(<div />) as any) as JSX.Element;
Los Hooks están incluidos en @types/react
desde la versión v16.8 en adelante.
useState
La inferencia de tipo funciona muy bien la mayoría de las veces:
const [val, toggle] = React.useState(false); // se infiere que `val` es un booleao, `toggle` solo toma booleanos
Consulta también la sección sobre el Uso de tipos inferidos si necesitas usar un tipo complejo y te has apoyado en la inferencia para hacerlo.
Sin embargo, muchos hooks son inicializados con valores por defecto de tipo null o semejantes y puede que te preguntes como proporcionar tipos. Declara explícitamente el tipo y utiliza un tipo de unión:
const [user, setUser] = React.useState<IUser | null>(null);
// later...
setUser(newUser);
useRef
Cuando utilices useRef
, tienes dos opciones al crear un contenedor de la ref que no tiene un valor inicial:
const ref1 = useRef<HTMLElement>(null!);
const ref2 = useRef<HTMLElement | null>(null);
La primera opción hará que ref1.current
sea de solo lectura y se usa para pasarla a los atributos incorporados ref
que React manejará (porque React maneja la actualización del valor current
por ti).
La segunda opción hará que ref2.current
sea mutable, y se usa para "variables de instancia" que quieres manejar tú mismo.
useEffect
Al utilizar useEffect
, asegúrate de no retornar otra cosa que no sea una función o undefined
, de otra forma tanto TypeScript como React se quejarán. Esto puede ser sútil al usar funciones flecha:
function DelayedEffect(props: { timerMs: number }) {
const { timerMs } = props;
// mal! setTimeout retorna implícitamente un número porque la función flecha no está envuelta en llaves
useEffect(
() =>
setTimeout(() => {
/* do stuff */
}, timerMs),
[timerMs]
);
return null;
}
useRef
function TextInputWithFocusButton() {
// inicializa con null, pero dile a TypeScript que estamos buscando un HTMLInputElement
const inputEl = React.useRef<HTMLInputElement>(null);
const onButtonClick = () => {
// Los chequeos estrictos de null requieren que verifiquemos si inputEl y current existen.
// ¡Pero una vez que current existe, es de tipo HTMLInputElement, por lo que
// tiene el método focus! ✅
if (inputEl && inputEl.current) {
inputEl.current.focus();
}
};
return (
<>
{/* Adicionalmente, inputEl solo puede ser usada en elementos input. ¡Sí! */}
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
Ejemplo creado por Stefan Baumgartner
useReducer
Puedes usar Uniones discriminadas para las acciones del reductor. No olvides definir el tipo de retorno del reductor, de lo contrario Typescript lo inferirá.
type AppState = {};
type Action =
| { type: "SET_ONE"; payload: string }
| { type: "SET_TWO"; payload: number };
export function reducer(state: AppState, action: Action): AppState {
switch (action.type) {
case "SET_ONE":
return {
...state,
one: action.payload // `payload` es de tipo string
};
case "SET_TWO":
return {
...state,
two: action.payload // `payload` es de tipo number
};
default:
return state;
}
}
Hooks personalizados
Si estás retornando un array en tu Hook personalizdo, querrás evitar la inferencia de tipo dado que Typescript inferirá un tipo de unión (cuando lo que realmente quieres son diferentes tipos en cada posición del array). En cambio, utiliza aserciones const de TS 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; // infiere [boolean, typeof load] en lugar de (boolean | typeof load)[]
}
De esta forma, cuando desestructuras realmente obtienes el tipo adecuado dependiendo de la posición de la desestructuración.
Alternativa: Aserción de un tipo de retorno de tupla
Si tienes problemas con las aserciones const, también puedes hacer aserciones o definir los tipos de retorno de la función:
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>
];
}
Una función utilitaria que automáticamente asigne los tipos también puede ser de ayuda si escribes muchos Hooks personalizados:
function tuplify<T extends any[]>(...elements: T) {
return elements;
}
function useArray() {
const numberValue = useRef(3).current;
const functionValue = useRef(() => {}).current;
return [numberValue, functionValue]; // type is (number | (() => void))[]
}
function useTuple() {
const numberValue = useRef(3).current;
const functionValue = useRef(() => {}).current;
return tuplify(numberValue, functionValue); // type is [number, () => void]
}
Nota, sin embargo, que el equipo de React recomienda que los Hooks personalizados que retornan más de dos valores deberían usar objetos reales en lugar de tuplas.
Lecturas adicionales sobre Hooks + Typescript:
- https://medium.com/@jrwebdev/react-hooks-in-typescript-88fce7001d0d
- https://fettblog.eu/typescript-react/hooks/#useref
Si estás escribiendo una biblioteca de Hooks de React, no olvides que deberías exponer tus tipos para que los usuarios los usen.
Ejemplos de bibliotecas con Hooks de React + Typescript
- https://github.com/mweststrate/use-st8
- https://github.com/palmerhq/the-platform
- https://github.com/sw-yx/hooks
Algo que añadir? Abre un issue.
Dentro de Typescript, React.Component
es un tipo genérico (alias React.Component<PropType, StateType>
), por lo que quieres añadirle parámetros (opcionales) de prop y estado:
type MyProps = {
// usar `interface` también está bien
message: string;
};
type MyState = {
count: number; // así
};
class App extends React.Component<MyProps, MyState> {
state: MyState = {
// segunda anotación opcional para una mejor inferencia de tipos
count: 0
};
render() {
return (
<div>
{this.props.message} {this.state.count}
</div>
);
}
}
No olvides que puedes exportar/importar/extender estos tipos/interfaces para su reutilización.
¿Por qué anotar `state` dos veces?
No es estrictamente necesario antotar la propiedad de clase state
, pero permite una mejor inferencia de tipos cuando se accede a this.state
y también al inicializar el estado.
Esto ocurre porque funcionan de dos formas diferentes, el segundo parámetro del tipo genérico permitirá que this.setState()
funcione correctamente, porque ese método viene de la clase base, pero al inicializar state
dentro del componente sobreescribe la implementación base por lo que te tienes que asegurar de decirle al compilador que en realidad no estás haciendo nada distinto.
No hay necesidad de anotar con readonly
A menudo ves código en ejemplos que incluyen readonly
para marcar las props y el estado como inmutables:
type MyProps = {
readonly message: string;
};
type MyState = {
readonly count: number;
};
Esto no es necesario, porque React.Component<P,S>
ya los marca como inmutables. (¡Consulta el PR y el debate!)
Métodos de clase: Hazlo como de costumbre, pero solo recuerda que cualquier argumento para tus funciones también debe tener tipos:
class App extends React.Component<{ message: string }, { count: number }> {
state = { count: 0 };
render() {
return (
<div onClick={() => this.increment(1)}>
{this.props.message} {this.state.count}
</div>
);
}
increment = (amt: number) => {
// así
this.setState(state => ({
count: state.count + amt
}));
};
}
Propiedades de clase: Si necesitas declarar propiedades de clase para un uso posterior, simplemente declaralas como state
, pero sin asignación:
class App extends React.Component<{
message: string;
}> {
pointer: number; // así
componentDidMount() {
this.pointer = 3;
}
render() {
return (
<div>
{this.props.message} and {this.pointer}
</div>
);
}
}
¿Algo que añadir? Abre un issue.
Para Typescript 3.0+, la inferencia de tipos debe funcionar, sin embargo algunos casos extremos aún son problemáticos. Simplemente asigna tus tipos como de costumbre, excepto que no uses React.FC
.
// ////////////////
// componentes de función
// ////////////////
type Props = { age: number } & typeof defaultProps;
const defaultProps = {
who: "Johny Five"
};
const Greet = (props: Props) => {
/*...*/
};
Greet.defaultProps = defaultProps;
Para los componentes de clase, hay un par de formas de hacerlo (incluido el uso del tipo utilitario Pick
) pero la recomendación es "revertir" la definición de las props:
type GreetProps = typeof Greet.defaultProps & {
age: number;
};
class Greet extends React.Component<GreetProps> {
static defaultProps = {
name: "world"
};
/*...*/
}
// ¡Hay chequeo de tipos! ¡No se requieren aserciones de tipo!
let el = <Greet age={3} />;
¿Por qué React.FC interfiere con las defaultProps?
Puedes chequear los debates aquí:
- https://medium.com/@martin_hotell/10-typescript-pro-tips-patterns-with-or-without-react-5799488d6680
- DefinitelyTyped/DefinitelyTyped#30695
- typescript-cheatsheets/react#87
Esto es solo el estado actual y podría ser solucionado en el futuro.
Typescript 2.9 y versiones anteriores
Para Typescript 2.9 y versiones anteriores, hay más de una forma de hacerlo, pero el mejor consejo que hemos visto hasta ahora es:
type Props = Required<typeof MyComponent.defaultProps> & {
/* additional props here */
};
export class MyComponent extends React.Component<Props> {
static defaultProps = {
foo: "foo"
};
}
Nuestra recomendación anterior usaba la funcionalidad Partial type
de Typescript, que significa que la interfaz actual cumplirá con una versión parcial de la interfaz envuelta. ¡De esa forma podemos extender defaultProps sin ningún cambio en los tipos!
interface IMyComponentProps {
firstProp?: string;
secondProp: IPerson[];
}
export class MyComponent extends React.Component<IMyComponentProps> {
public static defaultProps: Partial<IMyComponentProps> = {
firstProp: "default"
};
}
El problema con este enfoque es que causa complejos problemas con la inferencia de tipos en conjunto con JSX.LibraryManagedAttributes
. Básicamente ocasiona que el compilador piensa que cuando se crea una expresión JSX con ese componente, todas sus props son opcionales.
¿Algo que añadir? Abre un issue.
Las interfaces (interface
) son diferentes de los tipos (type
) en TypeScript, pero tienen usos muy similares en lo que concierne a los casos de usos comunes en React. Esta es una regla útil:
- siempre usa
interface
para las definiciones de API pública cuando se está escribiendo una biblioteca o las definiciones de tipos de ambiente de una biblioteca de terceros.
_ considera usar type
para las props y estado de tus componentes de React, porque tiene más restricciones.
Los tipos son útiles para los tipos de unión (p. ej. type MyType = TypeA | TypeB
) mientras las interfaces son mejores para declarar formas de dicionario y luego implementarlas
o extenderlas
.
Tabla útil para tipos vs. interfaces
Es un tema con muchos matices, no te preocupes demasiado por esto. He aquí un gráfico útil:(fuente: Karol Majewski)
¿Algo que añadir? Abre un issue.
type AppProps = {
message: string;
count: number;
disabled: boolean;
/** ¡array de un tipo! */
names: string[];
/** literales de string que especifican valores exactos de string, con un tipo de unión que los une */
status: "waiting" | "success";
/** cualquier objeto siempre y cuando no utilices sus propiedades (noi es común) */
obj: object;
obj2: {}; // casi igual que `object`, exactamente los mismo que `Object`
/** un objeto con propiedades definidas (preferido) */
obj3: {
id: string;
title: string;
};
/** ¡array de objetos! (común) */
objArr: {
id: string;
title: string;
}[];
/** cualquier función siempre y cuando no la invoques (no recomendado) */
onSomething: Function;
/** función que no toma ni devuelve nada (MUY COMÚN) */
onClick: () => void;
/** función con una prop nombrada (MUY COMÚN) */
onChange: (id: number) => void;
/** sintaxis alternativa de tipo función que toma un evento (MUY COMÚN) */
onClick(event: React.MouseEvent<HTMLButtonElement>): void;
/** una prop opcional (!MUY COMÚN!) */
optional?: OptionalType;
};
Nota que hemos usado /** commentarios */
con estilo TSDoc para cada prop. Puedes y te recomendamos dejar comentarios descriptivos en los componentes reutilizables. Para un ejemplo más completo y comentarios, consulta nuestra sección de comentar componentes en la guía avanzada.
export declare interface AppProps {
children1: JSX.Element; // mal, no tiene en cuenta los arreglos
children2: JSX.Element | JSX.Element[]; // más o menos, no acepta funciones
children3: React.ReactChildren; // a pesar del nombre, no es para nada apropieado; es un utilitario
children4: React.ReactChild[]; // mejor
children: React.ReactNode; // el mejor, acepta todo
functionChildren: (name: string) => React.ReactNode; // tipo recomendado para render prop del tipo función como hijo
style?: React.CSSProperties; // para pasar props de estilo
onChange?: React.FormEventHandler<HTMLInputElement>; // ¡Eventos de formulario! el parámetro genérico es el tipo de event.target
props: Props & React.PropsWithoutRef<JSX.IntrinsicElements["button"]>; // para imitar todas las props de un elemento button sin su ref
}
¿JSX.Element vs. React.ReactNode?
Citando a @ferdaber: Una explicación más técnica consiste en que un nodo válido de React no es lo mismo que lo que retorna React.createElement
. Sin importar el componente que se termina renderizando, React.createElement
siempre retorna un objeto, que es la interfaz JSX.Element
, pero React.ReactNode
es el conjunto de todos los posibles valores de retorno de un componente.
JSX.Element
-> Valor de retorno deReact.createElement
React.ReactNode
-> Valor de retorno de un componente
Más discusiones: Where ReactNode does not overlap with JSX.Element
¿Algo que añadir? Abre un issue.
Antes de que comiences a usar getDerivedStateFromProps
, por favor consulta la documentación y Probablemente no necestas estado derivado. El estado derivado puede conseguirse facilmente utilizando Hooks, los que también pueden ayudar a configurar fácilmente la memoización.
Aquí hay algunas formas en las que puedes anotar getDerivedStateFromProps
- Si has establecido explícitamente los tipos de tu estado derivado y quieres asegurarte de que el valor de retorno de
getDerivedStateFromProps
lo cumple.
class Comp extends React.Component<Props, State> {
static getDerivedStateFromProps(
props: Props,
state: State
): Partial<State> | null {
//
}
}
- Cuando quieres que el valor de retorno de la función determine tu estado.
class Comp extends React.Component<
Props,
ReturnType<typeof Comp["getDerivedStateFromProps"]>
> {
static getDerivedStateFromProps(props: Props) {}
}
- Cuando quieres estado derivado con otros campos de estado y memoización
type CustomValue = any;
interface Props {
propA: CustomValue;
}
interface DefinedState {
otherStateField: string;
}
type State = DefinedState & ReturnType<typeof transformPropsToState>;
function transformPropsToState(props: Props) {
return {
savedPropA: props.propA, // guardar para memoización
derivedState: props.propA
};
}
class Comp extends React.PureComponent<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
otherStateField: "123",
...transformPropsToState(props)
};
}
static getDerivedStateFromProps(props: Props, state: State) {
if (isEqual(props.propA, state.savedPropA)) return null;
return transformPropsToState(props);
}
}
Si el rendimiento no es un problema, tener los manejadores de eventos en línea es la forma más fácil dado que simplemente puedes usar la inferencia de tipos y el tipado contextual:
const el = (
<button
onClick={event => {
/* ... */
}}
/>
);
Pero si necesitas definir tus manejadores de eventos de forma separada, las herramientas del IDE pueden ser útiles en este caso, dado que las definiciones de @type vienen con muchos tipos. Escribe lo que estás buscando y usualmente el autocompletamiento te ayudará. Aquí se muestra el caso de onChange
para un evento de formulario:
class App extends React.Component<
{},
{
// sin props
text: string;
}
> {
state = {
text: ""
};
// anotar el tipo en la parte DERECHA de =
onChange = (e: React.FormEvent<HTMLInputElement>): void => {
this.setState({ text: e.currentTarget.value });
};
render() {
return (
<div>
<input type="text" value={this.state.text} onChange={this.onChange} />
</div>
);
}
}
En lugar de anotar los tipos de los argumentos y los valore de retorno con React.FormEvent<>
y void
, alternativamente puedes aplicar los tipos al manejador de eventos mismo (contribución de @TomasHubelbauer):
// anotar el tipo en la parte IZQUIERDA de =
onChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
this.setState({text: e.currentTarget.value})
}
¿Por qué dos formas de hacer lo mismo?
El primer método utiliza una firma inferida para el método (e: React.FormEvent<HTMLInputElement>): void
y la segunda fuerza un tipo del delegado proporcioanado por @types/react
. O sea que React.ChangeEventHandler<>
es simplemente un tipo "bendecido" por @types/react
, mientras puedes ver el método inferido como más... hecho artesanalmente. De cualquier forma es un buen patrón a conocer. Mira nuestro PR de Github PR para más información.
Tipado de onSubmit, con componentes no controlados en un formulario
Si no te importa mucho el tipo del evento, simplemente puedes utilizar React.SyntheticEvent. Si tu formulario objetivo tiene inputs con nombres personalizados a los que quisieras acceder, puedes utilizar la extensión de tipos:
<form
ref={formRef}
onSubmit={(e: React.SyntheticEvent) => {
e.preventDefault();
const target = e.target as typeof e.target & {
email: { value: string };
password: { value: string };
};
const email = target.email.value; // typechecks!
const password = target.password.value; // typechecks!
// etc...
}}
>
<div>
<label>
Email:
<input type="email" name="email" />
</label>
</div>
<div>
<label>
Password:
<input type="password" name="password" />
</label>
</div>
<div>
<input type="submit" value="Log in" />
</div>
</form>
Por supuesto, si estás construyendo algún tipo de formulario de consideración, deberías usar Formik, que está escrito en TypeScript.
Usar React.createContext
y getters de contexto para hacer un createCtx
sin defaultValue
, pero sin tener que chequear por undefined
:
// Crea un contexto sin un valor por defecto inicial
// sin tener que hacer chequeos de undefined todo el tiempo
function createCtx<A>() {
const ctx = React.createContext<A | undefined>(undefined);
function useCtx() {
const c = React.useContext(ctx);
if (!c) throw new Error("useCtx must be inside a Provider with a value");
return c;
}
return [useCtx, ctx.Provider] as const; // haz que TypeScript infiera una tupla, no un arreglo de tipos de unión
}
// uso
export const [useCtx, SettingProvider] = createCtx<string>(); // Se especifica el tipo, ¡pero sin necesidad de especificar el valor de antemano!
export function App() {
const key = useCustomHook("key"); // Se obtiene un valor de un hook, debe estar en un componente
return (
<SettingProvider value={key}>
<Component />
</SettingProvider>
);
}
export function Component() {
const key = useCtx(); // ¡Aún se puede usar sin chequeos de null!
return <div>{key}</div>;
}
Usando React.createContext
y useContext
para hacer un createCtx
con setters de contexto al estilo unstated
:
export function createCtx<A>(defaultValue: A) {
type UpdateType = React.Dispatch<React.SetStateAction<typeof defaultValue>>;
const defaultUpdate: UpdateType = () => defaultValue;
const ctx = React.createContext({
state: defaultValue,
update: defaultUpdate
});
function Provider(props: React.PropsWithChildren<{}>) {
const [state, update] = React.useState(defaultValue);
return <ctx.Provider value={{ state, update }} {...props} />;
}
return [ctx, Provider] as const; // alternativamente, [typeof ctx, typeof Provider]
}
// uso
const [ctx, TextProvider] = createCtx("someText");
export const TextContext = ctx;
export function App() {
return (
<TextProvider>
<Component />
</TextProvider>
);
}
export function Component() {
const { state, update } = React.useContext(TextContext);
return (
<label>
{state}
<input type="text" onChange={e => update(e.target.value)} />
</label>
);
}
Un versión basada en useReducer también puede resultar útil.
Contexto mutable usando un envoltorio de componente de clase
Contribuido por: @jpavon
interface ProviderState {
themeColor: string;
}
interface UpdateStateArg {
key: keyof ProviderState;
value: string;
}
interface ProviderStore {
state: ProviderState;
update: (arg: UpdateStateArg) => void;
}
const Context = React.createContext({} as ProviderStore); // type assertion on empty object
class Provider extends React.Component<{}, ProviderState> {
public readonly state = {
themeColor: "red"
};
private update = ({ key, value }: UpdateStateArg) => {
this.setState({ [key]: value });
};
public render() {
const store: ProviderStore = {
state: this.state,
update: this.update
};
return (
<Context.Provider value={store}>{this.props.children}</Context.Provider>
);
}
}
const Consumer = Context.Consumer;
¿Algo que añadir? Abre un issue.
Consulta la sección de Hooks para useRef
.
createRef
:
class CssThemeProvider extends React.PureComponent<Props> {
private rootRef = React.createRef<HTMLDivElement>(); // así
render() {
return <div ref={this.rootRef}>{this.props.children}</div>;
}
}
forwardRef
:
type Props = { children: React.ReactNode; type: "submit" | "button" };
export type Ref = HTMLButtonElement;
export const FancyButton = React.forwardRef<Ref, Props>((props, ref) => (
<button ref={ref} className="MyClassName" type={props.type}>
{props.children}
</button>
));
Si estás obteniendo las props de un componente que pasa las refs, usa ComponentPropsWithRef
.
Más información: https://medium.com/@martin_hotell/react-refs-with-typescript-a32d56c4d315
También puede que quieras hacer Renderizado condicional con forwardRef
.
¿Algo que añadir? Abre un issue.
Uso de ReactDOM.createPortal
:
const modalRoot = document.getElementById("modal-root") as HTMLElement;
// asumiento que tu archivo html tiene un div con id 'modal-root';
export class Modal extends React.Component {
el: HTMLElement = document.createElement("div");
componentDidMount() {
modalRoot.appendChild(this.el);
}
componentWillUnmount() {
modalRoot.removeChild(this.el);
}
render() {
return ReactDOM.createPortal(this.props.children, this.el);
}
}
Contexto del ejemplo
Este ejemplo está basado en el ejemplo Event Bubbling Through Portal de la documentación de React.
No escrito aún.
¿Algo que añadir? Abre un issue.
No escrito aún. observa https://github.com/sw-yx/fresh-async-react para más sobre React Suspense y Time Slicing.
¿Algo que añadir? Abre un issue.
⚠️ ¿Has leído la página de preguntas frecuentes de TypeScript?) !Tu respuesta podría estar allí!
¿Te encuentras ante extraños errores de tipo? No estás solo. Esta es la parte más difícil de usar TypeScript con React. Sé paciente (después de todo estás aprendiendo un nuevo lenguaje). Sin embargo, mientras mejor te vuelvas en esto, ¡menos tiempo estarás trabajando en contra del compilador y más tiempo estará el compilador trabajando para ti!
Intenta evitar asignar el tipo any
tanto como te sea posible para experimentar los máximos beneficios de TypeScript. En cambio, intentemos familiarizarnos con algunas estrategias comunes para resolver esos problemas.
Los tipos de unión son útiles para resolver algunos de estos problemas con el tipado:
class App extends React.Component<
{},
{
count: number | null; // así
}
> {
state = {
count: null
};
render() {
return <div onClick={() => this.increment(1)}>{this.state.count}</div>;
}
increment = (amt: number) => {
this.setState(state => ({
count: (state.count || 0) + amt
}));
};
}
Seguro de tipo: A veces los tipos de unión resuelven el problema en un área, pero crean otro debajo en la cadena. Si A
y B
son ambos tipos de objeto, A | B
no es "o bien A o bien B", es "A o B (o ambos)", lo que causa alguna confusión si esperabas que fuera lo primero. Aprende como escribir chequeos, seguros, y aserciones (también consulta la sección de renderizado condicional debajo). Por ejemplo:
interface Admin {
role: string;
}
interface User {
email: string;
}
// Método 1: usar la palabra clave `in`
function redirect(user: Admin | User) {
if ("role" in user) {
// Usa el operador `in` para seguros de tipo desde TS 2.7+
routeToAdminPage(user.role);
} else {
routeToHomePage(user.email);
}
}
// Método 2: Seguro de tipo personalizado, hace lo mismo en versiones anteriores de TS o donde `in` no es suficiente
function isAdmin(user: Admin | User): user is Admin {
return (user as any).role !== undefined;
}
El método 2 también se conoce como Seguros de tipo definidos por el usuario y puede ser muy útil para obtener un código legible. Así es como TS mismo refina tipos con typeof
e instanceof
.
Si necesitas, en cambio, cadenas de if...else
o la sentencia switch
, debería "simplemente funcionar", pero busca Uniones discriminadas si necesitas ayuda. (Ve también: El escrito de Basarat). Es útil para asignar tipos en useReducer
o Redux.
Si un componente tiene una prop opcional, añade un signo de interrogación y asigna durante las desestructuración (o usa defaultProps).
class MyComponent extends React.Component<{
message?: string; // así
}> {
render() {
const { message = "default" } = this.props;
return <div>{message}</div>;
}
}
También puedes usar un carácter !
para asegurar que algo no es undefined, pero no se recomienda.
Something to add? File an issue with your suggestions!
Las Enums en TypeScript se convierten por defecto a números. Usualmente querrás usarlas en cambio como strings:
export enum ButtonSizes {
default = "default",
small = "small",
large = "large"
}
Uso:
export const PrimaryButton = (
props: Props & React.HTMLProps<HTMLButtonElement>
) => <Button size={ButtonSizes.default} {...props} />;
Una alternativa más simple a los enum es simplemente declarar strings como una unión:
export declare type Position = "left" | "right" | "top" | "bottom";
Esto es útil, porque TypeScript lanzará errores cuando escribas mal un string para tus props.
En ocasiones que TypeScript no tiene la razón y que el tipo que estás usando es más restringido que lo que cree, o que los tipos de unión necesitan una aserción con un tipo más especifico para trabajar con otras APIs, para ello hazlo con la palabra clave as
. Esto le dice al razón que tienes la razón.
class MyComponent extends React.Component<{
message: string;
}> {
render() {
const { message } = this.props;
return (
<Component2 message={message as SpecialMessageType}>{message}</Component2>
);
}
}
Nota que no puedes hacer aserciones a tu forma a cualquier cosa (básicamente es solo para refinar tipos). Por tanto no es lo mismo que hacer "casting" a un tipo.
También puedes hacer una aserción de que una propiedad no es null al acceder a ella:
element.parentNode!.removeChild(element) // ! antes del punto
myFunction(document.getElementById(dialog.id!)! // ! después de acceder a la propiedad
let userID!: string // aserción de asignación definitiva... ten cuidado!
Por supuesto, intenta realmente manejar el caso null en lugar de hacer la aserción. :)
El tipado estructural de TS es útil, hasta que es inconveniente. Sin embargo puedes simular el tipado nominal usando marca de tipo
:
type OrderID = string & { readonly brand: unique symbol };
type UserID = string & { readonly brand: unique symbol };
type ID = OrderID | UserID;
Podemos crear estos valores con el patrón de objeto acompañante:
function OrderID(id: string) {
return id as OrderID;
}
function UserID(id: string) {
return id as UserID;
}
Ahora TypeScript no permitirá que uses el ID incorrecto en un lugar determinado:
function queryForUser(id: UserID) {
// ...
}
queryForUser(OrderID("foobar")); // Error, Argument of type 'OrderID' is not assignable to parameter of type 'UserID'
En el futuro podrás uar la palabra clave unique
para marcar el tipo. Consulta este PR.
Añadir dos tipos juntos puede ser útil, por ejemplo cuando se supone que tu componente refleje las props de un componente nativo como button
:
export interface Props {
label: string;
}
export const PrimaryButton = (
props: Props & React.HTMLProps<HTMLButtonElement> // se añaden mis Props junto con las props de button provistas por @types/react
) => <Button {...props} />;
También puedes usar tipos de intersección para hacer subconjuntos reutilizables de props para componentes similares:
type BaseProps = {
className?: string,
style?: React.CSSProperties
name: string // used in both
}
type DogProps = {
tailsCount: number
}
type HumanProps = {
handsCount: number
}
export const Human: React.FC<BaseProps & HumanProps> = // ...
export const Dog: React.FC<BaseProps & DogProps> = // ...
Asegúrate de no confundir los tipos de intersección (que son operaciones y) con los tipos de unión (que son operaciones o).
Esta sección aún no se ha escrito. (¡Por favor contribuye!). Mientras tanto, consulta nuestro comentario sobre los casos de uso de los tipos de unión.
La cheatsheet AVANZADA también tiene información sobre los tipos discriminados de unión, que son útiles cuando TypeScript no parece estar reduciendo tu tipo de unión como esperas.
Específicamente en lo concerniente a las funciones, puede que necesites sobreescribir en lugar de hacer una unión de tipos. La forma más común en que se escriben los tipos de función es la forma abreviada:
type FunctionType1 = (x: string, y: number) => number;
Pero esto no te permite hacer niguna sobrecarga. Si tienes la implementación, puedes poner los tipos detrás de cada elemento con la palabra clave function
:
function pickCard(x: { suit: string; card: number }[]): number;
function pickCard(x: number): { suit: string; card: number };
function pickCard(x): any {
// implementation with combined signature
// ...
}
Sin embargo, si no tienes ninguna implementación y solo estás escribiendo un archivo de definición d.ts
, esto tampoco ayudará. En este caso puedes renunciar a las formas abreviadas y escribirlo a la antigua. La clave es recordar que en lo que concierne a TypeScript, las funciones son solo objetos invocables sin llaves
:
type pickCard = {
(x: { suit: string; card: number }[]): number;
(x: number): { suit: string; card: number };
// no hay necesidad de una firma combinada en esta forma
// también puedes añadir tipos a las propiedades estáticas de funciones aquí ej. `pickCard.wasCalled`
};
Nota que cuando implementes la función sobrecargada en sí, la implementación necesitará declarar la firma combinada de la llamada que estás manejando, no lo inferirá por ti. Puedes ver ejemplos fácilmente de sobrecargas en las APIs del DOM, p. ej. createElement
.
Lee más sobre la sobrecarga en el manual.
Apoyarse en la inferencia de tipos de TypeScript es genial... hasta que te das cuenta de que necesiotas un tipo que fue inferido, y tienes que volver y declarar explícitamente los tipos o interfaces y así puedas exportarlos para su reutilización.
Afortunadamente, con typeof
, no tienes que hacerlo. Simplemente úsalo sobre cualquier valor:
const [state, setState] = React.useState({
foo: 1,
bar: 2
}); // el tipo state se infirió como {foo: number, bar: number}
const someMethod = (obj: typeof state) => {
// obteniendo el tipo del estado aún cuando fue inferido
// algún código usando obj
setState(obj); // funciona
};
Trabajar con porciones del estado o las props es común en React. Nuevamente, no necesitas realmente ir y redefinir explícitamente tus tipos si utilizas el tipo genérico Partial
:
const [state, setState] = React.useState({
foo: 1,
bar: 2
}); // el tipo inferido de state es {foo: number, bar: number}
// NOTA: La mezcla de estado anterior en realidad no se recomienda con React.useState
// aquí solo estamos demostrando como usar Partial
const partialStateUpdate = (obj: Partial<typeof state>) =>
setState({ ...state, ...obj });
// luego...
partialStateUpdate({ foo: 2 }); // funciona
Advertencias menores al usar Partial
Nota que hay algunos usuarios de TS que no están de acuerdo con usar Partial
como se comporta hoy. Mira sútiles problemas del ejemplo anterior aquí, y consulta este largo debate sobre por qué @types/react usa Pick en lugar de Partial.
Esto puede ser molesto, ¡pero hay forma de obtener los tipos!
- Obtener los tipos de las props de un componente:
React.ComponentProps
ytypeof
, y opcionalementeOmit
para omitir cualquier tipo solapado.
import { Button } from "library"; // ¡Pero no exporta ButtonProps! ¡Ay, no!
type ButtonProps = React.ComponentProps<typeof Button>; // !No hay problema! ¡Obtén el tuyo propio!
type AlertButtonProps = Omit<ButtonProps, "onClick">; // modifícalo
const AlertButton: React.FC<AlertButtonProps> = props => (
<Button onClick={() => alert("hello")} {...props} />
);
También puedes usar ComponentPropsWithoutRef
(en lugar de ComponentProps) y ComponentPropsWithRef
(si tu componente específicamente pasa las refs)
- Obtener el tipo de retorno de una función: utiliza
ReturnType
:
// dentro de alguna biblioteca - el tipo de retorno { baz: number } se infiere, pero no se exporta
function foo(bar: string) {
return { baz: 1 };
}
// dentro de tu aplicación, si necesitas { baz: number }
type FooReturn = ReturnType<typeof foo>; // { baz: number }
De hecho, puedes obtener virtualmente cualquier cosa que sea pública: mira esta publicación de Ivan Koshelev
function foo() {
return {
a: 1,
b: 2,
subInstArr: [
{
c: 3,
d: 4
}
]
};
}
type InstType = ReturnType<typeof foo>;
type SubInstArr = InstType["subInstArr"];
type SubIsntType = SubInstArr[0];
let baz: SubIsntType = {
c: 5,
d: 6 // ¡Se chequea correctamente el tipo!
};
// Podrías escribirlo en una sola línea,
// pero por favor asegúrate de que sea legible hacia adelante
// (lo puedes entender al leerlo una vez de izquierda a derecha sin saltos)
type SubIsntType2 = ReturnType<typeof foo>["subInstArr"][0];
let baz2: SubIsntType2 = {
c: 5,
d: 6 // ¡Se chequea correctamente el tipo!
};
Utiliza mezcla de declaraciones:
// declaration.d.ts
// en cualquier lugar de tu proyecto, NO el mismo nombre de cualquiera de tus archivos .ts/tsx
declare module "*.png";
// importación en un archivo tsx
import * as logo from "./logo.png";
Nota que tsc
no puede empaquetar estos archivos por ti, tendrás que usar Webpack o Parcel.
Issue relacionado: microsoft/TypeScript-React-Starter#12 y StackOverflow
typeof
yinstanceof
: consulta de tipos usadas para refinarkeyof
: obtiene llaves de un objetoO[K]
: búsqueda de una propiedad[K in O]
: tipos mapeados+
o-
oreadonly
o?
: modificadores de adición, substracción, solo lectura y opcionalidadx ? Y : Z
: Tipos condicionales para tipos genéricos, alias de tipo y tipos de parámetros de función!
: Aserción de no nulidad para tipos que pueden ser nulos=
: Valor por defecto del tipo paramétrico genérico en tipos genéricosas
: aserción de tipois
: protección de tipo para tipos de retorno de funciones
Los tipos condicionales son un tema difícil de comprender, por lo que aquí hay algunos recursos extra:
- Explicación completa paso a paso https://artsy.github.io/blog/2018/11/21/conditional-types-in-typescript/
- Bail out y otros temas avanzados https://github.com/sw-yx/ts-spec/blob/master/conditional-types.md
Todos estos están incorporados, mira el código en es5.d.ts:
ConstructorParameters
: una tupla de los tipos de los parámetros del constructorExclude
: excluye un tipo de otro tipoExtract
: selecciona un subtipo que es asignable a otro tipoInstanceType
: el tipo de instancia que obtienes al hacernew
anew
a un constructor de claseNonNullable
: excluyenull
yundefined
de un tipoParameters
: una tupla de los tipos de los parámetros de una funciónPartial
: Hace que todas las propiedades en un objeto sean opcionalesReadonly
: Hace que todas las propiedades en un objeto sean de solo lecturaReadonlyArray
: Hace un arreglo inmutable del tipo dadoPick
: Un subtipo de un tipo de objeto con un subconjunto de sus llavesRecord
: Un mapeo de un tipo de llave a un tipo de valorRequired
: Hace todas que todas las propiedades de un objeto sean requeridasReturnType
El tipo de retorno de una función
Nota: TSLint está en modo de mantenimiento y ESLint es el camino a seguir para TypeScript. Puedes convertir TSlint a ESlint con esta herramienta.
Esta sección necesita escribirse, pero probablemente puedes encontrar un buen punto para comenzar en la configuración de ESLint de Wes Bos (que viene con una introducción en YouTube).
¡Luego, revisa las secciones sobre Prettier y Linting cheatsheet AVANZADA!
Puedes encontrar todas las opciones del compilador en la documentación de TypeScript. Esta es la configuración que utilizo en APLICACIONES (no para bibliotecas; para bibliotecas puede que quieras ver las configuraciones que usamos en tsdx
):
{
"compilerOptions": {
"incremental": true,
"outDir": "build/lib",
"target": "es5",
"module": "esnext",
"lib": ["dom", "esnext"],
"sourceMap": true,
"importHelpers": true,
"declaration": true,
"rootDir": "src",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"allowJs": false,
"jsx": "react",
"moduleResolution": "node",
"baseUrl": "src",
"forceConsistentCasingInFileNames": true,
"esModuleInterop": true,
"suppressImplicitAnyIndexErrors": true,
"allowSyntheticDefaultImports": true,
"experimentalDecorators": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "build", "scripts"]
}
Por favor abre un issue y expresa tu opinión sobre si existen mejores elecciones a recomendar para React.
Banderas seleccionadas y por qué nos gustan:
esModuleInterop
: deshabilita imports de espacios de nombre (import * as foo from "foo"
) y habilita imports al estilo de CJS/AMD/UMD (import fs from "fs"
)strict
:strictPropertyInitialization
te obliga a inicializar las propiedades de clase o declarar explícitamente que pueden no estar definidas. Puedes evitarlo usando una aserción de asignación definitiva."typeRoots": ["./typings", "./node_modules/@types"]
: Por defecto, TypeScript busca ennode_modules/@types
y directorios ancestros para buscar declaraciones de tipos de terceros. Podrías querer sobreescribir esta resolución por defecto de manera tal de que pudieras poner todas tus declaraciones globales de tipos en un directorio especialtypings
.
La velocidad de la compilación crece linealmente con el tamaño de la base de código. Para proyectos grandes, querrás usar Referencias de proyecto. Mira nuestra cheatsheet AVANZADA para comentarios.
Si te encuentras errores en los tipos de tu biblioteca oficial, puedes copiarlos localmente y decirle a TypeScript que utilice tu versión local usando el campo "paths". En tu tsconfig.json
:
{
"compilerOptions": {
"paths": {
"mobx-react": ["../typings/modules/mobx-react"]
}
}
}
Gracias a @adamrackis for el consejo.
Si solo quieres añadir una interfaz, o añadir miembros que faltan a una interfaz existente, no necesitas copiar todo el paquete de tipos. En cambio, puedes utilizar la mezcla de declaraciones:
// my-typings.ts
declare module "plotly.js" {
interface PlotlyHTMLElement {
removeAllListeners(): void;
}
}
// MyComponent.tsx
import { PlotlyHTMLElement } from "plotly.js";
const f = (e: PlotlyHTMLElement) => {
e.removeAllListeners();
};
No siempre tienes que implementar el módulo, puedes simplementer importar el módulo como any
para un inicio rápido:
// my-typings.ts
declare module "plotly.js"; // cada uno de estas importaciones son `any`
Dado que no tienes que explícitamente exportarlo, se conoce como declaración de módulo de ambiente. puedes hacer una declaración de módulo de ambiente en un archivo .ts
en modo de script (sin imports o exports), o en un archivo .d.ts
en cualquier lugar de tu proyecto.
También puedes hacer declaraciones de variables de ambiente y de tipos de ambiente:
// tipo utilitario de ambiente
type ToArray<T> = T extends unknown[] ? T : T[];
// variable de ambiente
declare let process: {
env: {
NODE_ENV: "development" | "production";
};
};
process = {
env: {
NODE_ENV: "production"
}
};
También puedes ver ejemplos de ellos en las declaraciones de tipos incorporadas en el campo lib
de tsconfig.json
- https://github.com/jaredpalmer/formik
- https://github.com/jaredpalmer/react-fns
- https://github.com/palantir/blueprint
- https://github.com/Shopify/polaris
- https://github.com/NullVoxPopuli/react-vs-ember/tree/master/testing/react
- https://github.com/artsy/reaction
- https://github.com/benawad/codeponder (¡con programación en vivo!)
- https://github.com/artsy/emission (React Native)
- @reach/ui's community typings
Boilerplates de React:
- @jpavon/react-scripts-ts react-scripts alternativos con todas las funcionalidades de TypeScript usando ts-loader
- webpack config tool es una herramienta visual para la cración de proyectos de webpack con React y TypeScript
- https://github.com/innFactory/create-react-app-material-typescript-redux plantilla lista para usarse con Material-UI, enrutamiento y Redux
Boilerplates de React Native: contribución de @spoeck
- https://github.com/GeekyAnts/react-native-seed
- https://github.com/lopezjurip/ReactNativeTS
- https://github.com/emin93/react-native-template-typescript
- https://github.com/Microsoft/TypeScript-React-Native-Starter
- VSCode
- Extensión de VSCode de swyx: https://github.com/sw-yx/swyx-react-typescript-snippets
- amVim: https://marketplace.visualstudio.com/items?itemName=auiworks.amvim
- VIM
- https://github.com/Quramy/tsuquyomi
- ¿nvim-typescript?
- https://github.com/leafgarland/typescript-vim
- peitalin/vim-jsx-typescript
- NeoVim: https://github.com/neoclide/coc.nvim
- otros discusiones: https://mobile.twitter.com/ryanflorence/status/1085715595994095620
⚠️ Nota que TSLint ahora está en mantenimiento y deberías intentar usar ESLint en su lugar. Si está interesado en los consejos de TSLint, consulte este PR desde @azdanov. El resto de esta sección solo se centra en ESLint.
⚠️ Este es un tema en evolución.typescript-eslint-parser
ya no se mantiene y el trabajo ha comenzado recientemente sobretypescript-eslint
en la comunidad ESLint para lleve ESLint con TSLint.
Siga los documentos de TypeScript + ESLint en https://github.com/typescript-eslint/typescript-eslint:
yarn add -D @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint
agregue un script lint
a supackage.json
:
"scripts": {
"lint": "eslint 'src/**/*.ts'"
},
y en .eslintrc.json
adecuado:
{
"env": {
"es6": true,
"node": true,
"jest": true
},
"extends": "eslint:recommended",
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"parserOptions": {
"ecmaVersion": 2017,
"sourceType": "module"
},
"rules": {
"indent": ["error", 2],
"linebreak-style": ["error", "unix"],
"quotes": ["error", "single"],
"no-console": "warn",
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": [
"error",
{ "vars": "all", "args": "after-used", "ignoreRestSiblings": false }
],
"no-empty": "warn"
}
}
Esto está tomado de el tsdx
PR que es para bibliotecas.
Más opciones de .eslintrc.json
se pueden desear para aplicaciones:
{
"extends": [
"airbnb",
"prettier",
"prettier/react",
"plugin:prettier/recommended",
"plugin:jest/recommended",
"plugin:unicorn/recommended"
],
"plugins": ["prettier", "jest", "unicorn"],
"parserOptions": {
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
},
"env": {
"es6": true,
"browser": true,
"jest": true
},
"settings": {
"import/resolver": {
"node": {
"extensions": [".js", ".jsx", ".ts", ".tsx"]
}
}
},
"overrides": [
{
"files": ["**/*.ts", "**/*.tsx"],
"parser": "typescript-eslint-parser",
"rules": {
"no-undef": "off"
}
}
]
}
Puede leer una guía de configuración más completa de TypeScript + ESLint aquí de Matterhorn, en particular consulte https://github.com/MatterhornDev/learn-typescript-linting.
Si está buscando información sobre Prettier, consulte Prettier.
- !Yo! https://twitter.com/swyx
- https://github.com/piotrwitek/react-redux-typescript-guide - MUY MUY RECOMENDADO, escribí este repo antes de conocer sobre este, este tien muchas cosas que no cubro, incluyendo REDUX y JEST.
- Ultimate React Component Patterns with TypeScript 2.8
- Basarat's TypeScript gitbook has a React section con un curso de Egghead.io también.
- Palmer Group's Typescript + React Guidelines así como también otros trabajos de Jared como disco.chat
- Sindre Sorhus' TypeScript Style Guide
- TypeScript React Starter Template by Microsoft Una plantilla de inicio para TypeScript y React con un README detallado donde se describe cómo usarlos juntos. Nota: esto parece ya no actualizarse frecuentemente.
- Curso intermedio sobre React de Brian Holt en Frontend Masters (de pago) - Sección sobre convertir una aplicación a TypeScript
- Conversión a TypeScript:
- ¿Tú?.
- ¡Por favor ayuda a contribuir con esta nueva sección!
Lo creas o no, apenas hemos introducido TypeScript en esta cheatsheet. Hay un mundo complejo de loǵica de tipos genéricos al que eventualmente te enfrentarás, sin embargo se trata menos de lidiar con React que simplemente mejorar en TypeScript, y por eso está fuera de este ámbito. Pero al menos ahora ya puedes ser productivo en React :)
Vale la pena menicionar algunos recursos para inciarse:
- La sección de los 40+ ejemplos del playground, escrito por @Orta
- El resumen de TS de Anders Hejlsberg's: https://www.youtube.com/watch?v=ET4kT88JRXs
- Marius Schultz: https://blog.mariusschulz.com/series/typescript-evolution con un curso de Egghead.io
- La explicación profunda de Basarat: https://basarat.gitbooks.io/typescript/
- Rares Matei: El curso de Egghead.io Advanced Typescript es muy bueno para las funcionalidades más nuevas de TypeScript y aplicaciones prácticas de la lógica de tipos (p. ej. hacer recursivamente todas las propiedades de un tipo como
readonly
).
- Consulta la cheatsheet avanzada
- Abre un issue.
Este proyecto sigue la especificación de all-contributors. Mira CONTRIBUTORS.md para la lista completa. ¡Contribuciones de cualquier tipo son bienvenidas!