Hvordan tilføjer jeg attributter til eksisterende HTML-elementer i TypeScript/JSX?


Er der nogen, der ved, hvordan man korrekt tilføjer/udvider alle native HTML-elementattributter med brugerdefinerede?

Med TypeScript-dokumentationen for sammensmeltning af interfaces troede jeg, at jeg bare kunne gøre dette:

interface HTMLElement {
    block?: BEM.Block;
    element?: BEM.Element;
    modifiers?: BEM.Modifiers;
}

<div block="foo" />; // error

Men jeg får følgende Intellisense-fejl i vscode 1.6.1 (seneste):

[ts] Egenskaben 'block' findes ikke på typen 'HTMLProps'.

HTMLProps, som de henviser til, er React.HTMLProps<T>, og div-elementet erklæres til at bruge det på følgende måde:

namespace JSX {
    interface IntrinsicElements {
        div: React.HTMLProps<HTMLDivElement>
    }
}

Jeg prøvede at erklære div igen, men uden held.

Relateret: https://github.com/Microsoft/TypeScript/issues/11684

Rediger: Her er, hvad der endte med at virke for mig:

declare module 'react' {
    interface HTMLAttributes<T> extends DOMAttributes<T> {
        block?: string
        element?: string
        modifiers?: Modifiers // <-- custom interface
    }
}

Svar #1

Det ser ud til, at i ældre versioner af typedefinitionsfiler (v0.14) blev interfacene simpelthen erklæret under et globalt React-namespace, så tidligere kunne man bruge den standard sammenslagningssyntaks.

declare namespace React {

    interface HTMLProps<T> extends HTMLAttributes, ClassAttributes<T> {
    }
}

Dog har den nye version af d.ts-filen (v15.0) erklæret alt inde i en modul. Da moduler ikke understøtter sammensmeltning, synes den eneste mulighed lige nu, så vidt jeg ved, at være module augmentation: https://www.typescriptlang.org/docs/handbook/declaration-merging.html

Jeg udførte følgende eksperiment, og det virkede for mig:

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>
    )
};

Det er åbenlyst ret kedeligt, du kunne lægge udvidelseskoden i en anden fil som vist i eksemplet fra TypeScript-håndbogen og importere den:

import * as React from 'react';
import './react_augmented';

Men det er stadig ret beskidt. Så måske er det bedst at tage fat på problemet med bidragyderne til typedefinitionsfilen.

Svar #2

Jeg ville bruge glamors createElement-erstatning, som tilføjer en css-egenskab til hvert element.

For at tilføje til det accepterede svar ser det ud til, at modulforøgelse gør tricket, men HTMLProps virkede kun for ikke-input elementer. De korrekte interfaces at udvide ser ud til at være HTMLAttributes og SVGAttributes.

declare module 'react' {
  interface HTMLAttributes<T> {
    css?: any
  }

  interface SVGAttributes<T> {
    css?: any
  }
}

For at undgå at importere modulforøgelsen i hver komponent, gensend createElement:

// createElement.ts
import { createElement } from 'glamor/react'

declare module 'react' {
  interface HTMLAttributes<T> {
    css?: any
  }

  interface SVGAttributes<T> {
    css?: any
  }
}

export default createElement

Så fortæl TS at bruge vores createElement til JSX med denne tsconfig:

{
  "compilerOptions": {
    "jsx": "react",
    "jsxFactory": "createElement"
  }
}

Brug:

// MyComponent.tsx
import createElement from './createElement'

export default function MyComponent() {
  return <div css={{ color: 'red' }} />
}

Relaterede artikler