From a4159a6613d237ec1a5dcf9b486f321b241deb03 Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Sun, 16 Apr 2023 18:38:16 +0300 Subject: [PATCH] resolve intrinsics elements by jsx namespaced tag names --- src/compiler/binder.ts | 5 + src/compiler/checker.ts | 37 +++--- src/compiler/utilities.ts | 22 +++- src/services/services.ts | 4 + .../reference/jsxElementType.errors.txt | 17 ++- tests/baselines/reference/jsxElementType.js | 11 ++ .../reference/jsxElementType.symbols | 27 +++- .../baselines/reference/jsxElementType.types | 20 +++ .../jsxNamespacePrefixIntrinsics.errors.txt | 12 +- .../quickInfoOnJsxNamespacedName.baseline | 117 ++++++++++++++++++ tests/cases/compiler/jsxElementType.tsx | 10 ++ .../fourslash/quickInfoOnJsxNamespacedName.ts | 13 ++ 12 files changed, 273 insertions(+), 22 deletions(-) create mode 100644 tests/baselines/reference/quickInfoOnJsxNamespacedName.baseline create mode 100644 tests/cases/fourslash/quickInfoOnJsxNamespacedName.ts diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 48836b20b04c6..e15c754cf9871 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -89,6 +89,7 @@ import { getEnclosingBlockScopeContainer, getErrorSpanForNode, getEscapedTextOfIdentifierOrLiteral, + getEscapedTextOfJsxAttributeName, getExpandoInitializer, getHostSignatureFromJSDoc, getImmediatelyInvokedFunctionExpression, @@ -171,6 +172,7 @@ import { isJSDocTemplateTag, isJSDocTypeAlias, isJsonSourceFile, + isJsxNamespacedName, isLeftHandSideExpression, isLogicalOrCoalescingAssignmentExpression, isLogicalOrCoalescingAssignmentOperator, @@ -679,6 +681,9 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { const containingClassSymbol = containingClass.symbol; return getSymbolNameForPrivateIdentifier(containingClassSymbol, name.escapedText); } + if (isJsxNamespacedName(name)) { + return getEscapedTextOfJsxAttributeName(name); + } return isPropertyNameLiteral(name) ? getEscapedTextOfIdentifierOrLiteral(name) : undefined; } switch (node.kind) { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5e9c72ec48319..dafa2249c88fc 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -276,6 +276,7 @@ import { getErrorSpanForNode, getEscapedTextOfIdentifierOrLiteral, getEscapedTextOfJsxAttributeName, + getEscapedTextOfJsxNamespacedName, getESModuleInterop, getExpandoInitializer, getExportAssignmentExpression, @@ -429,6 +430,7 @@ import { InternalSymbolName, IntersectionType, IntersectionTypeNode, + intrinsicTagNameToString, IntrinsicType, introducesArgumentsExoticObject, isAccessExpression, @@ -782,6 +784,7 @@ import { JsxExpression, JsxFlags, JsxFragment, + JsxNamespacedName, JsxOpeningElement, JsxOpeningFragment, JsxOpeningLikeElement, @@ -29595,7 +29598,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function getStaticTypeOfReferencedJsxConstructor(context: JsxOpeningLikeElement) { - if (isJsxIntrinsicIdentifier(context.tagName)) { + if (isJsxIntrinsicTagName(context.tagName)) { const result = getIntrinsicAttributesTypeFromJsxOpeningLikeElement(context); const fakeSignature = createSignatureForJSXIntrinsic(context, result); return getOrCreateTypeFromSignature(fakeSignature); @@ -30315,7 +30318,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { checkJsxOpeningLikeElementOrOpeningFragment(node.openingElement); // Perform resolution on the closing tag so that rename/go to definition/etc work - if (isJsxIntrinsicIdentifier(node.closingElement.tagName)) { + if (isJsxIntrinsicTagName(node.closingElement.tagName)) { getIntrinsicTagSymbol(node.closingElement); } else { @@ -30355,8 +30358,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { /** * Returns true iff React would emit this tag name as a string rather than an identifier or qualified name */ - function isJsxIntrinsicIdentifier(tagName: JsxTagNameExpression): tagName is Identifier { - return tagName.kind === SyntaxKind.Identifier && isIntrinsicJsxName(tagName.escapedText); + function isJsxIntrinsicTagName(tagName: Node): tagName is Identifier | JsxNamespacedName { + return isIdentifier(tagName) && isIntrinsicJsxName(tagName.escapedText) || isJsxNamespacedName(tagName); } function checkJsxAttribute(node: JsxAttribute, checkMode?: CheckMode) { @@ -30561,8 +30564,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const intrinsicElementsType = getJsxType(JsxNames.IntrinsicElements, node); if (!isErrorType(intrinsicElementsType)) { // Property case - if (!isIdentifier(node.tagName)) return Debug.fail(); - const intrinsicProp = getPropertyOfType(intrinsicElementsType, node.tagName.escapedText); + if (!isIdentifier(node.tagName) && !isJsxNamespacedName(node.tagName)) return Debug.fail(); + const intrinsicProp = getPropertyOfType(intrinsicElementsType, isJsxNamespacedName(node.tagName) ? getEscapedTextOfJsxNamespacedName(node.tagName) : node.tagName.escapedText); if (intrinsicProp) { links.jsxFlags |= JsxFlags.IntrinsicNamedElement; return links.resolvedSymbol = intrinsicProp; @@ -30576,7 +30579,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } // Wasn't found - error(node, Diagnostics.Property_0_does_not_exist_on_type_1, idText(node.tagName), "JSX." + JsxNames.IntrinsicElements); + error(node, Diagnostics.Property_0_does_not_exist_on_type_1, intrinsicTagNameToString(node.tagName), "JSX." + JsxNames.IntrinsicElements); return links.resolvedSymbol = unknownSymbol; } else { @@ -30785,7 +30788,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { * @param node an intrinsic JSX opening-like element */ function getIntrinsicAttributesTypeFromJsxOpeningLikeElement(node: JsxOpeningLikeElement): Type { - Debug.assert(isJsxIntrinsicIdentifier(node.tagName)); + Debug.assert(isJsxIntrinsicTagName(node.tagName)); const links = getNodeLinks(node); if (!links.resolvedJsxElementAttributesType) { const symbol = getIntrinsicTagSymbol(node); @@ -30898,8 +30901,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const elementTypeConstraint = getJsxElementTypeTypeAt(jsxOpeningLikeNode); if (elementTypeConstraint !== undefined) { const tagName = jsxOpeningLikeNode.tagName; - const tagType = isJsxIntrinsicIdentifier(tagName) - ? getStringLiteralType(unescapeLeadingUnderscores(tagName.escapedText)) + const tagType = isJsxIntrinsicTagName(tagName) + ? getStringLiteralType(intrinsicTagNameToString(tagName)) : checkExpression(tagName); checkTypeRelatedTo(tagType, elementTypeConstraint, assignableRelation, tagName, Diagnostics.Its_type_0_is_not_a_valid_JSX_element_type, () => { const componentName = getTextOfNode(tagName); @@ -32519,7 +32522,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function getJsxReferenceKind(node: JsxOpeningLikeElement): JsxReferenceKind { - if (isJsxIntrinsicIdentifier(node.tagName)) { + if (isJsxIntrinsicTagName(node.tagName)) { return JsxReferenceKind.Mixed; } const tagType = getApparentType(checkExpression(node.tagName)); @@ -32566,7 +32569,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (getJsxNamespaceContainerForImplicitImport(node)) { return true; // factory is implicitly jsx/jsxdev - assume it fits the bill, since we don't strongly look for the jsx/jsxs/jsxDEV factory APIs anywhere else (at least not yet) } - const tagType = isJsxOpeningElement(node) || isJsxSelfClosingElement(node) && !isJsxIntrinsicIdentifier(node.tagName) ? checkExpression(node.tagName) : undefined; + const tagType = isJsxOpeningElement(node) || isJsxSelfClosingElement(node) && !isJsxIntrinsicTagName(node.tagName) ? checkExpression(node.tagName) : undefined; if (!tagType) { return true; } @@ -33970,7 +33973,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function resolveJsxOpeningLikeElement(node: JsxOpeningLikeElement, candidatesOutArray: Signature[] | undefined, checkMode: CheckMode): Signature { - if (isJsxIntrinsicIdentifier(node.tagName)) { + if (isJsxIntrinsicTagName(node.tagName)) { const result = getIntrinsicAttributesTypeFromJsxOpeningLikeElement(node); const fakeSignature = createSignatureForJSXIntrinsic(node, result); checkTypeAssignableToAndOptionallyElaborate(checkExpressionWithContextualType(node.attributes, getEffectiveFirstArgumentForJsxSignature(fakeSignature, node), /*inferenceContext*/ undefined, CheckMode.Normal), result, node.tagName, node.attributes); @@ -45435,7 +45438,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const isJSDoc = findAncestor(name, or(isJSDocLinkLike, isJSDocNameReference, isJSDocMemberName)); const meaning = isJSDoc ? SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Value : SymbolFlags.Value; if (name.kind === SyntaxKind.Identifier) { - if (isJSXTagName(name) && isJsxIntrinsicIdentifier(name)) { + if (isJSXTagName(name) && isJsxIntrinsicTagName(name)) { const symbol = getIntrinsicTagSymbol(name.parent as JsxOpeningLikeElement); return symbol === unknownSymbol ? undefined : symbol; } @@ -45682,6 +45685,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return isMetaProperty(node.parent) ? checkMetaPropertyKeyword(node.parent).symbol : undefined; case SyntaxKind.MetaProperty: return checkExpression(node as Expression).symbol; + case SyntaxKind.JsxNamespacedName: + if (isJSXTagName(node) && isJsxIntrinsicTagName(node)) { + const symbol = getIntrinsicTagSymbol(node.parent as JsxOpeningLikeElement); + return symbol === unknownSymbol ? undefined : symbol; + } + // falls through default: return undefined; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index a4d7c936ec68e..74c3a2c53441a 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -360,6 +360,7 @@ import { JsxElement, JsxEmit, JsxFragment, + JsxNamespacedName, JsxOpeningElement, JsxOpeningLikeElement, JsxSelfClosingElement, @@ -5819,7 +5820,7 @@ function isQuoteOrBacktick(charCode: number) { /** @internal */ export function isIntrinsicJsxName(name: __String | string) { const ch = (name as string).charCodeAt(0); - return (ch >= CharacterCodes.a && ch <= CharacterCodes.z) || stringContains((name as string), "-") || stringContains((name as string), ":"); + return (ch >= CharacterCodes.a && ch <= CharacterCodes.z) || stringContains((name as string), "-"); } const indentStrings: string[] = ["", " "]; @@ -10168,12 +10169,12 @@ export function tryGetJSDocSatisfiesTypeNode(node: Node) { /** @internal */ export function getEscapedTextOfJsxAttributeName(node: JsxAttributeName): __String { - return isIdentifier(node) ? node.escapedText : `${node.namespace.escapedText}:${idText(node.name)}` as __String; + return isIdentifier(node) ? node.escapedText : getEscapedTextOfJsxNamespacedName(node); } /** @internal */ export function getTextOfJsxAttributeName(node: JsxAttributeName): string { - return isIdentifier(node) ? idText(node) : `${idText(node.namespace)}:${idText(node.name)}`; + return isIdentifier(node) ? idText(node) : getTextOfJsxNamespacedName(node); } /** @internal */ @@ -10182,3 +10183,18 @@ export function isJsxAttributeName(node: Node): node is JsxAttributeName { return kind === SyntaxKind.Identifier || kind === SyntaxKind.JsxNamespacedName; } + +/** @internal */ +export function getEscapedTextOfJsxNamespacedName(node: JsxNamespacedName): __String { + return `${node.namespace.escapedText}:${idText(node.name)}` as __String; +} + +/** @internal */ +export function getTextOfJsxNamespacedName(node: JsxNamespacedName) { + return `${idText(node.namespace)}:${idText(node.name)}`; +} + +/** @internal */ +export function intrinsicTagNameToString(node: Identifier | JsxNamespacedName) { + return isIdentifier(node) ? idText(node) : getTextOfJsxNamespacedName(node); +} diff --git a/src/services/services.ts b/src/services/services.ts index ceb05c7420949..59a4083be0a2e 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -156,6 +156,7 @@ import { isJsxClosingElement, isJsxElement, isJsxFragment, + isJsxNamespacedName, isJsxOpeningElement, isJsxOpeningFragment, isJsxText, @@ -2064,6 +2065,9 @@ export function createLanguageService( if (isImportMeta(node.parent) && node.parent.name === node) { return node.parent; } + if (isJsxNamespacedName(node.parent)) { + return node.parent; + } return node; } diff --git a/tests/baselines/reference/jsxElementType.errors.txt b/tests/baselines/reference/jsxElementType.errors.txt index ddd2efe19cce7..f9a7859467417 100644 --- a/tests/baselines/reference/jsxElementType.errors.txt +++ b/tests/baselines/reference/jsxElementType.errors.txt @@ -34,9 +34,11 @@ tests/cases/compiler/jsxElementType.tsx(91,2): error TS2786: 'ReactNativeFlatLis tests/cases/compiler/jsxElementType.tsx(95,11): error TS2322: Type '{}' is not assignable to type 'LibraryManagedAttributes'. tests/cases/compiler/jsxElementType.tsx(98,2): error TS2304: Cannot find name 'Unresolved'. tests/cases/compiler/jsxElementType.tsx(99,2): error TS2304: Cannot find name 'Unresolved'. +tests/cases/compiler/jsxElementType.tsx(109,19): error TS2322: Type '{ a: string; b: string; }' is not assignable to type '{ a: string; }'. + Property 'b' does not exist on type '{ a: string; }'. -==== tests/cases/compiler/jsxElementType.tsx (18 errors) ==== +==== tests/cases/compiler/jsxElementType.tsx (19 errors) ==== /// import * as React from "react"; @@ -197,4 +199,17 @@ tests/cases/compiler/jsxElementType.tsx(99,2): error TS2304: Cannot find name 'U ; ~~~~~~~~~~ !!! error TS2304: Cannot find name 'Unresolved'. + + declare global { + namespace JSX { + interface IntrinsicElements { + ['a:b']: { a: string }; + } + } + } + + ; + ~ +!!! error TS2322: Type '{ a: string; b: string; }' is not assignable to type '{ a: string; }'. +!!! error TS2322: Property 'b' does not exist on type '{ a: string; }'. \ No newline at end of file diff --git a/tests/baselines/reference/jsxElementType.js b/tests/baselines/reference/jsxElementType.js index 13986b0c62e90..6d33af33a2a86 100644 --- a/tests/baselines/reference/jsxElementType.js +++ b/tests/baselines/reference/jsxElementType.js @@ -98,6 +98,16 @@ function f1 React.ReactElement>(Component: T) { ; ; + +declare global { + namespace JSX { + interface IntrinsicElements { + ['a:b']: { a: string }; + } + } +} + +; //// [jsxElementType.js] @@ -231,3 +241,4 @@ function f1(Component) { } React.createElement(Unresolved, null); React.createElement(Unresolved, { foo: "abc" }); +React.createElement("a:b", { a: "accepted", b: "rejected" }); diff --git a/tests/baselines/reference/jsxElementType.symbols b/tests/baselines/reference/jsxElementType.symbols index c932009d84a1f..d484bc27ba8f7 100644 --- a/tests/baselines/reference/jsxElementType.symbols +++ b/tests/baselines/reference/jsxElementType.symbols @@ -49,17 +49,17 @@ type NewReactJSXElementConstructor

= >P : Symbol(P, Decl(jsxElementType.tsx, 16, 35)) declare global { ->global : Symbol(global, Decl(jsxElementType.tsx, 18, 48)) +>global : Symbol(global, Decl(jsxElementType.tsx, 18, 48), Decl(jsxElementType.tsx, 98, 25)) namespace JSX { ->JSX : Symbol(JSX, Decl(react16.d.ts, 2493, 12), Decl(jsxElementType.tsx, 20, 16)) +>JSX : Symbol(JSX, Decl(react16.d.ts, 2493, 12), Decl(jsxElementType.tsx, 20, 16), Decl(jsxElementType.tsx, 100, 16)) type ElementType = string | NewReactJSXElementConstructor; >ElementType : Symbol(ElementType, Decl(jsxElementType.tsx, 21, 17)) >NewReactJSXElementConstructor : Symbol(NewReactJSXElementConstructor, Decl(jsxElementType.tsx, 13, 30)) interface IntrinsicElements { ->IntrinsicElements : Symbol(IntrinsicElements, Decl(react16.d.ts, 2514, 86), Decl(jsxElementType.tsx, 22, 67)) +>IntrinsicElements : Symbol(IntrinsicElements, Decl(react16.d.ts, 2514, 86), Decl(jsxElementType.tsx, 22, 67), Decl(jsxElementType.tsx, 101, 19)) ['my-custom-element']: React.DOMAttributes; >['my-custom-element'] : Symbol(IntrinsicElements['my-custom-element'], Decl(jsxElementType.tsx, 23, 33)) @@ -272,3 +272,24 @@ function f1 React.ReactElement>(Component: T) { ; >foo : Symbol(foo, Decl(jsxElementType.tsx, 98, 11)) +declare global { +>global : Symbol(global, Decl(jsxElementType.tsx, 18, 48), Decl(jsxElementType.tsx, 98, 25)) + + namespace JSX { +>JSX : Symbol(JSX, Decl(react16.d.ts, 2493, 12), Decl(jsxElementType.tsx, 20, 16), Decl(jsxElementType.tsx, 100, 16)) + + interface IntrinsicElements { +>IntrinsicElements : Symbol(IntrinsicElements, Decl(react16.d.ts, 2514, 86), Decl(jsxElementType.tsx, 22, 67), Decl(jsxElementType.tsx, 101, 19)) + + ['a:b']: { a: string }; +>['a:b'] : Symbol(IntrinsicElements['a:b'], Decl(jsxElementType.tsx, 102, 35)) +>'a:b' : Symbol(IntrinsicElements['a:b'], Decl(jsxElementType.tsx, 102, 35)) +>a : Symbol(a, Decl(jsxElementType.tsx, 103, 20)) + } + } +} + +; +>a : Symbol(a, Decl(jsxElementType.tsx, 108, 4)) +>b : Symbol(b, Decl(jsxElementType.tsx, 108, 17)) + diff --git a/tests/baselines/reference/jsxElementType.types b/tests/baselines/reference/jsxElementType.types index 4e9a5a9a8ccb6..c40216ea86285 100644 --- a/tests/baselines/reference/jsxElementType.types +++ b/tests/baselines/reference/jsxElementType.types @@ -290,3 +290,23 @@ function f1 React.ReactElement>(Component: T) { >Unresolved : any >foo : string +declare global { +>global : any + + namespace JSX { + interface IntrinsicElements { + ['a:b']: { a: string }; +>['a:b'] : { a: string; } +>'a:b' : "a:b" +>a : string + } + } +} + +; +> : JSX.Element +>a : any +>b : any +>a : string +>b : string + diff --git a/tests/baselines/reference/jsxNamespacePrefixIntrinsics.errors.txt b/tests/baselines/reference/jsxNamespacePrefixIntrinsics.errors.txt index c9a4a81eeeec1..22bacb161c5dd 100644 --- a/tests/baselines/reference/jsxNamespacePrefixIntrinsics.errors.txt +++ b/tests/baselines/reference/jsxNamespacePrefixIntrinsics.errors.txt @@ -1,7 +1,11 @@ tests/cases/compiler/jsxNamespacePrefixIntrinsics.tsx(15,18): error TS2339: Property 'element' does not exist on type 'JSX.IntrinsicElements'. +tests/cases/compiler/jsxNamespacePrefixIntrinsics.tsx(16,30): error TS2322: Type '{ attribute: string; }' is not assignable to type '{ "ns:attribute": string; }'. + Property 'attribute' does not exist on type '{ "ns:attribute": string; }'. Did you mean '"ns:attribute"'? +tests/cases/compiler/jsxNamespacePrefixIntrinsics.tsx(17,30): error TS2322: Type '{ "ns:invalid": string; }' is not assignable to type '{ "ns:attribute": string; }'. + Property 'ns:invalid' does not exist on type '{ "ns:attribute": string; }'. -==== tests/cases/compiler/jsxNamespacePrefixIntrinsics.tsx (1 errors) ==== +==== tests/cases/compiler/jsxNamespacePrefixIntrinsics.tsx (3 errors) ==== declare namespace JSX { interface IntrinsicElements { "ns:element": { @@ -20,5 +24,11 @@ tests/cases/compiler/jsxNamespacePrefixIntrinsics.tsx(15,18): error TS2339: Prop ~~~~~~~~~~~ !!! error TS2339: Property 'element' does not exist on type 'JSX.IntrinsicElements'. const invalid2 = ; + ~~~~~~~~~ +!!! error TS2322: Type '{ attribute: string; }' is not assignable to type '{ "ns:attribute": string; }'. +!!! error TS2322: Property 'attribute' does not exist on type '{ "ns:attribute": string; }'. Did you mean '"ns:attribute"'? const invalid3 = ; + ~~~~~~~~~~ +!!! error TS2322: Type '{ "ns:invalid": string; }' is not assignable to type '{ "ns:attribute": string; }'. +!!! error TS2322: Property 'ns:invalid' does not exist on type '{ "ns:attribute": string; }'. \ No newline at end of file diff --git a/tests/baselines/reference/quickInfoOnJsxNamespacedName.baseline b/tests/baselines/reference/quickInfoOnJsxNamespacedName.baseline new file mode 100644 index 0000000000000..b30eaf3dd5fe1 --- /dev/null +++ b/tests/baselines/reference/quickInfoOnJsxNamespacedName.baseline @@ -0,0 +1,117 @@ +=== /a.tsx === +// ; +// ^^^ +// | ---------------------------------------------------------------------- +// | (property) JSX.IntrinsicElements['a:b']: { +// | a: string; +// | } +// | ---------------------------------------------------------------------- + +[ + { + "marker": { + "fileName": "/a.tsx", + "position": 1, + "name": "" + }, + "item": { + "kind": "property", + "kindModifiers": "declare", + "textSpan": { + "start": 1, + "length": 3 + }, + "displayParts": [ + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "property", + "kind": "text" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "JSX", + "kind": "moduleName" + }, + { + "text": ".", + "kind": "punctuation" + }, + { + "text": "IntrinsicElements", + "kind": "interfaceName" + }, + { + "text": "[", + "kind": "punctuation" + }, + { + "text": "'a:b'", + "kind": "stringLiteral" + }, + { + "text": "]", + "kind": "punctuation" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "{", + "kind": "punctuation" + }, + { + "text": "\n", + "kind": "lineBreak" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "a", + "kind": "propertyName" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "string", + "kind": "keyword" + }, + { + "text": ";", + "kind": "punctuation" + }, + { + "text": "\n", + "kind": "lineBreak" + }, + { + "text": "}", + "kind": "punctuation" + } + ], + "documentation": [] + } + } +] \ No newline at end of file diff --git a/tests/cases/compiler/jsxElementType.tsx b/tests/cases/compiler/jsxElementType.tsx index 75585a2d99a43..37713a05fea59 100644 --- a/tests/cases/compiler/jsxElementType.tsx +++ b/tests/cases/compiler/jsxElementType.tsx @@ -99,3 +99,13 @@ function f1 React.ReactElement>(Component: T) { ; ; + +declare global { + namespace JSX { + interface IntrinsicElements { + ['a:b']: { a: string }; + } + } +} + +; diff --git a/tests/cases/fourslash/quickInfoOnJsxNamespacedName.ts b/tests/cases/fourslash/quickInfoOnJsxNamespacedName.ts new file mode 100644 index 0000000000000..85f2dc9ecdb24 --- /dev/null +++ b/tests/cases/fourslash/quickInfoOnJsxNamespacedName.ts @@ -0,0 +1,13 @@ +/// + +// @jsx: react + +// @Filename: /types.d.ts +////declare namespace JSX { +//// interface IntrinsicElements { ['a:b']: { a: string }; } +////} + +// @filename: /a.tsx +////; + +verify.baselineQuickInfo();