diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts
index 50399fd5c38..1dfecf33b89 100644
--- a/src/compiler/commandLineParser.ts
+++ b/src/compiler/commandLineParser.ts
@@ -77,6 +77,11 @@ namespace ts {
type: "string",
description: Diagnostics.Specify_the_object_invoked_for_createElement_and_spread_when_targeting_react_JSX_emit
},
+ {
+ name: "jsxFactory",
+ type: "string",
+ description: Diagnostics.Specify_the_JSX_factory_function_to_use_when_targeting_react_JSX_emit_e_g_React_createElement_or_h
+ },
{
name: "listFiles",
type: "boolean",
diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json
index 9a353e6fb36..79e31788390 100644
--- a/src/compiler/diagnosticMessages.json
+++ b/src/compiler/diagnosticMessages.json
@@ -2897,6 +2897,10 @@
"category": "Message",
"code": 6145
},
+ "Specify the JSX factory function to use when targeting 'react' JSX emit, e.g. 'React.createElement' or 'h'.": {
+ "category": "Message",
+ "code": 6146
+ },
"Variable '{0}' implicitly has an '{1}' type.": {
"category": "Error",
"code": 7005
diff --git a/src/compiler/program.ts b/src/compiler/program.ts
index 1b36d99d7c9..cb7efc49bf3 100644
--- a/src/compiler/program.ts
+++ b/src/compiler/program.ts
@@ -1670,7 +1670,12 @@ namespace ts {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "emitDecoratorMetadata", "experimentalDecorators"));
}
- if (options.reactNamespace && !isIdentifierText(options.reactNamespace, languageVersion)) {
+ if (options.jsxFactory) {
+ if (options.reactNamespace) {
+ programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_with_option_1, "reactNamespace", "jsxFactory"));
+ }
+ }
+ else if (options.reactNamespace && !isIdentifierText(options.reactNamespace, languageVersion)) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Invalid_value_for_reactNamespace_0_is_not_a_valid_identifier, options.reactNamespace));
}
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index fb1dffad87d..5ca542cfe11 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -3081,6 +3081,7 @@ namespace ts {
project?: string;
/* @internal */ pretty?: DiagnosticStyle;
reactNamespace?: string;
+ jsxFactory?: string;
removeComments?: boolean;
rootDir?: string;
rootDirs?: string[];
diff --git a/tests/baselines/reference/jsxFactoryAndReactNamespace.errors.txt b/tests/baselines/reference/jsxFactoryAndReactNamespace.errors.txt
new file mode 100644
index 00000000000..c4859fd534c
--- /dev/null
+++ b/tests/baselines/reference/jsxFactoryAndReactNamespace.errors.txt
@@ -0,0 +1,53 @@
+error TS5053: Option 'reactNamespace' cannot be specified with option 'jsxFactory'.
+
+
+!!! error TS5053: Option 'reactNamespace' cannot be specified with option 'jsxFactory'.
+==== tests/cases/compiler/Element.ts (0 errors) ====
+
+ declare namespace JSX {
+ interface Element {
+ name: string;
+ isIntrinsic: boolean;
+ isCustomElement: boolean;
+ toString(renderId?: number): string;
+ bindDOM(renderId?: number): number;
+ resetComponent(): void;
+ instantiateComponents(renderId?: number): number;
+ props: any;
+ }
+ }
+ export namespace Element {
+ export function isElement(el: any): el is JSX.Element {
+ return el.markAsChildOfRootElement !== undefined;
+ }
+
+ export function createElement(args: any[]) {
+
+ return {
+ }
+ }
+ }
+
+ export let createElement = Element.createElement;
+
+ function toCamelCase(text: string): string {
+ return text[0].toLowerCase() + text.substring(1);
+ }
+
+==== tests/cases/compiler/test.tsx (0 errors) ====
+ import { Element} from './Element';
+
+ let c: {
+ a?: {
+ b: string
+ }
+ };
+
+ class A {
+ view() {
+ return [
+ ,
+
+ ];
+ }
+ }
\ No newline at end of file
diff --git a/tests/baselines/reference/jsxFactoryAndReactNamespace.js b/tests/baselines/reference/jsxFactoryAndReactNamespace.js
new file mode 100644
index 00000000000..0a6402ca70c
--- /dev/null
+++ b/tests/baselines/reference/jsxFactoryAndReactNamespace.js
@@ -0,0 +1,81 @@
+//// [tests/cases/compiler/jsxFactoryAndReactNamespace.ts] ////
+
+//// [Element.ts]
+
+declare namespace JSX {
+ interface Element {
+ name: string;
+ isIntrinsic: boolean;
+ isCustomElement: boolean;
+ toString(renderId?: number): string;
+ bindDOM(renderId?: number): number;
+ resetComponent(): void;
+ instantiateComponents(renderId?: number): number;
+ props: any;
+ }
+}
+export namespace Element {
+ export function isElement(el: any): el is JSX.Element {
+ return el.markAsChildOfRootElement !== undefined;
+ }
+
+ export function createElement(args: any[]) {
+
+ return {
+ }
+ }
+}
+
+export let createElement = Element.createElement;
+
+function toCamelCase(text: string): string {
+ return text[0].toLowerCase() + text.substring(1);
+}
+
+//// [test.tsx]
+import { Element} from './Element';
+
+let c: {
+ a?: {
+ b: string
+ }
+};
+
+class A {
+ view() {
+ return [
+ ,
+
+ ];
+ }
+}
+
+//// [Element.js]
+"use strict";
+var Element;
+(function (Element) {
+ function isElement(el) {
+ return el.markAsChildOfRootElement !== undefined;
+ }
+ Element.isElement = isElement;
+ function createElement(args) {
+ return {};
+ }
+ Element.createElement = createElement;
+})(Element = exports.Element || (exports.Element = {}));
+exports.createElement = Element.createElement;
+function toCamelCase(text) {
+ return text[0].toLowerCase() + text.substring(1);
+}
+//// [test.js]
+"use strict";
+const Element_1 = require("./Element");
+let c;
+class A {
+ view() {
+ return [
+ Element_1.Element.createElement("meta", { content: "helloworld" }),
+ Element_1.Element.createElement("meta", { content: c.a.b })
+ ];
+ }
+}
diff --git a/tests/cases/compiler/jsxFactoryAndReactNamespace.ts b/tests/cases/compiler/jsxFactoryAndReactNamespace.ts
new file mode 100644
index 00000000000..2bdc954da3a
--- /dev/null
+++ b/tests/cases/compiler/jsxFactoryAndReactNamespace.ts
@@ -0,0 +1,54 @@
+//@jsx: react
+//@target: es6
+//@module: commonjs
+//@reactNamespace: Element
+//@jsxFactory: Element.createElement
+
+// @filename: Element.ts
+declare namespace JSX {
+ interface Element {
+ name: string;
+ isIntrinsic: boolean;
+ isCustomElement: boolean;
+ toString(renderId?: number): string;
+ bindDOM(renderId?: number): number;
+ resetComponent(): void;
+ instantiateComponents(renderId?: number): number;
+ props: any;
+ }
+}
+export namespace Element {
+ export function isElement(el: any): el is JSX.Element {
+ return el.markAsChildOfRootElement !== undefined;
+ }
+
+ export function createElement(args: any[]) {
+
+ return {
+ }
+ }
+}
+
+export let createElement = Element.createElement;
+
+function toCamelCase(text: string): string {
+ return text[0].toLowerCase() + text.substring(1);
+}
+
+// @filename: test.tsx
+import { Element} from './Element';
+
+let c: {
+ a?: {
+ b: string
+ }
+};
+
+class A {
+ view() {
+ return [
+ ,
+
+ ];
+ }
+}
\ No newline at end of file