原文:Using TypeScript with React
做者:Simon Knott
译者:不肥的肥羊
为保证文章的可读性,本文采用意译
阅读全文约 13 分钟html
这篇文章是基于我过去在 React JS & React Native Bonn Meetup 上所作的演讲。文章的目的是回答如下几个问题:前端
这是我在学校以外的第一次公开演讲,可以顺利结束真是极好的,谢谢全部前来捧场的朋友!😁演讲是我很是喜欢的一件事情,但愿将来能作更多的演讲。react
typescriptlang.org 对 TypeScript(缩写为 TS)有以下解释:git
TypeScript 是 JavaScript 的类型超集,能够编译为纯 JavaScript。github
上面这段描述能够解释为:typescript
让咱们看一个栗子,因为 TS 是 JS 的超集,所以如下的代码对两种语言都是有效的:编程
function add(a, b) {
return a + b;
}
const favNumber = add(31, 11);
复制代码
咱们已经了解到,TS 的特殊性在于它的类型系统。那么咱们为以上的代码添加类型声明,就会产生下面符合 TS 规范,却不符合 JS 规范的代码:react-native
function add(a: number, b: number): number {
return a + b;
}
const favNumber: number = add(31, 11);
复制代码
经过添加这些注解,TypeScript 编译器能够检查你的代码以查找出一些代码层面的 bug。例如,它可能会发现不匹配的参数类型,或者做用域外的调用等等。数组
使用 tsc
命令行工具编译 TypeScript 会经历:对代码进行类型检查 -> 全部检查都经过 -> 输出纯 JavaScript。输出的代码与源代码很类似,只是没有类型注解。浏览器
TypeScript 还支持基本类型推断,它经过省略函数返回值的类型以及大多数状况下的变量赋值类型来简化代码:
function add(a: number, b: number) {
return a + b;
}
const favNumber = add(31, 11);
复制代码
(与上一段代码的区别是,函数返回值没有类型注解。)
TS 中有效的基本类型有 boolean
,string
,number
,Symbol
,null
,undefined
和 BigInt
类型;还有 void
类型,用于表示函数不返回任何内容;还有 Function
类型,以及,一般用 Array<T>
来表示 string[]
或 number[]
这样须要注解数组内容类型的变量。还有像 ReactElement
,HTMLInputElement
以及 Express.App
这些在特定的部署环境或者有特定的依赖时才有效的类型。
TS 中最有趣的事情是你能够定义本身的类型。让咱们看看一些为你的领域建模的方式。首先是使用 interface
关键字定义你的对象:
interface User {
firstName: string;
lastName: string;
age: number;
state: UserState;
}
复制代码
你还能够定义 enums
:
enum UserState {
ACTIVE,
INACTIVE,
INVITED,
}
复制代码
TypeScript 甚至支持继承:
interface FunkyUser extends User {
isDancing: boolean;
}
复制代码
TypeScript 还有不少高级类型,好比联合类型,它能够用来代替枚举类型,它们的区别是,它更像 JavaScript 的书写方式。
type UserState =
"active" |
"inactive" |
"invited";
const correctState: UserState = "active"; // ✅
const incorrectState: UserState = "loggedin"; // ❌ TypeError
复制代码
在类型检查的时候,TypeScript 不会检查原型链或其余继承的方法,它只检查属性的类型标识以及处理对象的特性:
const bugs: User = {
firstName: "Bugs",
lastName: "Bunny",
age: "too old", // TypeError: expected `number` but got `string`
state: UserState.ACTIVE,
}
复制代码
你固然能够在 TypeScript 中使用 class
,我并无让你远离它,但要注意:我会在后文解释,为何你不须要它。看下面这个栗子:
class JavaLover implements User {
private firstName: string;
private lastName: string;
private age: number;
private state: UserState;
getOpinion() {
return [ "!!JAVA!1!" ];
}
}
复制代码
前面咱们看了一些基础的 TypeScript 代码,对于它作了什么有了一个大体的了解,如今让咱们来看看它在 React 中是如何使用的。
因为 Babel 7 支持了 TypeScript 的内建,把它整合到你的构建流程变得十分简单。我仍然建议你查看构建工具的文档,其中大部分都包含有关 TypeScript 设置,写的很好的操做指南。
配置好构建流程以后,你就能够在你的组件中使用 TypeScript 了。下面是一个简单的 React-Native 组件,它接收一个 TodoItem 和一个回调函数,并展现了一条 Todo 信息。
import * as React from "react";
import { View, Text, CheckBox } from "react-native";
interface TodoItem {
id: string;
name: string;
isCompleted: boolean;
}
interface TodoListItemProps {
item: TodoItem;
onComplete: (id: string) => void;
}
function TodoListItem(props: TodoListItemProps) {
const { item, onComplete } = props;
return (
<View> <Text>{item.name}</Text> <CheckBox isChecked={item.isCompleted} onClick={state => { onComplete(item.id); }} /> </View> ); } 复制代码
因为 React 本质上是纯 JavaScript,所以能够像纯 JavaScript 同样编写。你只须要使用 interface
(见 TodoListItemProps
的 interface
定义)来定义你组件的 props
结构,并使用刚刚定义的 TodoListItemProps
类型来定义组件 props
参数的类型。你无需指定返回类型(事实上它的类型是 JSX.Element
),由于能够从组件返回的表达式的类型中推断出来。
即使 JSX 不是 JavaScript 规范的一部分,TypeScript 仍然能够对它进行类型检查,这使它能够校验传入组件中的 props。
你还能够将 TypeScript 与 React 的 class
API 结合使用:
import * as React from "react";
interface TimerState {
count: number;
}
class Timer extends React.Component<{}, TimerState> {
state: TimerState = {
count: 0
}
timerId: number | undefined = undefined;
componentDidMount() {
this.timerId = setInterval(
() => {
this.setState(
state => ({
count: state.count + 1
})
);
},
1000
);
}
componentWillUnmount() {
if (this.timerId) {
clearInterval(this.timerId);
}
}
render() {
return (
<p>Count: {this.state.count}</p>
)
}
}
复制代码
在你编写一个 class 时,你会将类型参数传入到你 extend 出来的 React.Component
中。首先是前面示例组件中为空的 props({}
空对象);第二个通用类型参数是组件的 state,在这里是一个只包含数字类型的 count 字段。你还会发现这里使用了初始化为 undefined
的实例字段 timerId
,这也是为何它的类型被声明为 number | undefined
,这表示它能够是 number
或 undefined
类型。
如你所见,将 TypeScript 与 React 结合使用很是简单,不须要任何仪式。基本上,它是 prop-types
更强大的替代品,由于它支持更高级的类型,而且也可使用普通的 JS 代码。同时,TypeScript 能够提供编译时的校验,而 prop-types
只在开发环境中有效。
若是您想本身捣鼓一些代码,能够看看我在现场演示中使用的示例。它就在这个 repo 的 todo
文件夹中:github.com/Skn0tt/reac… 。
到目前为止,我假设您对 TypeScript 是什么以及它能够作什么有一个大体的了解。那么接下来我会继续说明,若是咱们不使用 TypeScript,那是由于什么。
TypeScript 并非编写 JavaScript 的全新方式,它只是扩展了 JavaScript 编写类型注解的能力。首先,最重要的是,TypeScript 是一个类型检查器,但你们彷佛有忽略这个事实的趋势,就像我最近看到的一些言论:
“TypeScript 太棒了,终究咱们的 Java 开发工做也能在前端环境下进行了。”
这看起来颇有意义,但其实是有害的。没错,Java 开发者已经习惯了类型系统、class、interface 等优秀特性,但 TypeScript 对他们来讲依然很是有吸引力。然而,若是 Java(或其余受面向对象编程影响较深的语言)开发者转到编写 TypeScript,又不了解 Java 与 JavaScript 之间根深蒂固的差别,那也许会带来很大的问题。记住一点:不管你的 JavaScript 代码是否由 TypeScript 编译而来,都不
会改变其运行时的行为。它一旦运行,就再也不会有类型信息,这是它的优点,也是它的痛点
。JavaScript 经过原型链继承实现了动态类型和强制类型的功能,这让它拥有了许多新的功能和特性,距离 Java 也不远了。在从 Java 开发转换到 TypeScript 开发时要一直记住这一点。
在项目中添加类型注解是代码结构文档化的极佳方式,它提供了一种为传递的数据注明结构的方式。TypeScript 仍是“机器可读”的,这意味着它能够由机器执行!这样就能够提供强大的编辑器支持(尝试用用 VS Code 吧,它 diao bao le!),这简化了代码重构的过程,并加强了静态的安全检查。
代码结构文档化这件事,TypeScript 不只是容许你去作,而是强制你去作。会让你停下来复盘你正在编写的代码,并思考是否能建立更清晰的代码结构以得到更高的代码质量。
至少对我来讲,编写类型注解让个人代码更利于理解,由于我不须要深刻研究项目就能够了解数据的结构,它消除了不少认知上的复杂性。