繁体   English   中英

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

[英]Why kebab-case non-standard attributes are allowed while others aren't? And how to define types like this in TypeScript?

使用foo作为属性会引发错误:

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

export default App
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)

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

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

export default App

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

答案在Typescript 手册的 JSX 部分

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

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

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

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

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

我发现 React 令人困惑的是data-*aria-*应该按原样编写,而不是像其他所有内容一样将它们转换为 camelCase。 特别是当这些属性在 vanilla DOM 中转换为 camelCase 时:

<div data-my-age="100" aria-label="A Test" />
const $div = document.querySelector('#test')

$div.dataset.myName = "D"
console.log({ dataset: $div.dataset }) // { myAge: "100", myName: "D" }
console.log($div.ariaLabel) // "A Test"

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

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

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

正如 Fabio 已经指出的,JSX 支持内置在编译器中。 然而,除了识别什么构成有效属性名称的能力之外,我不认为它有什么神奇之处:有一个有效 DOM 属性的综合列表。 如果您混合使用 kebab 和 camel case,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
}

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

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

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

const x: ComputedProps = {
    "data-apple": 'hi'
};

除此之外,TS 中目前没有任何机制可以区分 camelCase 和 kebab-case 字符串。


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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM