# 使用TypeScript创建React应用

文章链接:https://bobbyhadz.com/blog/react-create-react-app-typescript (opens new window)

作者:Borislav Hadzhiev (opens new window)

正文从这开始~

# 目录

  1. 使用TypeScript创建React应用-完整指南
  2. 在React TypeScript项目中类型声明props
  3. 在React TypeScript中使用useState钩子
  4. 在React TypeScript项目中键入事件
  5. 在React TypeScript项目中键入refs

# 使用TypeScript创建React应用-完整指南

要用Typescript创建一个React应用程序,需要运行npx create-react-app 命令,将--template 标记设置为typescript,比如npx create-react-app my-ts-app --template typescript

npx create-react-app my-ts-app --template typescript
1

如果执行命令报错,试着使用**create-react-app (opens new window)**最新版本的命令。

npx create-react-app@latest my-ts-app --template typescript
1

如果你已经存在使用JavaScript编写的创建React应用的项目,运行下面的命令行来添加TS的支持。

# 👇️ with NPM
npm install --save typescript @types/node @types/react @types/react-dom @types/jest

# 👇️ with YARN
yarn add typescript @types/node @types/react @types/react-dom @types/jest
1
2
3
4
5

接着重命名.js文件扩展为.tsxindex.js文件会变成index.tsx

然后,在项目的根目录下,使用下面的配置来创建tsconfig.json文件。

// tsconfig.json

{
  "compilerOptions": {
    "target": "es6",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx"
  },
  "include": ["src/**/*"]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

确保重启你的开发服务器和你的IDE。

使用.tsx文件扩展而不是.ts扩展来包含JSX代码是非常重要的。

不出意外的话,你的项目会产生一堆类型错误。你不得不在继续开发或者构建项目之前修复他们。

比如说index.tsx文件,当创建一个应用根节点时,需要使用类型断言。

// index.ts

const root = ReactDOM.createRoot(
  document.getElementById('root') as Element
);
1
2
3
4
5

# 在React TypeScript项目中类型声明props

使用类型别名或接口来类型声明组件的props。

// App.tsx

import React from 'react';

interface EmployeeProps {
  name: string;
  age: number;
  country: string;
  children?: React.ReactNode; // 👈️ for demo purposes
}

function Employee({name, age, country}: EmployeeProps) {
  return (
    <div>
      <h2>{name}</h2>
      <h2>{age}</h2>
      <h2>{country}</h2>
    </div>
  );
}

export default function App() {
  return (
    <div>
      <Employee name="Alice" age={29} country="Austria" />
    </div>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

Employee组件接收我们在EmployeeProps接口中定义的nameage,和country 三个props。

你可以使用问号标记将props标记为可选,也可以在函数定义里为props设置默认值。

// App.tsx

import React from 'react';

interface EmployeeProps {
  name?: string; // 👈️ marked optional
  age?: number; // 👈️ marked optional
  country: string;
  children?: React.ReactNode; // 👈️ for demo purposes
}

// 👇️ default values for props
function Employee({name = 'Alice', age = 29, country}: EmployeeProps) {
  return (
    <div>
      <h2>{name}</h2>
      <h2>{age}</h2>
      <h2>{country}</h2>
    </div>
  );
}

export default function App() {
  return (
    <div>
      <Employee country="Austria" />
    </div>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

EmployeeProps接口中,nameage 被标记为可选props。因此当使用组件时,他们不是必填的。

我们为nameage设置了默认值。所以如果使用组件时没有提供,那么组件就会使用默认值。

# 在React TypeScript中使用useState钩子

使用useState钩子上的泛型来类型声明它要存储的值。

// App.tsx

import {useState} from 'react';

function App() {
  // 👇️ array of strings
  const [strArr, setStrArr] = useState<string[]>([]);

  // 👇️ an array of objects
  const [objArr, setObjArr] = useState<{name: string; age: number}[]>([]);

  setStrArr(['a', 'b', 'c']);

  setObjArr([{name: 'A', age: 1}]);

  return (
    <div className="App">
      <div>Hello world</div>
    </div>
  );
}

export default App;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

上面的例子显示了如何将状态数组,类型声明为字符串数组或对象数组。

在React中使用TypeScript时,一定要确保显式地输入空数组。

# 在React TypeScript项目中键入事件

要在React TypeScript项目中键入一个事件,请将事件处理函数内联编写,并将鼠标悬停在event对象上以获得其类型。

// App.tsx

const App = () => {

  // 👇️ onClick event is written inline
  // hover over the `event` parameter with your mouse
  return (
    <div>
      <button onClick={event => console.log(event)}>Click</button>
    </div>
  );
};

export default App;
1
2
3
4
5
6
7
8
9
10
11
12
13
14

TypeScript能够推断出事件的类型,当它被内联编写时。这是十分有用的,因为会在所有事件上生效。只需写一个你的事件处理程序的内联 "模拟 "实现,并将鼠标悬停在事件参数上以获得其类型。

一旦你知道事件的类型是什么,你就能够提取你的处理函数并正确的类型声明。

现在我们知道本例中onClick事件的正确类型是,React.MouseEvent<HTMLButtonElement, MouseEvent> 。这样就可以提取到我们的处理函数。

// App.tsx

import React from 'react';

const App = () => {
  const handleClick = (
    event: React.MouseEvent<HTMLButtonElement, MouseEvent>,
  ) => {
    console.log(event.target);
    console.log(event.currentTarget);
  };

  return (
    <div>
      <button onClick={handleClick}>Click</button>
    </div>
  );
};

export default App;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

需要注意的是,你可以用这种方法来获取所有事件的类型,而不仅仅是onClick事件。

只要你把事件处理函数内联编写,并用鼠标在事件参数上悬停,TypeScript就能推断出事件的类型。

# 在React TypeScript项目中键入refs

使用useRef钩子上的泛型,在React TypeScript中类型声明一个ref

// App.tsx

import {useEffect, useRef} from 'react';

export default function App() {
  const inputRef = useRef<HTMLInputElement | null>(null);

  useEffect(() => {
    inputRef?.current?.focus();
  }, []);

  return (
    <div>
      <input ref={inputRef} />
    </div>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

我们在一个input元素上设置ref,所以我们使用了HTMLInputElementnull的类型,因为ref的初始值是null

元素类型被统一命名为HTML***Element。一旦你开始输入HTML...,你的IDE应该能够用自动完成来帮助你。

一些常用的类型包括:HTMLInputElement, HTMLButtonElement, HTMLAnchorElement, HTMLImageElement, HTMLTextAreaElement, HTMLSelectElement等。