Repository navigation
JSX: a way to make all the properties of value-based elements optional #7004
Description
Activity
- addedSuggestionAn idea for TypeScriptAn idea for TypeScriptNeeds ProposalThis issue needs a plan that clarifies the finer details of how it could be implemented.This issue needs a plan that clarifies the finer details of how it could be implemented.
on Feb 19, 2016 So what is the proposal?
RyanCavanaugh commented
on Feb 19, 2016 MemberMore actionsI think we could reasonably say that private / protected members get ignored when checking against the element attributes type.
Probably something like
declare namespace JSX { interface ElementOptionalAttributesProperty {} }
All the properties would be considered optional. Not ideal but could be a good start.
Ideally, I'd like to use decorators to mark required properties, like
@Inputand@Outputin Angular 2. But I can't think of a good way to tell the compiler about such rules.- addedIn DiscussionNot yet reached consensusNot yet reached consensusand removedNeeds ProposalThis issue needs a plan that clarifies the finer details of how it could be implemented.This issue needs a plan that clarifies the finer details of how it could be implemented.
on Feb 22, 2016 RyanCavanaugh commented
on May 9, 2016 MemberMore actionsDiscussed at the backlog slog today. There are a lot of scenarios here that would be helped by this, but the technical implications of the first type operator are very large. Some more notes are at #8228. We're continuing to think about it.
Just in terms of setting expectations, this isn't going to be something we can do in time for TypeScript 2.0
I opened duplicated issue #12016 .
I wrote my motivation to that issue.I read backlog slog. I think I do not want a big thing to this issue.
My proposal is below.
declare global { namespace JSX { // We use magic interface. this is pragma for compiler. interface ElementAttributesProperty { // use optional property ''?: any; } } }function checkJsxOpeningLikeElement(node: JsxOpeningLikeElement) { ... // add makeOptionalProperty condition from ElementAttributesProperty interface if (targetAttributesType && !sawSpreadedAny && !makeOptionalProperty) { ... }Reacted by Georgii DolzhykovI think we could reasonably say that private / protected members get ignored when checking against the element attributes type.
This is not so good for skate.js.
skate.Component extends HTMLElementsignature can't control modifier by myself.Reacted by Georgii DolzhykovYes, what I requested was just an alternative way to type check JSX, not 'optionalized types`, which is a separate complex feature.
aluanhaddad commented
on Nov 4, 2016 ContributorMore actionsPartial types, as proposed by Ryan Cavanaugh (@RyanCavanaugh) in #11233, might well be able to model this.
Aluan Haddad (@aluanhaddad) Partial and optionalized types are the same thing. Even though it sounds like overkill for this issue, it will work, however we'll need a way to tell the compiler that
<Foo ... />refers tosubset Foonot justFoo. So a new JSX-related setting will be needed.And if we need a new switch anyway, does its implementation really need to involve partial types if it can be done without them like Masahiro Wakame (@vvakame) wrote?
Now I am understood what Ryan Cavanaugh (@RyanCavanaugh) and Aluan Haddad (@aluanhaddad) are said.
We can use Partial types (a.k.a Mapped types) now.We need good proposal.
IMHO, If JSX.ElementAttributesProperty has callable property, Use the return type instead of the type passed as an argument for checking.
like this.interface ElementAttributesProperty { ''<T>(v: T): Partial<T>; }This issue is last 2 mile of SkateJS+TypeScript. (other 1 is #12488 )
If you have no seen SkateJS yet, please try it once. it's fun!
http://skate.js.org/
https://github.com/vvakame/skatejs-todo/Reacted by Georgii DolzhykovHm...
abstract class Component { /** A bogus type-system-only property. */ private __bogusProps: Partial<this>; } declare namespace JSX { type Element = string; interface ElementAttributesProperty { __bogusProps: Partial<this>; } } class FooComponent extends Component { foo: number; method() { } } // ta-da ! let z = <FooComponent foo={2}/>;
Reacted by xuld and Antanas A.EisenbergEffect commented
on Jun 12, 2018 More actionsRyan Cavanaugh (@RyanCavanaugh) It's been two years. Any progress on this front? We were hoping to enable JSX for Aurelia vNext but this issue is causing the compiler to be very unhappy with us.
Rob Eisenberg (@EisenbergEffect) Doesn't this solution work for you?
EisenbergEffect commented
on Jun 13, 2018 More actionsOne of the unique and important aspects of Aurelia's design is that it doesn't require using base classes for components. Components are plain classes. In fact, we discourage the use of inheritance and prefer guiding our community to use more compositional approaches. As such, even if the above did work, it wouldn't be an acceptable solution for us. That said, I've tried a few solutions that leverage the JSX namespace without inheritance and VS Code at least, doesn't seem to be affected at all in it's TS error reporting. It just doesn't work. I've been starting to wonder if there's a bug that was introduced in the last update... I need to experiment some more. It would be nice to have a real solution to this though.
- added a commit that references this issue
on Oct 3, 2018 Rob Eisenberg (@EisenbergEffect) It suddenly crossed my mind that we could extend
interface Objectinstead of a base class:interface Object { /** A bogus type-system-only property. */ __bogusProps: Partial<this>; } declare namespace JSX { type Element = string; interface ElementAttributesProperty { __bogusProps: any; } }
But alas... TS adds the
__bogusPropsproperty to every object, but it thinks that this property has the typePartial<Object>, notPartial<...our more specific type...>.BTW, I didn't quite understand your "even if the above did work". It does work.
Thanks for continuing to explore this Georgii Dolzhykov (@thorn0) I honestly can't remember if I tried the technique above or not. I know I tried a few things which didn't seem to work at least at one time. What I wanted to emphasize was that, either way, it's contrary to Aurelia's philosophy to force the use of a base class, so we wouldn't want to recommend this approach to our community as a best practice.
Ryan Cavanaugh (@RyanCavanaugh) What we really need is a way to declare that the element attributes are derived directly from the type itself. In other words the properties of the object are the attributes of the element. There's no special property involved.
Rob Eisenberg (@EisenbergEffect) Did you consider writing a design proposal?
RyanCavanaugh commented
on Oct 4, 2018 MemberMore actionsRob Eisenberg (@EisenbergEffect) that is supported already by declaring the type
JSX.ElementAttributesPropertyto have zero propertiesHere's my code:
import { createElement } from './templating/create-element'; declare namespace JSX { type Element = string; interface ElementAttributesProperty { } } class Foo { public bar: string; } const view = () => <div> <Foo bar="baz"></Foo> </div>;
I get the error
[ts] Property 'bar' does not exist on type '{}'.What have I done wrong?Rob Eisenberg (@EisenbergEffect) Because of the
importstatement, your file is a module, which means yourJSXnamespace is local, not global.To make it global, use
declare global:declare global { namespace JSX { type Element = string; interface ElementAttributesProperty { } } }
But after that you're going to face my original issue: the compiler requires that all the non-optional members of the class (including methods and private members) be represented as attributes.
Thanks Georgii Dolzhykov (@thorn0) ! I can confirm that exact behavior now. Here's my code:
import { createElement } from './templating/create-element'; declare global { namespace JSX { type Element = string; interface ElementAttributesProperty { } } } class Foo { public bar: string = ''; public baz: number = 0; } const view = () => <div> <Foo bar='test'></Foo> </div>;
Ryan Cavanaugh (@RyanCavanaugh) This would be 100% valid in Aurelia but TS is complaining that the
Fooelement doesn't include a value forbaz. It sounds like the TS checking for JSX scenarios is causing issues for both Angular and Aurelia. It would be nice to have a solution for this, some compiler setting or something that could be done with theElementAttributesPropertydeclaration to indicate what behavior we need.Hi, Masahiro Wakame (@vvakame) , the optional property is not working with
typescript 3.4.5.The typescript source code is here:

Now, i want to turn off required properties check in TSX context , what should i do?
I got it that how to turn off the required properties check in TSX context.
The code is below:
jsx-namespace.d.tsdeclare namespace JSX { // ... interface IntrinsicAttributes { [attributeName: string]: any } // ... }
It's not documented, but currently, if the
JSX.ElementAttributesPropertyinterface is declared empty,then the element attributes type becomes same as the element instance type. However, if the element instance type is a class, this leads to a situation where the compiler requires that all the members of the class be represented as attributes since they're not optional (and even cannot be marked as such as the class syntax doesn't allow optional members).
This prevents me from using JSX for type-checking Angular 1 templates (you can find my experiments in this area here).
What if we introduce some way to tell the compiler that not all the properties are required, but only some of them? E.g. those having a certain decorator?
A related issue: #5151