原文连接:TypeScript, why is so important?html
译文连接:TypeScript 为什么如此重要?前端
众所周知的经典编程语言,例如:Pascal、C、C++等都是强类型语言,这就意味着这些语言,必须在编译时设置更严格的类型规则。node
每次你声明变量或函数参数时,必须先明确标明它们的类型,而后再使用。这个概念背后的缘由能够追溯到好久之前,即所谓的为了确保程序有意义的类型理论。es6
硬件没法区分类型。类型能够看做是人抽象出来的东西,可让编程者实现更高层次的思考,让代码更加简洁明了。typescript
此外,从编译器的角度来看,类型还有一些优点,例如:便于优化。在编译阶段进行类型检查可让编译器更有效率的执行机器指令。安全是另外一个重要的考量,强类型系统能够帮助编译器提前发现错误。npm
随着像是 Basic,JavaScript,PHP,Python 等解释型语言新秀的出现,它们都是在运行时进行类型检查。编程者能够不用编译它们的代码,语言变得更灵活智能,能够基于上下文和数据进行类型检测。编程
你们不该该就关于强类型和弱类型孰优孰劣展开一场新争论,咱们必须了解,每一种语言都是基于某个特定的目的被设计创造出来的,没有人会预料到像是 JavaScript 这样的脚本语言会如此流行并普遍的应用于开发商业应用。json
给像 JavaScript 这样的弱类型语言增长强类型的能力,不只能够帮助开发团队写出整洁的自解释代码,并且能解决一个根本问题:在运行时以前的编译阶段捕获类型错误。数组
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 的官方文档也一直把预处理过程叫作编译。
咱们可使用 npm
和 yarn
安装 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"] }
此配置文件按照部分划分,以下咱们能看到一个最基本的配置文件有如下选项:
给 TypeScript 定义新的配置文件后,咱们就能在 src 文件夹下新建多个 TypeScript 文件,而后,咱们只须要在命令行运行 npx tsc
,就能编译文件而且把生成的文件放到输出文件夹。
咱们也能够在 package.json
中指定一个 tsc
任务,甚至可使用 watch
选项让文件在被改动后自动运行 tsc
。
根据你使用的技术和项目类型,能够有多种方式设置 TypeScript ,在本文中,咱们不会展现全部可能的配置方案,你们若是想了解更多的选项,鼓励你们去看一下官方文档。
TypeScript 是一个在软件开发过程当中帮助开发者给数据类型添加更严格的约束的工具。他必须跟其余好的实践一块儿配合例如适当使用 let
或 const
替换 var
进行局部变量声明。
让咱们来回顾一下 TS 提供的类型。
基本类型的声明以下:
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'
数组类型有两种写法:
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 函数,强类型,不可变的变量等。
这是一个好现象,由于它代表了软件行业日趋成熟,而且对于入行开发的新人及后来人来讲会更好。