Skip to content

Latest commit

 

History

History
1801 lines (1383 loc) · 80 KB

README.md

File metadata and controls

1801 lines (1383 loc) · 80 KB

React+TypeScript Cheatsheets en Español

react + ts logo

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! 👍


All Contributors

All React + TypeScript Cheatsheets

  • 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.

Tabla de contenidos de la Cheatsheet básica

Expandir tabla de contenidos

Sección 1: Configuración

Prerrequisitos

  1. Buena comprensión de React
  2. Estar familiarizado con con los tipos de TypeScript básicos (La guía de 2ality es de ayuda)
  3. Haber leído la sección sobre TypeScript en la documentación oficial de React.
  4. 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>.

Herramientas de inicio de React + TypeScript

  1. Create React App v2.1+ con Typescript: npx create-react-app my-app --template typescript
  1. La guía de Basarat para una configuración manual de React + TypeScript + Webpack + Babel

Importar React

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!

Sección 2: Comienzo

Componentes de función

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, y defaultProps - Sin embargo, actualmente existen problemas conocidos cuando se usa defaultProps con React.FunctionComponent. Consulta este issue para los detalles (navega hacia nuestra sección sobre defaultProps 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ícito children (p.ej. DefinitelyTyped#33006), y podría considerarse de todas formas ser explícito en los componentes que consumen children.

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;

Consulta aquí el comentario de @ferdaber.

Hooks

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>
    </>
  );
}

Ver en TypeScript Playground

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;
  }
}

Ver en TypeScript Playground

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)[]
}

Ver en TypeScript Playground

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:

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

Algo que añadir? Abre un issue.

Componentes de clase

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>
    );
  }
}

Ver en TypeScript Playground

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.

Consulta el comentario de @ferdaber aquí.

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
    }));
  };
}

Ver en TypeScript Playground

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>
    );
  }
}

Ver en TypeScript Playground

¿Algo que añadir? Abre un issue.

Tipos de defaultProps

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í:

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.

Consulta el comentario de @ferdaber aquí.

¿Algo que añadir? Abre un issue.

¿Tipos o interfaces?

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:

https://pbs.twimg.com/media/DwV-oOsXcAIct2q.jpg (fuente: Karol Majewski)

¿Algo que añadir? Abre un issue.

Ejemplos básicos de Prop Types

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.

Ejemplos útiles de tipos de props en React

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 de React.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.

getDerivedStateFromProps

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

  1. 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 {
    //
  }
}
  1. 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) {}
}
  1. 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);
  }
}

Ver en TypeScript Playground

Formularios y Eventos

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>
    );
  }
}

Ver en TypeScript Playground

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>

Ver en TypeScript Playground

Por supuesto, si estás construyendo algún tipo de formulario de consideración, deberías usar Formik, que está escrito en TypeScript.

Contexto

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>;
}

Ver en TypeScript Playground

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>
  );
}

Ver en TypeScript Playground

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.

forwardRef/createRef

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.

Portales

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);
  }
}

Ver en TypeScript Playground

Contexto del ejemplo

Este ejemplo está basado en el ejemplo Event Bubbling Through Portal de la documentación de React.

Barreras de error

No escrito aún.

¿Algo que añadir? Abre un issue.

Concurrent React/React Suspense

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.

Manual básico de resolución de problemas: Tipos

⚠️ ¿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.

Tipos de unión y seguro de tipo

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
    }));
  };
}

Ver en TypeScript Playground

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;
}

Ver en TypeScript Playground

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.

Tipos opcionales

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!

Tipos Enum

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.

Aserción de tipo

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>
    );
  }
}

Ver en TypeScript Playground

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. :)

Simulación de tipos nominales

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.

Tipos de intersección

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> = // ...

Ver en TypeScript Playground

Asegúrate de no confundir los tipos de intersección (que son operaciones y) con los tipos de unión (que son operaciones o).

Tipos de unión

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.

Sobrecarga de tipos de función

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.

Uso de tipos inferidos

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
};

Uso de tipos parciales

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.

¡Los tipos que necesito no fueron exportados!

Esto puede ser molesto, ¡pero hay forma de obtener los tipos!

  • Obtener los tipos de las props de un componente: React.ComponentProps y typeof, y opcionalemente Omit 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!
};

Manual de resolución de problemas: Imágenes y otros archivos que no son TS/TSX

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

Manual de resolución de problemas: Operadores

  • typeof y instanceof: consulta de tipos usadas para refinar
  • keyof: obtiene llaves de un objeto
  • O[K]: búsqueda de una propiedad
  • [K in O]: tipos mapeados
  • + o - o readonly o ?: modificadores de adición, substracción, solo lectura y opcionalidad
  • x ? 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éricos
  • as: aserción de tipo
  • is: 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:

Manual de resolución de problemas: Utilitarios

Todos estos están incorporados, mira el código en es5.d.ts:

  • ConstructorParameters: una tupla de los tipos de los parámetros del constructor
  • Exclude: excluye un tipo de otro tipo
  • Extract: selecciona un subtipo que es asignable a otro tipo
  • InstanceType: el tipo de instancia que obtienes al hacer new a new a un constructor de clase
  • NonNullable: excluye null y undefined de un tipo
  • Parameters: una tupla de los tipos de los parámetros de una función
  • Partial: Hace que todas las propiedades en un objeto sean opcionales
  • Readonly: Hace que todas las propiedades en un objeto sean de solo lectura
  • ReadonlyArray: Hace un arreglo inmutable del tipo dado
  • Pick: Un subtipo de un tipo de objeto con un subconjunto de sus llaves
  • Record: Un mapeo de un tipo de llave a un tipo de valor
  • Required: Hace todas que todas las propiedades de un objeto sean requeridas
  • ReturnType El tipo de retorno de una función

Manual de resolución de problemas: ESLint

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!

Manual de resolución de problemas: tsconfig.json

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 en node_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 especial typings.

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.

Manual de resolución de problemas: Errores en tipos oficiales

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

Bases de código de React + TypeScript recomendadas para aprender

Boilerplates de React:

Boilerplates de React Native: contribución de @spoeck

Herramientas e integración en editores

Linting

⚠️ 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.

Otros recursos sobre React + TypeScript

Charlas recomendadas sobre React + TypeScript

  • ¡Por favor ayuda a contribuir con esta nueva sección!

Hora de realmente aprender TypeScript

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:

Aplicación de ejemplo

¡Mi pregunta no está respondida aquí!

Contribuidores

Este proyecto sigue la especificación de all-contributors. Mira CONTRIBUTORS.md para la lista completa. ¡Contribuciones de cualquier tipo son bienvenidas!