简体   繁体   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?

Using foo as an attribute throws an error:使用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)

But using foo-foo is fine, why's that?但是使用foo-foo很好,为什么呢?

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

export default App

And most importantly, how to define types like this in TypeScript?最重要的是,如何在 TypeScript 中定义这样的类型? ie Only allowing standard or kebab-case attributes.即只允许标准或kebab-case 属性。

The answer is in the JSX section of the Typescript Handbook :答案在Typescript 手册的 JSX 部分

If an attribute name is not a valid JS identifier (like a data-* attribute), it is not considered to be an error if it is not found in the element attributes type.如果属性名称不是有效的JS 标识符(如data-*属性),如果在元素属性类型中找不到它,则不认为是错误。

The entire section on JSX is a very enlightening read. JSX 的整个部分是一本非常有启发性的读物。

I'm afraid the answer to the "how to define types like this in TypeScript" question is... We don't.恐怕“如何在 TypeScript 中定义这样的类型”问题的答案是......我们没有。 JSX support is built into the compiler. JSX 支持内置在编译器中。 There's plenty of interesting things we can customize, however.然而,我们可以定制很多有趣的东西。

The TS handbook note in Fabio's answer explained all of this, just want to expand a little bit. Fabio's answer中的TS手册说明解释了所有这些,只是想稍微扩展一下。 In short, kebab-case attributes are not considered valid by TS but will not throw error;简而言之,kebab-case 属性不被 TS 认为有效但不会抛出错误; but attribute prefixed by data- or aria- are considered valid.但是以data-aria-为前缀的属性被认为是有效的。

React ( since 16 ) accepts custom attributes, ie <div foo /> and <div whateverYouLike={2}> should work. React( 从 16 开始)接受自定义属性,即<div foo /><div whateverYouLike={2}>应该可以工作。

What I find confusing with React, is that data-* and aria-* should be written as-is, vs. converting them to camelCase like everything else.我发现 React 令人困惑的是data-*aria-*应该按原样编写,而不是像其他所有内容一样将它们转换为 camelCase。 Especially when these attributes are converted into camelCase in vanilla DOM:特别是当这些属性在 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"

There're no reasons ever given for this, so we can only speculate.没有给出任何理由,所以我们只能推测。 Perhaps something to do with a11y toolings, parsing convenience, etc.也许与 a11y 工具、解析便利等有关。

The reasons that <div foo /> throws in TS is because TS provides a strict set of valid property names. <div foo />在 TS 中抛出的原因是因为 TS 提供了一组严格的有效属性名称。 However, as noted by the other answer, TS will not throw error on random-foo because it is considered an invalid JS identifier.但是,正如另一个答案所指出的,TS 不会在random-foo上抛出错误,因为它被认为是无效的 JS 标识符。 My speculation is because DOM elements allow arbitrary properties, so this is a compromise that allow correct typing in most cases in TS but provide some sort of escape hatch.我的猜测是因为 DOM 元素允许任意属性,所以这是一种折衷方案,允许在 TS 中的大多数情况下正确键入,但提供某种逃生舱口。 Would love to know the reasons behind these decisions.很想知道这些决定背后的原因。

How to define types like this in TypeScript?如何在 TypeScript 中定义这样的类型? ie Only allowing standard or kebab-case attributes.即只允许标准或kebab-case 属性。

As Fabio has already pointed out, JSX support is built into the compiler.正如 Fabio 已经指出的,JSX 支持内置在编译器中。 However, beside the ability to identify what constitute a valid attribute name, I don't think there's a lot of magic to it: there's a comprehensive list of valid DOM attributes.然而,除了识别什么构成有效属性名称的能力之外,我不认为它有什么神奇之处:有一个有效 DOM 属性的综合列表。 TS doesn't throw error if you mix kebab & camel cases, ie <div data-myName> work, <div myName/> doesn't, etc., so it does not differ by casing either.如果您混合使用 kebab 和 camel case,TS 不会抛出错误,即<div data-myName>工作, <div myName/>没有,等等,所以它也不会因大小写而有所不同。

If you know all your valid props in advance, you can emulate the same thing.如果您事先知道所有有效的道具,您可以模仿同样的事情。

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

interface MyThing {
  name: string
  myName: string
  anotherProp: string
}

In case of kebab-case, template literal types could be helpful:在 kebab-case 的情况下,模板文字类型可能会有所帮助:

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

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

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

Beyond this, there's currently no mechanism in TS that can differ between camelCase & kebab-case string.除此之外,TS 中目前没有任何机制可以区分 camelCase 和 kebab-case 字符串。


If you're looking for a way to augment JSX to allow custom props and custom elements, this is a way to do it:如果您正在寻找一种方法来增强 JSX 以允许自定义道具和自定义元素,这是一种方法:

Augmenting JSX attribute to allow custom props & custom elements 增强 JSX 属性以允许自定义道具和自定义元素

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

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