TypeScript 2.8中同时编译Preact与React遇冲突求排查
我之前也踩过一模一样的坑!你猜的没错,虽然TypeScript 2.8引入了JSX命名空间解析的优化,但直接在同一个编译上下文里混用Preact和React,确实会因为全局JSX命名空间的类型重叠导致冲突——毕竟两个库的类型定义都各自声明了这个全局命名空间,编译时类型校验器根本分不清该用哪一套。
下面是我亲测有效的几种解决办法,你可以根据自己的项目情况选择:
1. 文件级JSX工厂注释(最直接的快速方案)
TypeScript支持在单个文件顶部用注释指定该文件使用的JSX工厂函数,这样就能绕过全局配置的冲突:
- 对于React组件文件,在顶部添加:
/** @jsx React.createElement */ import React from 'react'; const ReactComponent = () => <div>React Component</div>; - 对于Preact组件文件,在顶部添加:
/** @jsx h */ import { h } from 'preact'; const PreactComponent = () => <div>Preact Component</div>;
如果是TypeScript 4.1+版本,还可以用更简洁的/** @jsxImportSource preact */替代Preact的注释,它会自动导入对应的工厂函数,不用手动写import { h } from 'preact'。
2. 拆分目录+多tsconfig配置(适合大型项目)
如果你的项目里React和Preact代码量都不小,建议把它们拆到不同的子目录,然后用多tsconfig.json来隔离各自的JSX环境:
- 根目录创建基础
tsconfig.base.json,配置通用的编译选项(比如target、module等),但不要设置jsx相关选项; - React代码目录(比如
src/react)创建tsconfig.json,继承基础配置并设置:{ "extends": "../tsconfig.base.json", "compilerOptions": { "jsx": "react-jsx", "jsxImportSource": "react" }, "include": ["./**/*"] } - Preact代码目录(比如
src/preact)创建tsconfig.json,同样继承基础配置并设置:{ "extends": "../tsconfig.base.json", "compilerOptions": { "jsx": "react-jsx", "jsxImportSource": "preact" }, "include": ["./**/*"] }
编译时分别针对两个目录执行tsc -p src/react和tsc -p src/preact,就能完全隔离两个库的JSX类型环境。
3. 自定义类型声明覆盖(进阶方案)
如果上述方法还解决不了冲突,可以在项目里创建自定义类型声明文件(比如src/types/preact-fix.d.ts),明确限定Preact的JSX命名空间只在导入Preact的模块中生效:
declare module 'preact' { import type { JSX as PreactJSX } from 'preact/jsx-runtime'; export namespace JSX { export interface IntrinsicElements extends PreactJSX.IntrinsicElements {} // 按需覆盖其他JSX类型,比如函数组件、属性等 } }
不过这种方法需要对TypeScript类型系统有一定了解,避免破坏原有类型的完整性。
最后再提醒一下:确保你的TypeScript版本至少是2.8(当然更推荐用4.1+版本,jsxImportSource的支持更完善),同时检查根目录的tsconfig里不要全局设置jsx为react或preact,否则会覆盖文件级的注释配置。
内容的提问来源于stack exchange,提问作者changsoo

