从0到1构建typescript项目
静态强类型语言与动态弱类型语言
- 静态强类型语言
- 对类型极度严格
- 及时发现错误
- 运行时性能好
- 自文档化
- 动态弱类型语言
- 对类型非常放松
- Bug发现不及时(可单元测试发现)
- 运行时性能差(可通过引擎改善)
- 可读性差 (文档可以通过工具生成)
- 更具灵活性
构建TypeScript项目
预装软件 : nodeJs & VSCode
体验typescript
- 新建文件夹 ts_in_action
- 执行 npm init -y 初始化项目
- 执行 npm i typescript -g 安装ts
- 执行 tsc --init 创建ts配置项( tsc -h 查看帮助信息)
此时目录结构
- 新建 src 目录
- 在src目录下创建 index.ts
// index.ts let hello : string = 'Hello World!'
- 执行 tsc ./src/index.ts 编译 index.ts 文件
- 此时同目录下生成了 index.js 文件
// index.js var hello = 'Hello World!'; // :string 类型注解
配置typescript
-
安装构建工具 npm i webpack webpack-cli webpack-dev-server -D
-
ts_in_action下,新建 build 目录
-
拷贝四个配置文件
- webpack.base.config.js
// 公共环境的配置 const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { // 指定入口文件 entry: './src/index.ts', output: { // 输出文件名,输出目录默认disk filename: 'app.js', }, resolve: { // 指定扩展名 extensions: ['.js', '.ts', '.tsx'], }, module: { rules: [ { // 安装ts-loader : npm i ts-loader typescript -D // ts-loader正则,以ts或tsx结尾的文件 test: /\.tsx?$/i, use: [{ loader: 'ts-loader', }], // 排除 exclude: /node_modules/, }, ], }, plugins: [ // 插件,通过模板生成网站首页,把输入文件嵌入,需要安装 // npm i html-webpack-plugin -D new HtmlWebpackPlugin({ template: './src/tpl/index.html', }), ], };
- webpack.config.js
// 所有配置文件的入口 // npm i webpack-merge -D // const merge = require('webpack-merge'); 失效,替换如下 var { merge } = require('webpack-merge'); //引入其他配置文件 const baseConfig = require('./webpack.base.config'); const devConfig = require('./webpack.dev.config'); const proConfig = require('./webpack.pro.config'); // 判断当前环境变量 let config = process.NODE_ENV === 'development' ? devConfig : proConfig; // 合并 module.exports = merge(baseConfig, config);
- webpack.dev.config.js
// 开发环境的配置 module.exports = { devtool: 'cheap-module-eval-source-map', };
- webpack.pro.config.js
// 生产环境的配置 // npm i clean-webpack-plugin -D // 每次成功构建后,清空disk目录,避免缓存的无用文件 const { CleanWebpackPlugin } = require('clean-webpack-plugin'); module.exports = { plugins: [ new CleanWebpackPlugin(), ], };
- webpack.base.config.js
-
执行 npm i ts-loader typescript -D 安装ts-loader,并再次本地安装typescript
-
执行 npm i html-webpack-plugin -D 安装插件,通过模板生成网站首页,把输入文件嵌入
-
编写模版文件,在src目录下创建 tpl 目录,再创建 index.html 文件
<!-- index.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>hello ts</title> </head> <body> <div class="app"></div> </body> </html>
-
执行 npm i clean-webpack-plugin -D ,每次成功构建后,清空disk目录,避免缓存的无用文件
-
执行 npm i webpack-merge -D 安装webpack-merge
-
打开package.json
- 修改入口文件
"main": "./src/index.ts",
- 编写启动开发环境的命令
"scripts": { "start": "webpack-dev-server --mode=development --config ./build/webpack.config.js", "test": "echo \"Error: no test specified\" && exit 1" },
- 修改入口文件
-
执行npm start
-
在浏览器输入 localhost:8080
将index.ts中字符串插入到页面中
-
编写index.ts
let hello : string = 'Hello World!' document.querySelectorAll('.app')[0].innerHTML = hello;
构建生产环境的脚本
-
在package.json中修改
"scripts": { "start": "webpack-dev-server --mode=development --config ./build/webpack.config.js", "build": "webpack --mode=production --config ./build/webpack.config.js", "test": "echo \"Error: no test specified\" && exit 1" },
-
执行 npm run build
-
此时目录结构
typescript的数据类型
- ES6的数据类型
- Boolean
- number
- string
- array
- function
- object
- symbol
- undefined
- null
- typescript的数据类型(完全覆盖ES6)
- Boolean
- number
- string
- array
- function
- object
- symbol
- undefined
- null
- void
- any
- never
- 元组
- 枚举
- 高级类型
类型注解
- 作用: 相当于强类型语言中的类型声明
- 语法: (变量/函数):type
回到工程中
-
在src下新建 datatype.ts
// 原始类型 let bool: boolean = true let num: number = 123 let str: string = 'abc' // str = 123 //报错,数据类型不可以改变 // 数组 let arr1: number[] = [1, 2, 3] let arr2: Array<number> = [1, 2, 3] // 联合类型 let arr3: Array<number | string> = [1, 2, 3, '4'] // 元组 特殊数组 限定元素类型和个数 let tuple1: [number, string] = [0, '1'] tuple1.push(2) // 不报错,允许添加新元素,不建议这样使用 // tuple1[2] // 报错,不允许访问 // let tuple2: [number, string] = [1, '1'] //报错 // let tuple2: [number, string] = [0, '1', 1] //报错 // 函数 let add1 = (x: number, y: number): number => x + y let add2 = (x: number, y: number) => x + y //省略函数返回类型,ts具有类型推断功能 let compute: (x: number, y:number) => number //函数类型,没有具体实现 compute = (a, b) => a + b; //实现函数类型 // 对象 let obj1: object = {x: 1, y: 2} // obj1.x = 3 //报错,不允许 let obj2: {x: number, y: number} = {x: 1, y: 2} obj2.x = 3 // symbol 具有唯一的值 let s1: symbol = Symbol() let s2 = Symbol() console.log(s1 === s2) //false // undefined, null 只能赋值为本身 let un: undefined = undefined let nu: null = null // 在tsconfig.js中设置"strictNullChecks": false 或 使用联合类型 允许↓ // num = undefined // num = null // void let noReturn = () => {} // any 除特殊情况不建议使用 let x x = 1 x = [] x = '' x = () => {} // never let error = () =>{ throw new Error('error') } let endless = () =>{ while(true){} }
-
在index.js中引入
import './datatype'
typescript枚举类型
使用枚举类型可以提高程序可读性,以不变应万变。
-
枚举: 一组有名字的常量集合
- 例如手机中的通讯录 姓名+号码
-
在src下新建 enum.ts
// 数字枚举 enum Role { Reporter, Developer, Maintainer = 10, Owner, Guest } // 既可以用过名字索引,又可以通过值索引(反向映射) console.log(Role.Reporter) //0 console.log(Role.Developer) //1 console.log(Role.Maintainer) //10 console.log(Role.Developer) //11 // 字符串枚举 名称索引 enum Message { Success = '成功', Fail = '失败' } // 异构枚举 不建议使用 enum Answer { N, Y = 'yes' } // 枚举成员 性质 // Role.Reporter = 2 //报错,只读,不可修改 enum Char { // const a, b = Char.a, c = 1 + 3, //会在编译时计算出结果 // computed 需要计算的枚举成员,不会再编译阶段进行计算,运行时计算 d = Math.random(), e = '123'.length, // f //在computed后的成员需要被赋值 } // 常量枚举 会在编译时被移除 当不需要对象而需要对象的值的时候可以使用 const enum Month{ Jan, Feb, Mar } let month = [Month.Jan, Month.Feb, Month.Mar] //此时常量枚举会被替换为常量 // 枚举类型 enum E {a, b} enum F {a = 0, b = 1} enum G {a = 'apple', b = 'banana'} let e: E = 3 let f: F = 3 // e === f //报错,不允许不同类型进行比较 let e1: E.a = 1 let e2: E.b // e1 === e1 //报错 let e3: E.a = 1 e1 === e3 let g1: G = G.b let g2: G.a = G.a
typescript重要概念:接口
接口可以用来约束对象、函数、类的结构和类型。
代码协作的契约,不可改变。
对象类型接口
-
在src下新建 interface.ts
// interface.ts 对象类型接口 interface List { //成员 readonly id: number; // readonly 只读属性 name: string; // [x: string]: any; //字符串索引签名,用任意字符串去索引List,可以得到任意结果 age?: number // ? 可选属性 } interface Result { //成员的取值为 List数组 data: List[] } // 渲染函数 function render(result: Result){ // 遍历 List result.data.forEach((value) => { //打印 List中的 id, name console.log(value.id, value.name) //新需求 if(value.age){ console.log(value.age) } // value.id++ //只读属性不允许修改 }) } // 假设从后端接收到的数据 let result = { data: [ {id:1, name: 'A', sex: 'male'}, //允许字段超出定义,满足必要条件即可 {id:2, name: 'B'} ] } render(result) // 如果直接传入字面量,ts会对额外字段进行类型检查 render( { data: [ {id:1, name: 'A', sex: 'male'}, {id:2, name: 'B'}, {id:3, name: 'B', age: 10} ] } as Result //使用 类型断言/字符串索引签名 绕过类型检查 ) // 用数字索引的接口 interface StringArray { [index: number]: string //用任意数字索引StringArray,都会得到stirng } let chars: StringArray = ['A', 'B'] interface Names { [x: string]: string, //用任意字符串去索引Names,都会得到string // y: number //不允许 [z: number]: string , // [a: number]: number // 不允许,与string不兼容 }
函数类型接口
-
在 interface.ts 中追加
// interface.ts 函数类型接口 let add3: (x: number, y: number) => number // 定义函数类型接口 interface Add1 { (x: number, y: number): number } // 使用类型别名 为函数起一个名字 type Add2 = (x: number, y: number) => number // 实现具体函数 let add4: Add2 = (a, b) => a + b // 混合类型接口 既可以定义函数,有可以有属性或者方法 interface Lib { //函数 (): void; //属性 version: string; //方法 doSomething(): void; } //实现接口 封装到方法,可以创建多个实例 function getLib(){ let lib: Lib = ( () => {} ) as Lib; //使用类型断言,因为我们明确知道lib就是我们需要的 lib.version = '1.0'; lib.doSomething = () => {} return lib; } let lib1 = getLib(); lib1(); lib1.doSomething(); lib1.version; let lib2 = getLib();