Skip to content

Commit cfd2ae2

Browse files
committed
Instantiate generic ElementType declarations
1 parent 726b48f commit cfd2ae2

File tree

6 files changed

+210
-13
lines changed

6 files changed

+210
-13
lines changed

src/compiler/checker.ts

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29620,18 +29620,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2962029620
function getJsxManagedAttributesFromLocatedAttributes(context: JsxOpeningLikeElement, ns: Symbol, attributesType: Type) {
2962129621
const managedSym = getJsxLibraryManagedAttributes(ns);
2962229622
if (managedSym) {
29623-
const declaredManagedType = getDeclaredTypeOfSymbol(managedSym); // fetches interface type, or initializes symbol links type parmaeters
2962429623
const ctorType = getStaticTypeOfReferencedJsxConstructor(context);
29625-
if (managedSym.flags & SymbolFlags.TypeAlias) {
29626-
const params = getSymbolLinks(managedSym).typeParameters;
29627-
if (length(params) >= 2) {
29628-
const args = fillMissingTypeArguments([ctorType, attributesType], params, 2, isInJSFile(context));
29629-
return getTypeAliasInstantiation(managedSym, args);
29630-
}
29631-
}
29632-
if (length((declaredManagedType as GenericType).typeParameters) >= 2) {
29633-
const args = fillMissingTypeArguments([ctorType, attributesType], (declaredManagedType as GenericType).typeParameters, 2, isInJSFile(context));
29634-
return createTypeReference((declaredManagedType as GenericType), args);
29624+
const result = instantiateAliasOrInterfaceWithDefaults(managedSym, isInJSFile(context), ctorType, attributesType);
29625+
if (result) {
29626+
return result;
2963529627
}
2963629628
}
2963729629
return attributesType;
@@ -30690,6 +30682,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3069030682
return jsxNamespace && getSymbol(jsxNamespace.exports!, JsxNames.LibraryManagedAttributes, SymbolFlags.Type);
3069130683
}
3069230684

30685+
function getJsxElementTypeSymbol(jsxNamespace: Symbol) {
30686+
// JSX.ElementType [symbol]
30687+
return jsxNamespace && getSymbol(jsxNamespace.exports!, JsxNames.ElementType, SymbolFlags.Type);
30688+
}
30689+
3069330690
/// e.g. "props" for React.d.ts,
3069430691
/// or 'undefined' if ElementAttributesProperty doesn't exist (which means all
3069530692
/// non-intrinsic elements' attributes type is 'any'),
@@ -30826,11 +30823,31 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3082630823
}
3082730824

3082830825
function getJsxElementTypeTypeAt(location: Node): Type | undefined {
30829-
const type = getJsxType(JsxNames.ElementType, location);
30830-
if (isErrorType(type)) return undefined;
30826+
const ns = getJsxNamespaceAt(location);
30827+
if (!ns) return undefined;
30828+
const sym = getJsxElementTypeSymbol(ns);
30829+
if (!sym) return undefined;
30830+
const type = instantiateAliasOrInterfaceWithDefaults(sym, isInJSFile(location));
30831+
if (!type || isErrorType(type)) return undefined;
3083130832
return type;
3083230833
}
3083330834

