快速入门 参考:https://juejin.cn/column/7028956693232877605
TypeScript简介
TypeScript是JavaScript的超集。
它对JS进行了扩展,向JS中引入了类型的概念,并添加了许多新的特性。
TS代码需要通过编译器编译为JS,然后再交由JS解析器执行。
TS完全兼容JS,换言之,任何的TS代码都可以直接当成JS使用。
相较于JS而言,TS拥有了静态类型,更加严格的语法,更强大的功能;TS可以在代码执行前就完成代码的检查,减小了运行时异常的出现的几率;TS代码可以编译为任意版本的JS代码,可有效解决不同JS运行环境的兼容问题;同样的功能,TS的代码量要大于JS,但由于TS的代码结构更加清晰,变量类型更加明确,在后期代码的维护中TS却远远胜于JS。
强类型语言的特点就是不允许改变变量的数据类型,除非进行强制类型转换
弱类型的一个特点就是在计算时,不同类型之间对使用者透明地对变量进行隐式转换。 也就是说变量可以被赋予不同的数据类型
JavaScript是一门弱类型的语言 ,动态语言
一门语言在编译时报错,那么是静态语言 ,如果在运行时报错,那么是动态语言 。
TypeScript 开发环境搭建
安装Node.js
使用npm全局安装typescript
进入命令行
输入:npm i -g typescript
输入tsc
查看是否安装成功
创建一个ts文件
使用tsc对ts文件进行编译
进入命令行
进入ts文件所在目录
执行命令:tsc xxx.ts
1 2 3 D:\@yjs2\TypeScript\ts>tsc 01_HelloTS.ts # 编译 D:\@yjs2\TypeScript\ts>node 01_HelloTS.js # 执行 Hello TS
基本类型
类型声明
类型声明是TS非常重要的一个特点
通过类型声明可以指定TS中变量(参数、形参)的类型
指定类型后,当为变量赋值时,TS编译器会自动检查值是否符合类型声明,符合则赋值,否则报错
简而言之,类型声明给变量设置了类型,使得变量只能存储某种类型的值
语法:
1 2 3 4 5 6 7 let 变量: 类型;let 变量: 类型 = 值;function fn (参数: 类型, 参数: 类型 ): 类型{ ... }
自动类型判断
TS拥有自动的类型判断机制
当对变量的声明和赋值是同时进行的,TS编译器会自动判断变量的类型
所以如果你的变量的声明和赋值时同时进行的,可以省略掉类型声明
类型:
类型
例子
描述
number
1, -33, 2.5
任意数字
string
‘hi’, “hi”, hi
任意字符串
boolean
true、false
布尔值true或false
字面量
其本身
限制变量的值就是该字面量的值
any
*
任意类型
unknown
*
类型安全的any
void
空值(undefined)
没有值(或undefined)
never
没有值
不能是任何值
object
{name:’孙悟空’}
任意的JS对象
array
[1,2,3]
任意JS数组
tuple
[4,5]
元素,TS新增类型,固定长度数组
enum
enum{A, B}
枚举,TS中新增类型
number
1 2 3 4 5 6 7 8 9 let decimal : number = 6 ;let hex : number = 0xf00d ;let binary : number = 0b1010 ;let octal : number = 0o744 ;let big : bigint = 100n ;let a :number a = 10
boolean
1 let isDone : boolean = false ;
string
1 2 3 4 5 6 7 8 9 10 11 12 let color : string = "blue" ; color = 'red' ;let fullName : string = `Bob Bobbington` ;let age : number = 37 ;let sentence : string = `Hello, my name is ${fullName} . I'll be ${age + 1 } years old next month.` ;let str :string str = "hello"
字面量
也可以使用字面量去指定变量的类型,通过字面量可以确定变量的取值范围
1 2 let color : 'red' | 'blue' | 'black' ;let num : 1 | 2 | 3 | 4 | 5 ;
any
1 2 3 let d : any = 4 ; d = 'hello' ; d = true ;
unknown
1 2 let notSure : unknown = 4 ; notSure = 'hello' ;
void
1 let unusable : void = undefined ;
never
1 2 3 function error (message: string ): never { throw new Error (message); }
object(没啥用)
array
1 2 let list : number [] = [1 , 2 , 3 ];let list : Array <number > = [1 , 2 , 3 ];
tuple
1 2 let x : [string , number ]; x = ["hello" , 10 ];
enum
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 enum Color { Red , Green , Blue , }let c : Color = Color .Green ;enum Color { Red = 1 , Green , Blue , }let c : Color = Color .Green ;enum Color { Red = 1 , Green = 2 , Blue = 4 , }let c : Color = Color .Green ;
类型断言
代码:
1 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 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 let f = false let b :boolean = true function sum (a:number , b:number ) { return a + b }console .log (sum (1 , 2 ));function sum1 (a:number ,b:number ):number { return a + b }console .log (sum1 (1 , 3 ));let a : 10 ;let b : "male" | "female" ; b = "male" ; b = "female" ;let c : boolean | string ; c = true ; c = "hello" ;let d; d = 10 ; d = "hello" ; d = true ;let e : unknown ; e = 10 ; e = "hello" ; e = true ;let s : string ; s = d;if (typeof e === "string" ){ s = e; } s = e as string ; s = <string >e;function fn ( ): void { }function fn2 ( ): never { throw new Error ('报错了!' ); }let a : object ; a = {} a = function ( ) { }let b : {name : string , age?:number }; b = { name : "张三" , age : 18 }let c : {name : string , [propName : string ]: any }; c = { name : "张三" , age : 18 , gender : "男" }let d : (a:number , b:number ) => number ; d= function (n1: number ,n2:number ) :number { return n1 + n2; }let e : string []; e = ['a' ,'b' ,'c' ];let f : number [];let g : Array <number >; g = [1 , 2 , 3 ];let h : [string , number ]; h = ["张三" , 1 ];enum Gender { Male , Female }let i : {name : string , gender : Gender }; i = { name : "张三" , gender : Gender .Female }type myType = 1 | 2 | 3 | 4 | 5 ;let k : myType;let l : myType;let m : myType; k = 2 ;
void类型详解 当函数没有返回值的时候,定义函数的返回值类型就是void
在JavaScript中,如果定义一个函数不写返回值,那么它默认是返回 undefined
的~
什么都不返回
只写一个return
1 2 3 function fn ( ): void { return }
返回一个undefined
1 2 3 function fn ( ): void { return undefined }
返回null
1 2 3 function fn : void { return null }
也就是说只有null
和undefined
可以赋给void
其实在JS中void
是一种操作符,可以让任何表达式返回undefined
any类型详解 any
表示的是任意类型,一个变量设置类型为 any
后,相当于对该变量关闭了TS的类型检测 。
隐式any
:如果只声明变量不指定类型 也不给变量赋值 ,变量就是any
类型的
当把any
类型的变量可以赋值给其他类型的变量,不会报错
1 2 3 4 5 6 7 8 9 let alet b : any a = 1 a = '1' b = '1' b = 1 let c :string = '123' c = b
any
类型是多人协作项目的大忌,很可能把Typescript变成AnyScript,通常在不得已的情况下,不应该首先考虑使用此类型。
unknown 开发的时候有些变量就是事先不知道的类型怎么办呢?可以使用unknown
unknown
与any
的区别就是,将unknown
类型变量赋值给其他任意类型的变量时,会报错
1 2 3 4 5 6 7 8 9 10 11 let alet b : unknown a = 1 a = '1' b = '1' b = 1 let c :string = '123' c = b export {}
unknown
类型的变量就是类型安全的any
,不能直接赋值给其他类型的变量
需要在赋值之前做好类型判断 (这叫缩小类型范围)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 let alet b : unknown a = 1 a = '1' b = '1' b = 1 let c :string = '123' if (typeof b === 'string' ) { c = b } c = b as string c = <string >bexport {}
never类型详解 never
表示永远不会返回结果
报错
1 2 3 function error ( ): never { throw new Error ('报错了!' ) }
死循环
1 2 3 function endless ( ): never { while (true ) {} }
对象类型详解 一般来说,我们对于对象的类型检查一般是检查对象中有哪些属性,属性是什么类型。
可以用{}
来指定对象中可以包含哪些属性 语法:{属性名: 属性类型, ...}
1 2 3 4 5 let obj : {name : string } obj = {name : '123' }
可选属性,在属性名后面加一个 ?
1 2 3 4 let obj1 : {name : string , age?: number } obj1 = {name : '张三' } obj1 = {name : '张三' , age : 14 }
如果后续属性不确定叫什么名字,也不确定是什么类型,那么可以这样写[propName: string]: unknown
1 2 3 4 5 let obj2 : {name : string , [propName : string ] : unknown } obj2 = {name : '张三' } obj2 = {name : '张三' , age : 14 } obj2 = {name : '张三' , age : 14 , gender : '男' }
函数详解 因为function
是关键字,所有这里用Function
来声明函数
1 2 3 4 5 let fn : Function fn = function ( ) { return 1 }
和对象一样,我们一般会限制函数有几个参数,参数的类型,返回值的类型,所以就可以以一种类似箭头函数的方式来声明一个函数类型的变量
可选参数必须放在必选参数之后
剩余参数 ...rest : number[]
1 2 3 4 5 6 let fn2 : (a: number , b: number ) => number fn2 = function sum (a, b ) { return a+b }
函数重载
最容易匹配的定义要写在最前面
数组详解 比较常用的就是这种方式: 在元素类型后面接上 []
:
1 2 let arr : number [] = [1 , 2 , 3 ]let arr2 : string [] = ['y' , 'k' ]
泛型
1 2 3 4 let list : Array <number > = [1 , 2 , 3 ]let list2 : Array <string > = ['y' , 'k' ]let list3 : Array <number | string > = [1 , 2 , 3 , 'yk' ]
元组 Tuple详解 元组是TS新出的类型,表示固定长度的array
元组中包含的元素,必须与声明的类型一致,而且不能多 、不能少 ,甚至顺序 都不能不一样
1 2 3 4 let x : [string , number ] x = ['aa' , 1 ]
字面量 也可以使用字面量去指定变量的类型,这相当于给他设置成了一个常量
联合类型 可以给一个变量定义一个联合类型,类型名之间用 |
隔开
1 2 3 4 5 6 7 8 9 let gender : "male" | "female" gender = "male" let age : string | number age = 16 age= '11'
类型别名 这个时候,如果一个类型的字面量类型选择过多,重复编写就比较麻烦,这就可以使用类型别名
1 2 3 4 5 6 type myType = 1 | 2 | 3 | 4 | 5 let i : myTypelet j : myTypelet k : myType k = 2
可以通过类型别名来定义函数参数类型结构
1 2 3 4 type fn = (x: number , y:number ) => number let fn : (x: number , y:number ) => number let fun : fn = (x, y ) => x + y
类型断言 1 2 3 4 5 let someValue : unknown = "abcedefg" let someLength : number = (someValue as string ).length let someValue2 : unknown = "abcedefg" let someLength2 : number = (<string >someValue2).length
枚举 ts代码
1 2 3 4 enum Gender { 'male' , 'female' }
转换为的js代码 tsc enum.ts
1 2 3 4 5 6 var Gender ; (function (Gender ) { Gender [Gender ["male" ] = 0 ] = "male" ; Gender [Gender ["female" ] = 1 ] = "female" ; })(Gender || (Gender = {}));console .log (Gender )
不但可以通过Gender.male
取到0
还可以通过Gender[0]
取到male
自定义枚举【字符串枚举】 默认的枚举类型的值都是数字类型的,所以我们称之为数字枚举,默认是从0开始递增的,我们也可以自定义数字,如果只定义第一个,后面的数字就依据你定义的递增
enum.ts
1 2 3 4 enum Gender1 { 'male' = 5 , 'female' }
1 2 3 4 5 6 var Gender1 ; (function (Gender1 ) { Gender1 [Gender1 ["male" ] = 5 ] = "male" ; Gender1 [Gender1 ["female" ] = 6 ] = "female" ; })(Gender1 || (Gender1 = {}));console .log (Gender1 )
当然不止数字类型,也可以定义成字符串 类型
enum.ts
1 2 3 4 enum Gender2 { 'male' = 'this is a male' , 'female' = 'this is a female' }
编译后的js文件
1 2 3 4 5 6 var Gender2 ; (function (Gender2 ) { Gender2 ["male" ] = "this is a male" ; Gender2 ["female" ] = "this is a female" ; })(Gender2 || (Gender2 = {}));console .log (Gender2 )
当然也可以将两种类型混合定义【异构枚举】
1 2 3 4 enum Gender { 'male' = 'this is male.' , 'female' = 1 }
常量枚举 在enum
前面加上const,表示常量枚举
我们先看普通的枚举类型转义之后是什么
1 2 3 4 5 6 const enum Gender3 { 'male' , 'female' }let g = Gender .male
编译后的js
可以看到,代码一下子精简了很多。定义的枚举类编译之后直接以值(常量)的形式来使用
编译选项 自动编译文件 编译文件时,使用 -w 指令后,TS编译器会自动监视文件的变化,并在文件发生变化时对文件进行重新编译。
示例:
自动编译整个项目
如果直接使用tsc指令,则可以自动将当前项目下的所有ts文件编译为js文件。
但是能直接使用tsc命令的前提时,要先在项目根目录下(tsc --init
)生成一个ts的配置文件 ==tsconfig.json==
tsconfig.json是一个JSON文件,添加配置文件后,只需只需 tsc 命令即可完成对整个项目的编译
1 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 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 { "compilerOptions" : { "target" : "es2016" , "module" : "commonjs" , "esModuleInterop" : true , "forceConsistentCasingInFileNames" : true , "strict" : true , "skipLibCheck" : true } }
配置选项 1 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 { "compilerOptions" : { "target" : "es5" , "module" : "commonjs" , "moduleResolution" : "node" , "experimentalDecorators" : true , "allowSyntheticDefaultImports" : true , "sourceMap" : true , "strict" : true , "noImplicitAny" : true , "alwaysStrict" : true , "declaration" : true , "removeComments" : true , "noImplicitReturns" : true , "importHelpers" : true , "lib" : [ "es6" , "dom" ] , "typeRoots" : [ "node_modules/@types" ] , "outDir" : "./dist" , "rootDir" : "./src" } , "include" : [ "./src/**/*.ts" ] , "exclude" : [ "node_modules" , "dist" , "**/*.test.ts" , ] }
include 定义希望被编译文件所在的目录
是一个数组,用来指定需要编译的ts文件,其中 *
表示任意文件 **
表示任意目录
默认值:["**/*"]
示例:
1 "include" : [ "src/**/*" , "tests/**/*" ]
上述示例中,所有src目录和tests目录下的文件都会被编译
exclude
extends
定义被继承的配置文件
示例:
1 "extends" : "./configs/base"
上述示例中,当前配置文件中会自动包含config目录下base.json中的所有配置信息
files
compilerOptions 编译选项是配置文件中非常重要也比较复杂的配置选项
在compilerOptions中包含多个子选项,用来完成对编译的配置
项目选项
target
lib
module
outDir
outFile
rootDir
allowJs
checkJs
是否对js文件进行检查
示例:
1 2 3 4 "compilerOptions" : { "allowJs" : true , "checkJs" : true }
⑦ 其他配置
标题
功能
allowJs
是否对js文件编译,默认值:false
checkJs
是否对js文件进行语法检查,默认值:false
removeComments
是否删除注释,默认值:false
noEmit
不生成编译后的文件,默认值:false
noEmitOnError
当有错误的时候不生成编译后的文件,默认值:false
sourceMap
是否生成sourceMap,默认值:false
⑧严格检查
标题
功能
strict
启用所有的严格检查,设置后相当于开启了所有的严格检查,默认值:false
alwaysStrict
总是以严格模式对代码进行编译,默认值:false
noImplicitAny
禁止隐式的any类型,默认值:false
noImplicitThis
禁止类型不明确的this,默认值:false
strictBindCallApply
严格检查bind、call和apply的参数列表,默认值:false
strictFunctionTypes
严格检查函数的类型,默认值:false
strictNullChecks
严格的空值检查,默认值:false
strictPropertyInitialization
严格检查属性是否初始化,默认值:false
⑨ 额外检查
标题
功能
noFallthroughCasesInSwitch
检查switch语句包含正确的break
noImplicitReturns
检查函数没有隐式的返回值
noUnusedLocals
检查未使用的局部变量
noUnusedParameters
检查未使用的参数
allowUnreachableCode
检查不可达代码;true:忽略不可达代码,false:不可达代码将引起错误
noEmitOnError
有错误的情况下不进行编译,默认值:false
完整代码配置:
1 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 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 { "include" : [ "./src/**/*" ], "exclude" : [ "./src/hello/**/*" ], "compilerOptions" : { "target" : "ES2015" , "module" : "ES2015" , "outDir" : "./dist" , "removeComments" : true , "noEmit" : false , "noEmitOnError" : true , "strict" : true , "alwaysStrict" : true , "noImplicitAny" : true , "noImplicitThis" : true , "strictNullChecks" : true } }
webpack配置TS项目 通常情况下,实际开发中我们都需要使用构建工具对代码进行打包,TS同样也可以结合构建工具一起使用,下边以webpack为例介绍一下如何结合构建工具使用TS。
步骤:
初始化项目
进入项目根目录,执行命令 npm init -y
主要作用:创建package.json文件
下载构建工具
npm i -D webpack webpack-cli webpack-dev-server typescript ts-loader clean-webpack-plugin html-webpack-plugin
共安装了7个包
webpack
webpack-cli
webpack-dev-server
typescript
ts-loader
html-webpack-plugin
webpack中html插件,用来自动创建html文件
clean-webpack-plugin
webpack中的清除插件,每次构建都会先清除目录
webpack相关配置 根目录下创建webpack的配置文件webpack.config.js
1 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 const path = require ("path" );const HtmlWebpackPlugin = require ("html-webpack-plugin" );const { CleanWebpackPlugin } = require ("clean-webpack-plugin" );module .exports = { optimization :{ minimize : false }, entry : "./src/index.ts" , devtool : "inline-source-map" , devServer : { contentBase : './dist' }, output : { path : path.resolve (__dirname, "dist" ), filename : "bundle.js" , environment : { arrowFunction : false } }, resolve : { extensions : [".ts" , ".js" ] }, module : { rules : [ { test : /\.ts$/ , use : { loader : "ts-loader" }, exclude : /node_modules/ } ] }, plugins : [ new CleanWebpackPlugin (), new HtmlWebpackPlugin ({ title :'TS测试' }), ] }
以上是一些基本的配置,但是在实际开发中,webpack在配置开发环境与生产环境时,配置的有些东西不太相同,所以我们应该分开写我们生产环境和开发环境的webpack配置
所以我们就在根目录下创建build文件夹存放我们的webpack配置文件
安装
基本配置webpack.base.config.js
1 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 const path = require ("path" );const HtmlWebpackPlugin = require ("html-webpack-plugin" );module .exports = { entry : "./src/index.ts" , output : { path : path.resolve (__dirname, "dist" ), filename : "bundle.js" , environment : { arrowFunction : false , }, }, resolve : { extensions : [".js" , ".ts" ], }, module : { rules : [ { test : /.ts$/ , use : [ { loader : "ts-loader" , }, ], exclude : /node_modules/ , }, ], }, plugins : [ new HtmlWebpackPlugin ({ template : "./src/index.html" , }), ] };
开发环境配置webpack.dev.config.js
1 2 3 module .exports = { devtool : "inline-source-map" , };
生产环境配置webpack.pro.config.js
1 2 3 4 5 const { CleanWebpackPlugin } = require ("clean-webpack-plugin" );module .exports = { plugins : [new CleanWebpackPlugin ()], };
配置主文件webpack.config.js
1 2 3 4 5 6 7 8 9 const { merge } = require ("webpack-merge" );const baseConfig = require ("./webpack.base.config" );const devConfig = require ("./webpack.dev.config" );const proConfig = require ("./webpack.pro.config" );module .exports = (env, argv ) => { let config = argv.mode === "development" ? devConfig : proConfig; return merge (baseConfig, config); };
配置ts.config.json 根目录下创建tsconfig.json,配置可以根据自己需要
1 2 3 4 5 6 7 { "compilerOptions" : { "target" : "ES2015" , "module" : "ES2015" , "strict" : true } }
编写代码 src/index.html(模版)
1 2 3 4 5 6 7 8 9 10 11 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <title > Document</title > </head > <body > <div id ="app" > </div > </body > </html >
src/index.ts
1 2 3 4 5 6 let content : string = "Hello TypeScript" const app = document .getElementById ("app" )if (app != null ) { app.innerHTML = content }
修改package.json 修改package.json添加如下配置
1 2 3 4 "scripts" : { "start" : "webpack-dev-server --mode=development --config ./build/webpack.config.js" , "build" : "webpack --mode=production --config ./build/webpack.config.js" } ,
启动项目:npm run start
打包项目: npm run build
配置Babel 经过一系列的配置,使得TS和webpack已经结合到了一起,除了webpack,开发中还经常需要结合babel来对代码进行转换以使其可以兼容到更多的浏览器,在上述步骤的基础上,通过以下步骤再将babel引入到项目中。
安装依赖包:npm i -D @babel/core @babel/preset-env babel-loader core-js
共安装了4个包,分别是:
@babel/core
@babel/preset-env
@babel-loader
core-js
core-js用来使老版本的浏览器支持新版ES语法
修改webpack.config.js配置文件
1 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 ...略...module : { rules : [ { test : /\.ts$/ , use : [ { loader : "babel-loader" , options :{ presets : [ [ "@babel/preset-env" , { "targets" :{ "chrome" : "58" , "ie" : "11" }, "corejs" :"3" , "useBuiltIns" : "usage" } ] ] } }, { loader : "ts-loader" , } ], exclude : /node_modules/ } ] } ...略...
如此一来,使用ts编译后的文件将会再次被babel处理,使得代码可以在大部分浏览器中直接使用,可以在配置选项的targets中指定要兼容的浏览器版本。
面向对象 面向对象是程序中一个非常重要的思想,它被很多同学理解成了一个比较难,比较深奥的问题,其实不然。面向对象很简单,简而言之就是程序之中所有的操作都需要通过对象来完成。
举例来说:
操作浏览器要使用window对象
操作网页要使用document对象
操作控制台要使用console对象
一切操作都要通过对象,也就是所谓的面向对象,那么对象到底是什么呢?这就要先说到程序是什么,计算机程序的本质就是对现实事物的抽象,抽象的反义词是具体,比如:照片是对一个具体的人的抽象,汽车模型是对具体汽车的抽象等等。程序也是对事物的抽象,在程序中我们可以表示一个人、一条狗、一把枪、一颗子弹等等所有的事物。一个事物到了程序中就变成了一个对象。
在程序中所有的对象都被分成了两个部分数据和功能,以人为例,人的姓名、性别、年龄、身高、体重等属于数据,人可以说话、走路、吃饭、睡觉这些属于人的功能。数据在对象中被成为属性,而功能就被称为方法。所以简而言之,在程序中一切皆是对象。
传统的面向对象编程(OOP Object-Oriented Programming
)语言(例如Java)基本都是基于类的,而JavaScript是通过原型与构造函数来模拟实现类。好在 ES6 出现一些新语法糖,使得 JavaScript 拥有了 class
关键字。虽然本质依然是构造函数,但是这使得开发者可以更加方便的定义类和使用继承。但是 ES6 的 class
语法依然有一些比如修饰符 和抽象类 的特性还没有加入。
不使用class
关键字,你可以使用其他方式来创建对象和实现面向对象编程的特性。以下是一些可用的方法:
对象字面量法:你可以使用对象字面量来创建和初始化一个对象。对象字面量语法是使用花括号 {}
并可以在其中定义属性和方法。
1 2 3 4 5 6 7 8 9 10 let person = { name : "Alice" , age : 25 , sayHello : function ( ) { console .log (`Hello, my name is ${this .name} and I am ${this .age} years old.` ); } }; person.sayHello ();
构造函数法:你可以使用函数来定义一个构造函数,并通过 new
操作符来创建对象实例。
1 2 3 4 5 6 7 8 9 10 11 12 function Person (name, age ) { this .name = name; this .age = age; this .sayHello = function ( ) { console .log (`Hello, my name is ${this .name} and I am ${this .age} years old.` ); }; }const person1 = new Person ("Alice" , 25 ); person1.sayHello ();
原型法:你可以使用函数的原型来定义共享的属性和方法。通过在构造函数中使用 prototype
属性,你可以为所有对象实例共享一组属性和方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 function Person (name, age ) { this .name = name; this .age = age; }Person .prototype .sayHello = function ( ) { console .log (`Hello, my name is ${this .name} and I am ${this .age} years old.` ); };const person1 = new Person ("Alice" , 25 ); person1.sayHello ();
类(class) 要想面向对象,操作对象,首先便要拥有对象,那么下一个问题就是如何创建对象。要创建对象,必须要先定义类,所谓的类可以理解为对象的模型,程序中可以根据类创建指定类型的对象,举例来说:可以通过Person类来创建人的对象,通过Dog类创建狗的对象,通过Car类来创建汽车的对象,不同的类可以用来创建不同的对象。
定义类:
1 2 3 4 5 6 7 8 9 10 11 12 class 类名 { 属性名: 类型; constructor (参数: 类型 ){ this .属性名 = 参数; } 方法名(){ .... } }
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 class Person { name : string ; age : number ; constructor (name: string , age: number ){ this .name = name; this .age = age; } sayHello ( ){ console .log (`大家好,我是${this .name} ` ); } }
使用类:
1 2 const p = new Person ('孙悟空' , 18 ); p.sayHello ();
1 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 class Person { readonly name : string = '孙悟空' ; age = 10 ; static id : number = 123 ; sayHelo ( ) { console .log ("hello ts" ) } static say ( ) { console .log ("你好,ts,我是静态方法" ) } }const person = new Person ();console .log (person);console .log (person.name );console .log (Person .id ); person.sayHelo ()Person .say ()
面向对象的特点 封装
public 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Person { public name : string ; public age : number ; constructor (name: string , age: number ){ this .name = name; this .age = age; } sayHello ( ){ console .log (`大家好,我是${this .name} ` ); } }class Employee extends Person { constructor (name: string , age: number ){ super (name, age); this .name = name; } }const p = new Person ('孙悟空' , 18 ); p.name = '猪八戒' ;
protected 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Person { protected name : string ; protected age : number ; constructor (name: string , age: number ){ this .name = name; this .age = age; } sayHello ( ){ console .log (`大家好,我是${this .name} ` ); } }class Employee extends Person { constructor (name: string , age: number ){ super (name, age); this .name = name; } }const p = new Person ('孙悟空' , 18 ); p.name = '猪八戒' ;
private 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Person { private name : string ; private age : number ; constructor (name: string , age: number ){ this .name = name; this .age = age; } sayHello ( ){ console .log (`大家好,我是${this .name} ` ); } }class Employee extends Person { constructor (name: string , age: number ){ super (name, age); this .name = name; } }const p = new Person ('孙悟空' , 18 ); p.name = '猪八戒' ;
属性存取器
对于一些不希望被任意修改的属性,可以将其设置为private
直接将其设置为private将导致无法再通过对象修改其中的属性
我们可以在类中定义一组读取、设置属性的方法,这种对属性读取或设置的属性被称为属性的存取器
读取属性的方法叫做setter方法,设置属性的方法叫做getter方法
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Person { private _name : string ; constructor (name: string ){ this ._name = name; } get name (){ return this ._name ; } set name (name: string ){ this ._name = name; } }const p1 = new Person ('孙悟空' );console .log (p1.name ); p1.name = '猪八戒' ;
静态属性
this
继承
继承时面向对象中的又一个特性
通过继承可以将其他类中的属性和方法引入到当前类中
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Animal { name : string ; age : number ; constructor (name: string , age: number ){ this .name = name; this .age = age; } }class Dog extends Animal { bark ( ){ console .log (`${this .name} 在汪汪叫!` ); } }const dog = new Dog ('旺财' , 4 ); dog.bark ();
通过继承可以在不修改类的情况下完成对类的扩展
重写
多态
多态 ,是指一个类的同名方法,在不同情况下的实现细节不同 。多态机制实现不同的内部实现结构共用同一个外部接口
也就是说,多态有以下目的:
一个外部接口可被多个同类使用。
不同对象调用同个方法,可有不同实现。
在父类中定义一个方法,在多个子类中进行不同的实现。在程序运行时,会根据不同的对象执行不同的操作,实现了运行时的绑定。
1 2 3 abstract class Animal { abstract sleep (): void }
1 2 3 4 5 6 7 8 9 10 11 12 13 class Dog extends Animal { sleep ( ) { console .log ('dog sleep' ) } }class Cat extends Animal { sleep ( ) { console .log ('cat sleep' ) } }let dog = new Dog ()let cat = new Cat ()
多态的实际含义是:同一操作作用于不同的对象上面,可以产生不同的解释和不同的执行结果。
1 2 3 4 5 6 let animals : Animal [] = [dog, cat] animals.forEach (animal => { animal.sleep () })
This类型 类的成员方法可以直接返回 this
,这样可以方便的实现链式调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class WorkFlow { step1 ( ) { return this } step2 ( ) { return this } }new WorkFlow ().step1 ().step2 ()class MyFlow extends WorkFlow { next ( ) { return this } }new MyFlow ().next ().step1 ().next ().step2 ()
接口(Interface) 接口的作用类似于抽象类,不同点在于接口中的所有方法和属性都是没有实值的,换句话说接口中的所有方法都是抽象方法 。接口主要负责定义一个类的结构,接口可以去限制一个对象的接口,对象只有包含接口中定义的所有属性和方法时才能匹配接口。同时,可以让一个类去实现接口,实现接口时类中要保护接口中的所有属性。
示例(检查对象类型):
1 2 3 4 5 6 7 8 9 10 11 interface Person { name : string ; sayHello ():void ; }function fn (per: Person ){ per.sayHello (); }fn ({name :'孙悟空' , sayHello ( ) {console .log (`Hello, 我是 ${this .name} ` )}});
示例(实现)
1 2 3 4 5 6 7 8 9 10 11 12 13 interface Person { name : string ; sayHello ():void ; }class Student implements Person { constructor (public name: string ) { } sayHello ( ) { console .log ('大家好,我是' +this .name ); } }
接口可以写多个,会合并
约束对象的结构
对象作为函数的参数,使用接口进行约束
1 2 3 4 5 6 7 8 9 10 interface Person { name : string ; sayHello (): void ; }function fn (per: Person ){ per.sayHello (); }fn ({name :'孙悟空' , sayHello ( ) {console .log (`Hello, 我是 ${this .name} ` )}});
约束对象时还可以定义可选成员、只读成员、动态成员
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 interface Post { title : string content : string subtitle?: string readonly summary : string [prop : string ]: string }const hello : Post = { title : 'yk' , content : 'ykjun nb' , summary : 'ykjun' , yk : 'ykyk' } hello.kk = 'kk'
可索引类型 不确定对象中属性的个数时,可以使用可索引类型的接口
可索引类型具有一个索引签名,它描述了对象索引的类型,还有相应的索引返回值类型
比如:约束字符串数组
1 2 3 4 5 6 7 8 9 10 11 12 interface StringArr { [index : number ]: string }let stringArr : StringArr = ['a' , 'b' , 'c' ]interface stringArray { [index : number ] : string }let arr : stringArray = ['aaa' , 'bbb' ]console .log (arr[0 ])
约束函数的结构 1 2 3 4 5 6 7 interface Add { (a :number , b :number ): number }let add : Add = function (a,b ){ return a + b }
比如对象中有一个字段是一个函数,使用接口约束就比较方便
1 2 3 4 interface Obj { name : string add : Add }
约束对象数组 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 interface List { readonly id : string name : string sex?: string [x : string ]: any }interface Result { data : List [] }function render (result: Result ) { result.data .forEach ((value )=> { console .log (value.id , value.name ) }) }let result : Result = { data : [ { id : 1 , name : 'YK' , sex : 'male' }, { id : 2 , name : 'yk' , phone : 1234 } ] }render (result)
接口与类 类可以实现接口 用接口来约束类成员,使用implements
实现接口,约束类的属性和方法的类型。类实现接口,必须要实现接口定义所有的属性。接口只能约束类的公有成员,不能约束类的构造函数。
1 2 3 4 5 6 7 8 9 10 11 12 interface Human { name : string eat (): void }class Asian implements Human { name : string constructor (name: string ) { this .name = name } eat ( ) {} }
一个类还可以实现多个接口
1 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 interface Eat { eat (food : string ): void }interface Run { run (distance : number ): void }class Person implements Eat , Run { eat (food : string ):void { console .log (`坐着吃饭: ${food} ` ) } run (distance: number ) { console .log (`直立行走:${distance} ` ) } }class Animal implements Eat , Run { eat (food : string ):void { console .log (`啃着吃: ${food} ` ) } run (distance: number ) { console .log (`爬行:${distance} ` ) } }
接口继承 对接口使用 extends
关键字允许我们有效的从其他声明过的类型中拷贝成员,并且随意添加新成员。接口也可以继承多个类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 interface Human { name : string eat (): void }interface Man extends Human { run (): void }interface Child { cry (): void }interface Boy extends Man , Child {}let boy : Boy = { name : '' , run ( ) {}, eat ( ) {}, cry ( ) {}, }
接口还可以继承类,相当于把类的成员抽象出来(只有类的成员结构,没有具体实现)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Auto { state = 1 private state2 = 0 }interface AutoInterface extends Auto { }class C implements AutoInterface { state = 1 } 类继承父类实现接口class Bus extends Auto implements AutoInterface { }
接口之间可以相互继承,这样能够实现接口的复用
类之间可以相互继承,可以实现属性和方法的复用
类可以实现接口,接口只能约束类的公有成员
接口可以继承类的成员,包括公有、私有和受保护成员
接口与抽象类的区别 抽象类做为其它派生类的基类使用,它们一般不会直接被实例化,不同于接口,抽象类可以包含成员的实现细节.
接口与类型别名 类型别名和接口非常相似,大部分时候,你可以任意选择使用。接口的几乎所有特性都可以在 type
中使用,两者最关键的差别在于类型别名本身无法添加新的属性 ,而接口是可以扩展的 。
1 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 interface Animal { name : string }interface Bear extends Animal { honey : boolean }const bear = getBear () bear.name bear.honey type Animal = { name : string }type Bear = Animal & { honey : boolean }const bear = getBear (); bear.name ; bear.honey ;
添加新的属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 interface Window { title : string }interface Window { ts : TypeScriptAPI }const src = 'const a = "Hello World"' ;window .ts .transpileModule (src, {}); type Window = { title : string }type Window = { ts : TypeScriptAPI }
接口可能只会被用于声明对象的形状,不能重命名原始类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 interface AnObject1 { value : string }type AnObject2 = { value : string }type SanitizedString = string type EvenNumber = number interface X extends string { }
泛型(Generic) 定义一个函数或类时,有些情况下无法确定其中要使用的具体类型(返回值、参数、属性的类型不能确定),此时泛型便能够发挥作用。
举个例子:
1 2 3 function test (arg: any ): any { return arg; }
1 2 3 function test<T>(arg : T): T{ return arg; }
极客时间 基础篇 搭建环境 初始化项目
1 2 3 npm i typescript -g # 如果没有安装typescript要全局安装 npm init -y tsc --init # 生成ts.config.json
安装
1 2 3 4 5 npm i webpack webpack-cli webpack-dev-server -D npm i webpack-merge -D # 把两个webpack配置项合并 npm i ts-loader typescript -D npm i html-webpack-plugin -D npm i clean-webpack-plugin -D
build/webpack.base.config.js
1 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 const HtmlWebpackPlugin = require ('html-webpack-plugin' )module .exports = { entry : { app : './src/index.ts' }, output : { filename : 'app.js' }, resolve : { extensions : ['.js' , '.tsx' , '.ts' ] }, module : { rules : [ { test : /\.tsx?$/i , use : [ { loader : 'ts-loader' } ], exclude : /node_modules/ }, ], }, plugins : [ new HtmlWebpackPlugin ({ template : './src/index.html' }) ] }
build/webpack.dev.config.js
cheap 忽略 source-map 的链信息
module 定位到 ts 源码
eval-source-map 将 source-map 以 url 形式打包到文件中
1 2 3 4 module .exports = { devtool : 'inline-source-map' }
build/webpack.pro.config.js
1 2 3 4 5 const { CleanWebpackPlugin } = require ('clean-webpack-plugin' )module .exports = { plugins : [new CleanWebpackPlugin ()] }
build/webpack.config.js
1 2 3 4 5 6 7 8 9 const { merge } = require ("webpack-merge" );const baseConfig = require ("./webpack.base.config" );const devConfig = require ("./webpack.dev.config" );const proConfig = require ("./webpack.pro.config" );module .exports = (env, argv ) => { let config = argv.mode === "development" ? devConfig : proConfig; return merge (baseConfig, config); };
数据类型 ES6的数据类型 • Boolean
• Number
• String
• Array
(引用数据类型)
• Function
(引用数据类型)
• Object
(引用数据类型)
• Symbol
• undefined
• null
TypeScript数据类型 在上面的基础上增加了一下6个
• void
• any
• never
• 元组
• 枚举
• 高级类型
1 2 3 4 5 let triple : [number , string ] = [1 , 'abc' ]; triple.push (2 ) console .log (triple) console .log (triple[2 ])
1 2 3 4 5 6 let unde : undefined = undefined let nu : null = null unde = null nu = undefined
枚举类型 枚举成员只读,无法修改
上述内容参考
参考文章
接口 参考文章
可索引的类型 “通过索引得到”的类型,比如a[10]
或ageMap["daniel"]
。 可索引类型具有一个 索引签名,它描述了对象索引的类型,还有相应的索引返回值类型。
1 2 3 4 5 interface stringArray { [index : number ] : string }let arr : stringArray = ['aaa' , 'bbb' ]console .log (arr[0 ])
TypeScript支持两种索引签名:字符串和数字。 可以同时使用两种类型的索引,但是数字索引的返回值必须是字符串索引返回值类型的子类型。 这是因为当使用 number来索引时,JavaScript会将它转换成string然后再去索引对象。 也就是说用 100(一个number)去索引等同于使用”100”(一个string)去索引,因此两者需要保持一致。
1 2 3 4 5 6 7 8 9 10 interface myType { [a : number ] : number , [b : string ] : any }let arr2 : myType = [123 , 222 , 333 ]console .log (arr2[0 ])console .log (arr2)
泛型 泛型函数和泛型接口 函数重载:使用相同名称或不同参数数量或类型创建多个方法
联合类型:取值可以为多钟类型中的一个
泛型:不预先确定的数据类型,具体的类型在使用的时候才能确定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 function log (value: string ): string function log (value: string [] ): string [] { return value }function log (value: string | string [] ): string | string [] { return value }function log<T>(value : T): T { console .log (value) return value } log<string []>(['a' , 'b' ])log (['a' , 'b' ])
不仅可以用泛型定义一个函数,也可以定义一个函数类型
1 2 3 4 5 6 7 function log<T>(value : T): T { console .log (value) return value }type Log = <T>(value: T ) => Tlet myLog : Log = log
泛型接口
1 2 3 4 5 6 7 8 function log<T>(value : T): T { console .log (value) return value }interface Log <T> { <T>(value : T): T }
泛型类和泛型约束
1 2 3 4 5 6 7 8 9 10 class Log <T> { run (value: T ) { return value } }let log1 = new Log <number >() log1.run (1 )let log2 = new Log () log2.run ({ a : 1 }) log2.run ('1' )
1 2 3 4 5 6 7 8 9 10 interface Length { length : number }function log<T extends Length >(value : T): T { console .log (value, value.length ) return value }log ([1 ])log ('123' )log ({ length : 1 })
泛型好处
函数和类可以轻松地支持多种类型,增强程序的扩展性
不比写多余函数重载,冗长的联合声明,增强代码可读性
灵活控制类型之间的约束
类型检查机制 TypeScript 编译器在做类型检查时,所秉承的一些原则,以及表现出的一些行为 作用:辅助开发,提高开发效率
类型推断 类型推断: 不需要指定变量的类型(函数的返回值类型),TypeScript 可以根据某些规则自动地为其推断出一个类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 let a = 1 let b = [1 , null ]let c = (x = 1 ) => x + 1 window .onkeydown = event => { }interface Foo { bar : number }let foo : Foo = { bar : 1 , }
类型兼容性
源类型具备目标类型的必要属性(可以进行赋值)
两个接口 类型兼容性【成员少的能兼容成员多的 ]】[鸭式辨型法] 只要 y 接口具备 x 接口的所有属性,即使有额外的属性,y 仍然可以被认为是 x 类型
两个函数 类型兼容性【参数多的能兼容参数少的 】 注意:这个规则是和接口兼容性规则相反
1 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 interface X { a : any b : any }interface Y { a : any b : any c : any }let x : X = { a : 1 , b : 2 }let y : Y = { a : 1 , b : 2 , c : 3 } x = y interface Point3D { x : number y : number z : number }interface Point2D { x : number y : number }let p3d = (point: Point3D ) => {}let p2d = (point: Point2D ) => {} p3d = p2d
Handler 目标类型,传入的参数是 源类型,如果让目标函数兼容源函数:
目标函数参数个数多余源函数参数个数
固定参数是可以兼容可选参数 / 剩余参数
可选参数是不兼容固定参数 / 剩余参数
参数类型必须要匹配
目标函数返回值类型必须与源函数返回值类型相同或为其子类型
1 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 type Handler = (a: number , b: number ) => void function hof (handler: Handler ) { return handler }let handler1 = (a: number ) => {}hof (handler1)let handler2 = (a: number , b: number , c: number ) => {}let a = (p1: number , p2: number ) => {}let b = (p1?: number , p2?: number ) => {}let c = (...args: number [] ) => {} a = b a = c c = a c = b let handler3 = (a: string ) => {}let f = ( ) => ({ name : 'Alice' , })let g = ( ) => ({ name : 'Alice' , location : 'BeiJing' , }) f = gfunction overload (a: number , b: number ): number function overload (a: string , b: string ): string function overload (a: any , b: any ): any {}
枚举、类、泛型的兼容性:
枚举和 number 是完全兼容的,枚举之间是完全不兼容的
在比较两个类是否兼容时,静态成员和构造函数是不参与比较的 如果两个类具有相同的实例成员,实例就可以完全相互兼容如果两个类中有私有成员,两个类就不兼容了 父类和子类的实例是可以相互兼容的
如果泛型接口内没有任何成员是兼容的,如果有成员是不兼容的 如果泛型函数定义相同,没有指定泛型参数是相互兼容的
1 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 enum Fruit { Apple , Banana , }enum Color { Red , Yellow , }let fruit : Fruit .Apple = 3 let no : number = Fruit .Apple class A { constructor (p: number , q: number ) {} id : number = 1 private name : string = '' }class B { static s = 1 constructor (p: number ) {} id : number = 2 private name : string = '' }let aa = new A (1 , 2 )let bb = new B (1 )class C extends A {}let cc = new C (1 , 2 ) aa = cc cc = aainterface Empty <T> { value : T }let log1 = <T>(x : T): T => { return x }let log2 = <U>(y : U): U => { return y } log1 = log2
当一个类型 Y 可以被赋值给另一个类型 X 时,我们就可以说类型 X 兼容类型 Y X 兼容 Y: X(目标类型) = Y(源类型)
口诀:
结构之间兼容:成员少的兼容成员多的 (接口)
函数之间兼容:参数多的兼容参数少的
类型保护 TypeScript 能够在特定的区块中保证变量属于某种确定的类型。可以在这个区块中放心地引用次类型的属性,或者调用此类型的方法
instanceof 判断实例是否属于某个类
in 判断一个属性是否属于某个对象
typeof 判断基本类型
创建类型保护函数判断类型(参数是联合类型,函数值是 参数 is xxx【类行为词】)
1 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 54 55 56 57 58 59 60 61 62 63 enum Type { Strong , Week , }class Java { helloJava ( ) { console .log ('Hello Java' ) } java : any }class JavaScript { helloJavaScript ( ) { console .log ('Hello JavaScript' ) } javascript : any }function isJava (lang: Java | JavaScript ): lang is Java { return (lang as Java ).helloJava !== undefined }function getLanguage (type : Type, x: string | number ) { let lang = type === Type .Strong ? new Java () : new JavaScript () if (isJava (lang)) { lang.helloJava () } else { lang.helloJavaScript () } return lang }getLanguage (Type .Strong )
高级类型 keyof
操作符可以用来获取某种类型的所有键,其返回类型是联合类型
1 2 3 4 5 6 7 8 9 interface Person { name : string age : number location : string }type K1 = keyof Person type K2 = keyof Person [] type K3 = keyof { [x : string ]: Person }
交叉类型和联合类型 交叉类型适合对象的混入,联合类型可以使类型具有一定的不确定性
交叉类型:从名称看是可以访问所有类成员的交集,但实际只能访问所有类成员的并集 (&
)
联合类型:从名称看是可以访问所有类成员的并集,但实际只能访问所有类成员的交集 (|
)
1 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 interface DogInterface { run (): void }interface CatInterface { jump (): void }let pet : DogInterface & CatInterface = { run ( ) {}, jump ( ) {}, }let a : number | string = 'a' let b : 'a' | 'b' | 'c' let c : 1 | 2 | 3 class Dog implements DogInterface { run ( ) {} eat ( ) {} }class Cat implements CatInterface { jump ( ) {} eat ( ) {} }enum Master { Boy , Girl , }function getPet (master: Master ) { let pet = master === Master .Boy ? new Dog () : new Cat () pet.eat () pet.run () return pet }
可区分的联合类型(结合联合类型和字面量类型的一种类型保护方法)
核心思想:一个类型如果是多个类型的联合类型,并且每个类型之间有一个公共的属性,那么就可以凭借这个公共属性创建类型保护区块
1 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 interface Square { kind : 'square' size : number }interface Rectangle { kind : 'rectangle' width : number height : number }interface Circle { kind : 'circle' r : number }type Shape = Square | Rectangle | Circle function area (s: Shape ) { switch (s.kind ) { case 'square' : return s.size * s.size case 'rectangle' : return s.height * s.width case 'circle' : return Math .PI * s.r ** 2 default : return ((e: never ) => { throw new Error (e) })(s) } }console .log (area ({ kind : 'circle' , r : 1 }))
索引类型
索引类型的查询操作符 keyof T
:表示类型 T 的所有公共属性的字面量的联合类型
索引访问操作符 T[K]
:表示对象 T 的属性 K 所代表的类型
泛型约束 T extends U
:表示泛型变量通过继承某个类型获得某些属性
索引类型可以实现对对象属性的查询和访问,配合泛型约束能够建立对象、对象属性、属性值之间的约束关系
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 let obj = { a : 1 , b : 2 , c : 3 , }function getValues<T, K extends keyof T>(obj : T, keys : K[]): T[K][] { return keys.map (key => obj[key]) }console .log (getValues (obj, ['a' , 'b' ])) console .log (getValues (obj, ['c' , 'd' ])) interface Obj { a : number b : number }let key : keyof Obj let value : Obj ['a' ]
映射类型 通过映射类型可以从一个旧类型生成新类型,比如把一个类型中的所有属性变成只读
同态(只会作用于 Object)
非同态(会创建新属性)
映射类型本质:预先定义的泛型接口,通常会结合索引类型获取对象的属性和属性值从而将对象映射成想要的结构
1 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 interface Obj { a : string b : number c : boolean }type ReadonlyObj = Readonly <Obj >type PartialObj = Partial <Obj >type PickObj = Pick <Obj , 'a' | 'b' >type RecordObj = Record <'x' | 'y' , Obj >
条件类型 条件类型是由条件表达式决定的类型
1 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 type TypeName <T> = T extends string ? 'string' : T extends number ? 'number' : T extends boolean ? 'boolean' : T extends undefined ? 'undefined' : T extends Function ? 'function' : 'object' type T1 = TypeName <string >type T2 = TypeName <string []>type T3 = TypeName <string | string []>type Diff <T, U> = T extends U ? never : Ttype T4 = Diff <'a' | 'b' | 'c' , 'a' | 'e' >type NotNull <T> = Diff <T, undefined | null >type T5 = NotNull <string | number | undefined | null >type T6 = Extract <'a' | 'b' | 'c' , 'a' | 'e' >type T7 = ReturnType <() => string >
工程篇 ES6和CommonJS的模块系统
es6/a.ts
1 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 export let a = 1 let b = 2 let c = 3 export { b, c }export interface P { x : number ; y : number ; }export function f ( ) {}function g ( ) {}export { g as G }export default function ( ) { console .log ("I'm default" ) }export { str as hello } from './b'
es6/b.ts
1 2 export const str = 'Hello'
es6/c.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import { a, b, c } from './a' ; import { P } from './a' ; import { f as F } from './a' ; import * as All from './a' ; import myFunction from './a' ; console .log (a, b, c)let p : P = { x : 1 , y : 1 }console .log (All )myFunction ()
es6/d.ts
1 2 3 4 export = function ( ) { console .log ("I'm default" ) }
node/a.node.ts
1 2 3 4 5 6 7 let a = { x : 1 , y : 2 }module .exports = a
node/b.node.ts
1 2 3 4 5 6 exports .c = 3 exports .d = 4
node/c.node.ts
1 2 3 4 5 6 7 8 9 10 11 let c1 = require ('./a.node' )let c2 = require ('./b.node' )let c3 = require ('../es6/a' )import c4 = require ('../es6/d' )console .log (c1)console .log (c2)c4 ()
命名空间
1 2 3 4 5 6 namespace Shape { const pi = Math .PI export function cricle (r: number ) { return pi * r ** 2 } }
1 2 3 4 5 6 7 8 9 10 11 12 13 namespace Shape { export function square (x: number ) { return x * x } }console .log (Shape .cricle (2 ))console .log (Shape .square (2 ))import cricle = Shape .cricle console .log (cricle (2 ))
理解声明合并
1 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 interface A { x : number ; foo (bar : number ): number ; foo (bar : 'a' ): string ; }interface A { y : number ; foo (bar : string ): string ; foo (bar : string []): string []; foo (bar : 'b' ): string ; }let a : A = { x : 1 , y : 2 , foo (bar: any ) { return bar } }class C {}namespace C { export let state = 1 }console .log (C.state )function Lib ( ) {}namespace Lib { export let version = '1.0' }console .log (Lib .version )enum Color { Red , Yellow , Blue }namespace Color { export function mix ( ) {} }console .log (Color )
编写声明文件 关于 TypeScript 声明文件
global-lib.js
1 2 3 4 5 6 7 8 9 function globalLib (options) { console.log(options); } globalLib.version = '1.0.0' ; globalLib.doSomething = function() { console.log('globalLib do something' ); };
声明文件
global-lib.d.ts
1 2 3 4 5 6 7 8 9 declare function globalLib (options: globalLib.Options ): void ;declare namespace globalLib { const version : string ; function doSomething ( ): void ; interface Options { [key : string ]: any } }
module-lib.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 const version = '1.0.0' ;function doSomething ( ) { console .log ('moduleLib do something' ); }function moduleLib (options ) { console .log (options); } moduleLib.version = version; moduleLib.doSomething = doSomething;module .exports = moduleLib;
module-lib.d.ts
1 2 3 4 5 6 7 8 9 10 11 12 declare function moduleLib (options: Options ): void interface Options { [key : string ]: any }declare namespace moduleLib { const version : string function doSomething ( ): void }export = moduleLib
umd-lib.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 (function (root, factory ) { if (typeof define === "function" && define.amd ) { define (factory); } else if (typeof module === "object" && module .exports ) { module .exports = factory (); } else { root.umdLib = factory (); } }(this , function ( ) { return { version : '1.0.0' , doSomething ( ) { console .log ('umdLib do something' ); } } }));
umd-lib.d.ts
1 2 3 4 5 6 7 8 declare namespace umdLib { const version : string function doSomething ( ): void }export as namespace umdLib export = umdLib
在ts中引用js文件,需要有声明文件
1 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 import $ from 'jquery' $('.app' ).css ('color' , 'red' )globalLib ({x : 1 }) globalLib.doSomething ()import moduleLib from './module-lib' moduleLib ({y : 2 }) moduleLib.doSomething ()import umdLib from './umd-lib' umdLib.doSomething ()import m from 'moment' ;declare module 'moment' { export function myFunction ( ): void ; } m.myFunction = () => {}declare global { namespace globalLib { function doAnyting ( ): void } } globalLib.doAnyting = () => {}
在 TypeScript 文件中引入外部 JavaScript 包时,通常需要类型声明文件(.d.ts
文件),以便 TypeScript 可以理解包的类型信息并执行类型检查。这对于 TypeScript 能够正确推断和验证代码非常重要,特别是当你引入的包是非 TypeScript 编写的 JavaScript 代码时。
有几种方式来处理这个问题:
https://www.typescriptlang.org/dt/search
使用已有的声明文件: 许多常见的 JavaScript 包都有相应的 TypeScript 声明文件可供使用。你可以在 DefinitelyTyped(https://definitelytyped.org/)上查找或使用包管理器(如 npm)安装这些声明文件。例如,如果你想在 TypeScript 中使用 axios
这个 JavaScript 包,你可以运行以下命令来安装相关的声明文件:
1 npm install @types/axios
这将安装 axios
包的类型声明文件,使你能够在 TypeScript 文件中使用 axios
时获得类型提示和类型检查。
自己编写声明文件: 如果你的项目中使用了自定义的或不常见的 JavaScript 包,可能需要编写自己的声明文件。声明文件是以 .d.ts
扩展名结尾的文件,用来描述包的类型信息。你可以创建一个名为 packageName.d.ts
的文件,并在其中编写类型声明。例如,如果你有一个名为 my-js-library
的 JavaScript 包,你可以创建一个 my-js-library.d.ts
文件并在其中定义模块、类、函数等的类型信息。
使用 any
类型: 如果你不想或不能编写声明文件,还可以在 TypeScript 文件中将外部包的导入声明为 any
类型。这样做会禁用类型检查,但可以让你在不引发编译错误的情况下使用这些包。不过,这不是一个推荐的做法,因为失去了 TypeScript 提供的类型检查和类型安全性的好处。
综上所述,最佳做法是尽量使用已有的声明文件或编写自己的声明文件,以确保 TypeScript 能够正确地理解和检查外部 JavaScript 包的类型信息。这将有助于提高代码的可维护性和可靠性。
编译工具:从ts-loader到Babel
awesome-typescript-loader
与ts-loader的主要区别: 1)更适合与Babel集成,使用Babel的转义和缓存 2)不需要安装额外的插件,就可以把类型检查放在独立进程中进行
使用了TypeScript,为什么还需要 Babel?
如何选择 TypeScript 编译工具?
1)如果没有使用过Babel,首选TypeScript自身的编译器(可配合ts-loader使用) 2)如果项目中已经使用了Babel,安装@bable/preset-typescript (可配合tsc做类型检查) 3)两种编译工具不要混用
代码检查工具∶从TSLint到ESLint
Typescript官方转向ESLint的原因:
TSLint 执行规则的方式存在一些架构问题,从而影响了性能,而修复这些问题会破坏现有的规则;
ESLint的性能更好,并且社区用户通常拥有ESLint的规则配置(比如针对React和Vue的规则),而不会拥有TSLint的规则配置。
.eslintrc.json
1 2 3 4 5 6 7 8 9 10 11 12 13 { "parser" : "@typescript-eslint/parser" , "plugins" : [ "@typescript-eslint" ] , "parserOptions" : { "project" : "./tsconfig.json" } , "extends" : [ "plugin:@typescript-eslint/recommended" ] , "rules" : { "@typescript-eslint/no-inferrable-types" : "off" } }
package.json
1 2 3 4 5 6 "scripts" : { "start" : "webpack-dev-server --mode=development --config ./build/webpack.config.js" , "build" : "webpack --mode=production --config ./build/webpack.config.js" , "lint" : "eslint src --ext .js,.ts" , "test" : "jest" } ,
babel-eslint 与 typescript-eslint
babel-eslint:支持TypeScript没有的额外的语法检查,抛弃TypeScript,不支持类型检查
typescript-eslint:基于TypeScript的 AST,支持创建基于类型信息的规则(tsconfig.json)
建议∶ 两者底层机制不一样,不要一起使用。 Babel体系建议使用babel-eslint,否则可以使用typescript-eslint。
使用Jest进行单元测试
1 npm install jest, ts-jest, @types/jest -D
jest.config.js
1 2 3 4 module .exports = { preset : 'ts-jest' , testEnvironment : 'node' , };
test/math.test.ts
1 2 3 4 5 6 7 8 9 10 11 const math = require ('../src/math' );test ('add: 1 + 2 = 3' , () => { expect (math.add (1 , 2 )).toBe (3 ); });test ('sub: 1 - 2 = -1' , () => { expect (math.sub (1 , 2 )).toBe (-1 ); });
TypeScript 工具体系
实战篇 学完react要来学其中的一部分
搭建Vue开发环境 搭建步骤参考基础篇的搭建环境
当然采用vue-cli来搭建环境就不需要配置这么多了
安装
1 2 3 4 5 6 7 npm i webpack webpack-cli webpack-dev-server -D npm i webpack-merge -D # 把两个webpack配置项合并 npm i ts-loader typescript -D npm i html-webpack-plugin -D npm i clean-webpack-plugin -D npm i vue@2 npm i -D vue-loader@15 vue-template-compiler css-loader
ERROR in ./src/components/Hello.vue Module build failed (from ./node_modules/vue-loader/dist/index.js): TypeError: Cannot read properties of undefined (reading ‘styles’) at Object.loader (D:@面试\前端\TypeScript\TypeScript\ts-vue\node_modules\vue-loader\dist\index.js:95:34) @ ./src/index.ts 7:36-69
vue-loader最新的是17的和vue2.7.14匹配有问题会报错
package.json
1 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 { "name" : "ts-vue" , "version" : "1.0.0" , "description" : "" , "main" : "./src/index.ts" , "scripts" : { "start" : "webpack-dev-server --mode=development --config ./build/webpack.config.js" , "build" : "webpack --mode=production --config ./build/webpack.config.js" } , "keywords" : [ ] , "author" : "" , "license" : "ISC" , "devDependencies" : { "clean-webpack-plugin" : "^4.0.0" , "css-loader" : "^6.8.1" , "html-webpack-plugin" : "^5.5.3" , "ts-loader" : "^9.4.4" , "typescript" : "^5.2.2" , "vue-loader" : "^15.10.2" , "vue-template-compiler" : "^2.7.14" , "webpack" : "^5.88.2" , "webpack-cli" : "^5.1.4" , "webpack-dev-server" : "^4.15.1" , "webpack-merge" : "^5.9.0" } , "dependencies" : { "vue" : "^2.7.14" } }
build/webpack.base.config.js
1 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 const HtmlWebpackPlugin = require ('html-webpack-plugin' )const { VueLoaderPlugin } = require ("vue-loader" );module .exports = { entry : { app : './src/index.ts' }, output : { filename : '[name].[chunkhash:8].js' }, resolve : { extensions : ['.js' , '.ts' , '.tsx' , '.vue' ], alias : { 'vue' : 'vue/dist/vue.esm.js' } }, module : { rules : [ { test : /\.vue$/ , loader : 'vue-loader' }, { test : /\.tsx?$/ , use : [{ loader : 'ts-loader' , options : { appendTsSuffixTo : [/\.vue$/ ] } }], exclude : /node_modules/ }, { test : /\.css$/ , use : [ 'vue-style-loader' , 'css-loader' ] } ], }, plugins : [ new HtmlWebpackPlugin ({ template : './src/index.html' }), new VueLoaderPlugin () ], optimization : { splitChunks : { chunks : 'all' } } }
build/webpack.config.js
1 2 3 4 5 6 7 8 9 const { merge } = require ("webpack-merge" );const baseConfig = require ("./webpack.base.config" );const devConfig = require ("./webpack.dev.config" );const proConfig = require ("./webpack.pro.config" );module .exports = (env, argv ) => { let config = argv.mode === "development" ? devConfig : proConfig; return merge (baseConfig, config); };
build/webpack.dev.config.js
1 2 3 4 module .exports = { devtool : 'inline-source-map' }
build/webpack.pro.config.js
1 2 3 4 5 const { CleanWebpackPlugin } = require ('clean-webpack-plugin' )module .exports = { plugins : [new CleanWebpackPlugin ()] }
src/components/Hello.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <template> <h1>Hello {{ name }}</h1> </template> <script lang="ts"> import Vue from 'vue' export default Vue.extend({ data() { return { name: 'TypeScript' } } }) </script> <style scoped> h1 { color: blue } </style>
src/index.html
1 2 3 4 5 6 7 8 9 10 11 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <title > Document</title > </head > <body > <div id ="app" > </div > </body > </html >
src/index.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import Vue from "vue" ;import Hello from "./components/Hello.vue" let app2 = new Vue ({ el : '#app' , components : { Hello }, template : `<Hello/>` });
增加上声明文件
src/vue-shims.d.ts
vue-shims.d.ts
文件是一个用于 TypeScript 项目中的声明文件,通常用于解决使用第三方库或框架时 TypeScript 类型检查的问题。在特定于 Vue.js 的情况下,vue-shims.d.ts
文件用于告诉 TypeScript 如何处理 .vue
单文件组件,以便在开发过程中能够正确地进行类型检查。
1 2 3 4 declare module '*.vue' { import Vue from 'vue' export default Vue }
组件封装与发布 封装 相比于上一步,需要额外安装
1 2 npm install vue-class-component vue-property-decorator npm install webpack-node-externals -D
build/webpack.base.config.js
1 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 const { VueLoaderPlugin } = require ("vue-loader" );module .exports = { output : { filename : 'employee-query.js' }, resolve : { extensions : ['.js' , '.ts' , '.tsx' , '.vue' ], alias : { 'vue' : 'vue/dist/vue.esm.js' } }, module : { rules : [ { test : /\.vue$/ , loader : 'vue-loader' }, { test : /\.tsx?$/ , use : [{ loader : 'ts-loader' , options : { appendTsSuffixTo : [/\.vue$/ ] } }], exclude : /node_modules/ }, { test : /\.css$/ , use : [ 'vue-style-loader' , 'css-loader' ] } ], }, plugins : [ new VueLoaderPlugin () ] }
build/webpack.config.js
1 2 3 4 5 6 7 8 9 const { merge } = require ("webpack-merge" );const baseConfig = require ("./webpack.base.config" );const devConfig = require ("./webpack.dev.config" );const proConfig = require ("./webpack.pro.config" );module .exports = (env, argv ) => { let config = argv.mode === "development" ? devConfig : proConfig; return merge (baseConfig, config); };
build/webpack.dev.config.js
1 2 3 4 5 6 7 8 9 10 const HtmlWebpackPlugin = require ('html-webpack-plugin' )module .exports = { entry : './src/index.ts' , devtool : 'inline-source-map' , plugins : [ new HtmlWebpackPlugin ({ template : './src/index.html' }) ] }
build/webpack.pro.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 const { CleanWebpackPlugin } = require ('clean-webpack-plugin' )const nodeExternals = require ('webpack-node-externals' );module .exports = { entry : './src/main.ts' , output : { libraryTarget : 'umd' , library : 'EmployeeQuery' }, externals : [nodeExternals ()], plugins : [ new CleanWebpackPlugin () ] }
src/components/EmployeeQuery.vue
1 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 54 55 56 57 58 59 60 <template> <div class="employee-query"> <input placeholder="姓名" v-model.trim="tempName" /> <select v-model.number="tempSelected"> <option value="0">部门</option> <option v-for="option in department" :value="option.departmentId" :key="option.departmentId" > {{ option.department }} </option> </select> <button @click="query">查询</button> </div> </template> <script> import Vue from 'vue'; export default Vue.extend({ props: { name: { type: String, default: '', }, selected: { type: Number, default: 0, }, department: { type: Array, default: () => [], }, }, data() { return { tempName: this.name, tempSelected: this.selected, }; }, methods: { query() { this.$emit('query', { name: this.tempName, departmentId: this.tempSelected, }); }, }, }); </script> <style scoped> .employee-query { display: flex } input, select { margin-right: 10px } </style>
src/index.html
1 2 3 4 5 6 7 8 9 10 11 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <title > Document</title > </head > <body > <div id ="app" > </div > </body > </html >
src/index.ts (开发环境测试使用)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import Vue from "vue" ;import EmployeeQuery from './components/EmployeeQuery.vue' ;let app = new Vue ({ el : '#app' , components : { EmployeeQuery }, template : `<employee-query @query="getParam" :department="department" />` , data : { department : [ { department : '技术部' , departmentId : 1 }, { department : '产品部' , departmentId : 2 }, { department : '市场部' , departmentId : 3 }, { department : '运营部' , departmentId : 4 } ] }, methods : { getParam : function (param: any ) { console .log (param) } } });
src/main.ts (生产环境)
1 2 import EmployeeQuery from './components/EmployeeQuery.vue' ;export default EmployeeQuery ;
vue-shims.d.ts
1 2 3 4 declare module '*.vue' { import Vue from 'vue' ; export default Vue }
发布要用,并且要在package.json中配置
types/employee-query.d.ts
1 2 3 4 5 6 7 8 9 10 11 12 import Vue from 'vue' ;declare class EmployeeQuery extends Vue { name : string selected : number department : { department : string , departmentId : number }[] query (): void }export as namespace EmployeeQuery export = EmployeeQuery
package.json
1 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 { "name" : "dep-ts-vue-component" , "version" : "1.0.0" , "description" : "" , "main" : "./dist/employee-query.js" , "scripts" : { "start" : "webpack-dev-server --mode=development --config ./build/webpack.config.js" , "build" : "webpack --mode=production --config ./build/webpack.config.js" } , "keywords" : [ ] , "author" : "" , "license" : "ISC" , "devDependencies" : { "clean-webpack-plugin" : "^4.0.0" , "css-loader" : "^6.8.1" , "html-webpack-plugin" : "^5.5.3" , "ts-loader" : "^9.4.4" , "typescript" : "^5.2.2" , "vue-loader" : "^15.10.2" , "vue-template-compiler" : "^2.7.14" , "webpack" : "^5.88.2" , "webpack-cli" : "^5.1.4" , "webpack-dev-server" : "^4.15.1" , "webpack-merge" : "^5.9.0" , "webpack-node-externals" : "^3.0.0" } , "dependencies" : { "vue" : "^2.7.14" , "vue-class-component" : "^7.2.6" , "vue-property-decorator" : "^9.1.2" } , "types" : "./types/employee-query.d.ts" }
发布 先登陆
1 npm login --registry https://registry.npmjs.org/
因为配置了淘宝的镜像源,所以这里就用临时登陆
发布
报错因为报名重复
1 npm publish --registry https://registry.npmjs.org/
测试是否可用,在ts-vue文件中进行测试
1 npm i dep-ts-vue-component
index.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import Vue from "vue" ;import EmployeeQuery from 'dep-ts-vue-component' ;let app = new Vue ({ el : '#app' , components : { EmployeeQuery }, template : `<employee-query @query="getParam" :department="department" />` , data : { department : [ { department : '技术部' , departmentId : 1 }, { department : '产品部' , departmentId : 2 }, { department : '市场部' , departmentId : 3 }, { department : '运营部' , departmentId : 4 } ] }, methods : { getParam : function (param: any ) { console .log (param) } } });
运行后测试可以使用