为什么 kebab-case 非标准属性是允许的,而其他属性则不允许?以及如何在 TypeScript 中定义这样的类型?

Wen*_* Du 14 javascript jsx typescript reactjs vue.js

使用foo作为属性抛出一个错误:

// App.tsx
//                      throws
const App = () => <div foo></div>

export default App
Run Code Online (Sandbox Code Playgroud)
Type '{ foo: true; }' is not assignable to type 'DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>'.
  Property 'foo' does not exist on type 'DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>'.ts(2322)
Run Code Online (Sandbox Code Playgroud)

但使用foo-foo是好的,为什么呢?

// App.tsx
//                      no error is thrown
const App = () => <div foo-foo></div>

export default App
Run Code Online (Sandbox Code Playgroud)

最重要的是,如何在 TypeScript 中定义这样的类型?即只允许标准或 kebab-case 属性。

Fáb*_*sta 11

答案在Typescript 手册JSX 部分

如果属性名称不是有效的JS 标识符(如data-*属性),如果在元素属性类型中找不到,则不认为是错误。

JSX 的整个部分是一本非常有启发性的读物。

恐怕“如何在 TypeScript 中定义这样的类型”问题的答案是......我们没有。JSX 支持内置于编译器中。然而,我们可以自定义很多有趣的东西。


Der*_*yen 10

Fabio 的回答中的 TS 手册说明解释了所有这些,只是想扩展一点。简而言之,kebab-case 属性不被 TS 认为是有效的,但不会抛出错误;但以data-oraria-为前缀的属性被认为是有效的。

React(从 16 开始)接受自定义属性,即<div foo />并且<div whateverYouLike={2}>应该可以工作。

我找到与之反应混淆什么,是data-*aria-*应该写成-是,与它们转换为驼峰其他事物一样。特别是当这些属性在 vanilla DOM 中转换为驼峰式大小写时:

<div data-my-age="100" aria-label="A Test" />
Run Code Online (Sandbox Code Playgroud)
<div data-my-age="100" aria-label="A Test" />
Run Code Online (Sandbox Code Playgroud)

没有给出任何理由,所以我们只能推测。也许与 a11y 工具、解析方便等有关。

<div foo />抛出 TS的原因是因为 TS 提供了一组严格的有效属性名称。但是,正如另一个答案所指出的,TS 不会抛出错误,random-foo因为它被认为是无效的 JS 标识符。我的推测是因为 DOM 元素允许任意属性,所以这是一种妥协,允许在大多数情况下在 TS 中正确键入但提供某种逃生舱口。很想知道这些决定背后的原因。

如何在 TypeScript 中定义这样的类型?即只允许标准或 kebab-case 属性。

正如 Fabio 已经指出的那样,编译器内置了 JSX 支持。然而,除了能够识别构成有效属性名称的内容之外,我认为它没有什么神奇之处:有一个完整的有效 DOM 属性列表。如果您混合使用 kebab 和骆驼案例,TS 不会抛出错误,即<div data-myName>工作,<div myName/>不工作等,因此它也不会因大小写而有所不同。

如果您事先知道所有有效道具,则可以模拟相同的事情。

// allow only these prop names, which happened to be all camelCased

interface MyThing {
  name: string
  myName: string
  anotherProp: string
}
Run Code Online (Sandbox Code Playgroud)

在 kebab-case 的情况下,模板文字类型可能会有所帮助:

type ValidPrefix = "data" | "aria";
type ValidSuffix = "banana" | "apple" | "pear";

type ComputedProps = {
    [key in `${ValidPrefix}-${ValidSuffix}`]?: string;
};

const x: ComputedProps = {
    "data-apple": 'hi'
};
Run Code Online (Sandbox Code Playgroud)

除此之外,目前 TS 中没有任何机制可以在驼峰式字符串和烤肉串字符串之间有所不同。


如果您正在寻找一种方法来增强 JSX 以允许自定义道具和自定义元素,这是一种方法:

增加 JSX 属性以允许自定义道具和自定义元素