在 TypeScript 中使用带子级的 forwardRef 组件

使用@type/response 16.8.2和 TypeScript 3.3.1。

我直接从 反应文件中提取了这个正向参考示例,并添加了一些类型参数:

const FancyButton = React.forwardRef<HTMLButtonElement>((props, ref) => (
<button ref={ref} className="FancyButton">
{props.children}
</button>
));


// You can now get a ref directly to the DOM button:
const ref = React.createRef<HTMLButtonElement>();
<FancyButton ref={ref}>Click me!</FancyButton>;

我在 FancyButton的最后一行得到以下错误:

类型‘ { children: string; ref: RefObject<HTMLButtonElement>; }’则不是 可赋值为类型“ IntrinsicAttributes & RefAttributes<HTMLButtonElement>”。属性“ children”不能 存在于类型‘ IntrinsicAttributes & RefAttributes<HTMLButtonElement>’. ts (2322)上

似乎 React.forwardRef 的返回值的类型定义是错误的,没有正确地合并到子道具中。如果我让 <FancyButton>自动关闭,错误就会消失。缺少这个错误的搜索结果使我相信我遗漏了一些显而易见的东西。

97230 次浏览

trevorsg, you need to pass the button properties:

import * as React from 'react'


type ButtonProps = React.HTMLProps<HTMLButtonElement>


const FancyButton = React.forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => (
<button type="button" ref={ref} className="FancyButton">
{props.children}
</button>
))


// You can now get a ref directly to the DOM button:
const ref = React.createRef<HTMLButtonElement>()


<FancyButton ref={ref}>Click me!</FancyButton>

ADDED:

In recent versions of TS and @types/react, you can also use React.ComponentPropsWithoutRef<'button'> instead of React.HTMLProps<HTMLButtonElement>

The answer given by aMarCruz works well. However, if you also need to pass custom props to the FancyButton, here is how it can be done.

interface FancyButtonProps extends React.ComponentPropsWithoutRef<'button'> {
fooBar?: string; // my custom prop
}


const FancyButton = React.forwardRef<HTMLButtonElement, FancyButtonProps>((props, ref) => (
<button type="button" ref={ref} className="FancyButton">
{props.children}
{props.fooBar}
</button>
));




/// Use later


// You can now get a ref directly to the DOM button:
const ref = React.createRef<HTMLButtonElement>()


<FancyButton ref={ref} fooBar="someValue">Click me!</FancyButton>


Just adding here for completion.

The answers given by aMarCruz and euvs both work, but they lie to consumers a little bit. They say they accept all HTMLButtonElement props, but they ignore them instead of forwarding them to the button. If you're just trying to merge in the children prop correctly, then you might want to use React.PropsWithChildren instead:

import React from 'react';


interface FancyButtonProps {
fooBar?: string; // my custom prop
}


const FancyButton = React.forwardRef<HTMLButtonElement, React.PropsWithChildren<FancyButtonProps>>((props, ref) => (
<button type="button" ref={ref} className="fancy-button">
{props.children}
{props.fooBar}
</button>
));


FancyButton.displayName = 'FancyButton';

Or explicitly add a children prop:

interface FancyButtonProps {
children?: React.ReactNode;
fooBar?: string; // my custom prop
}


const FancyButton = React.forwardRef<HTMLButtonElement, FancyButtonProps>((props, ref) => (
<button type="button" ref={ref} className="fancy-button">
{props.children}
{props.fooBar}
</button>
));


FancyButton.displayName = 'FancyButton';

Or if you actually want to accept all the button props and forward them (let consumers choose button type="submit", for example), then you might want to use rest/spread:

import React from 'react';


interface FancyButtonProps extends React.ComponentPropsWithoutRef<'button'> {
fooBar?: string; // my custom prop
}


const FancyButton = React.forwardRef<HTMLButtonElement, FancyButtonProps>(
({ children, className = '', fooBar, ...buttonProps }, ref) => (
<button {...buttonProps} className={`fancy-button ${className}`} ref={ref}>
{children}
{fooBar}
</button>
),
);


FancyButton.displayName = 'FancyButton';