Skip to content

Commit b90d291

Browse files
authoredNov 15, 2018
Strongly typecheck unions of intrinsic tag names (microsoft#28557)
1 parent 1ad7b0f commit b90d291

File tree

6 files changed

+96
-22
lines changed

6 files changed

+96
-22
lines changed
 

‎src/compiler/checker.ts

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18355,16 +18355,31 @@ namespace ts {
1835518355
return getNameFromJsxElementAttributesContainer(JsxNames.ElementChildrenAttributeNameContainer, jsxNamespace);
1835618356
}
1835718357

18358-
function getUninstantiatedJsxSignaturesOfType(elementType: Type): ReadonlyArray<Signature> {
18358+
function getUninstantiatedJsxSignaturesOfType(elementType: Type, caller: JsxOpeningLikeElement): ReadonlyArray<Signature> {
18359+
if (elementType.flags & TypeFlags.String) {
18360+
return [anySignature];
18361+
}
18362+
else if (elementType.flags & TypeFlags.StringLiteral) {
18363+
const intrinsicType = getIntrinsicAttributesTypeFromStringLiteralType(elementType as StringLiteralType, caller);
18364+
if (!intrinsicType) {
18365+
error(caller, Diagnostics.Property_0_does_not_exist_on_type_1, (elementType as StringLiteralType).value, "JSX." + JsxNames.IntrinsicElements);
18366+
return emptyArray;
18367+
}
18368+
else {
18369+
const fakeSignature = createSignatureForJSXIntrinsic(caller, intrinsicType);
18370+
return [fakeSignature];
18371+
}
18372+
}
18373+
const apparentElemType = getApparentType(elementType);
1835918374
// Resolve the signatures, preferring constructor
18360-
let signatures = getSignaturesOfType(elementType, SignatureKind.Construct);
18375+
let signatures = getSignaturesOfType(apparentElemType, SignatureKind.Construct);
1836118376
if (signatures.length === 0) {
1836218377
// No construct signatures, try call signatures
18363-
signatures = getSignaturesOfType(elementType, SignatureKind.Call);
18378+
signatures = getSignaturesOfType(apparentElemType, SignatureKind.Call);
1836418379
}
18365-
if (signatures.length === 0 && elementType.flags & TypeFlags.Union) {
18380+
if (signatures.length === 0 && apparentElemType.flags & TypeFlags.Union) {
1836618381
// If each member has some combination of new/call signatures; make a union signature list for those
18367-
signatures = getUnionSignatures(map((elementType as UnionType).types, getUninstantiatedJsxSignaturesOfType));
18382+
signatures = getUnionSignatures(map((apparentElemType as UnionType).types, t => getUninstantiatedJsxSignaturesOfType(t, caller)));
1836818383
}
1836918384
return signatures;
1837018385
}
@@ -20547,21 +20562,8 @@ namespace ts {
2054720562
return resolveErrorCall(node);
2054820563
}
2054920564

20550-
if (exprTypes.flags & TypeFlags.StringLiteral) {
20551-
const intrinsicType = getIntrinsicAttributesTypeFromStringLiteralType(exprTypes as StringLiteralType, node);
20552-
if (!intrinsicType) {
20553-
error(node, Diagnostics.Property_0_does_not_exist_on_type_1, (exprTypes as StringLiteralType).value, "JSX." + JsxNames.IntrinsicElements);
20554-
return resolveUntypedCall(node);
20555-
}
20556-
else {
20557-
const fakeSignature = createSignatureForJSXIntrinsic(node, intrinsicType);
20558-
checkTypeAssignableToAndOptionallyElaborate(checkExpressionWithContextualType(node.attributes, getEffectiveFirstArgumentForJsxSignature(fakeSignature, node), /*mapper*/ undefined), intrinsicType, node.tagName, node.attributes);
20559-
return fakeSignature;
20560-
}
20561-
}
20562-
20563-
const signatures = getUninstantiatedJsxSignaturesOfType(apparentType);
20564-
if (exprTypes.flags & TypeFlags.String || isUntypedFunctionCall(exprTypes, apparentType, signatures.length, /*constructSignatures*/ 0)) {
20565+
const signatures = getUninstantiatedJsxSignaturesOfType(exprTypes, node);
20566+
if (isUntypedFunctionCall(exprTypes, apparentType, signatures.length, /*constructSignatures*/ 0)) {
2056520567
return resolveUntypedCall(node);
2056620568
}
2056720569

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//// [jsxIntrinsicUnions.tsx]
2+
/// <reference path="/.lib/react16.d.ts" />
3+
4+
import * as React from "react";
5+
6+
const El = Math.random() ? 'h1' : 'h2';
7+
8+
const tag = <El className="ok" key="key">{"Title"}</El>;
9+
10+
11+
//// [jsxIntrinsicUnions.js]
12+
"use strict";
13+
/// <reference path="react16.d.ts" />
14+
exports.__esModule = true;
15+
var React = require("react");
16+
var El = Math.random() ? 'h1' : 'h2';
17+
var tag = React.createElement(El, { className: "ok", key: "key" }, "Title");
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
=== tests/cases/compiler/jsxIntrinsicUnions.tsx ===
2+
/// <reference path="react16.d.ts" />
3+
4+
import * as React from "react";
5+
>React : Symbol(React, Decl(jsxIntrinsicUnions.tsx, 2, 6))
6+
7+
const El = Math.random() ? 'h1' : 'h2';
8+
>El : Symbol(El, Decl(jsxIntrinsicUnions.tsx, 4, 5))
9+
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
10+
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
11+
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
12+
13+
const tag = <El className="ok" key="key">{"Title"}</El>;
14+
>tag : Symbol(tag, Decl(jsxIntrinsicUnions.tsx, 6, 5))
15+
>El : Symbol(El, Decl(jsxIntrinsicUnions.tsx, 4, 5))
16+
>className : Symbol(className, Decl(jsxIntrinsicUnions.tsx, 6, 15))
17+
>key : Symbol(key, Decl(jsxIntrinsicUnions.tsx, 6, 30))
18+
>El : Symbol(El, Decl(jsxIntrinsicUnions.tsx, 4, 5))
19+
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
=== tests/cases/compiler/jsxIntrinsicUnions.tsx ===
2+
/// <reference path="react16.d.ts" />
3+
4+
import * as React from "react";
5+
>React : typeof React
6+
7+
const El = Math.random() ? 'h1' : 'h2';
8+
>El : "h1" | "h2"
9+
>Math.random() ? 'h1' : 'h2' : "h1" | "h2"
10+
>Math.random() : number
11+
>Math.random : () => number
12+
>Math : Math
13+
>random : () => number
14+
>'h1' : "h1"
15+
>'h2' : "h2"
16+
17+
const tag = <El className="ok" key="key">{"Title"}</El>;
18+
>tag : JSX.Element
19+
><El className="ok" key="key">{"Title"}</El> : JSX.Element
20+
>El : "h1" | "h2"
21+
>className : string
22+
>key : string
23+
>"Title" : "Title"
24+
>El : "h1" | "h2"
25+

‎tests/baselines/reference/tsxDynamicTagName3.errors.txt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
tests/cases/conformance/jsx/tsxDynamicTagName3.tsx(9,1): error TS2339: Property 'h1' does not exist on type 'JSX.IntrinsicElements'.
2+
tests/cases/conformance/jsx/tsxDynamicTagName3.tsx(9,2): error TS2604: JSX element type 'CustomTag' does not have any construct or call signatures.
23

34

4-
==== tests/cases/conformance/jsx/tsxDynamicTagName3.tsx (1 errors) ====
5+
==== tests/cases/conformance/jsx/tsxDynamicTagName3.tsx (2 errors) ====
56
declare module JSX {
67
interface Element { }
78
interface IntrinsicElements {
@@ -12,4 +13,6 @@ tests/cases/conformance/jsx/tsxDynamicTagName3.tsx(9,1): error TS2339: Property
1213
var CustomTag: "h1" = "h1";
1314
<CustomTag> Hello World </CustomTag> // This should be an error. we will try look up string literal type in JSX.IntrinsicElements
1415
~~~~~~~~~~~
15-
!!! error TS2339: Property 'h1' does not exist on type 'JSX.IntrinsicElements'.
16+
!!! error TS2339: Property 'h1' does not exist on type 'JSX.IntrinsicElements'.
17+
~~~~~~~~~
18+
!!! error TS2604: JSX element type 'CustomTag' does not have any construct or call signatures.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// @jsx: react
2+
/// <reference path="/.lib/react16.d.ts" />
3+
4+
import * as React from "react";
5+
6+
const El = Math.random() ? 'h1' : 'h2';
7+
8+
const tag = <El className="ok" key="key">{"Title"}</El>;

0 commit comments

Comments
 (0)
Please sign in to comment.