TypeScrpt学习
# TypeScript 学习
# TypeScript 介绍
TypeScript = Type + Script(标准JS)。我们从TS的官方网站上就能看到定义:TypeScript is a typed superset of JavaScript that compiles to plain JavaScript。TypeScript是一个编译到纯JS的有类型定义的JS超集。 !
# TypeScript 优势
目标:生命周期较长(常常持续几年)的复杂SPA应用,保障开发效率的同时提升代码的可维护性和线上运行时质量。
从开发效率上看,虽然需要多写一些类型定义代码,但TS在VSCode、WebStorm等IDE下可以做到智能提示,智能感知bug,同时我们项目常用的一些第三方类库框架都有TS类型声明,我们也可以给那些没有TS类型声明的稳定模块写声明文件。
从可维护性上看,长期迭代维护的项目开发和维护的成员会有很多,团队成员水平会有差异,而软件具有熵的特质,长期迭代维护的项目总会遇到可维护性逐渐降低的问题,有了强类型约束和静态检查,以及智能IDE的帮助下,可以降低软件腐化的速度,提升可维护性,且在重构时,强类型和静态类型检查会帮上大忙,甚至有了类型定义,会不经意间增加重构的频率(更安全、放心)。
从线上运行时质量上看,我们现在的SPA项目的很多bug都是由于一些调用方和被调用方(如组件模块间的协作、接口或函数的调用)的数据格式不匹配引起的,由于TS有编译期的静态检查,让我们的bug尽可能消灭在编译器,加上IDE有智能纠错,编码时就能提前感知bug的存在,我们的线上运行时质量会更为稳定可控。
类型已经被证明能够提高代码质量和可理解性。大型团队(谷歌、微软、Facebook)不断得出这样的结论。具体地说: 类型增加了重构时的敏捷性。让编译器捕获错误要比让事情在运行时失败好。 类型是最好的文档形式之一。函数签名是一个定理,函数体是证明。
以前总感觉 JS 一复杂,就感觉质量难以保证,线上运行也有点虚,说不定什么时候就爆出一个 's' is undefined, 'b' is not a function 之类的错误。现在有了静态检查,心里更有底了。
静态类型检查最明显的好处是可以尽早的检查出程序中的错误。错误被尽早的检查出来可以使它得到快速的修复,而不是潜伏在代码中,在中期甚至部署上线后才被发现。而且,错误在编译期可以被更精确的定位出来,而在运行时,错误产生的影响在程序出现问题前可能是不容易被发现的
在最近几年,随着V8平台、各大现代浏览器的起来,JS的运行平台再不断完善,但,JS对于大型应用的开发是非常困难的,JS语言设计出来的目的不是为了大型应用,他是一门脚本语言,他没有静态类型校验,但更重要的是,他没有提供大型应用必须的classes、modules/namespaces、interfaces等结构化的装置,中间也出来过GWT等为了其他语言开发者开发大型JS应用的项目,这些项目可以让你利用Java等面向对象语言开发大型Web应用,也可以利用到Eclipse等好用的IDE,但这些项目不是用JS写代码,所以如果你想用JS里的一些东西,你可能要比较费劲的在其他语言里把它给实现出来,所以我们考虑如何增强JS语言,提供如静态类型检查、classes、modules/namespaces、interfaces等大型应用装置,这就是TS语言:*TS是一种开发大型JS应用的语言,更详细一点来说,TS是有类型的编译到纯JS的JS超集。
最新的 vue 三方库 都开始更新 vue3 + ts
# 很多人 用完后····
# ts vs flow
https://github.com/niieani/typescript-vs-flowtype
// 类型推断和提示
// let a = '1'
// a = 5
2
3
- 类型是结构化的
interface Point2D {
x: number;
y: number;
}
interface Point3D {
x: number;
y: number;
z: number;
}
var point2D: Point2D = { x: 0, y: 10 }
var point3D: Point3D = { x: 0, y: 10, z: 20 }
function iTakePoint2D(point: Point2D) { /* do something */ }
iTakePoint2D(point2D); // exact match okay
iTakePoint2D(point3D); // extra information okay
iTakePoint2D({ x: 0 }); // Error: missing information `y`类型是结构化的
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
model
export default class CheckBill {
id: Nullable<string>
shop: Nullable<string>
number: Nullable<string>
created: Nullable<Date>
creator: Nullable<string>
createMethod: Nullable<string>
profitQty: number = 0
costAmount: number = 0
saleAmount: number = 0
remark: Nullable<string>
}
2
3
4
5
6
7
8
9
10
11
12
# vue 2使用
在 Vue2.x 使用过 TypeScript 的掘友肯定知道引入 TypeScript 不是一件简单的事情:
要用 vue-class-component 强化 vue 组件,让 Script 支持 TypeScript 装饰器 用 vue-property-decorator 来增加更多结合 Vue 特性的装饰器 引入 ts-loader 让 webpack 识别 .ts .tsx 文件 https://github.com/vuejs/vue-class-component/issues
# ts 使用成本
# 重点语法介绍
- 接口声明
- 可选与默认参数
function buildName(firstName: string, lastName?: string) { //lastName为可选参数
if (lastName)
return firstName + " " + lastName;
else
return firstName;
}
2
3
4
5
6
3.可选属性
interface SquareConfig { //定义了两个可选属性
color?: string;
width?: number;
}
2
3
4
4.枚举
enum Color {Red = 1, Green = 2, Blue = 4}
let c: Color = Color.Green;
2
- 抽象类
abstract class Animal {
eat (food: string): void {
console.log(`呼噜呼噜的吃: ${food}`)
}
abstract run (distance: number): void
}
class Dog extends Animal {
run(distance: number): void {
console.log('四脚爬行', distance)
}
}
const d = new Dog()
d.eat('嗯西马')
d.run(100)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- 装饰器 Javascript规范里的装饰器目前处在 建议征集的第二阶段,也就意味着不能在原生代码中直接使用,浏览器暂不支持。
- 函数
// 返回类型注解
function foo(sample: Foo): Foo {
return sample;
}
2
3
4
- 可选参数和默认参数
function foo(bar: number, bas?: string, cc: number = 5): void {
// ..
}
foo(123);
foo(123, 'hello');
2
3
4
5
6
- 函数重载
function padding(all: number);
function padding(topAndBottom: number, leftAndRight: number);
function padding(top: number, right: number, bottom: number, left: number);
2
3
# tsconfig.json
{
"compilerOptions": {
/* 基本选项 */
"target": "es5", // 指定 ECMAScript 目标版本: 'ES3' (default), 'ES5', 'ES6'/'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'
"module": "commonjs", // 指定使用模块: 'commonjs', 'amd', 'system', 'umd' or 'es2015'
"lib": [], // 指定要包含在编译中的库文件
"allowJs": true, // 允许编译 javascript 文件
"checkJs": true, // 报告 javascript 文件中的错误
"jsx": "preserve", // 指定 jsx 代码的生成: 'preserve', 'react-native', or 'react'
"declaration": true, // 生成相应的 '.d.ts' 文件
"sourceMap": true, // 生成相应的 '.map' 文件
"outFile": "./", // 将输出文件合并为一个文件
"outDir": "./", // 指定输出目录
"rootDir": "./", // 用来控制输出目录结构 --outDir.
"removeComments": true, // 删除编译后的所有的注释
"noEmit": true, // 不生成输出文件
"importHelpers": true, // 从 tslib 导入辅助工具函数
"isolatedModules": true, // 将每个文件作为单独的模块 (与 'ts.transpileModule' 类似).
/* 严格的类型检查选项 */
"strict": true, // 启用所有严格类型检查选项
"noImplicitAny": true, // 在表达式和声明上有隐含的 any类型时报错
"strictNullChecks": true, // 启用严格的 null 检查
"noImplicitThis": true, // 当 this 表达式值为 any 类型的时候,生成一个错误
"alwaysStrict": true, // 以严格模式检查每个模块,并在每个文件里加入 'use strict'
/* 额外的检查 */
"noUnusedLocals": true, // 有未使用的变量时,抛出错误
"noUnusedParameters": true, // 有未使用的参数时,抛出错误
"noImplicitReturns": true, // 并不是所有函数里的代码都有返回值时,抛出错误
"noFallthroughCasesInSwitch": true, // 报告 switch 语句的 fallthrough 错误。(即,不允许 switch 的 case 语句贯穿)
/* 模块解析选项 */
"moduleResolution": "node", // 选择模块解析策略: 'node' (Node.js) or 'classic' (TypeScript pre-1.6)
"baseUrl": "./", // 用于解析非相对模块名称的基目录
"paths": {}, // 模块名到基于 baseUrl 的路径映射的列表
"rootDirs": [], // 根文件夹列表,其组合内容表示项目运行时的结构内容
"typeRoots": [], // 包含类型声明的文件列表
"types": [], // 需要包含的类型声明文件名列表
"allowSyntheticDefaultImports": true, // 允许从没有设置默认导出的模块中默认导入。
/* Source Map Options */
"sourceRoot": "./", // 指定调试器应该找到 TypeScript 文件而不是源文件的位置
"mapRoot": "./", // 指定调试器应该找到映射文件而不是生成文件的位置
"inlineSourceMap": true, // 生成单个 soucemaps 文件,而不是将 sourcemaps 生成不同的文件
"inlineSources": true, // 将代码与 sourcemaps 生成到一个文件中,要求同时设置了 --inlineSourceMap 或 --sourceMap 属性
/* 其他选项 */
"experimentalDecorators": true, // 启用装饰器
"emitDecoratorMetadata": true // 为装饰器提供元数据的支持
}
}
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# d.ts
用 ts 写的模块在发布的时候仍然是用 js 发布,这就导致一个问题:ts 那么多类型数据都没了,所以需要一个 d.ts 文件来标记某个 js 库里面对象的类型 然后 typings 就是一个网络上的 d.ts 数据库
# lib.d.ts
当你安装 TypeScript 时,会顺带安装一个 lib.d.ts 声明文件。这个文件包含 JavaScript 运行时以及 DOM 中存在各种常见的环境声明。
- 它自动包含在 TypeScript 项目的编译上下文中;
- 它能让你快速开始书写经过类型检查的 JavaScript 代码。
# 从js 迁移 ts
- 添加一个 tsconfig.json 文件;
- 把文件扩展名从 .js 改成 .ts,开始使用 any 来减少错误;
- 开始在 TypeScript 中写代码,尽可能的减少 any 的使用;
- 回到旧代码,开始添加类型注解,并修复已识别的错误;
- 为第三方 JavaScript 代码定义环境声明。
# 老项目
对于老项目,由于TS兼容ES规范,所以可以比较方便的升级现有的JS(这里指ES6及以上)代码,逐渐的加类型注解,渐进式增强代码健壮性。迁移过程:
npm全局安装typescript包,并在工程根目录运行tsc --init,自动产生tsconfig.json文件。 默认的3个配置项:更多配置项说明
"target":"es5": 编译后代码的ES版本,还有es3,es2105等选项。 "module":"commonjs":编译后代码的模块化组织方式,还有amd,umd,es2015等选项。 "strict":true:严格校验,包含不能有没意义的any,null校验等选项。
初始化得到的tsconfig.json无需修改,增加"allowJs": true选项。
配置webpack配置,增加ts的loader,如awesome-typescript-loader。(如果是基于atool-build来构建的项目,则它内置了ts编译,这步省略) loaders: [ { test: /.tsx?$/, loader: "awesome-typescript-loader" } ]
此时你可以写文件名为ts和tsx(React)后缀的代码了,它可以和现有的ES6代码共存,VSCode会自动校验这部分代码,webpack打包也没问题了。
逐渐的,开始打算重构以前的ES6代码为TS代码,只需将文件后缀改成ts(x)就行,就可以享受TS及IDE智能感知/纠错带来的好处。
# 新项目
vue 项目可以通过 vite 创建 vue 的ts项目
yarn create @vitejs/app
# yarn
yarn create @vitejs/app my-vue-app --template vue
2
3
# 参考
https://juejin.cn/post/6844903497205448711 https://www.typescriptlang.org/docs/handbook/decorators.html#decorators https://jkchao.github.io/typescript-book-chinese/