TypeScript 为什么如此重要?

原文连接:TypeScript, why is so important?html

译文连接:TypeScript 为什么如此重要?前端


类型为何会存在?

众所周知的经典编程语言,例如:Pascal、C、C++等都是强类型语言,这就意味着这些语言,必须在编译时设置更严格的类型规则。node

每次你声明变量或函数参数时,必须先明确标明它们的类型,而后再使用。这个概念背后的缘由能够追溯到好久之前,即所谓的为了确保程序有意义的类型理论。es6

硬件没法区分类型。类型能够看做是人抽象出来的东西,可让编程者实现更高层次的思考,让代码更加简洁明了。typescript

此外,从编译器的角度来看,类型还有一些优点,例如:便于优化。在编译阶段进行类型检查可让编译器更有效率的执行机器指令。安全是另外一个重要的考量,强类型系统能够帮助编译器提前发现错误。npm

随着像是 Basic,JavaScript,PHP,Python 等解释型语言新秀的出现,它们都是在运行时进行类型检查。编程者能够不用编译它们的代码,语言变得更灵活智能,能够基于上下文和数据进行类型检测。编程

回归初心

你们不该该就关于强类型和弱类型孰优孰劣展开一场新争论,咱们必须了解,每一种语言都是基于某个特定的目的被设计创造出来的,没有人会预料到像是 JavaScript 这样的脚本语言会如此流行并普遍的应用于开发商业应用。json

给像 JavaScript 这样的弱类型语言增长强类型的能力,不只能够帮助开发团队写出整洁的自解释代码,并且能解决一个根本问题:在运行时以前的编译阶段捕获类型错误。数组

TypeScript 是什么?

JavaScript 是一个解释型或者说动态编译语言,开发人员在运行程序以前不须要编译代码。由于,咱们称 TypeScript 为JavaScript 的类型超集,意思是说它给开发人员提供了一组新的语法,能够给 JavaScript 这种弱类型语言加入类型。浏览器

举个例子,当咱们在 JavaScript 中声明一个变量时,是不须要指定类型的。但在 TypeScript 中声明变量就必须指定一个类型,固然你也能够不设置类型直接赋值。

let isDone: boolean
let decimal: number
let big: bigint
let color: string
let name = "John"

跟 JavaScript(.js)不一样,TypeScript 文件后缀使用 .ts 扩展名。浏览器是不识别 .ts 文件,因此使用时必须提早把 TS 代码转换成 JavaScript 代码。这个转换过程被称为转译,编译和转译的微小差异在于:

  • 编译是把源码转变成另外一种语言
  • 转译是把源码转变另外一个相同抽象层级的语言

实话实说,我必须澄清这个概念,由于我已经有不少次碰到这两个容易被混淆的概念了。不过,为了便于阅读,就连 TypeScript 的官方文档也一直把预处理过程叫作编译。

安装

咱们可使用 npmyarn 安装 TypeScript

yarn add typescript

npm install typescript

而后,咱们就可使用 tsc 命令编译 TS 文件。

npx tsc

配置

在咱们的项目中新建 TS 文件,并而后在命令行中用 tsc 编译。咱们新建一个文件叫作 app.ts

function add(num1: number, num2: number): number {
  return num1 + num2
}

而后再命令行执行:

npx tsc app.ts

会生成一个以下内容的名字叫作 app.js 的文件。

function add(num1, num2) {
  return num1 + num2
}

不过,有更简单的方式。最简单的一种是在你 JS 根目录建立一个 tsconfig.json 的文件,让编译器经过此配置执行。

{
  "compilerOptions": {
    "target": "es6",
    "rootDir": "./src",
    "outDir": "./dist",
    "module": "commonjs",
    "removeComments": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "**/*.spec.ts"]
}

