Skip to content

JSX in TypeScript #3203

Closed
Closed
@RyanCavanaugh

Description

@RyanCavanaugh

Updated 7/24 with some spec changes

TODO: Add language about string indexers on props types turning off surplus attribute errors

Up-front Requirements

To use JSX expressions in a TypeScript file, you must use the tsx extension. Additionally, during compilation, you must specify a --jsx <value>. Supported values are preserve, which emits the JSX as-is (modulo TypeScript desugaring inside JSX Expressions), or react, which emits React-compatible code according to their most recent stable release.

The JSX namespace

Similar to how we have special types like Array and Number in the global namespace to represent the types of [] and 42, we will add a new global optional namespace JSX with some special types in it.

This namespace has four special types in it. Let's examine them.

JSX.Element

The type of any <expr /> is JSX.Element:

// x is of type JSX.Element
var x = <Something hello="world" />;

If this type does not exist, it is instead an implicit any.

JSX.IntrinsicElements

When checking an expression of the form <foo ...> where foo begins with a lower-case letter, the type JSX.IntrinsicElements is examined. If that type has a property called foo, or a string indexer, then the element attributes type (see below) is the type of that property (or the type of that string indexer). This process is called intrinsic lookup.

JSX.ElementClass

For expressions of the form <Foo ... /> or <namespace.foo .../> we use to value-based lookup: Given an expression <Foo ... >, find a value named Foo in the current lexical scope.

If no value with this name exists, an error is issued. Otherwise, we treat the type of foo as an element class. The element class is the constructor or factory function that produces an instance of an element instance type.

It is an error if the element class is not assignable to the type JSX.ElementClass, or if value-based lookup occurs and no type named JSX.ElementClass exists.

The element instance type is the return types of the first construct signatures (if present), or the first call signature (if no construct signatures are present). If the element type is any, the element instance type is any.

It is an error if the element class has no call or construct signatures.

JSX.ElementAttributesProperty

Given an element instance type, we need to produce a type that lists the allowed attributes for that element. We call this the element attributes type. For example, in a React element <div>, the element attributes type includes properties like id, className, and onClick.

The interface JSX.ElementAttributesProperty defines this process. It may have 0 properties, in which case all attributes are assumed to be valid and of type any, or 1 property, in which case the attributes of the element must map to the properties of the type of that property.

Note that intrinsic lookup is not affected by ElementClass or ElementAttributesProperty.

Attribute Checking

<Something x={expr1} { ...spr } y={expr2} />

Given an element attributes type E derived from Something in the above example, the attributes of the element are checked as follows:

  • If the attribute is a normal attribute P initialized with expr:
    • If E has a property P, process expr with the contextual type of the type of E.P. Otherwise, issue an error.
    • It is an error if expr is not assignable to E.P.
    • Add P to the list of properties seen
  • If the attribute is a spread attribute on expr:
    • For each property P in the apparent type of spr:
      • If a later attribute assignment with the name P occurs (either as an explicit attribute, as shown above with y, or via another spread attribute), nothing happens
      • If E has a property P:
        • It is an error if spr.P is not assignable to E.P.
      • Add P to the list of properties seen
      • Otherwise, nothing happens
  • After all attributes have been processed, issue an error if any required property in E does not have a corresponding entry in the list of properties seen

Non-identifier attributes

If an attribute name would not be a valid identifier in JavaScript code, there need not be a corresponding property in the element attributes type. This exception does not apply to names that are invalid identifiers because they are reserved words (like var).

Metadata

Metadata

Assignees

Labels

CommittedThe team has roadmapped this issueFixedA PR has been merged for this issueSuggestionAn idea for TypeScript

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions