TypeScript และ React - เด็กพิมพ์?


142

ฉันมีส่วนประกอบการทำงานที่ง่ายมากดังนี้:

import * as React from 'react';

export interface AuxProps  { 
    children: React.ReactNode
 }


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

export default aux;

และส่วนประกอบอื่น:

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;

ฉันได้รับข้อผิดพลาดต่อไปนี้:

[ts] ประเภทองค์ประกอบ JSX 'ReactNode' ไม่ใช่ฟังก์ชันตัวสร้างสำหรับองค์ประกอบ JSX ประเภท 'ไม่ได้กำหนด' ไม่สามารถกำหนดให้กับประเภท 'ElementClass' ได้ [2605]

ฉันจะพิมพ์ให้ถูกต้องได้อย่างไร?

คำตอบ:


135

แค่ children: React.ReactNode


1
นี่คือคำตอบที่ถูกต้องที่นี่! JSX.Elementไม่ดีพอเนื่องจากลูก React ที่ถูกต้องอาจเป็นสตริงบูลีนโมฆะ ... ReactChildไม่สมบูรณ์ด้วยเหตุผลเดียวกัน
Pierre Ferry

2
@PierreFerry ปัญหานี้เกือบอะไรที่ReactNodeสามารถกำหนดให้ มันไม่ได้ช่วยในแง่ของความปลอดภัยเหมือนกับการพิมพ์childrenเป็นany- ดูคำตอบของฉันสำหรับคำอธิบาย
ford04

ขอบคุณสำหรับคำตอบของคุณ @ ford04 ในกรณีการใช้งานของฉันฉันต้องการให้เด็กพิมพ์แบบหลวม ๆ ส่วนประกอบของฉันยอมรับลูกปฏิกิริยาที่ถูกต้องทุกประเภท ดังนั้นฉันเชื่อว่านี่ยังคงเป็นคำตอบที่ฉันกำลังมองหา :)
Pierre Ferry

48

เพื่อที่จะใช้<Aux>ใน JSX ReactElement<any> | nullของคุณจะต้องมีฟังก์ชั่นที่ส่งกลับ นั่นคือความหมายของการเป็นองค์ประกอบที่ฟังก์ชั่น

อย่างไรก็ตามปัจจุบันถูกกำหนดให้เป็นฟังก์ชันที่ส่งกลับReact.ReactNodeซึ่งเป็นประเภทที่กว้างกว่ามาก ตามที่การพิมพ์ตอบสนองกล่าวว่า:

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

ตรวจสอบให้แน่ใจว่าประเภทที่ไม่ต้องการถูกทำให้เป็นกลางโดยการรวมค่าที่ส่งคืนลงใน React Fragment ( <></>):

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

ฉันไม่เข้าใจว่าทำไมจึงต้องห่อchildrenใน React Fragment สนใจที่จะอธิบาย? return children;ในการตอบสนองมันได้อย่างสมบูรณ์แบบที่ถูกต้องที่จะเพียงแค่
sanfilippopablo

1
ไม่ใช่ถ้าchildrenเป็นอาร์เรย์ หากเป็นเช่นนั้นคุณต้องรวมไว้ในโหนดเดียวหรือใช้ React Fragments
Karol Majewski

ใช่ในรหัสของฉันฉันมี: return React.Children.count(children) > 1 ? <>{children}</> : childrenแต่ typescript บ่นเกี่ยวกับเรื่องนั้น <>{children}</>ฉันแทนที่มันมีเพียงแค่
sanfilippopablo

2
มันบ่นเพราะchildrenเป็นรายการองค์ประกอบที่มีเพียง 1 รายการ ฉันคิดว่ามันจะได้ผลถ้าคุณทำreturn React.Children.count(children) > 1 ? <>{children}</> : children[0]@sanfilippopablo
tamj0rd2

ดูเหมือนว่าจะไม่ทำงานใน React เวอร์ชันใหม่กว่า ฉันเข้าสู่ระบบคอนโซลและสามารถยืนยันได้ว่าฉันมีลูกค้ำยัน แต่ถึงแม้จะ<React.Fragment>อยู่โดยรอบ{props.children}ฉันก็ไม่ได้คืนอะไร
มาร์ค

34

นี่คือสิ่งที่ใช้ได้ผลสำหรับฉัน:

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

แก้ไขฉันขอแนะนำให้ใช้children: React.ReactNodeแทนตอนนี้


8
คำจำกัดความนั้นไม่กว้างพอ เด็กอาจเป็นสตริง
Mike S

อย่างน้อยตัวอย่างนี้ก็ใช้ได้ผลสำหรับฉันเนื่องจากองค์ประกอบของฉันควรคาดหวัง 1 JSX ขึ้นไปองค์ประกอบ ขอบคุณ!
Italo Borges

1
นี้จะไม่ทำงานกับการแสดงออก JavaScript <MyComponent>{ condition ? <span>test</span> : null}</MyComponent>ในขณะที่เด็ก การใช้children: ReactNodeจะทำงาน
Nickofthyme

11

คุณสามารถประกาศส่วนประกอบของคุณได้ดังนี้:

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

10

คุณสามารถใช้ReactChildrenและReactChild:

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

interface AuxProps {
  children: ReactChild | ReactChildren;
}

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

export default Aux;

6

จากไซต์ TypeScript: https://github.com/Microsoft/TypeScript/issues/6471

วิธีปฏิบัติที่แนะนำคือเขียนอุปกรณ์ประกอบฉากเป็น {children ?: any}

ที่ได้ผลสำหรับฉัน โหนดลูกอาจมีหลายอย่างที่แตกต่างกันดังนั้นการพิมพ์อย่างชัดเจนจึงอาจพลาดกรณีได้

มีการอภิปรายอีกต่อไปเกี่ยวกับปัญหาการติดตามที่นี่: https://github.com/Microsoft/TypeScript/issues/13618แต่วิธีการใด ๆ ยังคงใช้งานได้


6

ประเภทการส่งคืนคอมโพเนนต์ของฟังก์ชันถูก จำกัด ไว้JSXElement | nullใน TypeScript นี่คือข้อ จำกัด ประเภทปัจจุบันการตอบสนองที่บริสุทธิ์ช่วยให้สามารถส่งคืนประเภทต่างๆได้มากขึ้น

ตัวอย่างข้อมูลการสาธิตขั้นต่ำ

คุณสามารถใช้การยืนยันประเภทหรือแฟรกเมนต์เป็นวิธีแก้ปัญหาชั่วคราว:

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

ReactNode

children: React.ReactNodeAuxอาจจะก่อให้เกิดผลลัพธ์ถ้าเป้าหมายคือการมีประเภทที่แข็งแกร่งสำหรับการ

เกือบทุกอย่างสามารถกำหนดให้กับReactNodeประเภทปัจจุบันซึ่งเทียบเท่ากับ{} | undefined | null. ประเภทที่ปลอดภัยกว่าสำหรับกรณีของคุณอาจเป็น:

interface AuxProps {
  children: ReactElement | ReactElement[]
}

ตัวอย่าง:

ได้รับAuxความต้องการตอบสนองเป็นองค์ประกอบchildrenเราบังเอิญได้เพิ่มstringให้กับมัน จากนั้นวิธีแก้ปัญหาข้างต้นจะผิดพลาดตรงกันข้ามกับReactNode- ดูที่สนามเด็กเล่นที่เชื่อมโยงกัน

การพิมพ์childrenยังมีประโยชน์สำหรับอุปกรณ์ประกอบฉากที่ไม่ใช่ JSX เช่นการเรียกกลับRender Prop



3

ทั่วไปวิธีการหาชนิดใด ๆ ที่เป็นตัวอย่าง ความสวยงามของ typescript คือคุณสามารถเข้าถึงได้ทุกประเภทตราบใดที่คุณมี@types/ไฟล์ที่ถูกต้อง

เพื่อตอบคำถามนี้ด้วยตัวเองฉันแค่นึกถึงส่วนประกอบที่ใช้ในการตอบสนองที่มีchildrenเสา สิ่งแรกที่อยู่ในใจ? แล้ว a <div />?

สิ่งที่คุณต้องทำคือเปิดvscodeและสร้าง.tsxไฟล์ใหม่ในโครงการโต้ตอบด้วย@types/react.

import React from 'react';

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

การวางเมาส์เหนือไม้childrenค้ำยันจะแสดงประเภท และคุณรู้อะไร - ประเภทของมันคือReactNode(ไม่จำเป็นสำหรับReactNode[])

ใส่คำอธิบายภาพที่นี่

จากนั้นหากคุณคลิกเข้าไปในคำจำกัดความประเภทมันจะนำคุณไปสู่คำจำกัดความของการchildrenมาจากDOMAttributesอินเตอร์เฟซ

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

หมายเหตุ: ควรใช้กระบวนการนี้เพื่อค้นหาประเภทที่ไม่รู้จัก! พวกเขาทั้งหมดรอให้คุณพบ :)


2

เป็นประเภทที่มีเด็กฉันใช้:

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

ประเภทคอนเทนเนอร์ลูกนี้เป็นประเภททั่วไปเพียงพอที่จะรองรับกรณีต่างๆทั้งหมดและยังสอดคล้องกับ ReactJS API

ดังนั้นสำหรับตัวอย่างของคุณมันจะเป็นดังนี้:

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

1

คำตอบเหล่านี้ดูเหมือนจะล้าสมัย - ตอนนี้ React มีประเภทในPropsWithChildren<{}>ตัว มีการกำหนดไว้คล้ายกับคำตอบที่ถูกต้องในหน้านี้:

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


1
คืออะไรPในกรณีของประเภทนั้น ๆ หรือไม่?
sunpietro

Pเป็นประเภทของอุปกรณ์ประกอบฉากของคุณ
Tim Iles


-2

คอมโพเนนต์การตอบสนองควรมีโหนด wrapper เดียวหรือส่งคืนอาร์เรย์ของโหนด

<Aux>...</Aux>ส่วนประกอบของคุณมีสองโหนดdivและmain.

พยายามที่จะห่อเด็กของคุณในdivในAuxองค์ประกอบ

import * as React from 'react';

export interface AuxProps  { 
  children: React.ReactNode
}

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

export default aux;

1
ไม่จริง. ใน React 16+ จะไม่เป็นเช่นนั้นอีกต่อไปบวกกับข้อผิดพลาดจะบอกว่าเป็นเช่นนี้
Andrew Li
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.