Как добавить атрибуты к существующим элементам HTML в TypeScript/JSX?
Кто-нибудь знает, как правильно добавить/расширить все собственные атрибуты HTML-элементов с помощью пользовательских?
С документацией TypeScript для объединения интерфейсов я подумал, что я мог бы просто сделать это:
interface HTMLElement {
block?: BEM.Block;
element?: BEM.Element;
modifiers?: BEM.Modifiers;
}
<div block="foo" />; // error
Но я получаю следующую ошибку Intellisense в vscode 1.6.1 (последняя версия):
[ts] Свойство 'block' не существует для типа 'HTMLProps'.
HTMLProps
на который они ссылаются, это React.HTMLProps<T>
и элемент div
объявлен для его использования следующим образом:
namespace JSX {
interface IntrinsicElements {
div: React.HTMLProps<HTMLDivElement>
}
}
Я попытался переопределить div
, но безрезультатно.
Связанный: https://github.com/Microsoft/TypeScript/issues/11684
Редактировать: Вот, что в конечном итоге работает для меня:
declare module 'react' {
interface HTMLAttributes<T> extends DOMAttributes<T> {
block?: string
element?: string
modifiers?: Modifiers // <-- custom interface
}
}
Ответы
Ответ 1
Похоже, что в более старых версиях файлов определений типов (v0.14) интерфейсы были просто объявлены в глобальном пространстве имен React, поэтому ранее вы могли использовать стандартный синтаксис слияния.
declare namespace React {
interface HTMLProps<T> extends HTMLAttributes, ClassAttributes<T> {
}
}
Однако новая версия файла d.ts(v15.0) объявила все внутри модуля. Поскольку модули не поддерживают слияние, насколько мне известно, сейчас единственным вариантом является module augmentation
: https://www.typescriptlang.org/docs/handbook/declaration-merging.html.
Я сделал следующий эксперимент, и он работал для меня:
import * as React from 'react';
declare module 'react' {
interface HTMLProps<T> {
block?:string;
element?:string;
modifiers?:string;
}
}
export const Foo = () => {
return (
<div block="123" element="456">
</div>
)
};
Очевидно, это довольно утомительно, вы можете поместить код дополнения в другой файл, как показано в примере из справочника по машинописи, и импортировать его:
import * as React from 'react';
import './react_augmented';
Но это все еще довольно грязно. Поэтому, возможно, лучше всего решить проблему с участниками файла определения типа.
Ответ 2
Я хотел использовать гламур createElement, который добавляет css
prop к каждому элементу.
Чтобы добавить к принятому ответу, увеличение модуля, похоже, делает трюк, но HTMLProps
работает только для элементов без ввода. Похоже, что правильные интерфейсы для расширения HTMLAttributes
и SVGAttributes
.
declare module 'react' {
interface HTMLAttributes<T> {
css?: any
}
interface SVGAttributes<T> {
css?: any
}
}
Чтобы избежать импорта расширения модуля в каждом компоненте, повторно экспортируйте элемент createElement:
// createElement.ts
import { createElement } from 'glamor/react'
declare module 'react' {
interface HTMLAttributes<T> {
css?: any
}
interface SVGAttributes<T> {
css?: any
}
}
export default createElement
Затем скажите TS, чтобы использовать наш createElement
для JSX с этим tsconfig:
{
"compilerOptions": {
"jsx": "react",
"jsxFactory": "createElement"
}
}
Использование:
// MyComponent.tsx
import createElement from './createElement'
export default function MyComponent() {
return <div css={{ color: 'red' }} />
}
Ответ 3
Актуальный пример (май 2019)
Файл определения типа index.d.ts
(по умолчанию - index.d.ts
при index.d.ts
create-react-app
) содержит список всех стандартных элементов HTML, а также известные атрибуты.
Для того чтобы разрешить пользовательские атрибуты HTML, вам нужно определить его типизацию. Сделайте это, расширив интерфейс HTMLAttributes
:
declare module 'react' {
interface HTMLAttributes<T> extends AriaAttributes, DOMAttributes<T> {
// extends React HTMLAttributes
custom?: string;
}
}