Skip to content

Instantiate generic ElementType declarations #53943

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 30 additions & 13 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29620,18 +29620,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
function getJsxManagedAttributesFromLocatedAttributes(context: JsxOpeningLikeElement, ns: Symbol, attributesType: Type) {
const managedSym = getJsxLibraryManagedAttributes(ns);
if (managedSym) {
const declaredManagedType = getDeclaredTypeOfSymbol(managedSym); // fetches interface type, or initializes symbol links type parmaeters
const ctorType = getStaticTypeOfReferencedJsxConstructor(context);
if (managedSym.flags & SymbolFlags.TypeAlias) {
const params = getSymbolLinks(managedSym).typeParameters;
if (length(params) >= 2) {
const args = fillMissingTypeArguments([ctorType, attributesType], params, 2, isInJSFile(context));
return getTypeAliasInstantiation(managedSym, args);
}
}
if (length((declaredManagedType as GenericType).typeParameters) >= 2) {
const args = fillMissingTypeArguments([ctorType, attributesType], (declaredManagedType as GenericType).typeParameters, 2, isInJSFile(context));
return createTypeReference((declaredManagedType as GenericType), args);
const result = instantiateAliasOrInterfaceWithDefaults(managedSym, isInJSFile(context), ctorType, attributesType);
if (result) {
return result;
}
}
return attributesType;
Expand Down Expand Up @@ -30690,6 +30682,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return jsxNamespace && getSymbol(jsxNamespace.exports!, JsxNames.LibraryManagedAttributes, SymbolFlags.Type);
}

function getJsxElementTypeSymbol(jsxNamespace: Symbol) {
// JSX.ElementType [symbol]
return jsxNamespace && getSymbol(jsxNamespace.exports!, JsxNames.ElementType, SymbolFlags.Type);
}

/// e.g. "props" for React.d.ts,
/// or 'undefined' if ElementAttributesProperty doesn't exist (which means all
/// non-intrinsic elements' attributes type is 'any'),
Expand Down Expand Up @@ -30826,11 +30823,31 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}

function getJsxElementTypeTypeAt(location: Node): Type | undefined {
const type = getJsxType(JsxNames.ElementType, location);
if (isErrorType(type)) return undefined;
const ns = getJsxNamespaceAt(location);
if (!ns) return undefined;
const sym = getJsxElementTypeSymbol(ns);
if (!sym) return undefined;
const type = instantiateAliasOrInterfaceWithDefaults(sym, isInJSFile(location));
if (!type || isErrorType(type)) return undefined;
return type;
}

function instantiateAliasOrInterfaceWithDefaults(managedSym: Symbol, inJs: boolean, ...typeArguments: Type[]) {
const declaredManagedType = getDeclaredTypeOfSymbol(managedSym); // fetches interface type, or initializes symbol links type parmaeters
if (managedSym.flags & SymbolFlags.TypeAlias) {
const params = getSymbolLinks(managedSym).typeParameters;
if (length(params) >= typeArguments.length) {
const args = fillMissingTypeArguments(typeArguments, params, typeArguments.length, inJs);
return length(args) === 0 ? declaredManagedType : getTypeAliasInstantiation(managedSym, args);
}
}
if (length((declaredManagedType as GenericType).typeParameters) >= typeArguments.length) {
const args = fillMissingTypeArguments(typeArguments, (declaredManagedType as GenericType).typeParameters, typeArguments.length, inJs);
return createTypeReference((declaredManagedType as GenericType), args);
}
return undefined;
}

/**
* Returns all the properties of the Jsx.IntrinsicElements interface
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
tests/cases/compiler/jsxElementTypeLiteralWithGeneric.tsx(21,9): error TS2339: Property 'ruhroh' does not exist on type 'JSX.IntrinsicElements'.
tests/cases/compiler/jsxElementTypeLiteralWithGeneric.tsx(21,10): error TS2786: 'ruhroh' cannot be used as a JSX component.
Its type '"ruhroh"' is not a valid JSX element type.


==== tests/cases/compiler/jsxElementTypeLiteralWithGeneric.tsx (2 errors) ====
/// <reference path="/.lib/react16.d.ts" />
import * as React from "react";

declare global {
namespace JSX {
type ElementType<P = any> =
| {
[K in keyof JSX.IntrinsicElements]: P extends JSX.IntrinsicElements[K]
? K
: never;
}[keyof JSX.IntrinsicElements]
| React.ComponentType<P>;
}
}

// should be fine - `ElementType` accepts `div`
let a = <div />;

// Should be an error.
// `ruhroh` is in neither `IntrinsicElements` nor `ElementType`
let c = <ruhroh />;
~~~~~~~~~~
!!! error TS2339: Property 'ruhroh' does not exist on type 'JSX.IntrinsicElements'.
~~~~~~
!!! error TS2786: 'ruhroh' cannot be used as a JSX component.
!!! error TS2786: Its type '"ruhroh"' is not a valid JSX element type.

34 changes: 34 additions & 0 deletions tests/baselines/reference/jsxElementTypeLiteralWithGeneric.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//// [jsxElementTypeLiteralWithGeneric.tsx]
/// <reference path="/.lib/react16.d.ts" />
import * as React from "react";

declare global {
namespace JSX {
type ElementType<P = any> =
| {
[K in keyof JSX.IntrinsicElements]: P extends JSX.IntrinsicElements[K]
? K
: never;
}[keyof JSX.IntrinsicElements]
| React.ComponentType<P>;
}
}

// should be fine - `ElementType` accepts `div`
let a = <div />;

// Should be an error.
// `ruhroh` is in neither `IntrinsicElements` nor `ElementType`
let c = <ruhroh />;


//// [jsxElementTypeLiteralWithGeneric.js]
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/// <reference path="react16.d.ts" />
var React = require("react");
// should be fine - `ElementType` accepts `div`
var a = React.createElement("div", null);
// Should be an error.
// `ruhroh` is in neither `IntrinsicElements` nor `ElementType`
var c = React.createElement("ruhroh", null);
50 changes: 50 additions & 0 deletions tests/baselines/reference/jsxElementTypeLiteralWithGeneric.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
=== tests/cases/compiler/jsxElementTypeLiteralWithGeneric.tsx ===
/// <reference path="react16.d.ts" />
import * as React from "react";
>React : Symbol(React, Decl(jsxElementTypeLiteralWithGeneric.tsx, 1, 6))

declare global {
>global : Symbol(global, Decl(jsxElementTypeLiteralWithGeneric.tsx, 1, 31))

namespace JSX {
>JSX : Symbol(JSX, Decl(react16.d.ts, 2493, 12), Decl(jsxElementTypeLiteralWithGeneric.tsx, 3, 16))

type ElementType<P = any> =
>ElementType : Symbol(ElementType, Decl(jsxElementTypeLiteralWithGeneric.tsx, 4, 17))
>P : Symbol(P, Decl(jsxElementTypeLiteralWithGeneric.tsx, 5, 21))

| {
[K in keyof JSX.IntrinsicElements]: P extends JSX.IntrinsicElements[K]
>K : Symbol(K, Decl(jsxElementTypeLiteralWithGeneric.tsx, 7, 9))
>JSX : Symbol(JSX, Decl(react16.d.ts, 2493, 12), Decl(jsxElementTypeLiteralWithGeneric.tsx, 3, 16))
>IntrinsicElements : Symbol(IntrinsicElements, Decl(react16.d.ts, 2514, 86))
>P : Symbol(P, Decl(jsxElementTypeLiteralWithGeneric.tsx, 5, 21))
>JSX : Symbol(JSX, Decl(react16.d.ts, 2493, 12), Decl(jsxElementTypeLiteralWithGeneric.tsx, 3, 16))
>IntrinsicElements : Symbol(IntrinsicElements, Decl(react16.d.ts, 2514, 86))
>K : Symbol(K, Decl(jsxElementTypeLiteralWithGeneric.tsx, 7, 9))

? K
>K : Symbol(K, Decl(jsxElementTypeLiteralWithGeneric.tsx, 7, 9))

: never;
}[keyof JSX.IntrinsicElements]
>JSX : Symbol(JSX, Decl(react16.d.ts, 2493, 12), Decl(jsxElementTypeLiteralWithGeneric.tsx, 3, 16))
>IntrinsicElements : Symbol(IntrinsicElements, Decl(react16.d.ts, 2514, 86))

| React.ComponentType<P>;
>React : Symbol(React, Decl(jsxElementTypeLiteralWithGeneric.tsx, 1, 6))
>ComponentType : Symbol(React.ComponentType, Decl(react16.d.ts, 117, 60))
>P : Symbol(P, Decl(jsxElementTypeLiteralWithGeneric.tsx, 5, 21))
}
}

// should be fine - `ElementType` accepts `div`
let a = <div />;
>a : Symbol(a, Decl(jsxElementTypeLiteralWithGeneric.tsx, 16, 3))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2546, 114))

// Should be an error.
// `ruhroh` is in neither `IntrinsicElements` nor `ElementType`
let c = <ruhroh />;
>c : Symbol(c, Decl(jsxElementTypeLiteralWithGeneric.tsx, 20, 3))

40 changes: 40 additions & 0 deletions tests/baselines/reference/jsxElementTypeLiteralWithGeneric.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
=== tests/cases/compiler/jsxElementTypeLiteralWithGeneric.tsx ===
/// <reference path="react16.d.ts" />
import * as React from "react";
>React : typeof React

declare global {
>global : any

namespace JSX {
type ElementType<P = any> =
>ElementType : ElementType<P>

| {
[K in keyof JSX.IntrinsicElements]: P extends JSX.IntrinsicElements[K]
>JSX : any
>JSX : any

? K
: never;
}[keyof JSX.IntrinsicElements]
>JSX : any

| React.ComponentType<P>;
>React : any
}
}

// should be fine - `ElementType` accepts `div`
let a = <div />;
>a : JSX.Element
><div /> : JSX.Element
>div : any

// Should be an error.
// `ruhroh` is in neither `IntrinsicElements` nor `ElementType`
let c = <ruhroh />;
>c : JSX.Element
><ruhroh /> : JSX.Element
>ruhroh : any

23 changes: 23 additions & 0 deletions tests/cases/compiler/jsxElementTypeLiteralWithGeneric.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// @strict: true
// @jsx: react
/// <reference path="/.lib/react16.d.ts" />
import * as React from "react";

declare global {
namespace JSX {
type ElementType<P = any> =
| {
[K in keyof JSX.IntrinsicElements]: P extends JSX.IntrinsicElements[K]
? K
: never;
}[keyof JSX.IntrinsicElements]
| React.ComponentType<P>;
}
}

// should be fine - `ElementType` accepts `div`
let a = <div />;

// Should be an error.
// `ruhroh` is in neither `IntrinsicElements` nor `ElementType`
let c = <ruhroh />;