React 的类型声明例如React.HTMLAttribute<HTMLButtonElement> 支持开箱即用的自定义数据属性吗?

问题描述 投票:0回答:1

我正在尝试创建一个可以传播到 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>

在上面的示例中,我似乎无法键入我的对象以允许自定义数据属性,而无需显式列出它们。如何键入对象以允许自定义数据属性而无需显式声明?

html reactjs typescript custom-data-attribute
1个回答
0
投票

虽然 自定义数据属性

data-*
在使用 JSX 语法时可以被 TypeScript 编译器识别——例如编译没有错误...

TS游乐场

import type { ReactElement } from "react";

function ExampleComponent(): ReactElement {
  return (
    <button
      data-foo="bar"
      onClick={(ev) => console.log(ev.currentTarget.dataset.foo)}
    >
      Click
    </button>
  );
}

…React 不提供包含它们的元素属性类型别名/接口。

为了允许在您自己的类型中使用自定义数据属性,您可以在每个定义中显式包含它们......

TS游乐场

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"

然后,在每个使用站点,将不再需要显式交集:

TS游乐场

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
中提供的属性之外,还可能具有专门的属性,因此您需要相应地键入元素属性,以便编译器识别这些属性具体属性。下面的示例显示了上述元素的一些专用属性:

TS游乐场

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
};
© www.soinside.com 2019 - 2024. All rights reserved.