原文链接:TypeScript, why is so important?
译文链接:TypeScript 为何如此重要?
类型为什么会存在?
家喻户晓的经典编程语言,例如:Pascal、C、C++等都是强类型语言,这就意味着这些语言,必须在编译时设置更严格的类型规定。
每次你申明变量或函数参数时,必须先明确表明它们的类型,而后再应用。这个概念背地的起因能够追溯到很久以前,即所谓的为了确保程序有意义的类型实践。
硬件无奈辨别类型。类型能够看作是人形象进去的货色,能够让编程者实现更高层次的思考,让代码更加简洁明了。
此外,从编译器的角度来看,类型还有一些劣势,例如:便于优化。在编译阶段进行类型查看能够让编译器更有效率的执行机器指令。平安是另一个重要的考量,强类型零碎能够帮忙编译器提前发现错误。
随着像是 Basic,JavaScript,PHP,Python 等解释型语言新秀的呈现,它们都是在运行时进行类型查看。编程者能够不必编译它们的代码,语言变得更灵便智能,能够基于上下文和数据进行类型检测。
回归初心
大家不应该就对于强类型和弱类型孰优孰劣开展一场新争执,咱们必须理解,每一种语言都是基于某个特定的目标被设计发明进去的,没有人会预料到像是 JavaScript 这样的脚本语言会如此风行并宽泛的利用于开发商业利用。
给像 JavaScript 这样的弱类型语言减少强类型的能力,不仅能够帮忙开发团队写出整洁的自解释代码,而且能解决一个基本问题:在运行时之前的编译阶段捕捉类型谬误。
TypeScript 是什么?
JavaScript 是一个解释型或者说动静编译语言,开发人员在运行程序之前不须要编译代码。因为,咱们称 TypeScript 为JavaScript 的类型超集,意思是说它给开发人员提供了一组新的语法,能够给 JavaScript 这种弱类型语言退出类型。
举个例子,当咱们在 JavaScript 中申明一个变量时,是不须要指定类型的。但在 TypeScript 中申明变量就必须指定一个类型,当然你也能够不设置类型间接赋值。
let isDone: booleanlet decimal: numberlet big: bigintlet color: stringlet 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"]}
此配置文件依照局部划分,如下咱们能看到一个最根本的配置文件有以下选项:
- 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 是一个在软件开发过程中帮忙开发者给数据类型增加更严格的束缚的工具。他必须跟其余好的实际一起配合例如适当应用 let
或 const
替换 var
进行局部变量申明。
根底类型
让咱们来回顾一下 TS 提供的类型。
Boolean、Number、String
根本类型的申明如下:
let isDone: boolean = falselet decimal: number = 6let hex: number = 0xf00dlet binary: number = 0b1010let octal: number = 0o744let big: bigint = 100nlet 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 typeif(typeof input === "string") { let name: string = input}
除了在赋值之前进行类型查看外,咱们还能够给它的类型转换为咱们晓得的类型。在 TypeScript 的强制转换如下:
let myinput: unknownlet mylength: number = (<string>myinput).length
或者
let myinput: unknownlet mylength: number = (myinput as string).length
有些状况咱们不想让 TS 进行类型查看,比方,当咱们应用一个不能管制的内部的库,或者咱们须要定义一个有可能返回任意类型的函数。对于这些状况,咱们能够应用 any
。
declare function getValue(key: string): anyconst 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> = aronumbers[0] = 4 //WRONG! It cannot be assigned//But it could be used for iterating over its values for reading purposesfor (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 函数,强类型,不可变的变量等。
这是一个好景象,因为它表明了软件行业日趋成熟,并且对于入行开发的新人及起初人来说会更好。