JSX SFC WIP

This commit is contained in:
Ryan Cavanaugh 2015-11-09 23:10:04 -08:00
parent 52b25a5437
commit e30a64fbdf
11 changed files with 19593 additions and 55 deletions

View File

@ -141,8 +141,8 @@ namespace ts {
let globalTemplateStringsArrayType: ObjectType;
let globalESSymbolType: ObjectType;
let jsxElementType: ObjectType;
/** Lazily loaded, use getJsxIntrinsicElementType() */
let jsxIntrinsicElementsType: ObjectType;
/** Things we lazy load from the JSX namespace */
let jsxTypes: {[name: string]: ObjectType} = {};
let globalIterableType: GenericType;
let globalIteratorType: GenericType;
let globalIterableIteratorType: GenericType;
@ -7641,12 +7641,11 @@ namespace ts {
return type;
}
/// Returns the type JSX.IntrinsicElements. May return `unknownType` if that type is not present.
function getJsxIntrinsicElementsType() {
if (!jsxIntrinsicElementsType) {
jsxIntrinsicElementsType = getExportedTypeFromNamespace(JsxNames.JSX, JsxNames.IntrinsicElements) || unknownType;
function getJsxType(name: string) {
if (jsxTypes[name] === undefined) {
return jsxTypes[name] = getExportedTypeFromNamespace(JsxNames.JSX, name) || unknownType;
}
return jsxIntrinsicElementsType;
return jsxTypes[name];
}
/// Given a JSX opening element or self-closing element, return the symbol of the property that the tag name points to if
@ -7669,7 +7668,7 @@ namespace ts {
return links.resolvedSymbol;
function lookupIntrinsicTag(node: JsxOpeningLikeElement | JsxClosingElement): Symbol {
let intrinsicElementsType = getJsxIntrinsicElementsType();
let intrinsicElementsType = getJsxType(JsxNames.IntrinsicElements);
if (intrinsicElementsType !== unknownType) {
// Property case
let intrinsicProp = getPropertyOfType(intrinsicElementsType, (<Identifier>node.tagName).text);
@ -7701,7 +7700,7 @@ namespace ts {
// Look up the value in the current scope
if (valueSymbol && valueSymbol !== unknownSymbol) {
links.jsxFlags |= JsxFlags.ClassElement;
links.jsxFlags |= JsxFlags.ValueElement;
if (valueSymbol.flags & SymbolFlags.Alias) {
markAliasSymbolAsReferenced(valueSymbol);
}
@ -7730,7 +7729,7 @@ namespace ts {
function getJsxElementInstanceType(node: JsxOpeningLikeElement) {
// There is no such thing as an instance type for a non-class element. This
// line shouldn't be hit.
Debug.assert(!!(getNodeLinks(node).jsxFlags & JsxFlags.ClassElement), "Should not call getJsxElementInstanceType on non-class Element");
Debug.assert(!!(getNodeLinks(node).jsxFlags & JsxFlags.ValueElement), "Should not call getJsxElementInstanceType on non-class Element");
let classSymbol = getJsxElementTagSymbol(node);
if (classSymbol === unknownSymbol) {
@ -7808,18 +7807,21 @@ namespace ts {
if (!links.resolvedJsxType) {
let sym = getJsxElementTagSymbol(node);
if (links.jsxFlags & JsxFlags.ClassElement) {
if (links.jsxFlags & JsxFlags.ValueElement) {
// Get the element instance type (the result of newing or invoking this tag)
let elemInstanceType = getJsxElementInstanceType(node);
// Is this is a stateless function component? See if its single signature is
// assignable to the JSX Element Type with either 0 arguments, or 1 argument
// that is an object type
// assignable to the JSX Element Type
let callSignature = getSingleCallSignature(getTypeOfSymbol(sym));
let callReturnType = callSignature && getReturnTypeOfSignature(callSignature);
let paramType = callSignature && (callSignature.parameters.length === 0 ? emptyObjectType : getTypeOfSymbol(callSignature.parameters[0]));
if (callReturnType && isTypeAssignableTo(callReturnType, jsxElementType) && paramType.flags & TypeFlags.ObjectType) {
// TODO: Things like 'ref' and 'key' are always valid, how to account for that?
let paramType = callReturnType && callSignature && (callSignature.parameters.length === 0 ? emptyObjectType : getTypeOfSymbol(callSignature.parameters[0]));
if (callReturnType && isTypeAssignableTo(callReturnType, jsxElementType) && (paramType.flags & TypeFlags.ObjectType)) {
// Intersect in JSX.IntrinsicAttributes if it exists
let intrinsicAttributes = getJsxType(JsxNames.IntrinsicAttributes);
if(intrinsicAttributes !== unknownType) {
paramType = intersectTypes(intrinsicAttributes, paramType);
}
return paramType;
}
@ -7851,14 +7853,35 @@ namespace ts {
return links.resolvedJsxType = emptyObjectType;
}
else if (isTypeAny(attributesType) || (attributesType === unknownType)) {
// Props is of type 'any' or unknown
return links.resolvedJsxType = attributesType;
}
else if (!(attributesType.flags & TypeFlags.ObjectType)) {
// Props is not an object type
error(node.tagName, Diagnostics.JSX_element_attributes_type_0_must_be_an_object_type, typeToString(attributesType));
return links.resolvedJsxType = anyType;
}
else {
return links.resolvedJsxType = attributesType;
// Normal case -- add in IntrinsicClassElements<T> and IntrinsicElements
let apparentAttributesType = attributesType;
let intrinsicClassAttribs = getJsxType(JsxNames.IntrinsicClassAttributes);
if (intrinsicClassAttribs !== unknownType) {
let typeParams = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(intrinsicClassAttribs.symbol);
if(typeParams) {
if(typeParams.length === 1) {
apparentAttributesType = intersectTypes(createTypeReference(<GenericType>intrinsicClassAttribs, [elemInstanceType]), apparentAttributesType);
}
} else {
apparentAttributesType = intersectTypes(attributesType, intrinsicClassAttribs);
}
}
let intrinsicAttribs = getJsxType(JsxNames.IntrinsicAttributes);
if(intrinsicAttribs !== unknownType) {
apparentAttributesType = intersectTypes(intrinsicAttribs, apparentAttributesType);
}
return links.resolvedJsxType = apparentAttributesType;
}
}
}
@ -7898,7 +7921,7 @@ namespace ts {
/// Returns all the properties of the Jsx.IntrinsicElements interface
function getJsxIntrinsicTagNames(): Symbol[] {
let intrinsics = getJsxIntrinsicElementsType();
let intrinsics = getJsxType(JsxNames.IntrinsicElements);
return intrinsics ? getPropertiesOfType(intrinsics) : emptyArray;
}

View File

@ -434,12 +434,16 @@ namespace ts {
export const enum JsxFlags {
None = 0,
/** An element from a named property of the JSX.IntrinsicElements interface */
IntrinsicNamedElement = 1 << 0,
/** An element inferred from the string index signature of the JSX.IntrinsicElements interface */
IntrinsicIndexedElement = 1 << 1,
ClassElement = 1 << 2,
UnknownElement = 1 << 3,
/** An element backed by a class, class-like, or function value */
ValueElement = 1 << 2,
/** Element resolution failed */
UnknownElement = 1 << 4,
IntrinsicElement = IntrinsicNamedElement | IntrinsicIndexedElement
IntrinsicElement = IntrinsicNamedElement | IntrinsicIndexedElement,
}

View File

@ -975,6 +975,7 @@ namespace Harness {
useCaseSensitiveFileNames?: boolean;
includeBuiltFile?: string;
baselineFile?: string;
libFiles?: string;
}
// Additional options not already in ts.optionDeclarations
@ -984,6 +985,7 @@ namespace Harness {
{ name: "baselineFile", type: "string" },
{ name: "includeBuiltFile", type: "string" },
{ name: "fileName", type: "string" },
{ name: "libFiles", type: "string" },
{ name: "noErrorTruncation", type: "boolean" }
];
@ -1115,6 +1117,15 @@ namespace Harness {
includeBuiltFiles.push({ unitName: builtFileName, content: normalizeLineEndings(IO.readFile(builtFileName), newLine) });
}
// Files from tests\lib that are requested by "@libFiles"
if (options.libFiles) {
ts.forEach(options.libFiles.split(','), filename => {
let libFileName = 'tests/lib/' + filename;
includeBuiltFiles.push({ unitName: libFileName, content: normalizeLineEndings(IO.readFile(libFileName), newLine) });
});
}
let useCaseSensitiveFileNames = options.useCaseSensitiveFileNames !== undefined ? options.useCaseSensitiveFileNames : Harness.IO.useCaseSensitiveFileNames();
let fileOutputs: GeneratedFile[] = [];

View File

@ -1,27 +1,37 @@
tests/cases/conformance/jsx/tsxStatelessFunctionComponents1.tsx(17,9): error TS2324: Property 'name' is missing in type '{ name: string; }'.
tests/cases/conformance/jsx/tsxStatelessFunctionComponents1.tsx(17,16): error TS2339: Property 'naaame' does not exist on type '{ name: string; }'.
tests/cases/conformance/jsx/tsxStatelessFunctionComponents1.tsx(12,9): error TS2324: Property 'name' is missing in type 'IntrinsicAttributes & { name: string; }'.
tests/cases/conformance/jsx/tsxStatelessFunctionComponents1.tsx(12,16): error TS2339: Property 'naaame' does not exist on type 'IntrinsicAttributes & { name: string; }'.
tests/cases/conformance/jsx/tsxStatelessFunctionComponents1.tsx(19,15): error TS2322: Type 'number' is not assignable to type 'string'.
tests/cases/conformance/jsx/tsxStatelessFunctionComponents1.tsx(21,15): error TS2339: Property 'naaaaaaame' does not exist on type 'IntrinsicAttributes & { name?: string; }'.
==== tests/cases/conformance/jsx/tsxStatelessFunctionComponents1.tsx (2 errors) ====
declare module JSX {
interface Element { el: any; }
interface IntrinsicElements { div: any; }
}
==== tests/cases/conformance/jsx/tsxStatelessFunctionComponents1.tsx (4 errors) ====
function Greet(x: {name: string}) {
return <div>Hello, {x}</div>;
}
function Meet({name = 'world'}) {
return <div>Hello, {x}</div>;
return <div>Hello, {name}</div>;
}
// OK
let x = <Greet name='world' />;
let a = <Greet name='world' />;
// Error
let y = <Greet naaame='world' />;
let b = <Greet naaame='world' />;
~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2324: Property 'name' is missing in type '{ name: string; }'.
!!! error TS2324: Property 'name' is missing in type 'IntrinsicAttributes & { name: string; }'.
~~~~~~
!!! error TS2339: Property 'naaame' does not exist on type '{ name: string; }'.
!!! error TS2339: Property 'naaame' does not exist on type 'IntrinsicAttributes & { name: string; }'.
// OK
let c = <Meet />;
// OK
let d = <Meet name='me' />;
// Error
let e = <Meet name={42} />;
~~~~~~~~~
!!! error TS2322: Type 'number' is not assignable to type 'string'.
// Error
let f = <Meet naaaaaaame='no' />;
~~~~~~~~~~
!!! error TS2339: Property 'naaaaaaame' does not exist on type 'IntrinsicAttributes & { name?: string; }'.

View File

@ -1,21 +1,25 @@
//// [tsxStatelessFunctionComponents1.tsx]
declare module JSX {
interface Element { el: any; }
interface IntrinsicElements { div: any; }
}
function Greet(x: {name: string}) {
return <div>Hello, {x}</div>;
}
function Meet({name = 'world'}) {
return <div>Hello, {x}</div>;
return <div>Hello, {name}</div>;
}
// OK
let x = <Greet name='world' />;
let a = <Greet name='world' />;
// Error
let y = <Greet naaame='world' />;
let b = <Greet naaame='world' />;
// OK
let c = <Meet />;
// OK
let d = <Meet name='me' />;
// Error
let e = <Meet name={42} />;
// Error
let f = <Meet naaaaaaame='no' />;
//// [tsxStatelessFunctionComponents1.jsx]
@ -24,9 +28,17 @@ function Greet(x) {
}
function Meet(_a) {
var _b = _a.name, name = _b === void 0 ? 'world' : _b;
return <div>Hello, {x}</div>;
return <div>Hello, {name}</div>;
}
// OK
var x = <Greet name='world'/>;
var a = <Greet name='world'/>;
// Error
var y = <Greet naaame='world'/>;
var b = <Greet naaame='world'/>;
// OK
var c = <Meet />;
// OK
var d = <Meet name='me'/>;
// Error
var e = <Meet name={42}/>;
// Error
var f = <Meet naaaaaaame='no'/>;

View File

@ -0,0 +1,52 @@
tests/cases/conformance/jsx/tsxStatelessFunctionComponents2.tsx(2,1): error TS1148: Cannot compile modules unless the '--module' flag is provided.
tests/cases/conformance/jsx/tsxStatelessFunctionComponents2.tsx(20,16): error TS2339: Property 'ref' does not exist on type 'IntrinsicAttributes & { name?: string; }'.
tests/cases/conformance/jsx/tsxStatelessFunctionComponents2.tsx(26,42): error TS2339: Property 'subtr' does not exist on type 'string'.
tests/cases/conformance/jsx/tsxStatelessFunctionComponents2.tsx(28,5): error TS2451: Cannot redeclare block-scoped variable 'f'.
tests/cases/conformance/jsx/tsxStatelessFunctionComponents2.tsx(28,33): error TS2339: Property 'notARealProperty' does not exist on type 'BigGreeter'.
tests/cases/conformance/jsx/tsxStatelessFunctionComponents2.tsx(31,5): error TS2451: Cannot redeclare block-scoped variable 'f'.
==== tests/cases/conformance/jsx/tsxStatelessFunctionComponents2.tsx (6 errors) ====
import React = require('react');
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS1148: Cannot compile modules unless the '--module' flag is provided.
function Greet(x: {name?: string}) {
return <div>Hello, {x}</div>;
}
class BigGreeter extends React.Component<{ name?: string }, {}> {
render() {
return <div></div>;
}
greeting: string;
}
// OK
let a = <Greet />;
// OK
let b = <Greet key="k" />;
// Error
let c = <Greet ref="myRef" />;
~~~
!!! error TS2339: Property 'ref' does not exist on type 'IntrinsicAttributes & { name?: string; }'.
// OK
let d = <BigGreeter ref={x => x.greeting.substr(10)} />;
// Error ('subtr')
let e = <BigGreeter ref={x => x.greeting.subtr(10)} />;
~~~~~
!!! error TS2339: Property 'subtr' does not exist on type 'string'.
// Error
let f = <BigGreeter ref={x => x.notARealProperty} />;
~
!!! error TS2451: Cannot redeclare block-scoped variable 'f'.
~~~~~~~~~~~~~~~~
!!! error TS2339: Property 'notARealProperty' does not exist on type 'BigGreeter'.
// OK
let f = <BigGreeter key={100} />;
~
!!! error TS2451: Cannot redeclare block-scoped variable 'f'.

View File

@ -0,0 +1,67 @@
//// [tsxStatelessFunctionComponents2.tsx]
import React = require('react');
function Greet(x: {name?: string}) {
return <div>Hello, {x}</div>;
}
class BigGreeter extends React.Component<{ name?: string }, {}> {
render() {
return <div></div>;
}
greeting: string;
}
// OK
let a = <Greet />;
// OK
let b = <Greet key="k" />;
// Error
let c = <Greet ref="myRef" />;
// OK
let d = <BigGreeter ref={x => x.greeting.substr(10)} />;
// Error ('subtr')
let e = <BigGreeter ref={x => x.greeting.subtr(10)} />;
// Error
let f = <BigGreeter ref={x => x.notARealProperty} />;
// OK
let f = <BigGreeter key={100} />;
//// [tsxStatelessFunctionComponents2.jsx]
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var React = require('react');
function Greet(x) {
return <div>Hello, {x}</div>;
}
var BigGreeter = (function (_super) {
__extends(BigGreeter, _super);
function BigGreeter() {
_super.apply(this, arguments);
}
BigGreeter.prototype.render = function () {
return <div></div>;
};
return BigGreeter;
})(React.Component);
// OK
var a = <Greet />;
// OK
var b = <Greet key="k"/>;
// Error
var c = <Greet ref="myRef"/>;
// OK
var d = <BigGreeter ref={function (x) { return x.greeting.substr(10); }}/>;
// Error ('subtr')
var e = <BigGreeter ref={function (x) { return x.greeting.subtr(10); }}/>;
// Error
var f = <BigGreeter ref={function (x) { return x.notARealProperty; }}/>;
// OK
var f = <BigGreeter key={100}/>;

View File

@ -1,19 +1,25 @@
//@filename: file.tsx
//@jsx: preserve
declare module JSX {
interface Element { el: any; }
interface IntrinsicElements { div: any; }
}
// @filename: file.tsx
// @jsx: preserve
// @noLib: true
// @libFiles: react.d.ts,lib.d.ts
function Greet(x: {name: string}) {
return <div>Hello, {x}</div>;
}
function Meet({name = 'world'}) {
return <div>Hello, {x}</div>;
return <div>Hello, {name}</div>;
}
// OK
let x = <Greet name='world' />;
let a = <Greet name='world' />;
// Error
let y = <Greet naaame='world' />;
let b = <Greet naaame='world' />;
// OK
let c = <Meet />;
// OK
let d = <Meet name='me' />;
// Error
let e = <Meet name={42} />;
// Error
let f = <Meet naaaaaaame='no' />;

View File

@ -0,0 +1,35 @@
// @filename: file.tsx
// @jsx: preserve
// @noLib: true
// @libFiles: react.d.ts,lib.d.ts
import React = require('react');
function Greet(x: {name?: string}) {
return <div>Hello, {x}</div>;
}
class BigGreeter extends React.Component<{ name?: string }, {}> {
render() {
return <div></div>;
}
greeting: string;
}
// OK
let a = <Greet />;
// OK
let b = <Greet key="k" />;
// Error
let c = <Greet ref="myRef" />;
// OK
let d = <BigGreeter ref={x => x.greeting.substr(10)} />;
// Error ('subtr')
let e = <BigGreeter ref={x => x.greeting.subtr(10)} />;
// Error
let f = <BigGreeter ref={x => x.notARealProperty} />;
// OK
let f = <BigGreeter key={100} />;

17264
tests/lib/lib.d.ts vendored Normal file

File diff suppressed because it is too large Load Diff

2054
tests/lib/react.d.ts vendored Normal file

File diff suppressed because it is too large Load Diff