React 18 TypeScript 儿童 FC

Mic*_*bry 168 javascript typescript reactjs

我升级到 React 18 并且编译得很好。如今,似乎每个使用子组件的组件都会抛出错误。Property 'children' does not exist on type 'IPageProps'.

在儿童道具自动包含在FC界面中之前。现在看来我必须手动添加children: ReactNode对于反应儿童来说,正确的打字稿类型是什么?

这是 React 18 更新的一部分,还是我的环境出了问题?

包.json

"react": "^18.0.0",
"react-dom": "^18.0.0",
"next": "12.1.4",
"@types/react": "18.0.0",
"@types/react-dom": "18.0.0",
Run Code Online (Sandbox Code Playgroud)

tsconfig.json

{
  "compilerOptions": {
    "target": "esnext",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "preserve",
    "alwaysStrict": true,
    "sourceMap": true,
    "incremental": true
  },
  "include": ["src"],
  "exclude": ["node_modules"]
}
Run Code Online (Sandbox Code Playgroud)

Dan*_*mov 224

尽管这个答案是正确的,但我想指出的是,您绝对不必使用这个PropsWithChildren助手。(它主要用于 codemod 而不是手动使用。)

相反,我发现手动定义它们更容易。

import * as React from 'react';

type Props = {};
const Component: React.FC<Props> = ({children}) => {...}
Run Code Online (Sandbox Code Playgroud)

import * as React from 'react';

type Props = {
  children?: React.ReactNode
};
const Component: React.FC<Props> = ({children}) => {...}
Run Code Online (Sandbox Code Playgroud)

这就是所需要的一切。

或者您可以React.FC完全停止使用。

import * as React from 'react';

type Props = {
  children?: React.ReactNode
};

function Component({children}: Props): React.ReactNode {
  ...
}
Run Code Online (Sandbox Code Playgroud)

在 React 中,children是一个常规的 prop,并不是什么特殊的东西。因此,您需要像定义所有其他 props 一样定义它。之前隐藏它的打字是错误的。

  • 在某些时候,我认为是这样的——在我们做到这一点之前,我们需要做出一些改变。我不明白为什么打字会很复杂。这可能是一个单独的讨论,欢迎您在 DefinelyTyped 存储库中开始它。 (4认同)
  • 那么“VFC”呢?这一切看起来都很简单:“FC”添加了隐式的“children”,而“VFC”则没有。现在在 React 18 中,两者似乎是相同的,因为......?看起来我将创建一个新的“type CFC&lt;T&gt; = FC&lt;PropsWithChildren&lt;T&gt;&gt;”来恢复这种区别。 (2认同)

sim*_*Pod 82

如何解决

没有道具

import React from 'react';

const Component: React.FC = ({children}) => {...}
Run Code Online (Sandbox Code Playgroud)

创建例如react.d.ts来定义你的助手类型 1

import React from 'react';

export type ReactFCWithChildren = React.FC<PropsWithChildren>;
Run Code Online (Sandbox Code Playgroud)
import {ReactFCWithChildren } from './react';

const Component: ReactFCWithChildren = ({children}) => {...}
Run Code Online (Sandbox Code Playgroud)

或者

import React from 'react';

const Component: React.FC<React.PropsWithChildren> = ({children}) => {...}
Run Code Online (Sandbox Code Playgroud)

带道具

import React from 'react';

interface Props {
  ...
}
const Component: React.FC<Props> = ({children}) => {...}
Run Code Online (Sandbox Code Playgroud)

import React from 'react';

interface Props {
  ...
}
const Component: React.FC<React.PropsWithChildren<Props>> = ({children}) => {...}
Run Code Online (Sandbox Code Playgroud)

或者

import React from 'react';

interface Props extends React.PropsWithChildren {
  ...
}

const Component: React.FC<Props> = ({children}) => {...}
Run Code Online (Sandbox Code Playgroud)

