本文详细介绍了如何搭建React+TypeScript开发环境,包括Node.js和npm的安装、创建React项目并集成TypeScript、配置TypeScript项目以及启动开发服务器。文章还涵盖了TypeScript的基础知识、React组件的编写与类型化、React Hook的类型化以及高阶组件的类型化等内容。
React+TypeScript环境搭建在开始使用React和TypeScript之前,需要确保已经安装了Node.js和npm。Node.js是一个开源的JavaScript运行时环境,而npm是Node.js的包管理器,用于安装和管理JavaScript库。
下载并安装Node.js
node -v npm -v
安装Create React App
npx create-react-app my-app --template typescript
安装依赖
cd my-app npm install
查看项目文件结构
my-app ├── node_modules ├── public ├── src ├── .gitignore ├── README.md ├── package.json └── tsconfig.json
tsconfig.json
文件用于配置TypeScript编译器的选项,以下是默认的tsconfig.json
文件内容:
{ "compilerOptions": { "target": "es5", "module": "commonjs", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "noEmit": true, "jsx": "react" }, "include": ["src"] }
target
和module
选项可以根据项目需求进行修改。npm start
在TypeScript中,变量需要显式声明其类型。这有助于确保代码的健壮性和可维护性。
基本类型
number
:表示数字类型。string
:表示字符串类型。boolean
:表示布尔类型。any
:表示任意类型,不推荐使用。void
:表示没有返回值的方法。never
:表示永远不会返回的函数。类型声明示范
let age: number = 25; let name: string = "张三"; let isStudent: boolean = true; // 使用any类型 let dynamicValue: any = 123; dynamicValue = "abc"; // 类型可以改变
let age = 25; // 推断为number类型 let name = "张三"; // 推断为string类型
在TypeScript中,函数需要显式声明其参数类型和返回值类型。
基本函数定义
function add(a: number, b: number): number { return a + b; }
let result: number = add(1, 2); // result为3
可选参数
?
表示参数是可选的。
function greet(name: string, greeting?: string) { if (greeting) { console.log(`${greeting}, ${name}!`); } else { console.log(`Hello, ${name}!`); } }
greet("张三"); // 输出 "Hello, 张三!"
greet("李四", "你好"); // 输出 "你好, 李四!"
默认参数值
=
给参数赋默认值。
function greet(name: string, greeting: string = "Hello") { console.log(`${greeting}, ${name}!`); }
greet("张三"); // 输出 "Hello, 张三!"
greet("李四", "你好"); // 输出 "你好, 李四!"
在TypeScript中,接口和类型别名用于定义结构化类型。
接口定义
interface Person { name: string; age: number; }
let person: Person = {
name: "张三",
age: 25
};
类型别名
type PersonType = { name: string; age: number; };
let person: PersonType = {
name: "张三",
age: 25
};
可选属性
?
表示属性是可选的。
interface Person { name: string; age?: number; // 可选属性 }
let person: Person = {
name: "张三"
};
方法定义
interface Person { name: string; age?: number; greet(): void; }
let person: Person = {
name: "张三",
age: 25,
greet() {
console.log(Hello, ${this.name}!
);
}
};
person.greet(); // 输出 "Hello, 张三!"
在React中,组件可以分为类组件和函数组件。这里先介绍如何创建一个简单的函数组件。
函数组件
const App: React.FC = () => { return <h1>Hello, World!</h1>; };
类组件
class App extends React.Component { render() { return <h1>Hello, World!</h1>; } }
带Props的函数组件
type Props = { name: string; age: number; };
const App: React.FC<Props> = (props) => {
return <h1>Hello, {props.name}! You are {props.age} years old.</h1>;
};
带Props的类组件
type Props = { name: string; age: number; };
class App extends React.Component<Props, {}> {
constructor(props: Props) {
super(props);
}
render() {
return <h1>Hello, {this.props.name}! You are {this.props.age} years old.</h1>;
}
}
在TypeScript中,可以使用 React.FC
类型来类型化函数组件。
类型化函数组件
React.FC
类型来类型化函数组件。
import React from 'react';
type Props = {
name: string;
};
const App: React.FC<Props> = (props) => {
return <h1>Hello, {props.name}!</h1>;
};
类型化类组件
React.Component
类来类型化类组件。
import React from 'react';
type Props = {
name: string;
};
class App extends React.Component<Props, {}> {
render() {
return <h1>Hello, {this.props.name}!</h1>;
}
}
在React中,组件可以接收Props和维护State。这里介绍如何在TypeScript中类型化Props和State。
类型化Props
type Props = { name: string; age: number; };
const App: React.FC<Props> = (props) => {
return <h1>Hello, {props.name}! You are {props.age} years old.</h1>;
};
类型化State
React.Component
类来类型化State。
import React from 'react';
type Props = {
name: string;
};
type State = {
age: number;
};
class App extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
age: 25
};
}
render() {
return <h1>Hello, {this.props.name}! You are {this.state.age} years old.</h1>;
}
}
React Hooks允许我们在不编写类的情况下使用React的状态和生命周期。这里介绍如何在TypeScript中类型化Hooks。
类型化useState
useState
Hook。
import React, { useState } from 'react';
const App: React.FC = () => {
const [name, setName] = React.useState<string>('张三');
const [age, setAge] = React.useState<number>(25);
return (
<div>
<h1>Hello, {name}! You are {age} years old.</h1>
<button onClick={() => setName('李四')}>Change Name</button>
<button onClick={() => setAge(30)}>Change Age</button>
</div>
);
};
类型化useReducer
useReducer
Hook。
import React, { useReducer } from 'react';
type State = {
name: string;
age: number;
};
type Action = {
type: 'setName' | 'setAge';
payload: string | number;
};
const initialState: State = {
name: '张三',
age: 25
};
const reducer = (state: State, action: Action): State => {
switch (action.type) {
case 'setName':
return { ...state, name: action.payload as string };
case 'setAge':
return { ...state, age: action.payload as number };
default:
return state;
}
};
const App: React.FC = () => {
const [state, dispatch] = React.useReducer(reducer, initialState);
return (
<div>
<h1>Hello, {state.name}! You are {state.age} years old.</h1>
<button onClick={() => dispatch({ type: 'setName', payload: '李四' })}>Change Name</button>
<button onClick={() => dispatch({ type: 'setAge', payload: 30 })}>Change Age</button>
</div>
);
};
自定义Hook可以封装通用逻辑,使代码更简洁和可复用。
创建自定义Hook
import React, { useState } from 'react';
type Props = {
name: string;
age: number;
};
const useGreeting = (props: Props) => {
const [greeting, setGreeting] = useState(Hello, ${props.name}! You are ${props.age} years old.
);
return greeting;
};
const App: React.FC<Props> = (props) => {
const greeting = useGreeting(props);
return <h1>{greeting}</h1>;
};
使用自定义Hook
import React from 'react'; import { useGreeting } from './hooks';
type Props = {
name: string;
age: number;
};
const App: React.FC<Props> = (props) => {
const greeting = useGreeting(props);
return <h1>{greeting}</h1>;
};
高阶组件(Higher-Order Component,HOC)是一种高级技术,用于封装和重用组件逻辑。HOC接受一个组件作为输入,返回一个新的增强过的组件。
创建HOC
import React from 'react';
type EnhancedComponent<T> = React.ComponentType<T> & {
WrappedComponent: React.ComponentType<T>;
};
const withLogging<T extends object>(WrappedComponent: React.ComponentType<T>) {
class EnhancedComponent extends React.Component<T> {
render() {
console.log(Rendering ${WrappedComponent.name}
);
return <WrappedComponent {...this.props} />;
}
}
return EnhancedComponent as EnhancedComponent<T>;
}
interface Props {
name: string;
age: number;
}
const App: React.FC<Props> = (props) => (
<div>
<h1>Hello, {props.name}! You are {props.age} years old.</h1>
</div>
);
const EnhancedApp = withLogging<Props>(App);
const App2: React.FC<Props> = (props) => (
<EnhancedApp name={props.name} age={props.age} />
);
具体项目实例
import React from 'react'; import { withLogging } from './HOC';
interface Props {
name: string;
age: number;
}
const App: React.FC<Props> = (props) => (
<div>
<h1>Hello, {props.name}! You are {props.age} years old.</h1>
</div>
);
const EnhancedApp = withLogging<Props>(App);
const App2: React.FC<Props> = (props) => (
<EnhancedApp name={props.name} age={props.age} />
);
在使用TypeScript时,编译器会生成详细的错误信息,帮助你快速定位和修复问题。
常见的TypeScript错误
TS2339: Property 'xxx' does not exist on type 'yyy'.
TS2322: Type 'xxx' is not assignable to type 'yyy'.
TS1005: Unexpected token ','
在开发过程中,可能会遇到一些常见的问题,以下是一些常见问题的解决方法。
类型不匹配
let name: string = "张三"; name = 123; // 错误,数字不能赋值给字符串类型
未声明的变量
let name = "张三"; // 缺少类型声明 let name: string = "张三"; // 正确
函数参数类型不匹配
function greet(name: string) { console.log(`Hello, ${name}!`); }
greet(123); // 错误,数字不能作为字符串参数传递
接口或类型别名定义错误
interface Person { name: string; age: number; }
let person: Person = {
name: "张三",
age: 25
};
let person: Person = {
name: "张三"
}; // 错误,缺少age属性
状态和属性类型不匹配
import React from 'react';
type Props = {
name: string;
age: number;
};
class App extends React.Component<Props, {}> {
constructor(props: Props) {
super(props);
}
render() {
return <h1>Hello, {this.props.name}! You are {this.props.age} years old.</h1>;
}
}
const app = <App name="张三" age="25" />; // 错误,age属性应该是数字类型
通过以上内容,你可以掌握React+TypeScript的基础知识和最佳实践。希望这些信息对你有所帮助。如果需要更深入的学习,可以参考慕课网提供的更多课程。