簡體   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