30835+
function instantiateAliasOrInterfaceWithDefaults(managedSym: Symbol, inJs: boolean, ...typeArguments: Type[]) {
30836+
const declaredManagedType = getDeclaredTypeOfSymbol(managedSym); // fetches interface type, or initializes symbol links type parmaeters
30837+
if (managedSym.flags & SymbolFlags.TypeAlias) {
30838+
const params = getSymbolLinks(managedSym).typeParameters;
30839+
if (length(params) >= typeArguments.length) {
30840+
const args = fillMissingTypeArguments(typeArguments, params, typeArguments.length, inJs);
30841+
return length(args) === 0 ? declaredManagedType : getTypeAliasInstantiation(managedSym, args);
30842+
}
30843+
}
30844+
if (length((declaredManagedType as GenericType).typeParameters) >= typeArguments.length) {
30845+
const args = fillMissingTypeArguments(typeArguments, (declaredManagedType as GenericType).typeParameters, typeArguments.length, inJs);
30846+
return createTypeReference((declaredManagedType as GenericType), args);
30847+
}
30848+
return undefined;
30849+
}
30850+
3083430851
/**
3083530852
* Returns all the properties of the Jsx.IntrinsicElements interface
3083630853
*/
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
tests/cases/compiler/jsxElementTypeLiteralWithGeneric.tsx(21,9): error TS2339: Property 'ruhroh' does not exist on type 'JSX.IntrinsicElements'.
2+
tests/cases/compiler/jsxElementTypeLiteralWithGeneric.tsx(21,10): error TS2786: 'ruhroh' cannot be used as a JSX component.
3+
Its type '"ruhroh"' is not a valid JSX element type.
4+
5+
6+
==== tests/cases/compiler/jsxElementTypeLiteralWithGeneric.tsx (2 errors) ====
7+
/// <reference path="/.lib/react16.d.ts" />
8+
import * as React from "react";
9+
10+
declare global {
11+
namespace JSX {
12+
type ElementType<P = any> =
13+
| {
14+
[K in keyof JSX.IntrinsicElements]: P extends JSX.IntrinsicElements[K]
15+
? K
16+
: never;
17+
}[keyof JSX.IntrinsicElements]
18+
| React.ComponentType<P>;
19+
}
20+
}
21+
22+
// should be fine - `ElementType` accepts `div`
23+
let a = <div />;
24+
25+
// Should be an error.
26+
// `ruhroh` is in neither `IntrinsicElements` nor `ElementType`
27+
let c = <ruhroh />;
28+
~~~~~~~~~~
29+
!!! error TS2339: Property 'ruhroh' does not exist on type 'JSX.IntrinsicElements'.
30+
~~~~~~
31+
!!! error TS2786: 'ruhroh' cannot be used as a JSX component.
32+
!!! error TS2786: Its type '"ruhroh"' is not a valid JSX element type.
33+
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//// [jsxElementTypeLiteralWithGeneric.tsx]
2+
/// <reference path="/.lib/react16.d.ts" />
3+
import * as React from "react";
4+
5+
declare global {
6+
namespace JSX {
7+
type ElementType<P = any> =
8+
| {
9+
[K in keyof JSX.IntrinsicElements]: P extends JSX.IntrinsicElements[K]
10+
? K
11+
: never;
12+
}[keyof JSX.IntrinsicElements]
13+
| React.ComponentType<P>;
14+
}
15+
}
16+
17+
// should be fine - `ElementType` accepts `div`
18+
let a = <div />;
19+
20+
// Should be an error.
21+
// `ruhroh` is in neither `IntrinsicElements` nor `ElementType`
22+
let c = <ruhroh />;
23+
24+
25+
//// [jsxElementTypeLiteralWithGeneric.js]
26+
"use strict";
27+
Object.defineProperty(exports, "__esModule", { value: true });
28+
/// <reference path="react16.d.ts" />
29+
var React = require("react");
30+
// should be fine - `ElementType` accepts `div`
31+
var a = React.createElement("div", null);
32+
// Should be an error.
33+
// `ruhroh` is in neither `IntrinsicElements` nor `ElementType`
34+
var c = React.createElement("ruhroh", null);
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
=== tests/cases/compiler/jsxElementTypeLiteralWithGeneric.tsx ===
2+
/// <reference path="react16.d.ts" />
3+
import * as React from "react";
4+
>React : Symbol(React, Decl(jsxElementTypeLiteralWithGeneric.tsx, 1, 6))
5+
6+
declare global {
7+
>global : Symbol(global, Decl(jsxElementTypeLiteralWithGeneric.tsx, 1, 31))
8+
9+
namespace JSX {
10+
>JSX : Symbol(JSX, Decl(react16.d.ts, 2493, 12), Decl(jsxElementTypeLiteralWithGeneric.tsx, 3, 16))
11+
12+
type ElementType<P = any> =
13+
>ElementType : Symbol(ElementType, Decl(jsxElementTypeLiteralWithGeneric.tsx, 4, 17))
14+
>P : Symbol(P, Decl(jsxElementTypeLiteralWithGeneric.tsx, 5, 21))
15+
16+
| {
17+
[K in keyof JSX.IntrinsicElements]: P extends JSX.IntrinsicElements[K]
18+
>K : Symbol(K, Decl(jsxElementTypeLiteralWithGeneric.tsx, 7, 9))
19+
>JSX : Symbol(JSX, Decl(react16.d.ts, 2493, 12), Decl(jsxElementTypeLiteralWithGeneric.tsx, 3, 16))
20+
>IntrinsicElements : Symbol(IntrinsicElements, Decl(react16.d.ts, 2514, 86))
21+
>P : Symbol(P, Decl(jsxElementTypeLiteralWithGeneric.tsx, 5, 21))
22+
>JSX : Symbol(JSX, Decl(react16.d.ts, 2493, 12), Decl(jsxElementTypeLiteralWithGeneric.tsx, 3, 16))
23+
>IntrinsicElements : Symbol(IntrinsicElements, Decl(react16.d.ts, 2514, 86))
24+
>K : Symbol(K, Decl(jsxElementTypeLiteralWithGeneric.tsx, 7, 9))
25+
26+
? K
27+
>K : Symbol(K, Decl(jsxElementTypeLiteralWithGeneric.tsx, 7, 9))
28+
29+
: never;
30+
}[keyof JSX.IntrinsicElements]
31+
>JSX : Symbol(JSX, Decl(react16.d.ts, 2493, 12), Decl(jsxElementTypeLiteralWithGeneric.tsx, 3, 16))
32+
>IntrinsicElements : Symbol(IntrinsicElements, Decl(react16.d.ts, 2514, 86))
33+
34+
| React.ComponentType<P>;
35+
>React : Symbol(React, Decl(jsxElementTypeLiteralWithGeneric.tsx, 1, 6))
36+
>ComponentType : Symbol(React.ComponentType, Decl(react16.d.ts, 117, 60))
37+
>P : Symbol(P, Decl(jsxElementTypeLiteralWithGeneric.tsx, 5, 21))
38+
}
39+
}
40+
41+
// should be fine - `ElementType` accepts `div`
42+
let a = <div />;
43+
>a : Symbol(a, Decl(jsxElementTypeLiteralWithGeneric.tsx, 16, 3))
44+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2546, 114))
45+
46+
// Should be an error.
47+
// `ruhroh` is in neither `IntrinsicElements` nor `ElementType`
48+
let c = <ruhroh />;
49+
>c : Symbol(c, Decl(jsxElementTypeLiteralWithGeneric.tsx, 20, 3))
50+
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
=== tests/cases/compiler/jsxElementTypeLiteralWithGeneric.tsx ===
2+
/// <reference path="react16.d.ts" />
3+
import * as React from "react";
4+
>React : typeof React
5+
6+
declare global {
7+
>global : any
8+
9+
namespace JSX {
10+
type ElementType<P = any> =
11+
>ElementType : ElementType<P>
12+
13+
| {
14+
[K in keyof JSX.IntrinsicElements]: P extends JSX.IntrinsicElements[K]
15+
>JSX : any
16+
>JSX : any
17+
18+
? K
19+
: never;
20+
}[keyof JSX.IntrinsicElements]
21+
>JSX : any
22+
23+
| React.ComponentType<P>;
24+
>React : any
25+
}
26+
}
27+
28+
// should be fine - `ElementType` accepts `div`
29+
let a = <div />;
30+
>a : JSX.Element
31+
><div /> : JSX.Element
32+
>div : any
33+
34+
// Should be an error.
35+
// `ruhroh` is in neither `IntrinsicElements` nor `ElementType`
36+
let c = <ruhroh />;
37+
>c : JSX.Element
38+
><ruhroh /> : JSX.Element
39+
>ruhroh : any
40+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// @strict: true
2+
// @jsx: react
3+
/// <reference path="/.lib/react16.d.ts" />
4+
import * as React from "react";
5+
6+
declare global {
7+
namespace JSX {
8+
type ElementType<P = any> =
9+
| {
10+
[K in keyof JSX.IntrinsicElements]: P extends JSX.IntrinsicElements[K]
11+
? K
12+
: never;
13+
}[keyof JSX.IntrinsicElements]
14+
| React.ComponentType<P>;
15+
}
16+
}
17+
18+
// should be fine - `ElementType` accepts `div`
19+
let a = <div />;
20+
21+
// Should be an error.
22+
// `ruhroh` is in neither `IntrinsicElements` nor `ElementType`
23+
let c = <ruhroh />;

0 commit comments

Comments
 (0)