Typescript: Suggestion: Allow intrinsic elements to vary by component

Created on 19 Apr 2017  路  7Comments  路  Source: microsoft/TypeScript

This suggestion is motivated by react-three-renderer, which creates a custom renderer for React around the THREE library, and specifically this issue: https://github.com/toxicFork/react-three-renderer/issues/127.

This library was not written with Typescript in mind, and uses lowercased JSX elements for children of its main React3 component. For example:

            <React3 width={800} height={400} clearColor={0xf0fff0} mainCamera="camera">
                <scene>
                    <perspectiveCamera fov={75} aspect={800/400} near={0.001} far={1000} name="camera" ref="camera"
                                       position={new THREE.Vector3(0,0,5)} rotation={new THREE.Euler()}/>

                    <ambientLight color={0x505050}/>

                    <line>
                        <geometry vertices={vertices}/>
                    </line>
                </scene>
            </React3>

While it's possible to avoid compiler warnings by modifying JSX.IntrinsicElements this causes issues where there are conflicts, for example line in the above example which is also an SVG element. Also, within the React3 component it is not sensible/valid to put normal HTML elements, so it would be good to exclude these.

I am following https://github.com/Microsoft/TypeScript/issues/14729 and https://github.com/Microsoft/TypeScript/issues/13618 which seem somewhat related, but don't appear to address this specific issue.

In summary, it should be possible to indicate somehow that a component uses a different set of intrinsic elements, other than JSX.IntrinsicElements.

Awaiting More Feedback Suggestion

Most helpful comment

Hi :)

I'm the author of react-three-renderer and would like to contribute to TypeScript's support for custom renderers if possible :)

Here are a few kinds of renderers that some initial research has shown:

  • some of them do not need react-dom and an application would be using only their elements
  • some would be working to to enhance dom and add a few new elements
  • and others would be creating rendering contexts within other renderers

To go into more detail with react-three-renderer, any element that is rendered within a React3 component would be handled by react-three-renderer instead of react-dom.

To extend the above example:

function testRender() {
  return <div>
    Check out this three dimensional thing:
    <div>
      <React3 {/* diving into react-three-renderer domain now */ ...{}}>
        <webGLRenderer width={800} height={600} antialias>
          <scene>
            <mesh>
              <boxGeometry width={10} height={10} depth={10} />
              <meshBasicMaterial
                color={"FF0000"}
              />
            </mesh>
          </scene>
        </webGLRenderer>
      </React3>
    </div>
  </div>;
}

By looking at this I can think of various potential solutions (assuming no technical limitations):

  • React3 component could somehow have a tag to specify "which type of elements it may contain as children"

    • This could also be extended to any element to help support web components, for example an x-search component would be accepting only children that are x-query and x-engine, while another x-wrapper component would be accepting normal dom nodes

    • (haha and then I could make a react3 web component to have best of both worlds... :D)

  • A typescript source file could perhaps have a tag to say "any element within this file will be of this or that kind"
  • A renderer function could somehow have a tag to say what kind of elements it will be rendering, for example:
import {Ref} from "react";

declare namespace ReactThreeRenderer {
  interface IBoxGeometryProps {
    width: number;
    height: number;
    depth: number;
  }

  interface IReactThreeRendererElement<T> {
    key?: string;
    ref?: Ref<T>;
  }

  interface IntrinsicElements {
    boxGeometry: IReactThreeRendererElement<THREE.BoxGeometry> & IBoxGeometryProps;
    meshBasicMaterial: IReactThreeRendererElement<THREE.MeshBasicMaterial> & THREE.MaterialParameters;
  }
}

/// <jsx-intrinsic-elements using="ReactThreeRenderer.IntrinsicElements"/>
function testRender() {
  return <scene>
    <mesh>
      <boxGeometry width={10} height={10} depth={10} />
      <meshBasicMaterial
        color={"FF0000"}
      />
    </mesh>
  </scene>;
}
  • A project could somehow flag "what sort of elements may it have for JSX" e.g. with a compiler flag
  • For more restrictions, a mesh element can contain only geometry and material types, e.g.:

    • boxGeometry, sphereGeometry and so on, meshBasicMaterial, meshLambertMaterial and so on

    • but it's fine if TypeScript cannot handle these, I'd mark it as "future work" rather than a "must-have" though :)

I hope it helps!

Firtina.

All 7 comments

+1. React Fiber should be released soon and many custom renderers will be appearing.

Hi @RyanCavanaugh you tagged with Awaiting More Feedback a while ago. Are you wanting feedback from me as the person who raised the issue or from the typescript team? Thanks!

