typescript 入门
typescript 是 JavaScript 的超集,熟悉 JavaScript 的人都知道JavaScript 是弱类型语言,比如声明一个变量可以为这个变量赋任意类型的值,后面可能会产生未知错误很难排查,而 ts 是强类型语言,重点就在其类型上面,可以说它是加强升级版的 js ,弥补 js 的不足
typescript 环境搭建
-
下载并安装 node
官网地址: https://nodejs.org/zh-cn/download/ (安装64位或者32位 看自己电脑配置)
检查是否安装成功: 任意位置打开命令行窗口输入
node -v
能够看到版本号就说明安装成功 -
使用 npm 全局安装 typescript
在命令行窗口 输入
npm i -g typescript
检查是否安装成功:命令行输入 tsc 如果出来很长一堆东西 说明安装成功
-
创建一个 ts 文件
-
使用 tsc 对 ts 文件进行编译
- 进入命令行窗口
- 在 ts 文件所在目录路径下
- 执行命令:tsc xxx.ts 即可
- 在同一位置会出现一个 js 文件说明编译成功
以上工作都做完就可以写自己的 ts 项目了
基本类型
-
类型声明
-
类型声明是TS非常重要的一个特点
-
通过类型声明可以指定TS中变量(参数,形参)的类型
-
指定类型后,当为变量赋值时,TS编译器会自动检查值是否符合类型声明,符合则赋值,否则报错
-
简而言之,类型声明给变量设置了类型,使得变量只能存储某中种类型的值
-
语法:
let 变量: 类型 let 变量: 类型 = 值 function fn(参数: 类型, 参数: 类型): 类型 { ..... }
-
-
类型用法示例
let a: number a = 20 // 这一行不会报错 a = 'Hello' // 这一行会出现报错 // 不能将类型“string”分配给类型“number”。 let b: string b = 'Hello' // 这一行不会报错 b = 123 // 这一行会出现报错 // 不能将类型“number”分配给类型“string”。 let c: boolean c = true // 这一行不会报错 c = 14 // 这一行会出现报错 // 不能将类型“number”分配给类型“boolean”。 c = 'Hello' // 这一行也会出现报错 // 不能将类型“string”分配给类型“boolean”。 function sum(a: number, b: number): number { return a + b } console.log(sum(10, 20)) // 30 // 这是一个两个数字求和的函数,在js 中不声明参数类型,可传入任意类型值,传入的是数字才会做加法运算,如果有一个非数字就成字符串拼接,在TS中声明参数类型,也可对返回值进行类型声明,如上小括号外面的 `: number` 就是对函数返回值的一个类型声明 sum(10, 'hello') // 第二个参数会报错,已声明 number,不可传入 string let obj: {name: string, age?: number} // 属性名后面加?号,说明该属性是可选的,可以给,也可以不给 obj = {name: 'zs', age: 12} // 不会报错 // obj = {name: 'zs'} 也不会报错 // 对象类型,name是必须值,后面可以传任意键值对,不传也可以 let y: { name: string, [propName: string]: any } y = { name: 'as', age: 23 } // 函数 let p: (a: number, b: number) => number p = function (a, b): number { return a+b } p(10, 23) // 数组 // let h: number[] let h: Array<number> h = [123, 23, 343434] let m: string[] m = ['23', '45'] // tuple 元组,元组就是固定长度的数组 let j: [number, string] j = [23, '23'] // enum 枚举 enum Gender{ Male = 0, Female = 1 } let i: { name: string, gender: Gender } i = { name: 'zs', gender: Gender.Male } console.log(i.gender === Gender.Male) // & 与 let x: {name: string} & {age: number} x = { name: 'ls', age: 23 } // 别名 type myType = { name: string, age: number } let bb: myType = { name: 'zs', age: 15 }
上面代码最后一行会出现警告,这就是 ts 指定类型时候这能复制该类型值,而不能是其他类型
-
自动判断类型
-
TS 拥有自动的类型判断机制
-
当对变量的声明和赋值时同时进行的,TS 编译器会自动判断变量的类型
-
所以如果你的变量的声明和赋值是同时进行的,可以省略类型声明
let d = 10 // 此时 ts 会给变量 d 添加上类型 number d = 'Hello' // 此时就会出现报错 // 不能将类型“string”分配给类型“number”。let d: number let e = true // let e: boolean let f = 'Hello' // let f: string // 跟上面一样的情况
-
-
类型
类型 例子 描述 number 1, 5, 98 任意数字 string ‘123’, ‘helli’ 任意字符串 boolean true, false 布尔值 true或false 字面量 本身 限制变量的值就是该字面量的值 any * 任意类型(类似 js) unknown * 类型安全的 any void 空值(undefined) 没有值(或 undefined) never 没有值 不能是任何值 object {name: ‘张三’} 任意的js对象 array [1,2,3] 任意js数组 tuple [4,5] 元素,TS新增类型,固定长度数组 enum enum{A, B} 枚举,TS新增类型 // any 类型 let a: any a = 2 a = true a = 'hello' // 都不会报错,可以是任意值 // unknown 类型 let b: unknown b = 2 b = true b = 'hello' // 跟 any 类型一样都不会报错 // 两者区别 let c: string = 'Hello' c = a // 不会报错, any类型的 a 会把 c 同化,c 的类型也会变得不清晰 c = b // 会报错, any类型的 b 不会同化 c // 不能将类型“unknown”分配给类型“string”。 // 解决报错 // 1. if(typeof b === 'string') { c = b // 此时会发现没有报错了 } // 2. c = b as string // 3. c = <string>b
-
重命名
使用 type 可以重命名类型
type myType = { name: string, age: number } const obj: myType = { name: 'zs', age: 13 }
编译选项
-
自动编译文件
-
编译文件时,使用 -w 指令后,TS 编译器会自动监视文件的变化,并在文件发生改变时对文件进行重新编译
-
示例:
tsc xxx.ts -w
-
-
自动编译整个项目
-
如果直接使用 tsc 指令,则可以自动将当前项目下的所有 ts 文件编译成 js 文件
-
但是能直接使用 tsc 命令的前提是要先在项目根目录下创建一个 ts 的配置文件
tsconfig.json
-
tsconfig.json
是一个JSON文件,添加配置文件后,只需 tsc 命令即可完成对整个项目的编译 -
配置选项:
-
include
- 定义希望被编译文件所在的目录
- 默认值:
["**/*"]
- 示例:
"include": ["src/**/*", "tests/**/*"]
- 上述示例中,所有src 目录和 tests 目录下的文件都会被编译
-
exclude
- 定义需要排除在外的目录
- 默认值:
["node_modules", "bower_components", "jspm_packages"]
- 示例:
"exclude": ["./src/hello/**/*"]
- 上述实例中,src下hello目录下的文件搜不会被编译
-
extends
- 定义被继承的配置文件
- 示例:
"extends": "./configs/base"
- 上述示例中,当前配置文件中自动包含config 目录下base.json中的所有配置信息
-
files
-
指定被编译文件的列表,只有需要编译的文件少时才会用到
-
示例:
"files": [ "core.ts", "sys.ts", "types.ts", "scanner.ts", "parser.ts", "utilities.ts", "binder.ts", "checker.ts", "tsc.ts" ]
-
-
compilerOptions
-
编译器的选项
-
示例:
"compilerOptions": { "target": "ES3", // target 用来指定TS被编译为的ES的版本 "module": "es2015", // module 指定要使用的模块化的规范 "lib": ["dom"], // lib 用来指定项目中要使用的库 // 一般不需要改动添加 "outDir": "./dist", // outDir 用来指定编译后的文件所在目录 "outFile": "./dist/app.js", // 将代码合并为一个文件 // 设置 outFile 后,所有的全局作用域中的代码会合并到同一个文件中 "allowJs": false, // 是否对 JS 文件进行编译,默认是 false "checkJs": false, // 是否检查 JS 代码是否符合语法规范,默认 false "removeComments": false, // 是否移除注释 "noEmit": false, // 不生成编译后的文件 "noEmitOnError": true, // 当有错误时不生成编译后的文件 "alwaysStrict": true, // 用来设置编译后的文件是否使用严格模式,默认false。 有模块化引入的时候就不用设置 "noImplicitAny": true, // 是否不允许隐式any类型 "noImplicitThis": false, // 不允许不明确类型的this, 默认 false "strictNullChecks": false, // 严格的检查空值 "strict": false, // 所有严格检查的总开关 }
-
-
-
webpack
-
安装 webpack
npm i webpack webpack-cli ts-loader -D
如果项目中已经下载 typescript 就无需再下载,如果没有请自行下载安装
-
可以在 package.json 文件中可以看到安装的工具,如果项目中没有 package.json ,可以初始化项目
npm init
,此时就会看到创建出来了一个 package.json 文件 -
在项目根目录下创建
webpack.config.js
-
在文件中配置
const path = require('path') module.exports = { // 指定入口文件 entry: './src/xxx.ts', // 指定打包文件所在目录 output: { // 指定打包文件目录 path: path.resolve(__dirname, 'dist'), // 打包后文件的名称 filename: 'bundle.js', // 告诉 webpack 打包生成的文件不使用箭头函数自调用 environment: { arrowFunction: fasle } }, // 指定 webpack 打包时要使用模块 module: { // 是定loader 规则 rules: [ { // test指定的是规则生效的文件 test: /\.ts$/, // 要使用的 loader use: 'ts-loader', // 要排除的文件 exclude: /node-modules/ } ] } }
以上就是最最基本的一个 webpack 配置文件
使用 webpack 需要在package.json 文件中的 scripts 节点下,配置
"build": "webpack"
在此时打包还得每次自己手写一个 html 页面然后将打包后的 js 文件引入其中然后用浏览器打开,非常的不方便,所以需要另外一个插件
html-webpack-plugin
打包自动生成 index.html
下载
npm i html-webpack-plugin -D
使用
-
在webpack.config.js 文件中使用
const HtmlWebpackPlugin = require('html-webpack-plugin') const htmlPlugin = new HtmlWebpackPlugin({ template: './src/index.html', filename: './index.html' }) module.exports = { // ... 省略其他 // 配置 webpack 插件 plugins: [htmlPlugin] }
自动打包
-
在目前为止,基本的配置已经完成,但是还有一个问题就是每次项目打包都得手动去打包,所以需要另外一个插件
webpack-dev-server
会启动一个 webpack 内置服务器,每次项目就会自动打包 -
安装
npm i webpack-dev-server -D
-
在 package.json 文件中的 scripts 节点下配置
"start": "webpack serve --open chrome.exe"
设置引用模块
-
在 webpack.config.js 文件中配置
module.exports = { // ... 省略其他 // 用来设置引用模块 resolve: { extensions: ['.ts', '.js'] } }
打包文件的兼容性
-
通过 babel 解决兼容问题
-
安装
npm i -D @babel/core @babel/preset-env babel-loader core-js
-
需要在 webpack.config.js 中配置
module.exports = { // 指定 webpack 打包时要使用模块 module: { // 是定loader 规则 rules: [ { // test指定的是规则生效的文件 test: /\.ts$/, // 要使用的 loader use: [ // 配置babel { // 指定加载器 loader: 'babel-loader', // 设置 babel options: { // 设置预定义环境 presets: [ [ // 指定环境插件 "@babel/preset-env", // 配置信息 { // 要兼容的目标浏览器 targets: { "chrome": "88" }, // 指定 corejs 版本 "corejs": "3", // 使用 corejs 的方式"usage"表示按需加载 "useBuiltIns": "usage" } ] ] } }, 'ts-loader'], // 要排除的文件 exclude: /node-modules/ } ] } }
抽象类
- 专门被用来继承的类
- 使用
extends
来继承
// 抽象类 以 abstract 开头的类 // 抽象类不能创建实例,只能被继承 // 抽象类就是专门用来继承的类 // 抽象类里可以添加抽象方法 abstract class Person { name: string constructor() { this.name = name } // 抽象方法 abstract sayHello(): void } // 继承 class China extends Person { // 这样如果内部没有定义sayHello 就会报错 sayHello() { console.log("Hello") } } const china = new China("zs")
接口
-
之前提到了 别名 type 可以重命名类型,适用于类型特别多的时候可以起别名,使用起来比较方便,有一颗跟它类似的东西—接口
-
接口可以重复声明,别名不可以重复声明,接口重复声明在使用的时候必须包含同名接口内的全部属性或者方法,否则会报错
-
通过
implements
来使用interface myInterface { name: string sayHello(): void } interface myInterface { age: number } // 通过 implements 来使用接口 class Obj implements myInterface { // 接口里面属性或者方法 在这里少一个都会报错,属性声明并且初始化,只声明不初始化也会报错 name: string age: number constructor(name: string, age: number) { this.name = name this.age = age } sayHello() { console.log('Hello') } }
属性的封装
-
public 修饰公共的属性,可以在类外部修改属性
-
private 修饰私有的属性,只能在类内部修改
-
protected 受保护的属性,只能在当前类或者当前类的子类中使用,类的实例对象无法访问
class Person { private name: string private age: number constructor(name: string, age: number) { this.name = name this.age = age } // 由于属性前面添加了 private 修饰符,外部无法访问,怎么可以让外部访问呢? // 可以提供方法把属性暴漏出去 get name() { return this.name } set name(value: string) { // 在这里面就可以添加一些限制条件,防止恶意更改 // 主动权在创造者而非使用者 this.name = value } } const per = new Person(name: 'zs', age: 22) // 如果类里面的属性添加了 private 修饰符,并且没有对外暴漏setter方法,这行代码会报错。 // 里面暴露出来的 set name() 就可以修改属性,此时就不会报错 per.name = 'ls' console.log(per) // 现在的属性直接在对象中设置的,属性可以任意的修改 // 属性可以任意修改将会导致对象中的数据变得非常的不安全 // 防止恶意更改所以可以使用修饰符
语法糖写法
class A { name: string age: number constructor(name: string, age: number) { this.name = name this.age = age } } // 等同于 class A { constructor(public name: string, public age: number) {} }
泛型
-
在定义函数或类时,如果遇到类型不明确就可以使用泛型
function fn<T>(a: T): T { return a } // 用法 // 可以直接调用 fn(10) // 不指定泛型,TS可以自动对类型进行判断 fn<string>('hello') // 指定泛型 // 同时指定多个泛型 function fn1<T, X>(a: T, b: X): T { console.log(b) return a } fn1(123, 'hello') fn1<number, string>(123, 'hello') // 有范围的泛型 interface Inter { length: number } function fn2<T extends Inter>(a: T): number { return a.length } // 类也可以使用泛型 class App<T> { name: T constructor(name: T) { this.name = name } } const app = new App('zs') // 未指定泛型 // const app = new App<string>('zs') // 指定泛型
-