1虽然手动定义似乎很容易,但最好利用 @types 包中已为您准备的类型。当将来类型发生更改时,它将自动从库中传播到代码中的任何位置,因此您不必亲自接触它。 children

由于某种原因抑制警告

您可以通过创建react.d.ts具有以下定义的文件来覆盖反应类型,这会将类型恢复为 @types/react v17

import * as React from '@types/react';

declare module 'react' {
  interface FunctionComponent<P = {}> {
    (props: PropsWithChildren<P>, context?: any): ReactElement<any, any> | null;
  }
}
Run Code Online (Sandbox Code Playgroud)

FC签名为何改变

childrenprop 已从React.FunctionComponent( React.FC) 中删除,因此您必须显式声明它。

TS 会告诉你错误,例如

输入 '{ 孩子:...; }' 与类型“IntrinsicAttributes”没有共同的属性。”

您可以在此处阅读原因。TLDR 它可以防止像这样的错误

const ComponentWithNoChildren: React.FC = () => <>Hello</>;

...

<ComponentWithNoChildren>
   // passing children is wrong since component does not accept any
   <UnusedChildrenSinceComponentHasNoChildren /> 
</ComponentWithNoChildren>
Run Code Online (Sandbox Code Playgroud)

  • 这太棒了,那么第三方库呢?我一直在破解它`const ThirdPartyHack:any = ThirdParty` (3认同)

Gar*_*vae 12

是的,React.FC 类型已经改变。但是你可以用二十一点和反应孩子来声明你自己的类型。

我的方法是src/types/react.d.ts使用这样的内容进行创建:

import React, { PropsWithChildren } from 'react';

export type ReactFCC<T> = React.FC<PropsWithChildren<T>>;
Run Code Online (Sandbox Code Playgroud)

更新#01

您可以为参数添加默认值T

import React, { PropsWithChildren } from 'react';

export type ReactFCC<T = Record<string, unknown>> = React.FC<PropsWithChildren<T>>;
Run Code Online (Sandbox Code Playgroud)

或者

import React, { PropsWithChildren } from 'react';

export type ReactFCC<T = unknown> = React.FC<PropsWithChildren<T>>;
Run Code Online (Sandbox Code Playgroud)

您现在可以选择不在泛型中指定类型ReactFCC而不发出警告。

前:

export const Component: ReactFCC<SomeType> = props => {

  const { children } = props;
  
  /* ... */
}
Run Code Online (Sandbox Code Playgroud)

后:

export const Component: ReactFCC = props => {

  const { children } = props;
  
  /* ... */
}
Run Code Online (Sandbox Code Playgroud)


ash*_*sut 8

创建您的自定义功能组件类型( 的修改FC)。

让我们命名它FCC(表示:- 带子项的功能组件;))

// Put this in your global types.ts

import { FC, PropsWithChildren } from "react";

// Custom Type for a React functional component with props AND CHILDREN
export type FCC<P={}> = FC<PropsWithChildren<P>>
Run Code Online (Sandbox Code Playgroud)

每当您想要childrenComponent 中的属性时props,请像这样使用它:

// import FCC from types.ts
const MyComponent: FCC = ({children}) => {
  return (
    <>{children}</>
  )
}
Run Code Online (Sandbox Code Playgroud)

或者

interface MyCompoProps{
  prop1: string
}

const MyComponent: FCC<MyCompoProps> = ({children, prop1}) => {
  return (
    <>{children}</>
  )
}
Run Code Online (Sandbox Code Playgroud)

PS这个答案可能看起来与 @Garvae 的答案类似,但他的ReactFCC<P> type答案应该这样写ReactFCC<P={}>,以防止出现以下错误:

Generic type 'ReactFCC' requires 1 type argument(s)

当您没有向组件传递任何 props 时,会发生此错误。儿童道具应该是可选道具。因此,给这些 props 一个默认{}值(即P = {})可以解决这个问题。