此配置文件按照部分划分,以下咱们能看到一个最基本的配置文件有如下选项:

  • target: 它决定编译后的 JS 版本: ES3,ES5,ES6……
  • rootDir: 它决定你源代码(.ts 文件)的根目录
  • outDir: 它决定编译后的 JS 文件的输出目录
  • module: 它设定了程序的模块系统:commonjs,UMD,AMD……
  • removeComments: 编译代码时移除注释,被认为是最佳实践
  • include: 它决定源代码所在的目录
  • exclude: 它决定了在编译过程当中,须要排除那些文件和文件夹

给 TypeScript 定义新的配置文件后,咱们就能在 src 文件夹下新建多个 TypeScript 文件,而后,咱们只须要在命令行运行 npx tsc ,就能编译文件而且把生成的文件放到输出文件夹。

咱们也能够在 package.json 中指定一个 tsc 任务,甚至可使用 watch 选项让文件在被改动后自动运行 tsc

根据你使用的技术和项目类型,能够有多种方式设置 TypeScript ,在本文中,咱们不会展现全部可能的配置方案,你们若是想了解更多的选项,鼓励你们去看一下官方文档

怎么使用 TypeScript ?

TypeScript 是一个在软件开发过程当中帮助开发者给数据类型添加更严格的约束的工具。他必须跟其余好的实践一块儿配合例如适当使用 letconst 替换 var 进行局部变量声明。

基础类型

让咱们来回顾一下 TS 提供的类型。

Boolean、Number、String

基本类型的声明以下:

let isDone: boolean = false
let decimal: number = 6
let hex: number = 0xf00d
let binary: number = 0b1010
let octal: number = 0o744
let big: bigint = 100n
let color: string = 'blue'

Arrays

数组类型有两种写法:

let list: number[] = [1, 2, 3]

或者

let list: Array<number> = [1, 2, 3]

元组

好比咱们要建立第一个元素是 string 和 第二个元素是 number 的数组这样或者相似的场景,咱们就可使用 Tuple

let x: [string, number]
x = ['hello', 10]

重要的是要理解 TS 对类型及其声明的顺序施加了严格的控制,因此,基于上面的定义,下面的代码就会报错。

x = [10, 'hello']  // WRONG

枚举

与其余语言(例如:C 或 C++)同样,TypeScript 也具备用于声明多个常数的枚举类型。跟其余语言不一样的是,TS 的枚举更灵活。

enum Color {
  Red,
  Green,
  Blue
}

let c: Color = Color.Green

枚举从 0 开始,因此 Red = 0 , Green = 1 , Blue = 2 ,不过在 TS 中,你能够经过下面的方式改变顺序:

enum Color {
  Red = 1,
  Green,
  Blue
}

或者给每一个常量分配不一样的数字

enum Color {
  Red = 2,
  Green = 6,
  Blue = 5
}

甚至能够给每一个常量分配字符串类型的值

enum Color {
  Up = "Up",
  Down = "Down",
  Left = "Left",
  Right = "Right"
}

特殊类型

如今,咱们已经知道了如何定义基本类型,可是,在弱类型语言中添增强类型校验会在不少方面产生巨大影响。

例如,咱们正在跟 DOM 进行交互,想从一个 HTML 元素中获取 value。咱们能够指明元素的类型,可是要确保,从元素上获取 value 以前,必须确保它存在。

const elem = document.getElementById('elementId')! as HTMLInputElement

最后的感叹号是告诉 TS,虽然 TS 不能肯定元素上是否存在这个值,可是咱们能够接受这个风险。

另外一个有趣的例子是,当咱们须要指明函数接受到的参数多是字符串或者数字时,换句话说,咱们传参能够是字符串或数字。

对于这个场景,咱们可使用管道符(|)合并全部可能接收的类型:

function combine(a: number | string, b: number | string): void {
  //logic to validate types and perform operations
}

这个管道还能够用来指明做为参数的特殊的字符串

function foo(color: 'yellow' | 'brown'){...}

上面的例子中,函数接收的字符串参数必须是 yello 或 brown 之一。

