TypeScript y React: ¿tipo infantil?


137

Tengo un componente funcional muy simple de la siguiente manera:

import * as React from 'react';

export interface AuxProps  { 
    children: React.ReactNode
 }


const aux = (props: AuxProps) => props.children;

export default aux;

Y otro componente:

import * as React from "react";

export interface LayoutProps  { 
   children: React.ReactNode
}

const layout = (props: LayoutProps) => (
    <Aux>
        <div>Toolbar, SideDrawer, Backdrop</div>
        <main>
            {props.children}
        </main>
    <Aux/>
);

export default layout;

Sigo recibiendo el siguiente error:

[ts] El tipo de elemento JSX 'ReactNode' no es una función de constructor para elementos JSX. El tipo 'indefinido' no se puede asignar al tipo 'ElementClass'. [2605]

¿Cómo escribo esto correctamente?

Respuestas:


132

Sólo children: React.ReactNode


¡Esta es la respuesta correcta aquí! JSX.Elementno es lo suficientemente bueno ya que un niño React válido podría ser una cadena, un booleano, nulo ... también ReactChildestá incompleto por las mismas razones
Pierre Ferry

2
@PierreFerry El problema es que se puede asignar a casi cualquier cosaReactNode . No ayuda en términos de seguridad de tipos, similar a escribir childrencomo any: vea mi respuesta para obtener una explicación.
Ford04

Gracias por tu respuesta @ ford04. En mi caso de uso, quiero que los niños sean de tipo libre. Mi componente acepta cualquier tipo de hijos React válidos. Así que creo que esta sigue siendo la respuesta que estaba buscando :)
Pierre Ferry

47

Para usarlo <Aux>en su JSX, debe ser una función que regrese ReactElement<any> | null. Esa es la definición de un componente de función. .

Sin embargo, actualmente se define como una función que devuelve React.ReactNode, que es un tipo mucho más amplio. Como dicen los mecanografiados de React:

type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined;

Asegúrese de que los tipos no deseados se neutralicen envolviendo el valor devuelto en React Fragment ( <></>):

const aux: React.FC<AuxProps> = props =>
  <>{props.children}</>;

No entiendo por qué es necesario envolverlo childrenen un fragmento de reacción. ¿Le importaria explicar? En React es perfectamente válido solo return children;.
sanfilippopablo

1
No si childrenson una matriz. Si ese es el caso, debe envolverlos en un solo nodo o usar React Fragments.
Karol Majewski

Sí, en mi código tengo:, return React.Children.count(children) > 1 ? <>{children}</> : childrenpero mecanografiado se queja de eso. Lo reemplacé con solo <>{children}</>.
sanfilippopablo

2
Se queja porque childrenes una lista de elementos que contiene solo 1 elemento. Creo que funcionaría si lo return React.Children.count(children) > 1 ? <>{children}</> : children[0]hicieras @sanfilippopablo
tamj0rd2

Esto no parece funcionar en las versiones más recientes de React. Inicié sesión en la consola y puedo confirmar que tengo un accesorio para niños, pero incluso con los <React.Fragment>alrededores {props.children}no devuelvo nada.
Mark

34

Esto es lo que funcionó para mí:

interface Props {
  children: JSX.Element[] | JSX.Element
}

Editar , recomendaría usar en su children: React.ReactNodelugar ahora.


8
Esa no es una definición lo suficientemente amplia. El niño podría ser una cuerda.
Mike S

Al menos este ejemplo funcionó para mí, ya que se suponía que mi componente esperaba 1 o más JSX.Elements. ¡Gracias!
Italo Borges

1
Esto no funcionará con expresiones de JavaScript como elementos secundarios <MyComponent>{ condition ? <span>test</span> : null}</MyComponent>. El uso children: ReactNodefuncionará.
Nickofthyme

10

puedes declarar tu componente así:

const MyComponent: React.FunctionComponent = (props) => {
    return props.children;
}

9

Puede utilizar ReactChildreny ReactChild:

