Question
How can I suppress React imports in TypeScript output to allow CDN references?
I am trying to use CDN URLs to reference the react and react-dom libraries in my typescript app. I do not want to bundle them into my compiled .js file, as I'm concerned with performance in production. But there seems no straightforward way to achieve this without manually editing the compiled .js file to remove the import references. See this minimal repro (https://jsfiddle.net/grm6ze1b/):
test.html:
<!DOCTYPE html>
<div id="app">
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.min.js"></script>
<script src="test.js" type="module"></script>
test.ts:
import * as React from 'react';
import * as ReactDOM from 'react-dom/client';
class HelloComponent extends React.Component {
render() {
return React.createElement("div", null, "Hello, World!");
}
}
document.addEventListener("DOMContentLoaded", () => {
const oReactRoot = ReactDOM.createRoot(document.getElementById("app")!);
oReactRoot.render(React.createElement(HelloComponent, null));
});
tsconfig.json:
{
"compilerOptions": {
"target": "es2020",
"moduleResolution": "node",
"strict": true,
"noEmitOnError": true
},
"include": [
"**/*.ts"
]
}
First, I got "Uncaught SyntaxError: import declarations may only appear at top level of a module" when loading the page in a browser. OK, so I fixed that by adding type="module"
to my script tag for test.js.
Then, I got 'Uncaught TypeError: The specifier “react” was a bare specifier, but was not remapped to anything. Relative module specifiers must start with “./”, “../” or “/”.'. This is where I'm stuck. I can resolve the issue by manually commenting out the top two import
lines in my compiled test.js file. But this seems absurd, since I'll have to do it every time I run tsc
to recompile my .ts file.
I've pored over the https://www.typescriptlang.org/tsconfig/ docs, and there are various options controlling the handling of imports and whether they appear in the compiled JS.At https://www.typescriptlang.org/tsconfig/#verbatimModuleSyntax it says:
By default, TypeScript does something called import elision. Basically, if you write something like
import { Car } from "./car"; export function drive(car: Car) { // ... }
TypeScript detects that you’re only using an import for types and drops the import entirely.
But this is not happening for me. My compiled test.js still contains the imports, even though I'm only importing the React types and not the full library:
PS C:\inetpub\wwwroot\TypeScriptReactTest> npm ls --all
TypeScriptReactTest@ C:\inetpub\wwwroot\TypeScriptReactTest
+-- @types/react-dom@18.2.25
| `-- @types/react@18.2.79 deduped
`-- @types/react@18.2.79
+-- @types/prop-types@15.7.12
`-- csstype@3.1.3
A third solution I tried is to change my imports to reference directives:
///<reference types="react"/>
///<reference types="react-dom/client"/>
But this yielded "error TS2339: Property 'createRoot' does not exist on type 'typeof import("C:/inetpub/wwwroot/TypeScriptReactTest/node_modules/@types/react-dom/index")'." (Same error if I tried reference paths=""
instead.)
Lastly, I tried defining an import map to tell the browser where to find the modules I am importing:
<script type="importmap">
{
"imports": {
"react": "https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js",
"react-dom/client": "https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.min.js"
}
}
</script>
That yielded a new error: "Uncaught TypeError: class heritage React.Component is not an object or null".
So I am banging my head against a brick wall here, and would be grateful for any help. I really hope the answer is not that I have to use something like https://webpack.js.org/configuration/externals/ to handle this. My full production app (not this repro) uses ASP.NET Core WebOptimizer bundling, and I'd really hate to add another step to my toolchain just to handle what I assume is pretty basic and common use case.