20. JSX 支持1. 概述TypeScript 提供了对 JSX 语法的原生支持允许在 TypeScript 文件中编写 JSX/TSX 代码。JSX 是一种 JavaScript 的语法扩展主要用于 React 等框架中描述用户界面。┌─────────────────────────────────────────────────────────────┐ │ TypeScript JSX 系统 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 文件类型 │ │ ├── .tsx包含 JSX 的 TypeScript 文件 │ │ └── .ts不含 JSX 的 TypeScript 文件 │ │ │ │ 配置选项 │ │ ├── jsx控制 JSX 输出格式 │ │ ├── jsxFactory指定工厂函数 │ │ ├── jsxFragmentFactory指定片段工厂函数 │ │ └── jsxImportSource指定 JSX 导入源 │ │ │ │ JSX 模式 │ │ ├── preserve保留 JSX 原样 │ │ ├── react转换为 React.createElement │ │ ├── react-jsx使用 JSX 转换React 17 │ │ ├── react-jsxdev开发模式 JSX 转换 │ │ └── react-native保留 JSX用于 React Native │ │ │ └─────────────────────────────────────────────────────────────┘2. 启用 JSX2.1 文件扩展名.tsx用于包含 JSX 语法的 TypeScript 文件.ts不含 JSX 的普通 TypeScript 文件2.2 tsconfig.json 配置{compilerOptions:{jsx:react-jsx,jsxFactory:React.createElement,jsxFragmentFactory:React.Fragment,jsxImportSource:react}}3. JSX 模式详解3.1 preserve 模式保留 JSX 原样不进行转换。{compilerOptions:{jsx:preserve}}// 输入 const element divHello/div; // 输出保持不变 const element divHello/div;3.2 react 模式转换为React.createElement调用。{compilerOptions:{jsx:react}}// 输入 const element div classNamecontainerHello/div; // 输出 const element React.createElement(div, { className: container }, Hello);3.3 react-jsx 模式React 17使用新的 JSX 转换无需显式导入 React。{compilerOptions:{jsx:react-jsx}}// 输入 const element divHello/div; // 输出 import { jsx as _jsx } from react/jsx-runtime; const element _jsx(div, { children: Hello });3.4 react-native 模式保留 JSX 原样用于 React Native。{compilerOptions:{jsx:react-native}}4. JSX 类型定义4.1 基本类型// 组件 Props 类型定义 interface ButtonProps { children?: React.ReactNode; onClick?: () void; disabled?: boolean; variant?: primary | secondary | danger; } // 函数组件 const Button: React.FCButtonProps ({ children, onClick, disabled, variant primary }) { return ( button onClick{onClick} disabled{disabled} className{btn btn-${variant}} {children} /button ); }; // 使用 const App () ( div Button variantprimary onClick{() console.log(clicked)} Click Me /Button /div );4.2 事件类型import { ChangeEvent, MouseEvent, FormEvent } from react; interface FormProps { onSubmit: (data: FormData) void; } const FormComponent: React.FCFormProps ({ onSubmit }) { const handleSubmit (e: FormEventHTMLFormElement) { e.preventDefault(); const formData new FormData(e.currentTarget); onSubmit(formData); }; const handleChange (e: ChangeEventHTMLInputElement) { console.log(e.target.value); }; const handleClick (e: MouseEventHTMLButtonElement) { console.log(Clicked at (${e.clientX}, ${e.clientY})); }; return ( form onSubmit{handleSubmit} input typetext nameusername onChange{handleChange} / input typeemail nameemail onChange{handleChange} / button typesubmit onClick{handleClick} Submit /button /form ); };4.3 Ref 类型import { useRef, useEffect, RefObject } from react; interface InputProps { inputRef?: RefObjectHTMLInputElement; autoFocus?: boolean; } const InputComponent: React.FCInputProps ({ inputRef, autoFocus }) { const internalRef useRefHTMLInputElement(null); const ref inputRef || internalRef; useEffect(() { if (autoFocus ref.current) { ref.current.focus(); } }, [autoFocus, ref]); return input ref{ref} typetext /; }; // 使用 forwardRef const FancyInput React.forwardRefHTMLInputElement, InputProps((props, ref) ( input ref{ref} classNamefancy {...props} / ));5. 泛型组件// 泛型组件 interface ListPropsT { items: T[]; renderItem: (item: T, index: number) React.ReactNode; keyExtractor?: (item: T) string | number; } function ListT({ items, renderItem, keyExtractor }: ListPropsT) { return ( ul {items.map((item, index) ( li key{keyExtractor ? keyExtractor(item) : index} {renderItem(item, index)} /li ))} /ul ); } // 使用 interface User { id: number; name: string; } const users: User[] [ { id: 1, name: Alice }, { id: 2, name: Bob } ]; const UserList () ( ListUser items{users} keyExtractor{(user) user.id} renderItem{(user) span{user.name}/span} / );6. 高阶组件类型// HOC 类型定义 interface WithLoadingProps { isLoading: boolean; } function withLoadingP extends WithLoadingProps( WrappedComponent: React.ComponentTypeP ) { return function WithLoadingComponent( props: OmitP, keyof WithLoadingProps ) { const [isLoading, setIsLoading] useState(false); if (isLoading) { return divLoading.../div; } return WrappedComponent {...(props as P)} isLoading{isLoading} /; }; } interface DataComponentProps extends WithLoadingProps { data: string[]; } const DataComponent: React.FCDataComponentProps ({ data, isLoading }) ( div {isLoading ? Loading... : data.join(, )} /div ); const EnhancedComponent withLoading(DataComponent);7. 上下文类型import { createContext, useContext } from react; // 定义上下文类型 interface ThemeContextType { theme: light | dark; toggleTheme: () void; } const ThemeContext createContextThemeContextType | undefined(undefined); // Provider 组件 const ThemeProvider: React.FC{ children: React.ReactNode } ({ children }) { const [theme, setTheme] useStatelight | dark(light); const toggleTheme () { setTheme(prev prev light ? dark : light); }; return ( ThemeContext.Provider value{{ theme, toggleTheme }} {children} /ThemeContext.Provider ); }; // 自定义 Hook const useTheme () { const context useContext(ThemeContext); if (!context) { throw new Error(useTheme must be used within ThemeProvider); } return context; }; // 使用 const ThemedButton: React.FC () { const { theme, toggleTheme } useTheme(); return ( button onClick{toggleTheme} style{{ background: theme light ? #fff : #333, color: theme light ? #333 : #fff }} Current theme: {theme} /button ); };8. 完整示例Todo 应用import React, { useState, useRef, FormEvent, ChangeEvent } from react; // 类型定义 interface Todo { id: number; text: string; completed: boolean; createdAt: Date; } type FilterType all | active | completed; // 组件定义 const TodoInput: React.FC{ onAdd: (text: string) void } ({ onAdd }) { const [text, setText] useState(); const inputRef useRefHTMLInputElement(null); const handleSubmit (e: FormEvent) { e.preventDefault(); if (text.trim()) { onAdd(text.trim()); setText(); inputRef.current?.focus(); } }; const handleChange (e: ChangeEventHTMLInputElement) { setText(e.target.value); }; return ( form onSubmit{handleSubmit} classNametodo-form input ref{inputRef} typetext value{text} onChange{handleChange} placeholderAdd a new todo... classNametodo-input / button typesubmit classNametodo-add-btn Add /button /form ); }; const TodoItem: React.FC{ todo: Todo; onToggle: (id: number) void; onDelete: (id: number) void; } ({ todo, onToggle, onDelete }) { const [isEditing, setIsEditing] useState(false); const [editText, setEditText] useState(todo.text); const handleSave () { if (editText.trim()) { // 保存逻辑 } setIsEditing(false); }; if (isEditing) { return ( li classNametodo-item editing input typetext value{editText} onChange{(e) setEditText(e.target.value)} onBlur{handleSave} onKeyDown{(e) e.key Enter handleSave()} autoFocus / /li ); } return ( li className{todo-item ${todo.completed ? completed : }} input typecheckbox checked{todo.completed} onChange{() onToggle(todo.id)} / span onDoubleClick{() setIsEditing(true)} {todo.text} /span button onClick{() onDelete(todo.id)}Delete/button /li ); }; const TodoFilter: React.FC{ filter: FilterType; onFilterChange: (filter: FilterType) void; } ({ filter, onFilterChange }) { const filters: { value: FilterType; label: string }[] [ { value: all, label: All }, { value: active, label: Active }, { value: completed, label: Completed } ]; return ( div classNametodo-filter {filters.map(f ( button key{f.value} className{filter f.value ? active : } onClick{() onFilterChange(f.value)} {f.label} /button ))} /div ); }; // 主组件 const TodoApp: React.FC () { const [todos, setTodos] useStateTodo[]([ { id: 1, text: Learn TypeScript, completed: false, createdAt: new Date() }, { id: 2, text: Build a React app, completed: false, createdAt: new Date() } ]); const [filter, setFilter] useStateFilterType(all); const addTodo (text: string) { const newTodo: Todo { id: Date.now(), text, completed: false, createdAt: new Date() }; setTodos([...todos, newTodo]); }; const toggleTodo (id: number) { setTodos(todos.map(todo todo.id id ? { ...todo, completed: !todo.completed } : todo )); }; const deleteTodo (id: number) { setTodos(todos.filter(todo todo.id ! id)); }; const filteredTodos todos.filter(todo { if (filter active) return !todo.completed; if (filter completed) return todo.completed; return true; }); return ( div classNametodo-app h1Todo App/h1 TodoInput onAdd{addTodo} / TodoFilter filter{filter} onFilterChange{setFilter} / ul classNametodo-list {filteredTodos.map(todo ( TodoItem key{todo.id} todo{todo} onToggle{toggleTodo} onDelete{deleteTodo} / ))} /ul div classNametodo-stats {todos.filter(t !t.completed).length} items left /div /div ); }; export default TodoApp;9. 总结配置值说明jsxreactReact.createElementjsxreact-jsx新版 JSX 转换jsxpreserve保留 JSXjsxreact-native保留 JSXRNjsxFactory函数名自定义工厂函数jsxFragmentFactory函数名片段工厂函数jsxImportSource模块名JSX 导入源
20. JSX 支持
发布时间:2026/5/23 21:12:11
20. JSX 支持1. 概述TypeScript 提供了对 JSX 语法的原生支持允许在 TypeScript 文件中编写 JSX/TSX 代码。JSX 是一种 JavaScript 的语法扩展主要用于 React 等框架中描述用户界面。┌─────────────────────────────────────────────────────────────┐ │ TypeScript JSX 系统 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 文件类型 │ │ ├── .tsx包含 JSX 的 TypeScript 文件 │ │ └── .ts不含 JSX 的 TypeScript 文件 │ │ │ │ 配置选项 │ │ ├── jsx控制 JSX 输出格式 │ │ ├── jsxFactory指定工厂函数 │ │ ├── jsxFragmentFactory指定片段工厂函数 │ │ └── jsxImportSource指定 JSX 导入源 │ │ │ │ JSX 模式 │ │ ├── preserve保留 JSX 原样 │ │ ├── react转换为 React.createElement │ │ ├── react-jsx使用 JSX 转换React 17 │ │ ├── react-jsxdev开发模式 JSX 转换 │ │ └── react-native保留 JSX用于 React Native │ │ │ └─────────────────────────────────────────────────────────────┘2. 启用 JSX2.1 文件扩展名.tsx用于包含 JSX 语法的 TypeScript 文件.ts不含 JSX 的普通 TypeScript 文件2.2 tsconfig.json 配置{compilerOptions:{jsx:react-jsx,jsxFactory:React.createElement,jsxFragmentFactory:React.Fragment,jsxImportSource:react}}3. JSX 模式详解3.1 preserve 模式保留 JSX 原样不进行转换。{compilerOptions:{jsx:preserve}}// 输入 const element divHello/div; // 输出保持不变 const element divHello/div;3.2 react 模式转换为React.createElement调用。{compilerOptions:{jsx:react}}// 输入 const element div classNamecontainerHello/div; // 输出 const element React.createElement(div, { className: container }, Hello);3.3 react-jsx 模式React 17使用新的 JSX 转换无需显式导入 React。{compilerOptions:{jsx:react-jsx}}// 输入 const element divHello/div; // 输出 import { jsx as _jsx } from react/jsx-runtime; const element _jsx(div, { children: Hello });3.4 react-native 模式保留 JSX 原样用于 React Native。{compilerOptions:{jsx:react-native}}4. JSX 类型定义4.1 基本类型// 组件 Props 类型定义 interface ButtonProps { children?: React.ReactNode; onClick?: () void; disabled?: boolean; variant?: primary | secondary | danger; } // 函数组件 const Button: React.FCButtonProps ({ children, onClick, disabled, variant primary }) { return ( button onClick{onClick} disabled{disabled} className{btn btn-${variant}} {children} /button ); }; // 使用 const App () ( div Button variantprimary onClick{() console.log(clicked)} Click Me /Button /div );4.2 事件类型import { ChangeEvent, MouseEvent, FormEvent } from react; interface FormProps { onSubmit: (data: FormData) void; } const FormComponent: React.FCFormProps ({ onSubmit }) { const handleSubmit (e: FormEventHTMLFormElement) { e.preventDefault(); const formData new FormData(e.currentTarget); onSubmit(formData); }; const handleChange (e: ChangeEventHTMLInputElement) { console.log(e.target.value); }; const handleClick (e: MouseEventHTMLButtonElement) { console.log(Clicked at (${e.clientX}, ${e.clientY})); }; return ( form onSubmit{handleSubmit} input typetext nameusername onChange{handleChange} / input typeemail nameemail onChange{handleChange} / button typesubmit onClick{handleClick} Submit /button /form ); };4.3 Ref 类型import { useRef, useEffect, RefObject } from react; interface InputProps { inputRef?: RefObjectHTMLInputElement; autoFocus?: boolean; } const InputComponent: React.FCInputProps ({ inputRef, autoFocus }) { const internalRef useRefHTMLInputElement(null); const ref inputRef || internalRef; useEffect(() { if (autoFocus ref.current) { ref.current.focus(); } }, [autoFocus, ref]); return input ref{ref} typetext /; }; // 使用 forwardRef const FancyInput React.forwardRefHTMLInputElement, InputProps((props, ref) ( input ref{ref} classNamefancy {...props} / ));5. 泛型组件// 泛型组件 interface ListPropsT { items: T[]; renderItem: (item: T, index: number) React.ReactNode; keyExtractor?: (item: T) string | number; } function ListT({ items, renderItem, keyExtractor }: ListPropsT) { return ( ul {items.map((item, index) ( li key{keyExtractor ? keyExtractor(item) : index} {renderItem(item, index)} /li ))} /ul ); } // 使用 interface User { id: number; name: string; } const users: User[] [ { id: 1, name: Alice }, { id: 2, name: Bob } ]; const UserList () ( ListUser items{users} keyExtractor{(user) user.id} renderItem{(user) span{user.name}/span} / );6. 高阶组件类型// HOC 类型定义 interface WithLoadingProps { isLoading: boolean; } function withLoadingP extends WithLoadingProps( WrappedComponent: React.ComponentTypeP ) { return function WithLoadingComponent( props: OmitP, keyof WithLoadingProps ) { const [isLoading, setIsLoading] useState(false); if (isLoading) { return divLoading.../div; } return WrappedComponent {...(props as P)} isLoading{isLoading} /; }; } interface DataComponentProps extends WithLoadingProps { data: string[]; } const DataComponent: React.FCDataComponentProps ({ data, isLoading }) ( div {isLoading ? Loading... : data.join(, )} /div ); const EnhancedComponent withLoading(DataComponent);7. 上下文类型import { createContext, useContext } from react; // 定义上下文类型 interface ThemeContextType { theme: light | dark; toggleTheme: () void; } const ThemeContext createContextThemeContextType | undefined(undefined); // Provider 组件 const ThemeProvider: React.FC{ children: React.ReactNode } ({ children }) { const [theme, setTheme] useStatelight | dark(light); const toggleTheme () { setTheme(prev prev light ? dark : light); }; return ( ThemeContext.Provider value{{ theme, toggleTheme }} {children} /ThemeContext.Provider ); }; // 自定义 Hook const useTheme () { const context useContext(ThemeContext); if (!context) { throw new Error(useTheme must be used within ThemeProvider); } return context; }; // 使用 const ThemedButton: React.FC () { const { theme, toggleTheme } useTheme(); return ( button onClick{toggleTheme} style{{ background: theme light ? #fff : #333, color: theme light ? #333 : #fff }} Current theme: {theme} /button ); };8. 完整示例Todo 应用import React, { useState, useRef, FormEvent, ChangeEvent } from react; // 类型定义 interface Todo { id: number; text: string; completed: boolean; createdAt: Date; } type FilterType all | active | completed; // 组件定义 const TodoInput: React.FC{ onAdd: (text: string) void } ({ onAdd }) { const [text, setText] useState(); const inputRef useRefHTMLInputElement(null); const handleSubmit (e: FormEvent) { e.preventDefault(); if (text.trim()) { onAdd(text.trim()); setText(); inputRef.current?.focus(); } }; const handleChange (e: ChangeEventHTMLInputElement) { setText(e.target.value); }; return ( form onSubmit{handleSubmit} classNametodo-form input ref{inputRef} typetext value{text} onChange{handleChange} placeholderAdd a new todo... classNametodo-input / button typesubmit classNametodo-add-btn Add /button /form ); }; const TodoItem: React.FC{ todo: Todo; onToggle: (id: number) void; onDelete: (id: number) void; } ({ todo, onToggle, onDelete }) { const [isEditing, setIsEditing] useState(false); const [editText, setEditText] useState(todo.text); const handleSave () { if (editText.trim()) { // 保存逻辑 } setIsEditing(false); }; if (isEditing) { return ( li classNametodo-item editing input typetext value{editText} onChange{(e) setEditText(e.target.value)} onBlur{handleSave} onKeyDown{(e) e.key Enter handleSave()} autoFocus / /li ); } return ( li className{todo-item ${todo.completed ? completed : }} input typecheckbox checked{todo.completed} onChange{() onToggle(todo.id)} / span onDoubleClick{() setIsEditing(true)} {todo.text} /span button onClick{() onDelete(todo.id)}Delete/button /li ); }; const TodoFilter: React.FC{ filter: FilterType; onFilterChange: (filter: FilterType) void; } ({ filter, onFilterChange }) { const filters: { value: FilterType; label: string }[] [ { value: all, label: All }, { value: active, label: Active }, { value: completed, label: Completed } ]; return ( div classNametodo-filter {filters.map(f ( button key{f.value} className{filter f.value ? active : } onClick{() onFilterChange(f.value)} {f.label} /button ))} /div ); }; // 主组件 const TodoApp: React.FC () { const [todos, setTodos] useStateTodo[]([ { id: 1, text: Learn TypeScript, completed: false, createdAt: new Date() }, { id: 2, text: Build a React app, completed: false, createdAt: new Date() } ]); const [filter, setFilter] useStateFilterType(all); const addTodo (text: string) { const newTodo: Todo { id: Date.now(), text, completed: false, createdAt: new Date() }; setTodos([...todos, newTodo]); }; const toggleTodo (id: number) { setTodos(todos.map(todo todo.id id ? { ...todo, completed: !todo.completed } : todo )); }; const deleteTodo (id: number) { setTodos(todos.filter(todo todo.id ! id)); }; const filteredTodos todos.filter(todo { if (filter active) return !todo.completed; if (filter completed) return todo.completed; return true; }); return ( div classNametodo-app h1Todo App/h1 TodoInput onAdd{addTodo} / TodoFilter filter{filter} onFilterChange{setFilter} / ul classNametodo-list {filteredTodos.map(todo ( TodoItem key{todo.id} todo{todo} onToggle{toggleTodo} onDelete{deleteTodo} / ))} /ul div classNametodo-stats {todos.filter(t !t.completed).length} items left /div /div ); }; export default TodoApp;9. 总结配置值说明jsxreactReact.createElementjsxreact-jsx新版 JSX 转换jsxpreserve保留 JSXjsxreact-native保留 JSXRNjsxFactory函数名自定义工厂函数jsxFragmentFactory函数名片段工厂函数jsxImportSource模块名JSX 导入源