import React, { ReactChildren, ReactChild } from 'react';

interface AuxProps {
  children: ReactChild | ReactChildren;
}

const Aux = ({ children }: AuxProps) => (<div>{children}</div>);

export default Aux;


6

El tipo de retorno del componente de función está limitado a JSXElement | nullTypeScript. Esta es una limitación de tipo actual, React puro permite más tipos de retorno.

Fragmento de demostración mínimo

Puede usar una aserción de tipo o Fragmentos como solución alternativa:

const Aux = (props: AuxProps) => <>props.children</>; 
const Aux2 = (props: AuxProps) => props.children as ReactElement; 

ReactNode

children: React.ReactNode puede ser subóptimo, si el objetivo es tener tipos fuertes para Aux .

Casi todo se puede asignar al ReactNodetipo actual , que es equivalente a {} | undefined | null. Un tipo más seguro para su caso podría ser:

interface AuxProps {
  children: ReactElement | ReactElement[]
}

Ejemplo:

Dadas las Auxnecesidades de los elementos de React como children, accidentalmente le agregamos un string. Entonces la solución anterior sería un error en contraste conReactNode : eche un vistazo a los patios de recreo vinculados.

Typed childrentambién es útil para accesorios que no son JSX, como una devolución de llamada de Render Prop .


5

También puede utilizar React.PropsWithChildren<P>.

type ComponentWithChildProps = React.PropsWithChildren<{example?: string}>;

3

La forma general de encontrar cualquier tipo es mediante un ejemplo. La belleza del mecanografiado es que tiene acceso a todos los tipos, siempre que tenga los @types/archivos correctos .

Para responder a esto yo mismo, solo pensé en un componente que usa react que tiene el childrenprop. ¿Lo primero que le vino a la mente? ¿Qué tal un <div />?

Todo lo que necesita hacer es abrir vscode y crear un nuevo .tsxarchivo en un proyecto de reacción con @types/react.

import React from 'react';

export default () => (
  <div children={'test'} />
);

Al pasar el cursor sobre el childrenaccesorio, se muestra el tipo. ¿Y qué sabes? Su tipo es ReactNode(no es necesario ReactNode[]).

ingrese la descripción de la imagen aquí

Luego, si hace clic en la definición de tipo, lo lleva directamente a la definición de interfaz childrenproveniente DOMAttributes.

// node_modules/@types/react/index.d.ts
interface DOMAttributes<T> {
  children?: ReactNode;
  ...
}

Nota: ¡Este proceso debe usarse para encontrar cualquier tipo desconocido! Todos están ahí esperando que los encuentres :)


2

Como tipo que contiene niños, estoy usando:

type ChildrenContainer = Pick<JSX.IntrinsicElements["div"], "children">

Este tipo de contenedor para niños es lo suficientemente genérico para admitir todos los casos diferentes y también está alineado con la API de ReactJS.

Entonces, para su ejemplo, sería algo como:

const layout = ({ children }: ChildrenContainer) => (
    <Aux>
        <div>Toolbar, SideDrawer, Backdrop</div>
        <main>
            {children}
        </main>
    <Aux/>
)

1

Estas respuestas parecen estar desactualizadas: React ahora tiene un tipo integrado PropsWithChildren<{}>. Se define de manera similar a algunas de las respuestas correctas en esta página:

type PropsWithChildren<P> = P & { children?: ReactNode };


1
¿Qué hay Pen el caso de ese tipo?
sunpietro

Pes el tipo de accesorios de su componente
Tim Iles

-2

Los componentes de React deben tener un solo nodo contenedor o devolver una matriz de nodos.

Su <Aux>...</Aux>componente tiene dos nodos divy main.

Trate de envolver a sus hijos en una divde Auxcomponente.

import * as React from 'react';

export interface AuxProps  { 
  children: React.ReactNode
}

const aux = (props: AuxProps) => (<div>{props.children}</div>);

export default aux;

1
No es verdad. En React 16+, este ya no es el caso, además el error diría esto si fuera el caso
Andrew Li
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.