microsoft/TypeScript

Mimicking JSX attributes behaviour with object types

OliverJAsh opened this issue ยท 7 comments

Search Terms

jsx object invalid data attribute nested component react

Suggestion

We have this behaviour for JSX:

Note: 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.

https://www.typescriptlang.org/docs/handbook/jsx.html#attribute-type-checking

import React, { FC } from 'react';

const MyComponent: FC<{ myProp: string }> = props => null;
<MyComponent myProp="foo" data-test="foo" />;

I want this same behaviour but for object types. This is necessary for times when we want to pass some props as a nested object (for whatever reason). Currently this errors:

const MyComponent2: FC<{ props: { myProp: string } }> = props => null;
<MyComponent2 props={{ myProp: 'foo', 'data-test': 'foo' }} />;

As far as I'm aware, there's currently no way to extend the above props type to allow for this behaviour.

Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.

You want a combination of #6579 (#21044) and #26797. Today, the logic is hardcoded into how we check jsx literals, but with those you could define, eg, type Props<P> = P & {[index: /^data-/]: unknown}

It seems like this can be solved in TypeScript 4.4 with template literal types as object keys:

import * as React from 'react';

type DataAttributeKey = `data-${string}`;

interface HTMLAttributes extends React.HTMLAttributes<any> {
    [dataAttribute: DataAttributeKey]: any;
}

function Component(props: { host: HTMLAttributes }) {
    return <div {...props.host} />
}

<Component host={{
    'data-testid': 'component',
    'role': 'generic',
    // @ts-expect-error
    shouldError: 1
}} />

Playground Link

Possibly related: #47211

in vue3 typescript I am having some property does not exist in the HtmlElement error. please help me to solve this issue.

(property) dataButtons: string

(property) data-buttons: string

Type '{ class: string; dataButtons: string; "data-buttons": string; }' is not assignable to type 'HTMLAttributes'.

Property 'dataButtons' does not exist on type 'HTMLAttributes'.ts(2322)

Together with module augmentation it can be defined once in a global type definitions module, e.g. src/types/react.d.ts:

import 'react';

declare module 'react' {
  export interface HTMLAttributes<T> {
    [dataAttribute: `data-${string}`]: any;
  }
}

Not sure why this isn't supported in @types/react already

Nobody proposed the change yet. It may not be trivial to do since adding properties may break existing typechecking. Somebody just needs to own this change.