Unable to extend JSX properties of already defined elements
See original GitHub issueBug Report
We’ve just release Preact Signals which are sort of an addon to both Preact and React that allow you to pass an observable object directly into JSX. As part of that we need to extend the existing JSX definitions to allow passing our observable type too, instead of just the existing types. But I’m unable todo so. I keep getting a type error.
🔎 Search Terms
JSX, declaration merging, React, Preact, Signals, IntrinsicElements
🕗 Version & Regression Information
- This is the behavior in every version I tried, and I reviewed the FAQ for entries about JSX + declaration merging
⏯ Playground Link
Playground link with relevant code
💻 Code
import * as React from "react"
// This and the JSX additions live in a library
interface Signal<T> {
value: T
}
declare global {
namespace JSX {
interface IntrinsicElements {
// Doesn't work
input:
| React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>,HTMLInputElement>
| Signal<string>
}
}
}
// This represents app code the user writes
const foo: Signal<string> = {
value: "foo"
}
const a = <input value={foo} />
🙁 Actual behavior
Passing a Signal
as value
errors with a type error
const foo = { value: "foo" };
const a = <input value={foo} />
🙂 Expected behavior
Passing a Signal
as value
should work
const foo = { value: "foo" };
const a = <input value={foo} />
Issue Analytics
- State:
- Created a year ago
- Comments:6 (4 by maintainers)
Top Results From Across the Web
Typescript/React extending default functional component ...
On the other hand, built-in components like <div> or <span> have already defined the props that correspond to their HTML elements.
Read more >Documentation - JSX - TypeScript
It is determined by the type of a property on the element instance type that was previously determined. Which property to use is...
Read more >React Without JSX
Using React without JSX is especially convenient when you don't want to set up compilation in your build environment. Each JSX element is...
Read more >React with TypeScript Cheatsheet - Bits and Pieces
How to type (extend) HTML elements. Sometimes, you want to create a small, modular component that takes the attributes of a native HTML...
Read more >Custom Elements v1 - Reusable Web Components
Custom elements allow web developers to define new HTML tags, extend existing ones ... Elements can react to attribute changes by defining a ......
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
It’s not really clear how to make this scenario work in a generalizable way, except via the one already proposed upthread. If
Signal2
also existed and also wants to make things work, what happens?Is TS supposed to merge these two types using Union? Intersection? How/why? It’s very unclear. Even if we had some keyword like
merge
so you could writeinput: merge | Signal<string>
to refer to the property you’re overwriting, then you run into declaration ordering problems because repeated utterances ofmerge
in the same property slot could manifest in ways that have order-dependent nesting (e.g.input: Foo<merge>
)This is a valid concern but there is still probably a way to handle this through the composition of types. Note that this would at least reference the “composed” module somewhere and it would make it discoverable - instead of relying on implicit extensions. It also wouldn’t require any new language constructs to be introduced - right now those interfaces just behave in the very same way the usual interface merging works but that, of course, doesn’t fit your use case. This behavior can’t change - so something new would have to be introduced to account for this.
The problem with interface merging, in general, is that it requires the full knowledge of the project to work correctly - so by nature, it slows down the project startup times and stuff. Just my 2 cents but explicit deps >>> implicit deps, for this very reason.