Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JSX expression type inferencing. #6208

Closed
jameskeane opened this issue Dec 22, 2015 · 6 comments
Closed

JSX expression type inferencing. #6208

jameskeane opened this issue Dec 22, 2015 · 6 comments
Labels
Domain: JSX/TSX Relates to the JSX parser and emitter Question An issue which isn't directly actionable in code

Comments

@jameskeane
Copy link
Contributor

I am working on a jsx library for using CustomElements, and I would like to be able to use the element instance type directly from a JSX expression. Reading through #3203 it seems that JSX.Element is the final return type of all JSX expressions.

Could the JSX expression type not be inferred by evaluating the return type of React.createElement? i.e. run the type-inferencing as if the code has been de-sugared.

Consider:

namespace React {
  interface ElementConstructor<T> {
      new(): T
  }

  // i.e. the element instance type created is of the passed in constructor.
  function createElement<T>(cls: ElementConstructor<T>, props: any, ...children: any[]): T {
  }
}

class ExampleTag {
  exampleFunction() { }
}

// The next line fails type checking
(<ExampleTag />).exampleFunction(); // The type of the JSX expression is specific.

Notice the inconsistency of the type inferencing between JSX and it's de-sugared equivalent.

(<ExampleTag />).exampleFunction();  // Fails type check
(React.createElement(ExampleTag)).exampleFunction();  // Passes
@ivogabe
Copy link
Contributor

ivogabe commented Dec 22, 2015

+1, would also be very useful for usage without React.

@RyanCavanaugh RyanCavanaugh added the Question An issue which isn't directly actionable in code label Dec 22, 2015
@RyanCavanaugh
Copy link
Member

This is a common misunderstanding of how React works.

<ExampleTag /> is not a class instance of ExampleTag. Your definition of createElement is also incorrect - createElement does not instantiate the class. (<ExampleTag />).exampleFunction() doesn't typecheck because it doesn't work at runtime either.

If you have code like this:

class MyElement {
  constructor() {
    console.log('ctor invoked');
  }
  render() {
    return <div>Hello world</div>;
  }
}

console.log('Creating an element');
let x = <MyElement />;
console.log('Rendering it');
ReactDOM.render(x, document.getElementById('target'));

You'll see that the output is:

Creating an element
Rendering it
ctor invoked

Note that the class is not instantiated at the point where we wrote <MyExample /> -- it's only when the component is rendered that React instantiates your class.

@jameskeane
Copy link
Contributor Author

I think you are misunderstanding, I am implementing React.createElement so that our internal library can use JSX. This issue has nothing to do with how React works, it is about how typescript fundamentally supports JSX implementations.

@RyanCavanaugh
Copy link
Member

It's very confusing to call your library React as well 😄

One of our decision points when implementing JSX support was what non-React consumers of JSX were doing. We can't support every possible library (for example, we don't know how they determine what the set of valid attributes are), so we chose to implement a conservative set that didn't have to provide per-object data. This was also an issue for allowing React itself because things like <div> don't route through an actual div value identifier to use for resolution.

If your library (or one with identical behavior) becomes popular and has well-defined semantics, we can look into how to support it more fully.

@jameskeane
Copy link
Contributor Author

You are right, it is confusing to have to define React to use typescript's built-in JSX support 😉.

I guess what I really want is --jsx generic, so I can define JSX.createElement. I'll open another ticket soon with more details.

But before doing that, I would like to know if it is even possible to infer a JSX expression type from the createElement method signature or if there is some structural or other reason for using the JSX.Element type.

@RyanCavanaugh
Copy link
Member

TypeScript doesn't look at React when resolving JSX elements -- everything goes through the JSX namespace. The emitter cares if you're using --jsx react, but presumably a non-React library would use --jsx preserve, in which case React never enters the picture.

It's certainly possible to support this pattern (can I call it "XAML-like" ?). I'd like to understand more what its rules are and what other libraries are doing this. Function call resolution might be the right pattern, or maybe something else, depending on exactly how it works.

@DanielRosenwasser DanielRosenwasser added the Domain: JSX/TSX Relates to the JSX parser and emitter label Jan 30, 2016
@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Domain: JSX/TSX Relates to the JSX parser and emitter Question An issue which isn't directly actionable in code
Projects
None yet
Development

No branches or pull requests

4 participants