You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
(however that issue is for mapped types in general, while this one is for fixing how TypeScript's internal mechanism reads types passed into JSX (UppercaseComponents) or JSX.IntrinsicElements)
✅ Viability Checklist
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, new syntax sugar for JS, etc.)
When using a class definition for a JSX type, the JSX prop types will type check against the getter, not the setter of a class property.
📃 Motivating Example
Although JSX can require or not require certain values, JSX is effectively a language feature for passing or setting values.
They should be treated mode like optional parameters ? is specified, and should otherwise look at setter types to achieve the semantics they truly align with.
Here's an example:
classMyClass{getfoo(): number{...}setfoo(value: string|number){...}}functionComponent(){// This is valid:return<MyClassfoo={"123"}/>// TYPE ERROR ("string not assignable to number")}
We get a type error, but "123" is a valid value because the foo setter accepts string values based on its type definition.
But this is the crux: we're not getting the class value, we're setting it, for all intents and purposes. It doesn't matter if under the hood we're actually creating a vdom object, as that is merely an implementation detail, while the language itself is really determing other use cases:
we're passing values to a function
we're setting values on an object
we're assigning values onto an object
As an example, this is especially true with Custom Elements, in situations like this,
return<some-custom-elementsomeProp={123}/>
where we are setting a property on the DOM element instance.
The problem is that TypeScript is type checking JSX props based on the getter type of a property, but in fact they are effectively setters that are meant to pass values to their destination, the user is not getting a value from the JSX.
An example Custom Element definition:
classSomeElextendsHTMLElement{
#someProp =123// type is 'number'// returns 'number'getsomeProp(){returnthis.#someProp }// accepts 'string | number'setsomeProp(val: string|number){this.#someProp =Number(val)}}customElements.define('some-custom-element',SomeEl)
To represent this, TypeScript needs to effectively treat JSX props as similar to function parameters instead of as an object type, allowing some "parameters" to be optional, and preferring setter types when those exist.
It is possible someone will assign SomeEl into JSX.IntrinsicElements for JSX type checking,
so the system would need to take in the object/class type, and map that to its internal handling of JSX which would be more like parameter, having preferred any setter types when they exist.
Current approaches are limited to getter types, and JSX cannot fully represent desired types.
Workarounds include declaring fake properties with conventional syntax such as __jsx__foo: string | number from which a mapped type can map to a JSX type definition. Example:
constMyClass=classMyClass{getfoo(): number{...}setfoo(value: string|number){...}/** End users: do not use this, this is for JSX types only. */__jsx__foo: string|number}// This is a special mapped type that maps `__jsx__*` properties for JSX types.typeMyClass=MySpecialMappedType<typeofMyClass>functionComponent(){return<MyClassfoo={"123"}/>//fixed}
Not sure what the solution is for the direct-to-JSX types (the example in the OP without using Pick), but if there were something like PickWithSetters<obj, 'foo' | 'etc'> that would certainly help.
🔍 Search Terms
"typescript github jsx setter type"
Related:
(however that issue is for mapped types in general, while this one is for fixing how TypeScript's internal mechanism reads types passed into JSX (UppercaseComponents) or JSX.IntrinsicElements)
✅ Viability Checklist
⭐ Suggestion
When using a class definition for a JSX type, the JSX prop types will type check against the getter, not the setter of a class property.
📃 Motivating Example
Although JSX can require or not require certain values, JSX is effectively a language feature for passing or setting values.
They should be treated mode like optional parameters
?
is specified, and should otherwise look at setter types to achieve the semantics they truly align with.Here's an example:
We get a type error, but
"123"
is a valid value because thefoo
setter accepts string values based on its type definition.But this is the crux: we're not getting the class value, we're setting it, for all intents and purposes. It doesn't matter if under the hood we're actually creating a vdom object, as that is merely an implementation detail, while the language itself is really determing other use cases:
As an example, this is especially true with Custom Elements, in situations like this,
where we are setting a property on the DOM element instance.
The problem is that TypeScript is type checking JSX props based on the getter type of a property, but in fact they are effectively setters that are meant to pass values to their destination, the user is not getting a value from the JSX.
An example Custom Element definition:
To represent this, TypeScript needs to effectively treat JSX props as similar to function parameters instead of as an object type, allowing some "parameters" to be optional, and preferring setter types when those exist.
It is possible someone will assign
SomeEl
intoJSX.IntrinsicElements
for JSX type checking,so the system would need to take in the object/class type, and map that to its internal handling of JSX which would be more like parameter, having preferred any setter types when they exist.
This playground example show a type error for a valid value.
💻 Use Cases
__jsx__foo: string | number
from which a mapped type can map to a JSX type definition. Example:The workaround in point 3 is cumbersome, but that is the only way.
The idea in #60162 would help make the mapped type workaround simpler, but would not solve the actual JSX issue.
The text was updated successfully, but these errors were encountered: