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

jbose2ul  于 5个月前  发布在  React
关注(0)|答案(1)|浏览(40)

我试图创建一个可以扩展到React组件的对象,这个对象必须包含HTML Custom Data Attribute

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>

字符串
在上面的例子中,我似乎不能在不显式列出自定义数据属性的情况下输入对象来允许自定义数据属性。我如何在不显式声明的情况下输入对象来允许自定义数据属性?

vzgqcmou

vzgqcmou1#

虽然custom data attributes data-*在使用JSX语法时被TypeScript编译器识别-例如,这编译没有错误。
TSPlayground

import type { ReactElement } from "react";

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

字符串
. React不provide元素属性类型别名/包含它们的接口。
为了允许在自己的类型中自定义数据属性,您可以在每个定义中显式地包含它们。
TSPlayground

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 — Add import type "mod"
然后,在每个使用站点,将不再需要显式交集:
TSPlayground

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

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
};

相关问题