as the person who raised the issue or from the typescript team?

It means it is awaiting more feedback from the community as a whole. It means that it isn't obvious to the core team that it is worth expending effort on this suggestion unless there is wider support from the community.

(I was going to point you at the label list but it appears this one isn't listed)

This means we'd like to hear from more people who would be helped by this feature, understand their use cases, and possibly contrast with other proposals that might solve the problem in a simpler way (or solve many other problems at once).

If this feature looks like a good fit for you, please use the :+1: reaction on the original post. Comments outlining different scenarios that would be benefited from the feature are also welcomed.

Hi :)

I'm the author of react-three-renderer and would like to contribute to TypeScript's support for custom renderers if possible :)

Here are a few kinds of renderers that some initial research has shown:

  • some of them do not need react-dom and an application would be using only their elements
  • some would be working to to enhance dom and add a few new elements
  • and others would be creating rendering contexts within other renderers

To go into more detail with react-three-renderer, any element that is rendered within a React3 component would be handled by react-three-renderer instead of react-dom.

To extend the above example:

function testRender() {
  return <div>
    Check out this three dimensional thing:
    <div>
      <React3 {/* diving into react-three-renderer domain now */ ...{}}>
        <webGLRenderer width={800} height={600} antialias>
          <scene>
            <mesh>
              <boxGeometry width={10} height={10} depth={10} />
              <meshBasicMaterial
                color={"FF0000"}
              />
            </mesh>
          </scene>
        </webGLRenderer>
      </React3>
    </div>
  </div>;
}

By looking at this I can think of various potential solutions (assuming no technical limitations):

  • React3 component could somehow have a tag to specify "which type of elements it may contain as children"

    • This could also be extended to any element to help support web components, for example an x-search component would be accepting only children that are x-query and x-engine, while another x-wrapper component would be accepting normal dom nodes

    • (haha and then I could make a react3 web component to have best of both worlds... :D)

  • A typescript source file could perhaps have a tag to say "any element within this file will be of this or that kind"
  • A renderer function could somehow have a tag to say what kind of elements it will be rendering, for example:
import {Ref} from "react";

declare namespace ReactThreeRenderer {
  interface IBoxGeometryProps {
    width: number;
    height: number;
    depth: number;
  }

  interface IReactThreeRendererElement<T> {
    key?: string;
    ref?: Ref<T>;
  }

  interface IntrinsicElements {
    boxGeometry: IReactThreeRendererElement<THREE.BoxGeometry> & IBoxGeometryProps;
    meshBasicMaterial: IReactThreeRendererElement<THREE.MeshBasicMaterial> & THREE.MaterialParameters;
  }
}

/// <jsx-intrinsic-elements using="ReactThreeRenderer.IntrinsicElements"/>
function testRender() {
  return <scene>
    <mesh>
      <boxGeometry width={10} height={10} depth={10} />
      <meshBasicMaterial
        color={"FF0000"}
      />
    </mesh>
  </scene>;
}
  • A project could somehow flag "what sort of elements may it have for JSX" e.g. with a compiler flag
  • For more restrictions, a mesh element can contain only geometry and material types, e.g.:

    • boxGeometry, sphereGeometry and so on, meshBasicMaterial, meshLambertMaterial and so on

    • but it's fine if TypeScript cannot handle these, I'd mark it as "future work" rather than a "must-have" though :)

I hope it helps!

Firtina.

I think this could be handled by checking the children of intrinsic elements the same as non-intrinsic elements.

I've run into a similar case where I'm leveraging JSX to do declarative xml building. I have certain constraints on child-parent relationships that are great for non-intrinsic elements, but don't appear to be type-checked for intrinsic elements.

I also vote for this feature. The use case I would image is following:
in angular or vue templates we are able to use components defined and registered in top-level module, so it would be nice to use these components without importing them explicitly as modules. It could look something like

import { myModule } from '../myModule'

myModule.component({
  name: 'foo',
  render: () => (
    <sui-button size="large">Submit</sui-button>
  )
})

with render function typed somehow that it's allow to use custom JSX.IntrinsicElements inside of it

interface Component {
  name: string,
  render: JSX.RenderFunction<{
    "sui-button": {
      size?: "large" | "small"
    },
    "other-component": {
      someProp?: number
    }
  }>
}

I guess it can possibly allow supporting directives in JSX as well

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Roam-Cooper picture Roam-Cooper  路  3Comments

jbondc picture jbondc  路  3Comments

MartynasZilinskas picture MartynasZilinskas  路  3Comments

uber5001 picture uber5001  路  3Comments

zhuravlikjb picture zhuravlikjb  路  3Comments