函数的返回类型也是须要重点关注的,若是咱们想建立一个抛出错误的函数,它的返回值是什么类型的?

就像这个例子, TS 有一种类型叫作:never。这个类型是指不会发生。不过,它常常被用做函数抛出异常。

function error(msg: string): never {
  throw new Error('msg')
}

另外,函数没有返回应该用 void 声明。

function message(msg: string): void {
  console.log('msg')
}

若是不知道数据是什么类型的,咱们可使用 unknown 关键字。在下面的例子中,TypeScript 不会控制它的类型,不过,必须在分配给其余类型前进行类型验证。

let input: unknown

/before assigning it we should check its type
if(typeof input === "string") {
  let name: string = input
}

除了在赋值以前进行类型检查外,咱们还能够给它的类型转换为咱们知道的类型。在 TypeScript 的强制转换以下:

let myinput: unknown
let mylength: number = (<string>myinput).length

或者

let myinput: unknown
let mylength: number = (myinput as string).length

有些状况咱们不想让 TS 进行类型检查,好比,当咱们使用一个不能控制的外部的库,或者咱们须要定义一个有可能返回任意类型的函数。对于这些状况,咱们可使用 any

declare function getValue(key: string): any
const str: string = getValue('test')

接口

跟其余语言相似,接口与定义类型相关,建立接口类型的对象时,必须遵照接口类型的定义。

因此,咱们假设一个函数接收一个 user 对象。在使用它以前咱们能够先建立一个接口来约束对象的结构或者说是规则。

interface User {
  name: string
  age: number
}

function displayPersonalInfo(user: User) {
  console.log(`Name: ${user.name} - Age: ${user.age}`)
}

建立接口时,能够添加一些修饰符,相似 ? 代表属性多是 null,也可使用 readonly 关键字设置一个不可修改的属性。

interface Square {
  color?: string
  width?: number
}

interface Point {
  readonly x: number
  readonly y: number
}

let square: Square = {
  width: 14,
}

顺便说一下,readonly 是一个有趣的关键字,能够应用于其余类型。例如,存在一个 ReadonlyArray定义,可让开发者建立一个元素不能修改的数组。

let a: number[] = [1, 2, 3, 4]
let ronumbers: ReadonlyArray<number> = a

ronumbers[0] = 4 //WRONG! It cannot be assigned

//But it could be used for iterating over its values for reading purposes
for (const num of ronumbers) {
  console.log(num)
}

泛型

最后要重点介绍面向对象语言最关键的特性之一:泛型,在 TypeScript 中也是存在的。

可复用组件是每一种现代强类型语言的基础,引入了强类型语言的 JavaScript 也是如此,咱们必须给开发者一种能够定义对于不一样的类型数据有相同处理逻辑的函数。

对于使用过像是 C++,C#,Kotlin,Java 甚至 Rust 的人来讲,他们对这个概念很是熟悉。

对于其余开发人员,咱们仍是要解释一下,泛型是一种声明数组、类或函数的方法,数组,类或函数在声明过程当中使用了他们不知道的类型。

泛型的用法是一对 <>,中间能够包含任何字母,这些字符在以后的实现逻辑中做为标记,并在定义发生时被实际类型替换。

function myMax<T>(x: T, y: T): T {
  return x > y ? x : y
}

const intMax = myMax<number>(12, 50)

console.log(intMax)

上面的例子中,咱们定义了一个比较两个值并返回最大的那一个的函数,注意,实际类型(number)是在后面才传入的。

总结

咱们能够总结一下 TypeScript ,做为一个静态类型的校验语言,给 JavaScript 这个前端语言增长了一层逻辑让它更健壮。仔细观察,咱们还能够了解到大多数语言是如何添加相似的特性的:函数式编程、lambda 函数,强类型,不可变的变量等。

这是一个好现象,由于它代表了软件行业日趋成熟,而且对于入行开发的新人及后来人来讲会更好。

相关文章
相关标签/搜索