我正在尝试创建一个可以传播到 React 组件中的对象,并且该对象必须包含一个 HTML 自定义数据属性。
import { HTMLAttributes } from 'react'
interface FooProps extends HTMLAttributes<HTMLButtonElement> {
fooProp: string
//'data-testid': string // do HTML Custom Attributes not come out of the box?
}
const fooObj: FooProps = {
fooProp: 'yay',
'data-testid': 'nay' // Object literal may only specify known properties, and ''data-testid'' does not exist in type 'Foo'.(2353)
}
const Foo = () => <button {...fooObj}></button>
在上面的示例中,我似乎无法键入我的对象以允许自定义数据属性,而无需显式列出它们。如何键入对象以允许自定义数据属性而无需显式声明?
虽然 自定义数据属性
data-*
在使用 JSX 语法时可以被 TypeScript 编译器识别——例如编译没有错误...
import type { ReactElement } from "react";
function ExampleComponent(): ReactElement {
return (
<button
data-foo="bar"
onClick={(ev) => console.log(ev.currentTarget.dataset.foo)}
>
Click
</button>
);
}
…React 不提供包含它们的元素属性类型别名/接口。
为了允许在您自己的类型中使用自定义数据属性,您可以在每个定义中显式包含它们......
import type { ButtonHTMLAttributes, ReactElement } from "react";
type DataAttributes = Record<`data-${string}`, string>;
// Explicitly intersect the indexed type:
const buttonAttrs: ButtonHTMLAttributes<HTMLButtonElement> & DataAttributes = {
// ^^^^^^^^^^^^^^^^
"data-foo": "bar",
onClick: (ev) => console.log(ev.currentTarget.dataset.foo),
};
function ExampleComponent(): ReactElement {
return <button {...buttonAttrs}>Click</button>;
}
…或者 — 您可以通过使用模块增强模式,以更DRY的方式来处理它:
在项目中的路径中创建类型声明文件,该路径包含在程序编译中(例如
src/types/react_data_attributes.d.ts
):
import type {} from "react";
declare module "react" {
interface HTMLAttributes<T> {
[name: `data-${string}`]: string;
}
}
参考:microsoft/TypeScript#36812 — 添加
import type "mod"
然后,在每个使用站点,将不再需要显式交集:
import type { ButtonHTMLAttributes, ReactElement } from "react";
// Now, only the base button attributes type annotation is needed:
const buttonAttrs: ButtonHTMLAttributes<HTMLButtonElement> = {
"data-foo": "bar",
onClick: (ev) => console.log(ev.currentTarget.dataset.foo),
};
function ExampleComponent(): ReactElement {
return <button {...buttonAttrs}>Click</button>;
}
关于
HTMLElement
亚型的注释:
请注意,每个子类型(例如
<button>
、<a>
等)除了基础 HTMLAttributes
中提供的属性之外,还可能具有专门的属性,因此您需要相应地键入元素属性,以便编译器识别这些属性具体属性。下面的示例显示了上述元素的一些专用属性:
import type {
AnchorHTMLAttributes,
ButtonHTMLAttributes,
HTMLAttributes,
} from "react";
type ButtonSpecificAttributes = Exclude<
/* ^? type ButtonSpecificAttributes =
| "disabled"
| "form"
| "formAction"
| "formEncType"
| "formMethod"
| "formNoValidate"
| "formTarget"
| "name"
| "type"
| "value"
*/
keyof ButtonHTMLAttributes<HTMLButtonElement>,
keyof HTMLAttributes<HTMLButtonElement>
>;
type AnchorSpecificAttributes = Exclude<
/* ^? type AnchorSpecificAttributes =
| "download"
| "href"
| "hrefLang"
| "media"
| "ping"
| "referrerPolicy"
| "target"
| "type"
*/
keyof AnchorHTMLAttributes<HTMLAnchorElement>,
keyof HTMLAttributes<HTMLAnchorElement>
>;
const buttonAttrs0: HTMLAttributes<HTMLButtonElement> = {
disabled: true, /* Error
~~~~~~~~
Object literal may only specify known properties, and 'disabled' does not exist in type 'HTMLAttributes<HTMLButtonElement>'.(2353) */
};
const buttonAttrs1: ButtonHTMLAttributes<HTMLButtonElement> = {
disabled: true, // Ok
};
const anchorAttrs0: HTMLAttributes<HTMLAnchorElement> = {
href: "https://stackoverflow.com/", /* Error
~~~~
Object literal may only specify known properties, and 'href' does not exist in type 'HTMLAttributes<HTMLAnchorElement>'.(2353) */
};
const anchorAttrs1: AnchorHTMLAttributes<HTMLAnchorElement> = {
href: "https://stackoverflow.com/", // Ok
};