学习typeScript (第三周 )
1.开发环境搭建
1.1 安装node地址:https://nodejs.org/zh-cn/
1.2 使用npm全局安装typescript window+ R => npm i -g typescript
1.3 使用tsc 对 typescript 进行编译成 js tsc xxx.ts
1.4 运行ts 可以全局安装 ts-node =>npm i -g ts-node
1.5 运行ts文件 ts-node xxx.ts
ts 可以编译任意版本的 js // 确保兼容
如果变量的声明和赋值是同时进行的(变量声明可以省略),TS可以自动对变量进行检测
let c = false c = true c === 123 // 报错
2.TS 的类型说明
2.1 声明变量为number 类型
let a :number = 1 a = '222' // 会报错,但是可以编译 js文件
2.2 声明变量为 String 类型
let a:string = '我是小仙女'
2.3 声明变量为 boolean类型
let a:boolean = true
2.4 为函数声明默认值并指定类型
在函数后面跟:number 是规定函数返回值的类型
function sum(a:number,b:number):number{ return a+b return a + '2222'// 报错 } let res = let res = sum(1,1) // 代码提示 res:number console.log(sum(1,1)) // 传参多少都会报错
2.5 字面量进行类型声明 (联合类型)
| 代表或的意思 a 的值只能是下面定义的 不能进行修改 let a :'小仙女' | '大帅哥' a = '小仙女' a = '大帅哥' let b :boolean | string | number b = false b = '小仙女' b = 1
2.6 any 表示任意类型 一个百年来设置any 后相当于对该变量关闭了ts 的类型检测 (不建议使用)
声明变量如果比指定类型,则ts解析器会自动判断变量的类型为any (隐式的any)
let b; // 要避免使用 let b :any // 要避免使用 b = 1 b = '小仙女' b = false 注:any 值它可以赋值给任意变量 let s = string s = b console.log(c) // false
2.7 unknown 未知类型的值
unknown 实际上就是一个类型安全的any
unknown 类型的变量,不能直接赋值给其他变量
类型断言 语法:1 : 变量 as 类型 2.<类型> 变量
let b :unknown b = 1 b = 'aaa' b = false let s : string b = 'wwww' s = b // 报错不能将类型 unknown 分配给 类型 string // 解决办法1 if (typeof b === 'string') { s = b } // 解决办法2 类型断言 可以用来告诉解析器变量的实际类型 s = b as string s = <string> b
2.8 void 用来表示空 以函数为例 就表示没有返回值 空值 或 undefiend
function fn() :void{ return 123 // 报错 }
2.9 never 表示永远不会返回结果
3.0 Object 定义对象
let a :object a = {} a = function name() { } 这种声明不常用 因为 在js 中 万物皆对象
{} 用来指定对象张包含那些属性
语法:{属性名:属性值,属性名:属性值}
在属性名后面加?,代表属性是可选的
let b: { name: string, age: number } b = { name: '小仙女', age: 12 ,like:'男' } // 报错 因为在 b中没有声明 like 属性 let b: { name: string, age: number,like?:string } ? 代表可选 但是 不能 每一个参数都这样赋值 b = { name: '小仙女', age: 12 } let b: { name: string, [prpName:string]:any } // 代表name 必有属性 后面都是可选的属性 b = {name:'122',age:12,num:'1111',like:undefined}
3.1 定义 函数
设置函数结构的类型说明 语法:(形参:类型) =>返回值
let b: (a: number, b: number) => number b = function (n: number, n2: number) { return n + n2 }
3.2 数组 array
表示方式 1 类型[] 2.Array<类型>
let s :string[] s = ['1','2','3'] let n :number[] n = [12,3,4] let a :Array<string> a = ['1','1'] let f :Array<number> // => any 尽量不要用 f = [1,2,3]
3.3 元组 tuple 就是固定长度的数组
let b :[string,number] b = ['1',3] // 不能多不能少
注意:函数中的泛型
// 泛型 函数中的 泛型 function join<T>(first:T,second:T) { console.log(first,second); } join<string>('nsskjs','2') // 指定类型 join<number>(1,2) 两个 参数 function join<T,N>(first:T,second:N) { console.log(first,second); } join<string,number>('nsskjs',2)
泛型中数组的使用
泛型中数组的使用 2种 写法
function addnum(params:T[]) { console.log(params); } addnum(['1','2']) // 第二种 function addnum<T>(params:Array<T>) { console.log(params); } addnum<string>(['1','2'])类中使用泛型
class SlectGirl{ constructor(private girls:string[]){} getGril(index:number):string { return this.girls[index] } } const slectGirl = new SlectGirl <string> (['小红','小丽','小静']) console.log(slectGirl.getGril(1)); // 小丽泛型中的继承
interface girl { name:string; age:number } class GirlName <T extends girl>{ constructor(private girl: T[]) {} getAge(index:number) : string | number { return this.girl[index].name + '年龄是' + this.girl[index].age } } const Girl = new GirlName ( [ {name:'小红',age:15}, {name:'小丽',age:16}, {name:'小静',age:17} ] ) console.log(Girl.getAge(2));
先声明一个接口,让泛型进行继承,就必须要求传递肥肉参数要有 name 属性,getAge 返回参数就是接口参数格式
// 泛型的约束 extends
class GirlName <T extends string | number>{ // 约束泛型 必须是 string 或者 number constructor(private girl: T[]) {} getAge(index:number) : T { return this.girl[index] } } const Girl = new GirlName <string>( [ '小红','小丽','小静' ] ) console.log(Girl.getAge(2));
3.4 枚举 就是列车 某一个值 可能存在的情况
enum Status { like = 1, age, name, } function getStatus(status:any) { if (status === Status.like) { return '我喜欢ni' } else if (status === Status.age) { return '我年龄18岁' } else if (status === Status.name) { return '我的名字是关关' } } console.log(getStatus(0)); // 我喜欢ni console.log(getStatus(Status.age)); // 我年龄18岁 console.log(Status.like); // 0 默认从0 开始 不想从0 开始需要 将上面的赋值 改成 1 后 下标就从 1开始了
3.5 补充 & 要同时满足
let b :{name:string} & {age:number} b = {name:"小仙女",age:12}
类型的别名
type myType = 1 | 2 | 3 | 4 | 5 let k : myType let j : myType
3.TS 编译选项
- tsc XXX.ts -w // -w 代表watch 但是编译会有时间间隔
只能监视一个文件,文件多,需要监视多文件,不利于开发 - 新建一个 tsconfig.json 在执行 tsc -w 进行所有文件监听
编译选项{ /* tsconfig.json 是ts 编译器的配置文件,ts编译器可以根据它的信息碎代码进行编译 根目录的 “include” : 用来指定哪些ts 需要被编译 路径:** 表示任意目录 * 表示任意文件 "exclude" : 不需要被编译的文件目录 默认值:["node_modules","bower_components","jspm_packages"] extends:定义被继承的配置文件 eg: 'extends':'./config/base' files: 指定被编译文件的列表,只有需要编译的文件少时才会用到 'files':["a.ts","b.ts"] */ "include": [ "./src/**/*" ], // 重要 "compilerOptions": { // target 用来指定ts 被编译为 ES 的版本 ESnext 最新版本 // 可以编译 为es3', 'es5', 'es6', 'es2015', 'es2016', 'es2017', 'es2018', 'es2019', 'es2020', 'esnext "target": "es5", "module": "es2015", //module可选 'none', 'commonjs', 'amd', 'system', 'umd', 'es6', 'es2015', 'es2020', 'esnext' // "lib": [''] // 项目中 用到的库 // 可选 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'es2019', 'es2020', 'esnext', 'dom', 'dom.iterable', 'webworker', 'webworker.importscripts', 'webworker.iterable', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'es2018.asyncgenerator', 'es2018.asynciterable', 'es2018.intl', 'es2018.promise', 'es2018.regexp', 'es2019.array', 'es2019.object', 'es2019.string', 'es2019.symbol', 'es2020.bigint', 'es2020.promise', 'es2020.sharedmemory', 'es2020.string', 'es2020.symbol.wellknown', 'es2020.intl', 'esnext.array', 'esnext.symbol', 'esnext.asynciterable', 'esnext.intl', 'esnext.bigint', 'esnext.string', 'esnext.promise', 'esnext.weakref'. "outDir": "./dist", // 编译后的当文件的目录 // "outFile": "./dist/index.js", // 全局作用域的代码会合并到同一个文件中 "allowJs": true, // 是否对 js 文件进行编译 默认是 false "checkJs": true, // 是否检测js 代码是否符合语法规范 默认是false "removeComments": true, // 是否移除注释 编译后 "noEmit": false, // 不生成编译文件 "noEmitOnError": false, // 编译有错误的时候 不生成编译后文件 "strict": false, // 所有严格格式下总开关 如果设置 true 下面可以不写 "alwaysStrict": false, // 用来设置编译后的文件是否使用严格模式,默认false "noImplicitAny": true, // 不允许 隐式any "noImplicitThis": true, // 不允许不明确的 this "strictNullChecks": true // 严格下检查 空值 下面的例子 box 不存在 可以做判断 if (box !== null) { 方法} // let box = document.getElementById('#box') //box.addEventListener('click',function () { // alert(1111) //}) }
4.使用webpack 打包typescript
41. 创建一个文件夹 在目录下 npm init -y 初始化文件
42. 然后 cnpm i -D webpack webpack-cli typescript ts-loader (ts-loader ts加载器,将webpack和ts整合) (typescript核心包) (webpack-cli 命令行工具) (webpack核心代码) (-D安装开发依赖 全写 --save dev)
43. 在跟目录新建一个 webpack.config.js
// webpack 中得所有配置文件都要写在这里 module.exports
module.exports = { 1.首先要引入一个node path 用来连接路径 const path = require ('path') const HtmlWebpackPlugin = require('html-webpack-plugin') 2.指定入口文件 entry: 'src/index.ts', 3.指定出口文件 output:{ // 指定打包出口文件夹 path: path.resolve(__dirname,dist) // 打包后得文件 filename: 'bundle.js', // 告诉webpack 不要使用 箭头函数 environment:{ arrowFunction:false } }, // 指定webpack打包时要使用得模块 module:{ // 指定要加载得规则 rules:[ // 指定规则生效得文件 test:/\.ts$/, // 使用losder use:'ts-loader', // 不使用得文件 exclude: /node-modules/ ] }, // 配置webpack插件 plugins:[ new HtmlWebpackPlugin({ template:'./src/index.html' //位置可以任意 }) ], // 用来告诉文件 哪些文件可以当作模块引用 用来设置引用模块 resolve: { extensions: ['.ts', '.js'] } }
上述如果想在 网页中浏览得话,需要在根目录新建html 使用script 标签引入 ,但是这样如果有100 个ts 文件怎么办?,这时候就需要安装一个webpack 插件,项目中有多少js css 等等,进行自动引入,则需要安装 cnpm i -D html-webpack-plugin 作用帮助我们自动生成 html 文件
plugins:[ new HtmlWebpackPlugin( { // title:'小仙女文件' template:'./src/index.html' //位置可以任意 } ) ]
引入后 执行npm run build 这时候就会发现 dist 文件下 多出一个index.html 查看html 会发现script标签自动引入了bundle.js
相关改 html title的话 上述,但是设置是实现了自定义,但是网页比较复杂,不能一个一个去自定义,所有这时候需要一个模板,在配置中添加一个模板,并指定文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>我是小仙女</title> </head> <body> <div>我是一个王爷</div> </body> </html> // 执行 npm run build 查看 dist -> index.html <!doctype html> <html lang="en"><head><meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width,initial-scale=1"> <title>我是小仙女</title><script defer="defer" src="bundle.js"></script> </head> <body><div>我是一个王爷</div></body> </html>
想要打开文件 vs code 右键直接在浏览器中打开运行,但是我们有一个更加灵活的方式,我们的浏览器和服务器是有联系的,当我们修改代码,浏览器自动刷新更新内容。需要安装 插件 cnpm i -D webpack-dev-server
需要在 package.json 进行配置 "start": "webpack serve --open chrome.exe" 意思为
意思为 打开webpack 服务 并且打开谷歌浏览器 npm start
但是现在我们每次编译,其实是不会删去原来 dist 文件下的文件内容的,如果生成新的文件,是在原来的基础上进行替换的,这样回带来一些不必要的问题,所以需要每次编译前,先把 dist 目录下的文件内容先清空。在把新文件放进去,这样确保,每次构建,都是最新的文件,避免存在旧文件的情况。cnpm i -D clean-webpack-plugin
和index.ts 同级新建一个 data.ts 在data.ta 暴露一个常量,在index.ts 引入并使用,在运行 build 会发出报错
是因为 data.ts 是一个模块,拓展名是 .ts, 在webpack中是不知道,.ts 可以当作模块使用的,所以需要告诉,webpack 哪些文件能当作模块被使用,及引用的,和 plugins 同级 resolve :{extensions:['.ts','.js']},如上展示 在bundlr.js 就可以看到
上述初步的配置是完成,但是在bundlr.js会发现 是以箭头函数运行,一些浏览器是不兼容的,下面重点学习loader,低浏览器兼容。但是这时候你会问,webpack.config.js 中的 target不是可以指定ts代码编译的版本么,但是有些功能 ts 里面是不具备的,target 里面的转换只是进行简单的语法转换,但是对一些复杂的功能,比如es6 promise 通过语法的转换,是转不过去的。就需要工具去解决。
5.babel 学习
babel,新语法转换旧语法,还可以新的技术,新的类等,在浏览器中不支持的,通过一些方式让他进行支持。
安装依赖 cnpm i -D @babel/core @babel/preset-env babel-loader core.js (core 核心工具 ) (preset-env 预先设置的开发环境 比如 谷歌 火狐等开发环境) (babel-loader 将babel 和 webpack 结合的工具) (core.js 模拟js 运行环境)
开始改 webpack.config.js 在model 模块进行修改,因为都是对 .ts 文件生效,所以不需要在增加一个规则,之前只用一个 ts-loader 加载器,现在需要两个加载器 增加一个 babel 所以把 use 改成 [],加载器的执行顺序谁在后面先执行谁。所以需要把 ts-loader 放在后面,需要把 ts 转 js 在用babel 转低版本的 js。
rules: [ { test: /\.ts$/, // 指定规则生效的文件 use: [ { loader: "babel-loader, //设置配置项 // options 里面配置了 我这边 出错了,具体什么原因 不太了解 options: { presets: [ [ // 配置指定环境插件 "@babel/preset-env", // 配置信息 { // 要兼容的目标浏览器 targets: { "chrome": "86", // 高版本 "chrome": "56", // 高版本 "ie":"11" }, //指定corejs的版本 usage 按需加载 "corejs": "3", "useBuiltIns": "usage" } ] ] } }, // 复杂版 可以进行配置 //谁写在后面谁先执行 执行顺序从后往前执行 所以需要将ts转 js 在将 新js 转 旧版 js 'babel-loader' // 不需要配置 可以这样写 'ts-loader', ], // 要使用loader exclude: / node - modules/ } ]
ts 文件 没有用babel
const obj = { name:'孙悟空', age:19 } obj.age = 80 console.log(obj);
打包后
*ts 文件 用babel 发现打包后还是 const 是因为上面写的targets 兼容浏览器是 86 版本的 是支持 const *
*打包后是 var *
在 ts 中简单 console.log(promise)
接下来说 core.js ,比如说 ts 文件中使用 promise 但是 ie11 不支持特殊类的,即使用了 babel 但是老的版本还是不能使用,这时候就需要 core.js ,这时候 引入 core.js 中的 promise ,供 ie11 使用,这时候你就会问,那是不是ie 11就可以使用了。这是为啥啊?我们打开 ie 看
看到 bundel.js 第4个字符出错。发现第4个字符是箭头函数,自调用的形式。创建了一个作用域,防止代码之前互相干扰。即使最简单代码打包后也是 箭头函数,也会报错。
但是在 ts 文件中写箭头函数,打包后,也是会编译成普通的函数。
为什么最外层打包后编译比成普通函数呢?这个箭头函数不是我们手写的,是webpack自动生成的,没有经过babel,可能是webpack 已经不想兼容 ie 了,这时候我们怎么办呢?所以我们的告诉 webpack 我们需要兼容老的浏览器。 需要在
output: { // 指定打包出口文件夹 path: path.resolve(__dirname, 'dist'), // 打包后文件的文件 filename: 'bundle.js', // 告诉webpack 不要使用 箭头函数 重要 environment:{ arrowFunction:false } },