Skip to content

Mimicking JSX attributes behaviour with object types #28960

Description

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.

Activity

  1. weswigham commented on Dec 11, 2018

    @weswigham
    Member

    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}

  2. added a commit that references this issue on May 4, 2021
  3. eps1lon commented on Aug 23, 2021

    @eps1lon
    Contributor

    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

  4. TrevorBurnham commented on Dec 21, 2021

    @TrevorBurnham

    Possibly related: #47211

  5. RahelSiddiqi commented on Mar 22, 2022

    @RahelSiddiqi

    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)

  6. OliverJAsh commented on Feb 13, 2023

    @OliverJAsh
    ContributorAuthor
  7. amitbeck commented on Sep 5, 2024

    @amitbeck

    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

  8. eps1lon commented on Sep 5, 2024

    @eps1lon
    Contributor

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    In DiscussionNot yet reached consensusSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions