├── .gitignore ├── 1.体验typescript └── 1.md ├── 2.基础类型和入门高级类型 ├── 2.md └── 2.ts ├── 3.泛型 ├── 3.md └── 3.ts ├── 4.高级类型 ├── 4.md └── 4.ts ├── 5.命名空间(namespace) ├── 5.md ├── 5.ts └── 51.ts ├── 6.如何写声明文件(declare) ├── 6.d.ts ├── 6.js ├── 6.md ├── 6.ts └── 8.md ├── 7."is"的用法 └── 7.md ├── LICENSE ├── README.md ├── abc.d.ts ├── index.js ├── package.json ├── test.js ├── tsconfig.json ├── yarn-error.log └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | -------------------------------------------------------------------------------- /1.体验typescript/1.md: -------------------------------------------------------------------------------- 1 | # 为vue3学点typescript(1), 体验typescript 2 | 看了**vue conf 2019**的视频, 特别兴奋, vue3要来了! 3 | vue3是用typescript开发的, 我揣测在vue的带领下typescript会成为主流呢, 要不先学点. 4 | 5 | ![](https://ws1.sinaimg.cn/large/005IQkzXly1g4kc7zn0nrj30ck07ugmh.jpg) 6 | 7 | ![](https://ws1.sinaimg.cn/large/005IQkzXly1g4kc89gd3fj30cu05ugm7.jpg) 8 | 9 | ## 19年最酷的前端技术 10 | 我是19年初开始使用的**typescript**, 自从开始用上了就喜欢上了, 真的爱不释手, 最爱他几点: 11 | 12 | 1. 很多小错误比如: 对象的**字段不存**在或者字段名字**拼写错误**, 编辑器会在写代码的时候就提示你, 降低出低级错误的几率. 13 | 2. 标注了类型的变量, 使用的时候编辑器都会列出变量上的方法和属性, 开发体验更舒服. 14 | 3. 很多大神的项目都用typescript开发, 看源码的时候因为有了类型标注, 更容易理解. 15 | 4. 让自己写的代码看起来很厉害的样子🐂. 16 | 17 | ![typescript代码](https://ws1.sinaimg.cn/large/005IQkzXly1g4k9jo1hkpj312k0gttcf.jpg) 18 | 19 | 我也希望更多的同行都开始用typescript, 让他成为前端涨工资的又一个工具(回想下webpack和vue给你带来什么, 最早"**吃螃蟹**"的人, 肯定**享受最大的红利**). 20 | 21 | 放几个我学习typescript过程中写的项目,自从用了typescript就特别喜欢造轮子, 写的不好, 就是有份热情, 抛砖引玉, 大家肯定能写出更好的. 22 | 23 | 手势库: https://github.com/any86/any-touch 24 | 25 | 命令式调用vue组件: https://github.com/any86/vue-create-root 26 | 27 | 工作中常用的一些代码片段: https://github.com/any86/useful-utils 28 | 29 | 一个mini的事件管理器: https://github.com/any86/any-event 30 | 31 | ## typescript工作原理 32 | 通过**typescript**命令行工具, 把**typescript**转成**javascript**, 从而支持在浏览器运行. 33 | 34 | 注: 后面的文章中**typescript**简称**ts**, **javascript**简称**js**. 35 | 36 | ## typescript特性 37 | ts和js最大的区别就是ts多了**类型注解**功能, 通过名字中的"**type**"也能看出语言的重点在"**类型**"上. 这个类型分为**基础类型**和**高级类型**, 高级类型就是通过**基础类型**组成的**自定义类型**. 38 | 39 | ##### 基础类型 40 | ts中包含了**boolean** / **number** / **string** / **object** / **数组(数组的表示有多种, 后续文章会展开)** / **元组** / **枚举** / **any** / **Undefined** / **Null** / **Void** / **Never** 41 | 42 | **any**是本文的重点, 一会会对他着重讲解. 43 | 44 | *补充说明*: 上面列出的类型, 是ts中表示类型的关键字, 其中`object`其实是包含**数组**/**元祖**/**枚举**, 在ts的概念中, 这个叫做**类型兼容**, 就是说数组类型数据, 也可以用**object**来标注: 45 | ```typescript 46 | let array:object = [12,321]; 47 | ``` 48 | 49 | 50 | ##### 高级类型 51 | 大部分情况是对**object**类型做更细的标注, 此处不多讲, 先放个例子了解即可, 知道关键词**interface**即可, 中文名"**接口**", 后续章节会展开. 52 | 53 | ```typescript 54 | interface Article { 55 | title: string; 56 | count: number; 57 | content: string; 58 | tags: string[]; // 数组里的元素都是字符串 59 | } 60 | ``` 61 | 62 | ##### 聪明的vscode 63 | 当我们使用**vscode**编辑器的时候, 编辑器会根据我们的"**类型注解**"进行**代码提示**和**错误提示**: 64 | 65 | ![](https://ws1.sinaimg.cn/large/005IQkzXly1g4k65l94s1j30k909r0tk.jpg) 66 | 67 | 类型写错了, 也会提示: 68 | 69 | ![](https://ws1.sinaimg.cn/large/005IQkzXly1g4k690h7d1j30qd06x0u7.jpg) 70 | 71 | 72 | ## 动手开始, 安装 73 | 1. [安装nodejs](https://nodejs.org) 74 | 2. 在命令行运行`npm i -g typescript`, 安装**编译器**到全局. 75 | 3. 安装[vscode编辑器](https://code.visualstudio.com/). 76 | 77 | ## 开始写代码 78 | 79 | ##### 生成js 80 | 1. 建立一个文件夹, 在里面新建一个hello.ts文件, 注意扩展名是**ts**. 81 | 2. 用**vscode**打开**hello.ts**文件. 82 | 3. 输入如下代码, 让我们体验下ts: 83 | ```typescript 84 | interface A { 85 | a:number, 86 | b:string 87 | } 88 | let obj:A = {a:123,b:'456'}; 89 | ``` 90 | 4. 命令行进入文件夹, 执行命令 91 | ```shell 92 | npx tsc hello.ts 93 | ``` 94 | 好了我们可以看下文件内部多了一个**hello.js**, 打开看看: 95 | ```javascript 96 | var obj = { a: 123, b: '456' }; 97 | ``` 98 | 代码中的"**类型注解**"不见了 ,我们的ts被编译成js了, 是不是很神奇. 99 | 100 | ##### 错误提示 101 | ```typescript 102 | interface A { 103 | a:number, 104 | b:string 105 | } 106 | // 错误, 会提示b的类型错误, 应该为string类型 107 | let obj:A = {a:123,b:456}; 108 | ``` 109 | 110 | ## any类型 111 | any代表任意类型, 这个类型特别适合ts新手, 初期有些类型不知道如何表达, 我们就可以**暂时**使用any表达, 等熟练ts后再标注精准的类型. 112 | 113 | 下面的情况新手可能就不会了, 以为n标记为number, 但是循环中i大于5的时候就是字符串了, 所以ts就会提示错误. 114 | ```typescript 115 | let n:number; 116 | for(let i = 0;i<10;i++) { 117 | if(i <= 5) n = 10; 118 | else n = '100'; 119 | } 120 | // ts提示: 不能将类型“"100"”分配给类型“number” 121 | ``` 122 | 123 | 作为新手如果初期你不知道"**联合类型**"这个概念, 你就可以直接把**n**标记为**any**: 124 | ```typescript 125 | // 熟练后会是这么标记的 126 | // let n:string|number 127 | let n:any; 128 | for(let i = 0;i<10;i++) { 129 | if(i <= 5) n = 10; 130 | else n = '100'; 131 | } 132 | ``` 133 | 134 | ## 补充 135 | 突然想起如果我们的配置不一样,可能会看到不一样的提示, 所以补充下我的ts配置, 请大家学习的时候按照这个配置来学习本课程, 每个配置我都加了注释, 如果译文请在下方留言, 知无不言. 136 | 137 | 用法也很简单, tsconfig.json放在你项目的根目录即可: 138 | ```javascript 139 | // tsconfig.json 140 | { 141 | "compilerOptions": { 142 | // 不报告执行不到的代码错误。 143 | "allowUnreachableCode": true, 144 | // 必须标注为null类型,才可以赋值为null 145 | "strictNullChecks": true, 146 | // 严格模式, 强烈建议开启 147 | "strict": true, 148 | // 支持别名导入: 149 | // import * as React from "react" 150 | "esModuleInterop": true, 151 | // 目标js的版本 152 | "target": "es5", 153 | // 目标代码的模块结构版本 154 | "module": "es6", 155 | // 在表达式和声明上有隐含的 any类型时报错。 156 | "noImplicitAny": true, 157 | // 删除注释 158 | "removeComments": true, 159 | // 保留 const和 enum声明 160 | "preserveConstEnums": false, 161 | // 生成sourceMap 162 | "sourceMap": true, 163 | // 目标文件所在路径 164 | "outDir": "./lib", 165 | // 编译过程中需要引入的库文件的列表 166 | "lib": [ 167 | "dom", 168 | "es7" 169 | ], 170 | // 额外支持解构/forof等功能 171 | "downlevelIteration": true, 172 | // 是否生成声明文件 173 | "declaration": true, 174 | // 声明文件路径 175 | "declarationDir": "./lib", 176 | // 此处设置为node,才能解析import xx from 'xx' 177 | "moduleResolution": "node" 178 | }, 179 | // 入口文件 180 | "include": [ 181 | "src/main.ts" 182 | ] 183 | } 184 | ``` 185 | 186 | ## 总结 187 | 19年ts一定大火, 请大家放心学ts吧, 初期开发可以先用**any**体验ts, 慢慢学习1个月左右其实就可以实战了, 这篇后我也会在**本月陆续更新完本typescript的教程**, 保证大家在一个月内学会. -------------------------------------------------------------------------------- /2.基础类型和入门高级类型/2.md: -------------------------------------------------------------------------------- 1 | # 为vue3学点typescript(2), 基础类型和入门高级类型 2 | 3 | ## 导航 4 | 5 | ## 很重要 6 | 这一节很重要, 可以说是**ts的最核心**部分, 这一节学完其实就可以开始用ts写代码了, 想想typescript中的**type**, 再看看标题中的"**类型**"2字, 所以请大家务必认真. 7 | 8 | ## 什么是入门高级类型 9 | 因为高级类型的内容比较多, 但是有些基础类型的知识点还必须要用到高级类型的知识讲解才连贯, 所以本节课把最常用的高级类型提前讲解一下, 比如**接口/联合类型/交叉类型**. 10 | 11 | ## 基础类型 12 | ts中**基础类型**有如下几种:**boolean** / **number** / **string** / **object** / **数组** / **元组** / **枚举** / **any** / **undefined** / **null** / **void** / **never**, 下面我们一一举例学习: 13 | 14 | ### 字面量 15 | 介绍类型前,有一个**前置知识点**就是**字面量**, 字面量的意思就是直接声明, 而非**new**关键词实例化出来的数据: 16 | 17 | ```typescript 18 | // 字面量 19 | const n:number = 123; 20 | const s:string = '456'; 21 | const o:object = {a:1,b:'2'}; 22 | 23 | // 非字面量 24 | const n:Number = new Number(123); 25 | const s:String = new String('456'); 26 | const o:Object = new Object({a:1,b:'2'}); 27 | ``` 28 | 通过上面的例子, 大家应该看明白为什么ts中有些类型的**开头字母是小写**的了吧. 这是因为ts中用小写字母开头的类型代表字面量, 大写的是用来表示通过**new**实例化的数据. 29 | 30 | ### boolean 31 | **布尔类型**, 取值只有`true` / `false` 32 | ```typescript 33 | const IS_MOBILE:boolean = true; 34 | const IS_TABLE:boolean = false; 35 | ``` 36 | 37 | ### number 38 | **数字类型**, 整数/小数都包括, 同时支持2/8/10/16进制**字面量**. 39 | ```typescript 40 | let decLiteral: number = 6; 41 | let hexLiteral: number = 0xf00d; 42 | let binaryLiteral: number = 0b1010; 43 | let octalLiteral: number = 0o744; 44 | ``` 45 | 46 | ### string 47 | **字符串类型** 48 | ```typescript 49 | let s1:string = 'hello world!'; 50 | let s2:string = 'hello ${name}`; 51 | ``` 52 | 53 | ### 数组 54 | 数组有2种表示方式: 55 | 56 | 第1种, 通过在**指定类型**后面增加`[]`, 表示该数组内的元素都是该**指定类型**: 57 | ```typescript 58 | let numbers:number[] = [1,2,3,4,5]; 59 | // number|string代表联合类型, 下面的高级类型中会讲 60 | let numbers:(number|string)[] = [1,2,3,4,'5']; 61 | ``` 62 | 63 | 第2种, 通过泛型表示, `Array<元素类型>`, **泛型**会在后面课讲解, 先做了解即可: 64 | ```typescript 65 | let numbers:Array = [1,2,3,4,5]; 66 | ``` 67 | 68 | ### 元组(Tuple) 69 | **元组类型**表示一个已知**元素数量**和**类型**的**数组**, 各元素的类型不必相同: 70 | ```typescript 71 | let list1:[number, string] = [1, '2', 3]; // 错误, 数量不对, 元组中只声明有2个元素 72 | let list2:[number, string] = [1, 2]; // 错误, 第二个元素类型不对, 应该是字符串'2' 73 | let list3:[number, string] = ['1', 2]; // 错误, 2个元素的类型颠倒了 74 | let list4:[number, string] = [1, '2']; // 正确 75 | ``` 76 | 77 | ### 枚举(enum) 78 | **枚举是ts中有而js中没有的类型**, 编译后会被转化成**对象**, 默认元素的值从1开始, 如下面的`Color.Red`的值为1, 以此类推`Color.Green`为2, `Color.Blue`为3: 79 | ```typescript 80 | enum Color {Red, Green, Blue} 81 | // 等价 82 | enum Color {Red=1, Green=2, Blue=3} 83 | ``` 84 | 85 | 当然也可以自己手动赋值: 86 | ```typescript 87 | enum Color {Red=1, Green=2, Blue=4} 88 | ``` 89 | 90 | 并且我们可以反向通过值得到他的键值: 91 | ```typescript 92 | enum Color {Red=1, Green=2, Blue=4} 93 | Color[2] === 'Green' // true 94 | ``` 95 | 看下编译成js后的枚举代码, 你就明白为什么可以反向得到键值: 96 | ```javascript 97 | var Color; 98 | (function (Color) { 99 | Color[Color["Red"] = 0] = "Red"; 100 | Color[Color["Green"] = 1] = "Green"; 101 | Color[Color["Blue"] = 2] = "Blue"; 102 | })(Color || (Color = {})); 103 | // Color的值为: {0: "Red", 1: "Green", 2: "Blue", Red: 0, Green: 1, Blue: 2} 104 | ``` 105 | 106 | ### any(任意类型) 107 | any代表任意类型, 也就是说, 如果你不清楚变量是什么类型, 就可以用any进行标记, 比如引入一些比较老的js库, 没有声明类型, 使用的时候就可以标记为any类型, 这样ts就不会提示错误了. 当然不能所有的地方都用any, 那样ts就没有使用的意义了. 108 | ```typescript 109 | let obj:any = {}; 110 | // ts自己推导不出forEach中给obj增加了'a'和'b'字段. 111 | ['a', 'b'].forEach(letter=>{ 112 | obj[letter] = letter; 113 | }); 114 | 115 | // 但是因为标记了any, 所以ts认为a可能存在 116 | obj.a = 123 117 | ``` 118 | 119 | ### void 120 | **void**的意义和**any**相反, 表示不是任何类型, 一般出现在函数中, 用来标记函数没有返回值: 121 | ```typescript 122 | function abc(n:number):void{ 123 | console.log(n); 124 | } 125 | ``` 126 | 127 | void类型对应2个值, 一个是undefined,一个null: 128 | ```typescript 129 | const n1:void = undefined; 130 | const n2:void = null; 131 | ``` 132 | 133 | ### null 和 undefined 134 | 默认情况下null和undefined是所有类型的子类型, 比如: 135 | ```typescript 136 | const n1:null = 123; 137 | const n2:undefined = '123'; 138 | ``` 139 | **注意:** 这是因为默认情况下的编译选项**strictNullChecks**为false, 但是为了避免一些奇怪的问题出现, 我还是建议大家设置为true(编译选项设置的内容, 会在后面的课程讲解), 请用精准的类型去标注. 140 | 141 | 如果一个变量的值确实需要是`null`或者`undefined`, 可以像下面这么用, ts会自动根据`if/else`推导出正确类型: 142 | 143 | ```typescript 144 | // 这是"联合类型", 在"高级类型"中会有详细介绍, 表示n可能是undefined也可能是number 145 | let num: undefined|number; 146 | 147 | if(Math.random()>0.5) num = 1; 148 | 149 | if(undefined !== num) { 150 | num++; 151 | } 152 | ``` 153 | 154 | ### never 155 | **never**表示不可达, 用文字还真不好描述, 主要使用在`throw`的情况下: 156 | ```typescript 157 | function error():never{ 158 | throw '错了!'; 159 | } 160 | ``` 161 | 162 | ### object 163 | **object**表示非原始类型, 也就是除**number**/ **ss**/ **boolean**/ **symbol**/ **null**/ **undefined**之外的类型: 164 | ```typescript 165 | let o1:object = []; 166 | let o2:object = {a:1,b:2}; 167 | ``` 168 | 但是, 我们**实际上基本不用object**类型的, 因为他标注的非常不具体, 一般都用**接口**来标注更具体的对象类型, 请继续看下面的**接口**的内容. 169 | 170 | ## 高级类型入门 171 | 通过基础类型组合而来的, 我们可以叫他高级类型. 包含: **交叉类型** / **联合类型** / **接口**等等, 当然不止他们3个, 为了不让本节课太长造成读者疲劳, 本节只先讲这3个, 不过不要着急, **下节课会完结高级类型**. 172 | 173 | ### 接口(interface) 174 | 一种定义复杂类型的格式, 比如我们用对象格式存储一篇文章, 那么就可以用接口定义文章的**类型**: 175 | ```typescript 176 | interface Article { 177 | title: stirng; 178 | count: number; 179 | content:string; 180 | fromSite: string; 181 | } 182 | 183 | const article: Article = { 184 | title: '为vue3学点typescript(2), 类型', 185 | count: 9999, 186 | content: 'xxx...', 187 | fromSite: 'baidu.com' 188 | } 189 | ``` 190 | 在这种情况下,当我们给`article`赋值的时候, 如果**任何一个字段没有被赋值**或者**字段对应的数据类型不对**, ts都会提示错误, 这样就保证了我们写代码不会出现上述的小错误. 191 | 192 | #### 非必填(?) 193 | 还是上面的例子, `fromSite`的意思是文章来自那个网站,如果文章都是原创的, 该字段就不会有值, 但是如果我们不传又会提示错误, 怎么办? 194 | 这时候就需要标记`fromSite`字段为**非必填**, 用"?"标记: 195 | ```typescript 196 | interface Article { 197 | title: stirng; 198 | count: number; 199 | content:string; 200 | fromSite?: string; // 非必填 201 | } 202 | 203 | // 不会报错 204 | const article: Article = { 205 | title: '为vue3学点typescript(2), 类型', 206 | count: 9999, 207 | content: 'xxx...', 208 | } 209 | ``` 210 | 211 | #### 用接口定义函数 212 | 接口不仅可以定义对象, 还可以定义函数: 213 | ```typescript 214 | // 声明接口 215 | interface Core { 216 | (n:number, s:string):[number,string] 217 | } 218 | 219 | // 声明函数遵循接口定义 220 | const core:Core = (a,b)=>{ 221 | return [a,b]; 222 | } 223 | ``` 224 | 225 | #### 用接口定义类 226 | 先简单看下如何给类定义接口, 后面的课程具体讲类: 227 | ```typescript 228 | // 定义 229 | interface Animate { 230 | head:number; 231 | body:number; 232 | foot:number; 233 | eat(food:string):void; 234 | say(word:string):string; 235 | } 236 | 237 | // implements 238 | class Dog implements Animate{ 239 | head=1; 240 | body=1; 241 | foot=1; 242 | eat(food){ 243 | console.log(food); 244 | } 245 | say(word){ 246 | return word; 247 | } 248 | } 249 | ``` 250 | 251 | ### 交叉类型(&) 252 | 交叉类型是将多个类型合并为一个类型, 表示"**并且**"的关系,用`&`连接多个类型, 常用于对象合并: 253 | ```typescript 254 | interface A {a:number}; 255 | interface B {b:string}; 256 | 257 | const a:A = {a:1}; 258 | const b:B = {b:'1'}; 259 | const ab:A&B = {...a,...b}; 260 | ``` 261 | 262 | ### 联合类型(|) 263 | 交叉类型也是将多个类型合并为一个类型, 表示"**或**"的关系,用`|`连接多个类型: 264 | ```typescript 265 | function setWidth(el: HTMLElement, width: string | number) { 266 | el.style.width = 'number' === typeof width ? `${width}px` : width; 267 | } 268 | ``` 269 | 注意: 我这里标记了el为**HTMLElement**, 可以在typescript的仓库看到ts还定义了很多元素, 请自行浏览(不用背, 用的时候现查), 270 | https://github.com/microsoft/TypeScript/blob/master/lib/lib.dom.d.ts 271 | 272 | ## 总结 273 | 如果您看完了上面的所有知识点, 你就可以开始动手造轮子练习了, 加油. 下面是我用ts造的轮子, 如果喜欢请帮忙点下star, 谢谢. 274 | 275 | 手势库: https://github.com/any86/any-touch 276 | 277 | 命令式调用vue组件: https://github.com/any86/vue-create-root 278 | 279 | 工作中常用的一些代码片段: https://github.com/any86/useful-utils 280 | 281 | 一个mini的事件管理器: https://github.com/any86/any-event 282 | -------------------------------------------------------------------------------- /2.基础类型和入门高级类型/2.ts: -------------------------------------------------------------------------------- 1 | interface Animate { 2 | head: number; 3 | body: number; 4 | foot: number; 5 | eat(food: string): void; 6 | say(word: string): string; 7 | } 8 | 9 | class Dog implements Animate { 10 | head = 1; 11 | body = 1; 12 | foot = 1; 13 | 14 | eat(food:string) { 15 | console.log(food); 16 | } 17 | 18 | say(word:string) { 19 | return word; 20 | } 21 | } 22 | 23 | enum Color { Red, Green, Blue }; 24 | const b: object = Color; -------------------------------------------------------------------------------- /3.泛型/3.md: -------------------------------------------------------------------------------- 1 | ## 往期 2 | [第一课, 体验typescript](https://juejin.im/post/5d19ad6de51d451063431864) 3 | 4 | [第二课, 基础类型和入门高级类型](https://juejin.im/post/5d1af3426fb9a07ed4411a9b) 5 | 6 | [第三课, 为vue3学点typescript, 泛型](https://juejin.im/post/5d27f160e51d45108223fcf9) 7 | 8 | ## 插一课 9 | 本来打算接着上节课, 把高级类型都讲完, 但是写着写着我发现高级类型中, 有很多地方都需要泛型的知识, 那么先插一节泛型. 10 | 11 | ## 什么是"类型变量"和"泛型" 12 | **变量**的概念我们都知道, 可以表示任意数据, **类型变量**也一样, 可以表示**任意类型**: 13 | ```typescript 14 | // 在函数名后面用"<>"声明一个类型变量 15 | function convert(input:T):T{ 16 | return input; 17 | } 18 | ``` 19 | `convert`中参数我们标记为类型`T`, 返回值也标记为`T`, 从而表示了: 函数的输入和输出的类型一致. 这样使用了"类型变量"的函数叫做**泛型函数**, 那有"**泛型类**"吗? 20 | 21 | **注意**: `T`是我随便定义的, 就和变量一样, 名字你可以随便起, 只是建议都是大写字母,比如`U` / `RESULT`. 22 | 23 | ##### 泛型类 24 | ```typescript 25 | class Person { 26 | who: U; 27 | 28 | constructor(who: U) { 29 | this.who = who; 30 | } 31 | 32 | say(code:U): string { 33 | return this.who + ' :i am ' + code; 34 | } 35 | } 36 | ``` 37 | 在类名后面通过"<>"声明一个类型变量`U`, 类的方法和属性都可以用这个`U`, 接下来我们**使用下泛型类**: 38 | ```typescript 39 | let a = new Person('詹姆斯邦德'); 40 | a.say(007) // 错误, 会提示参数应该是个string 41 | a.say('007') // 正确 42 | ``` 43 | 我们传入了类型变量(`string)`,告诉ts这个类的`U`是`string`类型, 通过`Person`的定义, 我们知道`say`方法的参数也是`string`类型, 所以`a.say(007)`会报错, 因为007是`number`. 多以我们可以通过**传入类型变量来约束泛型**. 44 | 45 | ##### 自动推断类型变量的类型 46 | 其实我们也可以不指定类型变量为`string`, 因为ts可以根据实例化时传入的参数的类型推断出`U`为`string`类型: 47 | ```typescript 48 | let a = new Person('詹姆斯邦德'); 49 | // 等价 let a = new Person('詹姆斯邦德'); 50 | a.say(007) // 错误, 会提示参数应该是个string 51 | a.say('007') // 正确 52 | ``` 53 | 54 | ##### 泛型方法 55 | 其实方法和函数的定义方式一样: 56 | ```typescript 57 | class ABC{ 58 | // 输入T[], 返回T 59 | getFirst(data:T[]):T{ 60 | return data[0]; 61 | } 62 | } 63 | ``` 64 | 65 | ##### 泛型是什么? 66 | 说实话ts的文档我找了好几遍, 也没看到他给**泛型**正式做定义, **只是表达他是一种描述多种类型(类型范围)的格式**, 我觉得有点抽象, 我用自己的理解具象下: 用动态的类型(**类型变量**)描述函数和类的方式. 67 | 68 | ## 泛型类型 69 | 70 | 我们可以用**类型变量**去描述一个**类型**(类型范围), ts的数组类型`Array`本身就是一个泛型类型, 他需要传递具体的类型才能变的精准: 71 | ```typescript 72 | let arr : Array; 73 | arr = ['123']; // 错误, 提示数组中只可以有number类型 74 | arr = [123]; 75 | ``` 76 | 77 | 下面我们自己定义一个泛型类型, 就对开头的`convert`函数定义: 78 | ```typescript 79 | function convert(input:T):T{ 80 | return input; 81 | } 82 | 83 | // 定义泛型类型 84 | interface Convert { 85 | (input:T):T 86 | } 87 | 88 | // 验证下 89 | let convert2:Convert = convert // 正确不报错 90 | ``` 91 | 92 | ## 泛型接口 93 | 通过传入不同的类型参数, 让属性更灵活: 94 | ```typescript 95 | interface Goods{ 96 | id:number; 97 | title: string; 98 | size: T; 99 | } 100 | 101 | let apple:Goods = {id:1,title: '苹果', size: 'large'}; 102 | let shoes:Goods = {id:1,title: '苹果', size: 43}; 103 | ``` 104 | 105 | ## 扩展类型变量(泛型约束) 106 | 107 | ```typescript 108 | function echo(input: T): T { 109 | console.log(input.name); // 报错, T上不确定是否由name属性 110 | return input; 111 | } 112 | ``` 113 | 前面说过T可以代表任意类型, 但对应的都是基础类型, 所以当我们操作`input.name`的时候就需要标记input上有name属性, 这样就相当于我们**缩小了类型变量的范围, 对泛型进行了约束**: 114 | ```typescript 115 | // 现在T是个有name属性的类型 116 | function echo(input: T extends {name:string}): T { 117 | console.log(input.name); // 正确 118 | return input; 119 | } 120 | ``` 121 | 122 | ## 一个泛型的应用, 工厂函数 123 | ```typescript 124 | function create(O: {new(): T|U; }): T|U { 125 | return new O(); 126 | } 127 | ``` 128 | 129 | 主要想说3个知识点: 130 | 1. 可以定义多个类型变量. 131 | 2. 类型变量和普通类型用法一直, 也支持联合类型/交叉类型等类型. 132 | 3. 如果一个数据是可以实例化的, 我们可以用`{new(): any}`表示. 133 | 134 | ## 不要乱用泛型 135 | 泛型主要是为了约束, 或者说缩小类型范围, 如果不能约束功能, 就代表不需要用泛型: 136 | ```typescript 137 | function convert(input:T[]):number{ 138 | return input.length; 139 | } 140 | ``` 141 | 这样用泛型就没有什么意义了, 和`any`类型没有什么区别. 142 | 143 | ## 总结 144 | 泛型是编译型语言最重要的特性, 泛型写的好就会让人觉得代码很高级, 可以说泛型是一个成手ts程序员必须熟练的技巧, 面试的时候是加分项, 所以大家写代码多多用泛型练习哦, 加油ヾ(◍°∇°◍)ノ゙. -------------------------------------------------------------------------------- /3.泛型/3.ts: -------------------------------------------------------------------------------- 1 | interface Goods{ 2 | id:number; 3 | title: string; 4 | size: T; 5 | } 6 | 7 | let apple:Goods = {id:1,title: '苹果', size: 'large'}; 8 | let shoes:Goods = {id:1,title: '苹果', size: 43}; 9 | 10 | 11 | let obj:object = [12,321] -------------------------------------------------------------------------------- /4.高级类型/4.md: -------------------------------------------------------------------------------- 1 | # 为vue3学点typescript, 解读高级类型 2 | 3 | ## 直达 4 | [第一课, 体验typescript](https://juejin.im/post/5d19ad6de51d451063431864) 5 | 6 | [第二课, 基础类型和入门高级类型](https://juejin.im/post/5d1af3426fb9a07ed4411a9b) 7 | 8 | [第三课, 泛型](https://juejin.im/post/5d27f160e51d45108223fcf9) 9 | 10 | [第四课, 解读高级类型](https://juejin.im/post/5d3fe80fe51d456206115987) 11 | 12 | 13 | ## 回顾 14 | 第二课的时候为了更好的讲解**基础类型**, 所以我们讲解了**一部分高级类型**, 比如"接口( **interface** )" / "联合类型( **|** )" / "交叉类型( **&** )", **本节课我会把剩余高级类型都讲完**. 15 | 16 | ## 知识点摘要 17 | 本节课主要关键词为: **自动类型推断** / **类型断言** / **类型别名(type)** / **映射类型(Pick/Record等...)** / **条件类型(extends)** / **类型推断(infer)** 18 | 19 | ## 自动类型推断(不用你标类型了,ts自己猜) 20 | 第二课我们讲了那么多基础类型, 大家现在写ts的时候一定会在每个变量后面都加上类型吧? 但是? 21 | 22 | 现在告诉大家**有些情况下你不需要标注类型**, ts可以根据你写的代码来**自动推断出类型**: 23 | 24 | ##### 赋值字面量给变量 25 | ```typescript 26 | 27 | let n = 1; // ts会自动推断出n是number类型 28 | n+=3 // 不报错,因为已知类型 29 | 30 | let arr1 = []; // 类型为: any[] 31 | arr1.push(1,2,{o:3}); // 报错, 32 | 33 | let arr = [1]; // 内部要有数字, 才能推断出正确类型 34 | arr.push(2); 35 | 36 | ``` 37 | 38 | ##### 自动阅读if条件判断 39 | ```typescript 40 | let n: number|null = 0.5 < Math.random() ? 1:null; 41 | if(null !== n){ 42 | n+=3 // ts知道现在n不是null是number 43 | } 44 | ``` 45 | 46 | ##### 浏览器自带api 47 | ```typescript 48 | document.ontouchstart = ev=>{ // 能自动推断出ev为TouchEvent 49 | console.log(ev.touches); // 不报错, TouchEvent上有touches属性 50 | } 51 | ``` 52 | 53 | ##### typeof 54 | `typeof`就是js中的`typeof`, ts会根据你代码中出现的`typeof`来自动推断类型: 55 | ```typescript 56 | let n:number|string = 0.5 < Math.random()? 1:'1'; 57 | 58 | // 如果没有typeof, n*=2会报错, 提示没法推断出当前是number类型, 不能进行乘法运算 59 | if('number' === typeof n) { 60 | n*= 2; 61 | } else { 62 | n= '2'; 63 | } 64 | ``` 65 | **注意**: 在ts文档中, 该部分的知识点叫做**typeof类型保护**, 和其他类型推断的内容是分开的, 被写在**高级类型/类型保护**章节中. 66 | 67 | ##### instanceof 68 | ts会根据你代码中出现的`instanceof`来自动推断类型: 69 | ```typescript 70 | let obj = 0.5 < Math.random() ? new String(1) : new Array(1); 71 | if(obj instanceof String){ 72 | // obj推断为String类型 73 | obj+= '123' 74 | } else { 75 | // obj为any[]类型 76 | obj.push(123); 77 | } 78 | ``` 79 | **注意**: 在ts文档中, 该部分的知识点叫做**instanceof类型保护**, 和其他类型推断的内容是分开的, 被写在**高级类型/类型保护**章节中. 80 | 81 | 82 | 83 | ## 类型断言(你告诉ts是什么类型, 他都信) 84 | 有些情况下系统没办法自动推断出正确的类型, 就需要我们标记下, 断言有2种语法, 一种是通过"**<>**", 一种通过"**as**", 举例说明: 85 | 86 | ```typescript 87 | let obj = 0.5 < Math.random() ? 1 : [1]; // number|number[] 88 | 89 | // 断言, 告诉ts, obj为数组 90 | (obj).push(1); 91 | 92 | //等价 93 | (obj as number[]).push(1); 94 | ``` 95 | 96 | ## 类型别名(type) 97 | 类型别名可以表示很多接口表示不了的类型, 比如字面量类型(常用来校验取值范围): 98 | ```typescript 99 | type A = 'top'|'right'|'bottom'|'left'; // 表示值可能是其中的任意一个 100 | type B = 1|2|3; 101 | type C = '红'|'绿'|'黄'; 102 | type D = 150; 103 | 104 | let a:A = 'none'; // 错误, A类型中没有'none' 105 | ``` 106 | 107 | ##### 更多组合: 108 | ```typescript 109 | interface A1{ 110 | a:number; 111 | } 112 | type B = A1 | {b:string}; 113 | type C = A1 & {b:string}; 114 | 115 | // 与泛型组合 116 | type D = A1 | T[]; 117 | ``` 118 | 119 | ## 索引类型(keyof) 120 | js中的`Object.keys`大家肯定都用过, **获取对象的键值**, ts中的`keyof`和他类似, 可以用来获取对象类型的键值: 121 | ```typescript 122 | type A = keyof {a:1,b:'123'} // 'a'|'b' 123 | type B = keyof [1,2] // '0'|'1'|'push'... , 获取到内容的同时, 还得到了Array原型上的方法和属性(实战中暂时没遇到这种需求, 了解即可) 124 | ``` 125 | 可以获得键值, 也可以获取对象类型的值的类型: 126 | ```typescript 127 | type A = {a:1,b:'123'}; 128 | type C = A['a'] // 等于type C = 1; 129 | let c:C = 2 // 错误, 值只能是1 130 | ``` 131 | 132 | ## 映射类型(Readonly, Pick, Record等...) 133 | 映射类型比较像修改类型的工具函数, 比如`Readonly`可以把每个属性都变成只读: 134 | ```typescript 135 | type A = {a:number, b:string} 136 | type A1 = Readonly // {readonly a: number;readonly b: string;} 137 | ``` 138 | 打开**node_modules/typescript/lib**文件夹可以找到`lib.es5.d.ts`, 在这我们能找到`Readonly`的定义: 139 | ```typescript 140 | type Readonly = { 141 | readonly [P in keyof T]: T[P]; 142 | }; 143 | ``` 144 | 其实不是很复杂, 看了本节课前面前面的内容, 这个很好理解是吧: 145 | 1. 定义一个支持泛型的类型别名, 传入类型参数`T`. 146 | 2. 通过`keyof`获取`T`上的键值集合. 147 | 3. 用`in`表示循环`keyof`获取的键值. 148 | 4. 添加`readonly`标记. 149 | 150 | ##### Partial\, 让属性都变成可选的 151 | ```typescript 152 | type A = {a:number, b:string} 153 | type A1 = Partial // { a?: number; b?: string;} 154 | ``` 155 | 156 | ##### Required\, 让属性都变成必选 157 | ```typescript 158 | type A = {a?:number, b?:string} 159 | type A1 = Required // { a: number; b: string;} 160 | ``` 161 | 162 | ##### Pick, 只保留自己选择的属性, U代表属性集合 163 | ```typescript 164 | type A = {a:number, b:string} 165 | type A1 = Pick // {a:number} 166 | ``` 167 | 168 | ##### Omit 实现排除已选的属性 169 | ```typescript 170 | type A = {a:number, b:string} 171 | type A1 = Omit // {b:string} 172 | ``` 173 | 174 | ##### Record, 创建一个类型,T代表键值的类型, U代表值的类型 175 | ```typescript 176 | type A1 = Record // 等价{[k:string]:string} 177 | ``` 178 | 179 | ##### Exclude, 过滤T中和U相同(或兼容)的类型 180 | ```typescript 181 | type A = {a:number, b:string} 182 | type A1 = Exclude // number 183 | 184 | // 兼容 185 | type A2 = Exclude // never , 因为any兼容number, 所以number被过滤掉 186 | ``` 187 | 188 | ##### Extract, 提取T中和U相同(或兼容)的类型 189 | ```typescript 190 | type A = {a:number, b:string} 191 | type A1 = Extract // string 192 | ``` 193 | 194 | ##### NonNullable, 剔除T中的undefined和null 195 | ```typescript 196 | type A1 = NonNullable // number|string 197 | ``` 198 | 199 | ##### ReturnType, 获取T的返回值的类型 200 | ```typescript 201 | type A1= ReturnType<()=>number> // number 202 | ``` 203 | 204 | ##### InstanceType, 返回T的实例类型 205 | ts中类有2种类型, 静态部分的类型和实例的类型, 所以`T`如果是构造函数类型, 那么`InstanceType`可以返回他的实例类型: 206 | 207 | ```typescript 208 | interface A{ 209 | a:HTMLElement; 210 | } 211 | 212 | interface AConstructor{ 213 | new():A; 214 | } 215 | 216 | function create (AClass:AConstructor):InstanceType{ 217 | return new AClass(); 218 | } 219 | ``` 220 | 221 | ##### Parameters 获取函数参数类型 222 | 返回类型为元祖, 元素顺序同参数顺序. 223 | 224 | ```typescript 225 | interface A{ 226 | (a:number, b:string):string[]; 227 | } 228 | 229 | type A1 = Parameters // [number, string] 230 | ``` 231 | 232 | 233 | ##### ConstructorParameters 获取构造函数的参数类型 234 | 和`Parameters`类似, 只是`T`这里是构造函数类型. 235 | 236 | ```typescript 237 | interface AConstructor{ 238 | new(a:number):string[]; 239 | } 240 | 241 | type A1 = ConstructorParameters // [number] 242 | ``` 243 | 244 | 245 | ## extends(条件类型) 246 | ```typescript 247 | T extends U ? X : Y 248 | ``` 249 | 用来表示类型是不确定的, 如果`U`的类型可以表示`T`, 那么返回`X`, 否则`Y`. 举几个例子: 250 | ```typescript 251 | type A = string extends '123' ? string :'123' // '123' 252 | type B = '123' extends string ? string :123 // string 253 | ``` 254 | 明显`string`的范围更大, `'123'`可以被`string`表示, 反之不可. 255 | 256 | 257 | ## infer(类型推断) 258 | 单词本身的意思是"推断", 实际表示在`extends`条件语句中**声明**待推断的类型变量. 我们上面介绍的**映射类型**中就有很多都是ts在`lib.d.ts`中实现的, 比如`Parameters`: 259 | 260 | ```typescript 261 | type Parameters any> = T extends (...args: infer P) => any ? P : never; 262 | ``` 263 | 264 | 上面声明一个`P`用来表示`...args`可能的类型, 如果`(...args: infer P)`可以**表示** `T`, 那么返回`...args`对应的类型, 也就是函数的参数类型, 反之返回`never`. 265 | 266 | **注意:** 开始的`T extends (...args: any) => any`用来校验输入的`T`是否是函数, 如果不是ts会报错, 如果直接替换成`T`不会有报错, 会一直返回`never`. 267 | 268 | ##### 应用infer 269 | 接下来我们利用`infer`来实现"删除元祖类型中第一个元素", 这常用于简化函数参数, [这有一个我之前的应用](https://github.com/any86/vue-create-root/blob/master/src/main.ts#L29) 270 | 271 | ```typescript 272 | export type Tail = ((...args: Tuple) => void) extends ((a: any, ...args: infer T) => void) ? T : never; 273 | ``` 274 | 275 | ## 总结 276 | 多写多练, 很快就上手, 放几个我用ts写的项目当做参考, 抛砖引玉, 加油! 277 | 278 | 手势库: https://github.com/any86/any-touch 279 | 280 | 命令式调用vue组件: https://github.com/any86/vue-create-root 281 | 282 | 工作中常用的一些代码片段: https://github.com/any86/useful-utils 283 | 284 | 一个mini的事件管理器: https://github.com/any86/any-event -------------------------------------------------------------------------------- /4.高级类型/4.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/any86/ts-class/a70c54df01b3923394f74d44976d8eb3db21efa7/4.高级类型/4.ts -------------------------------------------------------------------------------- /5.命名空间(namespace)/5.md: -------------------------------------------------------------------------------- 1 | # 为 Vue3 学点 TypeScript, 命名空间(namespace) 2 | 3 | ## 往期目录 4 | [第一课, 体验typescript](https://juejin.im/post/5d19ad6de51d451063431864) 5 | 6 | [第二课, 基础类型和入门高级类型](https://juejin.im/post/5d1af3426fb9a07ed4411a9b) 7 | 8 | [第三课, 泛型](https://juejin.im/post/5d27f160e51d45108223fcf9) 9 | 10 | [第四课, 解读高级类型](https://juejin.im/post/5d3fe80fe51d456206115987) 11 | 12 | [第五课, 命名空间(namespace)是什么](https://juejin.im/post/5d5d04dfe51d4561af16dd24) 13 | 14 | 15 | ## 什么时候要用命名空间? 16 | 如果你发现自己写的功能(函数/类/接口等...)越来越多, 你想对他们进行**分组管理**就可以用**命名空间**, 下面先用"**类**"举例: 17 | 18 | ``` typescript 19 | namespace Tools { 20 | const TIMEOUT = 100; 21 | 22 | export class Ftp { 23 | constructor() { 24 | setTimeout(() => { 25 | console.log('Ftp'); 26 | }, TIMEOUT) 27 | } 28 | } 29 | 30 | export class Http { 31 | constructor() { 32 | console.log('Http'); 33 | } 34 | } 35 | 36 | export function parseURL(){ 37 | console.log('parseURL'); 38 | } 39 | } 40 | ``` 41 | 仔细看你会发现`namespace`下还有`export`, `export`在这里用来表示哪些功能是可以外部访问的: 42 | 43 | ```typescript 44 | Tools.TIMEOUT // 报错, Tools上没有这个属性 45 | Tools.parseURL() // 'parseURL' 46 | ``` 47 | 48 | 最后我们看下编译成js后的代码: 49 | ```javascript 50 | "use strict"; 51 | var Tools; 52 | (function (Tools) { 53 | const TIMEOUT = 100; 54 | class Ftp { 55 | constructor() { 56 | setTimeout(() => { 57 | console.log('Ftp'); 58 | }, TIMEOUT); 59 | } 60 | } 61 | Tools.Ftp = Ftp; 62 | class Http { 63 | constructor() { 64 | console.log('Http'); 65 | } 66 | } 67 | Tools.Http = Http; 68 | function parseURL() { 69 | console.log('parseURL'); 70 | } 71 | Tools.parseURL = parseURL; 72 | })(Tools || (Tools = {})); 73 | ``` 74 | 看js代码能发现, 在js中命名空间其实就是一个全局对象. 如果你开发的程序想要暴露一个全局变量就可以用`namespace`; 75 | 76 | ## 如何用命名空间来管理类型? 77 | 命名空间不仅可以用在逻辑代码中, 也可以用在**类型**, 用来给类型分组: 78 | ```typescript 79 | 80 | namespace Food { 81 | export type A = Window; 82 | export interface Fruits{ 83 | taste: string; 84 | hardness: number; 85 | } 86 | 87 | export interface Meat{ 88 | taste: string; 89 | heat: number; 90 | } 91 | } 92 | 93 | let meat: Food.Meat; 94 | let fruits: Food.Fruits; 95 | ``` 96 | 97 | ## 如何引入写好的命名空间? 98 | 有2种方式, 一种`/// `, 还有就是`import`: 99 | 100 | ### 通过 "/// " 导入 101 | 通过`reference`进行导入相当于`xxx.ts`文件内的命名空间和当前文件进行了合并: 102 | 103 | ###### xxx.ts 104 | ```typescript 105 | // xxx.ts 106 | namespace Food { 107 | export interface Fruits{ 108 | taste: string; 109 | hardness: number; 110 | } 111 | } 112 | ``` 113 | 114 | ###### yyy.ts 115 | ```typescript 116 | // yyy.ts 117 | 118 | 119 | let meat: Food.Meat; 120 | let fruits: Food.Fruits; 121 | ``` 122 | 现在在`yyy.ts`中我们就可以直接使用`xxx.ts`中的`Food`类型了, 而不需要使用`import`. 123 | 124 | ### 通过import导入 125 | 如果命名空间是用`export`导出的, 那么使用的时候就不可以用`/// `了, 要用`import`导入: 126 | 127 | ###### xxx.ts 128 | ```typescript 129 | // xxx.ts 130 | // 使用export导出 131 | export interface Fruits{ 132 | taste: string; 133 | hardness: number; 134 | } 135 | 136 | export interface Meat{ 137 | taste: string; 138 | heat: number; 139 | } 140 | ``` 141 | 142 | ###### yyy.ts 143 | ```typescript 144 | // yyy.ts 145 | import {Food} from './xxx'; // 使用import导入 146 | let meat: Food.Meat; 147 | let fruits: Food.Fruits; 148 | ``` 149 | 150 | ## 如何合并多个命名空间 151 | 我们知道**接口**是可以合并的, **命名空间**也是可以的, 下面我们把`Vegetables`类型合并到`Food`类型中: 152 | ###### xxx.ts 153 | ```typescript 154 | // xxx.ts 155 | namespace Food { 156 | export interface Fruits{ 157 | taste: string; 158 | hardness: number; 159 | } 160 | } 161 | ``` 162 | 163 | ###### yyy.ts 164 | ```typescript 165 | // yyy.ts 166 | 167 | namespace Food { 168 | export interface Vegetables{ 169 | title: string; 170 | heat: number; 171 | } 172 | } 173 | 174 | type Vh = Food.Vegetables['heat'] // number; 175 | ``` 176 | 177 | ## export= 178 | 179 | 如果你的`tsconfig`中设置了`"module": "umd",`, 那么`export = Food`等价于`export default Food`, `export=`常见于支持**umd**的插件的声明文件. 180 | 181 | ## 命名空间在lodash里的应用 182 | 其实我们看下那些老牌插件(jq/lodash)里使用`namespace`特性的代码, 可以发现主要是在声明文件中(xxx.d.ts), 用来表示暴露出来的全局变量(比如lodash的"_"). 183 | 184 | ![](https://ws1.sinaimg.cn/large/005IQkzXly1g67dbezdx1j30lo0hatd2.jpg) 185 | 186 | ## 关于声明文件 187 | 上面为了解释**命名空间**提及了**声明文件(xxx.d.ts)**, 但由于**声明**(declare)的内容很多, 所以我会在下一节详细介绍. 188 | 189 | ## 总结 190 | 其实如果你的项目直接是用ts写的可能用不上`namespace`, 毕竟`export`就可以产生**模块**, 模块天然就有隔离分组作用. 191 | 192 | 能力有限, 如果路过大神看到不对的地方还请多多指点, 我必虚心接受. 193 | 194 | 最后建议大家多写多练, 祝大家早日上手ts, 放几个我用ts写的项目当做参考, 抛砖引玉, 加油! 195 | 196 | 手势库, 支持点击/拖拽/旋转/缩放: https://github.com/any86/any-touch 197 | 198 | 为vue组件生成this.$xxx的命令: https://github.com/any86/vue-create-root 199 | -------------------------------------------------------------------------------- /5.命名空间(namespace)/5.ts: -------------------------------------------------------------------------------- 1 | 2 | namespace Food { 3 | export type A = Window; 4 | export interface Fruits{ 5 | taste: string; 6 | hardness: number; 7 | } 8 | 9 | export interface Meat{ 10 | taste: string; 11 | heat: number; 12 | } 13 | 14 | } 15 | 16 | export default Food; 17 | 18 | namespace Tools { 19 | const TIMEOUT = 100; 20 | export class Ftp { 21 | constructor() { 22 | setTimeout(() => { 23 | console.log('Ftp'); 24 | }, TIMEOUT) 25 | } 26 | } 27 | 28 | export class Http { 29 | constructor() { 30 | console.log('Http'); 31 | } 32 | } 33 | 34 | export function parseURL(){ 35 | console.log('parseURL'); 36 | } 37 | } -------------------------------------------------------------------------------- /5.命名空间(namespace)/51.ts: -------------------------------------------------------------------------------- 1 | 2 | import Food from "./5"; 3 | 4 | // import q = Tools 5 | 6 | type Vh = Food.Fruits -------------------------------------------------------------------------------- /6.如何写声明文件(declare)/6.d.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/any86/ts-class/a70c54df01b3923394f74d44976d8eb3db21efa7/6.如何写声明文件(declare)/6.d.ts -------------------------------------------------------------------------------- /6.如何写声明文件(declare)/6.js: -------------------------------------------------------------------------------- 1 | MyPlugin.say('123'); 2 | -------------------------------------------------------------------------------- /6.如何写声明文件(declare)/6.md: -------------------------------------------------------------------------------- 1 | # 为 Vue3 🔥 学点 TypeScript, 什么是声明文件(declare)? [🦕全局声明篇] 2 | 3 | ## 往期目录 4 | 5 | [第一课, 体验typescript](https://juejin.im/post/5d19ad6de51d451063431864) 6 | 7 | [第二课, 基础类型和入门高级类型](https://juejin.im/post/5d1af3426fb9a07ed4411a9b) 8 | 9 | [第三课, 泛型](https://juejin.im/post/5d27f160e51d45108223fcf9) 10 | 11 | [第四课, 解读高级类型](https://juejin.im/post/5d3fe80fe51d456206115987) 12 | 13 | [第五课, 命名空间(namespace)是什么](https://juejin.im/post/5d5d04dfe51d4561af16dd24) 14 | 15 | [特别篇, 在vue3🔥源码中学会typescript🦕 - "is"](https://juejin.im/post/5da6d1aae51d4524ad10d1d8) 16 | 17 | [第六课, 什么是声明文件(declare)? 🦕 - 全局声明篇](https://juejin.im/post/5dcbc9e2e51d451bcb39f123) 18 | 19 | 20 | ## 全局声明篇 21 | 年底比较忙🔥, 受个人时间限制, 暂把"声明"部分的内容分为"**全局声明篇**"和"**模块声明篇**", 👷还请多多包涵, 本次先说"**全局**". 22 | 23 | ## 什么是声明文件? 24 | **声明文件就是给js代码补充类型标注**. 这样在ts编译环境下就不会提示js文件"缺少类型". 25 | 26 | 声明变量使用关键字`declare`来表示声明其后面的**全局**变量的类型, 比如: 27 | 28 | ``` typescript 29 | // packages/global.d.ts 30 | declare var __DEV__: boolean 31 | declare var __TEST__: boolean 32 | declare var __BROWSER__: boolean 33 | declare var __RUNTIME_COMPILE__: boolean 34 | declare var __COMMIT__: string 35 | declare var __VERSION__: string 36 | ``` 37 | 38 | 看过vue3源码的同学一定知道这些是vue中的变量, 上面代码表示`__DEV__`等变量是全局, 并且标注了他们的类型. 这样无论在项目中的哪个ts文件中使用`__DEV__`, 变量ts编译器都会知道他是`boolean`类型. 39 | 40 | ## 声明文件在哪里? 41 | 首先声明文件的文件名是有规范要求的, 必须以`.d.ts`结尾, 看了上面的代码你肯定想练习写下声明文件, 但是你可能想问了"**写完放在哪里**", 网上说声明文件放在项目里的**任意路径\/文件名**都可以被ts编译器识别, 但实际开发中发现, 为了规避一些奇怪的问题, **推荐放在根目录下**. 42 | 43 | ![1.png](https://user-gold-cdn.xitu.io/2019/11/13/16e640c86bce4486?w=363&h=273&f=jpeg&s=44889) 44 | 45 | 46 | 47 | ## 别人写好的声明文件( @types/xxx ) 48 | 一般比较大牌的第三方js插件在npm上都有对应的声明文件, 比如jquery的声明文件就可以在npm上下载: 49 | ```shell 50 | npm i @types/jquery 51 | ``` 52 | 53 | `npm i @types/jquery`中的jquery可以换成任意js库的名字, 当然**前提是有人写了对应的声明文件发布到了npm**. 54 | 55 | **安装后**, 我们可以在`node_modules/@types/jquery`中的看到声明文件, 这里我打开`mise.d.ts`文件: 56 | 57 | ![](https://user-gold-cdn.xitu.io/2019/11/13/16e640c86c1f2d63?w=849&h=436&f=jpeg&s=217731) 58 | 59 | ## 声明文件对纯js项目有什么帮助? 60 | 即便你只写js代码, 也可以安装声明文件, 因为如果你用的是**vscode**, 那么他会自动分析js代码, 如果存在对应的声明文件, vscode会把声明文件的内容作为**代码提示**. 61 | 62 | `jquery`在安装了声明文件后 63 | 64 | ![1.png](https://user-gold-cdn.xitu.io/2019/11/13/16e640c86cd65f35?w=798&h=381&f=jpeg&s=121858) 65 | 66 | ## 什么情况下要自己写声明文件? 67 | 如果"@types/"下找不到声明文件, 那么就需要我们自己手写了. 68 | 69 | ## 🔥如何写声明文件? 70 | 71 | 声明文件分2大类, 一类是全局声明, 一类是对模块的声明. 这节只说"全局". 72 | 73 | ### 全局声明 74 | 通过`declare`我们可以标注js全局变量的类型. 75 | 76 | #### 简单应用 77 | ```typescript 78 | // global.d.ts 79 | declare var n: number; 80 | declare let s: string; 81 | declare const o: object; 82 | declare function f(s: string): number; 83 | declare enum dir { 84 | top, 85 | right, 86 | bottom, 87 | left 88 | } 89 | ``` 90 | 91 | 声明之后,我们就可以在任意文件中直接操作变量: 92 | ```typescript 93 | n = 321 94 | s = '文字' 95 | let o1 = o; 96 | f('123').toFixed(); 97 | dir.bottom.toFixed(); 98 | 99 | // 报错 100 | n = '312' 101 | s = 123 102 | ``` 103 | 104 | #### declare namespace 105 | 这个`namespace`代表后面的全局变量是一个对象: 106 | ```typescript 107 | // global.d.ts 108 | declare namespace MyPlugin { 109 | var n:number; 110 | var s:string; 111 | var f:(s:string)=>number; 112 | } 113 | ``` 114 | 115 | ```typescript 116 | MyPlugin.s.substr(0,1); 117 | MyPlugin.n.toFixed(); 118 | MyPlugin.f('文字').toFixed(); 119 | 120 | // 报错 121 | MyPlugin.s.toFixed(); 122 | MyPlugin.n.substr(0,1); 123 | MyPlugin.f(123); 124 | ``` 125 | 126 | #### 修改已存在的全局声明 127 | 其实我们安装完t**ypescript**, 会自动给我们安装一些系统变量的声明文件, 存在`node_modules/typescript/lib`下. 128 | 129 | ![](https://user-gold-cdn.xitu.io/2019/11/13/16e640c86d3f92bf?w=363&h=507&f=jpeg&s=101622) 130 | 131 | 如果你要修改**已存在**的全局变量的声明可以这么写, 下面用node下的`global`举例, 132 | ```typescript 133 | declare global { 134 | interface String { 135 | hump(input: string): string; 136 | } 137 | } 138 | // 注意: 修改"全局声明"必须在模块内部, 所以至少要有 export{}字样 139 | // 不然会报错❌: 全局范围的扩大仅可直接嵌套在外部模块中或环境模块声明中 140 | export {} 141 | ``` 142 | 143 | 现在`String`类型在vscode的语法提示下多了一个`hump`的方法,不过我们只是声明, 并没有用js实现, 所以运行会报错, 所以不要忘了写js的实现部分哦. 144 | 145 | ![1.png](https://user-gold-cdn.xitu.io/2019/11/13/16e640c86cc0d727?w=397&h=132&f=jpeg&s=23343) 146 | 147 | 148 | ## 总结 149 | 年底真是比较忙, 但是之前答应了更新ts的内容, 所以只能一步步完成"声明"部分的内容, 还请见谅👷. 150 | 151 | 多写多练, 很快就上手, 放几个我用ts写的项目当做参考, 抛砖引玉, 加油! 152 | 153 | **✋ 移动/pc端手势库, 支持: tap/press/pan/swipe/rotate/pinch** 154 | 155 | https://github.com/any86/any-touch 156 | 157 | 158 | **🍭 把vue组件变成this.$xxx这样的命令** 159 | 160 | https://github.com/any86/vue-create-root 161 | 162 | ## 微信群 163 | 感谢大家的阅读, 如有疑问可以加我微信, 我拉你进入**微信群**(由于腾讯对微信群的100人限制, 超过100人后必须由群成员拉入) 164 | 165 | ![](https://user-gold-cdn.xitu.io/2019/9/19/16d474d245b69492?w=512&h=512&f=jpeg&s=27137) -------------------------------------------------------------------------------- /6.如何写声明文件(declare)/6.ts: -------------------------------------------------------------------------------- 1 | n = 321 2 | s = '文字' 3 | let o1 = o; 4 | f('123').toFixed(); 5 | dir.bottom.toFixed(); 6 | MyPlugin.s.substr(0,1); 7 | MyPlugin.n.toFixed(); 8 | MyPlugin.f('文字').toFixed(); 9 | 10 | let abc:ABC = {a:1}; -------------------------------------------------------------------------------- /6.如何写声明文件(declare)/8.md: -------------------------------------------------------------------------------- 1 | # 为 Vue3 学点 TypeScript, 什么是声明文件(declare)? 2 | 3 | ## 直达 4 | [第一课, 体验typescript](https://juejin.im/post/5d19ad6de51d451063431864) 5 | 6 | [第二课, 基础类型和入门高级类型](https://juejin.im/post/5d1af3426fb9a07ed4411a9b) 7 | 8 | [第三课, 什么是泛型?](https://juejin.im/post/5d27f160e51d45108223fcf9) 9 | 10 | [第四课, 解读高级类型](https://juejin.im/post/5d3fe80fe51d456206115987) 11 | 12 | [第五课, 命名空间(namespace)是什么?](https://juejin.im/post/5d5d04dfe51d4561af16dd24) 13 | 14 | ## ## interface 和 type 15 | **注意**在.d.ts文件中`interface`和`type`关键字前面不需要`declare`即可代表他们是全局的: 16 | 17 | ## 总结 18 | 多写多练, 很快就上手, 放几个我用ts写的项目当做参考, 抛砖引玉, 加油! 19 | 20 | 手势库: https://github.com/any86/any-touch 21 | 22 | 命令式调用vue组件: https://github.com/any86/vue-create-root 23 | 24 | 工作中常用的一些代码片段: https://github.com/any86/useful-utils 25 | 26 | 一个mini的事件管理器: https://github.com/any86/any-event -------------------------------------------------------------------------------- /7."is"的用法/7.md: -------------------------------------------------------------------------------- 1 | # 在vue3🔥源码中学会typescript🦕 - "is" 2 | 3 | vue3🔥是用**typescript**实现的, 所以我认为他的源码是我们学习tyepscript的**最佳实践**, 下面我就用他源码中的实例让大家学会使用typescript的"`is`"特性. 4 | 5 | ## "is"用在什么地方? 6 | 我先说用在什么地方, 经常用来封装"类型判断函数", 这类函数都必须用"is"特性, 这类函数一般起名都会叫`isString`/`isFood`/`isVnode`等等, 比如: 7 | ```typescript 8 | const isString = (val: any): val is string => typeof val === 'string' 9 | ``` 10 | 11 | ## 概念 12 | **是一种类型推断表达式的关键字, 通过和函数返回值的比较, 从而"缩小"参数的类型范围.** 13 | 14 | 😠看完这句话 "**没明白就对了**" , 想理解这句话什么意思, 请继续看下面的例子, 😁我保证第一个例子过后你就会明白这句话. 15 | 16 | ## vue在哪里用了"is" 17 | 首先教大家一个**vscode**的小技巧, 搜索中我们可以按"正则"搜索, 比如搜索`\): \w+ is`我们就可以找到所有使用了"is"特性的代码. 18 | 19 | ![](https://user-gold-cdn.xitu.io/2019/10/16/16dd32dd3cc345f0?imageView2/2/w/480/h/480/q/85/interlace/1) 20 | 21 | [更多常用正则看这里: https://juejin.im/post/5d245d4151882555300feb77](https://juejin.im/post/5d245d4151882555300feb77) 22 | 23 | ## 实例解释 24 | 从搜索到的代码中, 我们拿出最有代表性的一个函数说明. 25 | ```typescript 26 | const isString = (val: any): val is string => typeof val === 'string' 27 | ``` 28 | 29 | #### 划重点 30 | 可以看见在返回值部分返回的不是类型而是一个表达式"`val is string`", 这段代码的意思是当`isString`返回值为`true`的时候, 参数`val`就是`string`类型. 31 | 32 | 33 | #### 直接返回boolean不行吗? 34 | **不行!** 看下面的代码, 我们虽然知道在`if`判断后aa一定是`string`,但是ts不知道, ts会提示`aa`可能是`null`类型, 不能执行`substring`方法. 35 | 36 | ![](https://user-gold-cdn.xitu.io/2019/10/16/16dd3581007d7545?w=787&h=200&f=png&s=37234) 37 | 38 | **所以**需要使用`is`特性. ts可以**根据 if 判断推断**出当前的`aa`为`string`类型: 39 | 40 | ![](https://user-gold-cdn.xitu.io/2019/10/16/16dd383d4ef477bc?w=802&h=224&f=png&s=41524) 41 | 42 | ## 更多"is"在vue3中的实例 43 | 44 | ```typescript 45 | // 是否是对象 46 | export const isObject = (val: any): val is Record => 47 | val !== null && typeof val === 'object' 48 | 49 | // 是否ref对象 50 | export function isRef(v: any): v is Ref { 51 | return v ? v[refSymbol] === true : false 52 | } 53 | 54 | // 是否vnode 55 | export function isVNode(value: any): value is VNode { 56 | return value ? value._isVNode === true : false 57 | } 58 | 59 | // 是否插槽节点 60 | export const isSlotOutlet = ( 61 | node: RootNode | TemplateChildNode 62 | ): node is SlotOutletNode => 63 | node.type === NodeTypes.ELEMENT && node.tagType === ElementTypes.SLOT 64 | ``` 65 | 更多例子, 可以在源码中搜索`\): \w+ is`, 大概有16条类似的代码. 66 | 67 | 通过总结我们发现, "`is`"主要都是应用在类型判断函数上, 让后续逻辑判断中可以正确的推断出参数的类型, 好了现在可以在回头看开头的解释"**is是一种类型推断表达式的关键字, 通过和函数返回值的比较, 从而"缩小"参数的类型范围.**", 现在是不是已经理解了呢. 68 | 69 | ## 练习 70 | 我们自己写一个"**判断是否正则表达式**"的函数. 71 |
72 | 🚀答案 73 | 74 | function isRegExp (input: any): input is RegExp { 75 | return '[object RegExp]' === Object.prototype.toString.call(input); 76 | } 77 | 78 |
79 | 80 | ## 总结 81 | 喜欢ts, 如果你也喜欢ts的话可以看我之前写的ts基础文章. 82 | 83 | [第一课, 体验typescript](https://juejin.im/post/5d19ad6de51d451063431864) 84 | 85 | [第二课, 基础类型和入门高级类型](https://juejin.im/post/5d1af3426fb9a07ed4411a9b) 86 | 87 | [第三课, 什么是泛型?](https://juejin.im/post/5d27f160e51d45108223fcf9) 88 | 89 | [第四课, 解读高级类型](https://juejin.im/post/5d3fe80fe51d456206115987) 90 | 91 | [第五课, 命名空间(namespace)是什么?](https://juejin.im/post/5d5d04dfe51d4561af16dd24) 92 | 93 | 平时还请大家多多练习, 祝早日熟练ts, 放2个我用ts写的项目当做参考, 抛砖引玉, 加油! 94 | 95 | 96 | **✋ 移动/pc端手势库, 支持: tap/press/pan/swipe/rotate/pinch** 97 | https://github.com/any86/any-touch 98 | 99 | **🍭 把vue组件变成this.$xxx这样的命令** 100 | https://github.com/any86/vue-create-root 101 | 102 | ## 微信群 103 | 感谢大家的阅读, 如有疑问可以加我微信, 我拉你进入**微信群**(由于腾讯对微信群的100人限制, 超过100人后必须由群成员拉入) 104 | 105 | ![](https://user-gold-cdn.xitu.io/2019/9/19/16d474d245b69492?w=512&h=512&f=jpeg&s=27137) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 any86 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ts-class 2 | ts课程, 为了升职加薪, (๑•̀ㅂ•́)و✧加油 3 | 4 | ## "错误信息"分析和解决 5 | 6 | #### 全局范围的扩大仅可直接嵌套在外部模块中或环境模块声明中 7 | 写"修改全局声明"的声明文件的时候,没有`export`关键字, 如果实在没有要导出的东西就这么写: `export {}`. -------------------------------------------------------------------------------- /abc.d.ts: -------------------------------------------------------------------------------- 1 | declare var n: number; 2 | declare let s: string; 3 | declare const o: object; 4 | declare function f(s: string): number; 5 | declare enum dir { 6 | top, 7 | right, 8 | bottom, 9 | left 10 | } 11 | 12 | declare namespace MyPlugin { 13 | var n:number; 14 | var s:string; 15 | var f:(s:string)=>number; 16 | } 17 | 18 | 19 | interface ABC{ 20 | a:1|2|3, 21 | b:'t'|'s', 22 | c:'中'|'国' 23 | } -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | console.log(global); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "build": "npx tsc" 4 | }, 5 | "dependencies": { 6 | "@types/jquery": "^3.3.31", 7 | "@types/lodash": "^4.14.137", 8 | "@types/three": "^0.103.2", 9 | "any-touch": "^0.4.4", 10 | "typescript": "^3.5.2" 11 | }, 12 | "devDependencies": { 13 | "@types/jquery": "^3.3.31" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | /^(?:[1-9]|[1-9]\d{1}|[1-2]\d{2}|3[0-5]\d|36[0-5])$/.test(365) // 365 2 | 3 | function genRule(number) { 4 | if ('number' !== typeof number) throw '请输入number类型的数字'; 5 | const numberString = String(number); 6 | const { 7 | length 8 | } = numberString; 9 | const headerNumber = Number(numberString[0]); 10 | const tempArr1 = []; 11 | const tempArr2 = []; 12 | for (let i = 0; i < length; i++) { 13 | if (i < length - 1) { 14 | tempArr2.push(genFixHeaderPart(number, i + 1)); 15 | // 16 | tempArr1.push(`[1-9]\\d{${i}}`); 17 | } else { 18 | if (0 < headerNumber - 1) { 19 | tempArr1.push(`[0-${headerNumber-1}]\\d{${length-1}}`); 20 | } 21 | } 22 | } 23 | return new RegExp(`^(?:${[...tempArr1, ...tempArr2].join('|')})$`); 24 | } 25 | 26 | 27 | 28 | function genFixHeaderPart(number, fixLength = 1) { 29 | const numberString = String(number); 30 | const { 31 | length 32 | } = numberString; 33 | if (fixLength >= length) throw '已确定的位数不能大于等于数字位数' 34 | 35 | const tempArr = []; 36 | 37 | for (let i = 0; i < fixLength; i++) { 38 | tempArr.push(numberString[i]); 39 | } 40 | 41 | // 适配位 42 | // 当前非最后一位 43 | if (1 < length - fixLength) { 44 | let max = Number(numberString[fixLength]) - 1; 45 | if(0 > max) max = 0 ; 46 | 47 | tempArr.push(0 === max ? `[0]`:`[0-${max}]`); 48 | } 49 | // 适配位是最后一位 50 | else { 51 | const max = Number(numberString[fixLength]); 52 | tempArr.push(0 === max ? `[0]`:`[0-${max}]`); 53 | } 54 | 55 | for (let i = 0; i < length - fixLength - 1; i++) { 56 | tempArr.push(`\\d`); 57 | } 58 | 59 | return tempArr.join(''); 60 | } 61 | 62 | // genFixHeaderPart(151, 2) 63 | 64 | 65 | const NUM = 140 66 | const rule = genRule(NUM); 67 | 68 | // 测试 69 | for (let i = 1; i < NUM; i++) { 70 | if (!rule.test(i)) { 71 | console.warn(i); 72 | } 73 | } 74 | console.log(`匹配1-${NUM}完毕, 正则: ${rule}`) -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | // 不报告执行不到的代码错误。 4 | "allowUnreachableCode": true, 5 | "strictNullChecks": true, 6 | // 严格模式, 强烈建议开启 7 | "strict": true, 8 | // 支持别名导入: 9 | // import * as React from "react" 10 | "esModuleInterop": true, 11 | // 目标js的版本 12 | "target": "es5", 13 | // 目标代码的模块结构版本 14 | // "module": "es6", 15 | // 在表达式和声明上有隐含的 any类型时报错。 16 | "noImplicitAny": true, 17 | // 删除注释 18 | "removeComments": true, 19 | // 保留 const和 enum声明 20 | "preserveConstEnums": false, 21 | // 生成sourceMap 22 | "sourceMap": true, 23 | // 目标文件所在路径 24 | "outDir": "./dist", 25 | // 编译过程中需要引入的库文件的列表 26 | "lib": [ 27 | "dom", 28 | "esnext" 29 | ], 30 | // 额外支持解构/forof等功能 31 | "downlevelIteration": true, 32 | // 是否生成声明文件 33 | "declaration": true, 34 | // 声明文件路径 35 | "declarationDir": "./dist", 36 | // 此处设置为node,才能解析import xx from 'xx' 37 | "moduleResolution": "node", 38 | } 39 | } -------------------------------------------------------------------------------- /yarn-error.log: -------------------------------------------------------------------------------- 1 | Arguments: 2 | /usr/local/bin/node /usr/local/bin/yarn add @types/three.js 3 | 4 | PATH: 5 | /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin 6 | 7 | Yarn version: 8 | 1.17.3 9 | 10 | Node version: 11 | 10.16.1 12 | 13 | Platform: 14 | darwin x64 15 | 16 | Trace: 17 | Error: https://registry.npmjs.org/@types%2fthree.js: Not found 18 | at Request.params.callback [as _callback] (/usr/local/lib/node_modules/yarn/lib/cli.js:66830:18) 19 | at Request.self.callback (/usr/local/lib/node_modules/yarn/lib/cli.js:140464:22) 20 | at Request.emit (events.js:198:13) 21 | at Request. (/usr/local/lib/node_modules/yarn/lib/cli.js:141436:10) 22 | at Request.emit (events.js:198:13) 23 | at IncomingMessage. (/usr/local/lib/node_modules/yarn/lib/cli.js:141358:12) 24 | at Object.onceWrapper (events.js:286:20) 25 | at IncomingMessage.emit (events.js:203:15) 26 | at endReadableNT (_stream_readable.js:1145:12) 27 | at process._tickCallback (internal/process/next_tick.js:63:19) 28 | 29 | npm manifest: 30 | { 31 | "scripts": { 32 | "build": "npx tsc" 33 | }, 34 | "dependencies": { 35 | "@types/jquery": "^3.3.31", 36 | "@types/lodash": "^4.14.137", 37 | "any-touch": "^0.4.4", 38 | "typescript": "^3.5.2" 39 | }, 40 | "devDependencies": { 41 | "@types/jquery": "^3.3.31" 42 | } 43 | } 44 | 45 | yarn manifest: 46 | No manifest 47 | 48 | Lockfile: 49 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 50 | # yarn lockfile v1 51 | 52 | 53 | "@types/jquery@^3.3.31": 54 | version "3.3.31" 55 | resolved "https://registry.npmjs.org/@types/jquery/-/jquery-3.3.31.tgz#27c706e4bf488474e1cb54a71d8303f37c93451b" 56 | integrity sha512-Lz4BAJihoFw5nRzKvg4nawXPzutkv7wmfQ5121avptaSIXlDNJCUuxZxX/G+9EVidZGuO0UBlk+YjKbwRKJigg== 57 | dependencies: 58 | "@types/sizzle" "*" 59 | 60 | "@types/node@^12.7.4": 61 | version "12.7.4" 62 | resolved "https://registry.npmjs.org/@types/node/-/node-12.7.4.tgz#64db61e0359eb5a8d99b55e05c729f130a678b04" 63 | integrity sha512-W0+n1Y+gK/8G2P/piTkBBN38Qc5Q1ZSO6B5H3QmPCUewaiXOo2GCAWZ4ElZCcNhjJuBSUSLGFUJnmlCn5+nxOQ== 64 | 65 | "@types/sizzle@*": 66 | version "2.3.2" 67 | resolved "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz#a811b8c18e2babab7d542b3365887ae2e4d9de47" 68 | integrity sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg== 69 | 70 | any-event@^0.4.1: 71 | version "0.4.1" 72 | resolved "https://registry.npm.taobao.org/any-event/download/any-event-0.4.1.tgz#538a2c5ae66f587a85cc48c0438c021b2c61d352" 73 | integrity sha1-U4osWuZvWHqFzEjAQ4wCGyxh01I= 74 | 75 | any-touch@^0.4.4: 76 | version "0.4.4" 77 | resolved "https://registry.npm.taobao.org/any-touch/download/any-touch-0.4.4.tgz#50bf2b01accdea2726dcbc3e25d1c4a5c6adfffa" 78 | integrity sha1-UL8rAazN6icm3Lw+JdHEpcat//o= 79 | dependencies: 80 | any-event "^0.4.1" 81 | 82 | typescript@^3.5.2: 83 | version "3.6.2" 84 | resolved "https://registry.npmjs.org/typescript/-/typescript-3.6.2.tgz#105b0f1934119dde543ac8eb71af3a91009efe54" 85 | integrity sha512-lmQ4L+J6mnu3xweP8+rOrUwzmN+MRAj7TgtJtDaXE5PMyX2kCrklhg3rvOsOIfNeAWMQWO2F1GPc1kMD2vLAfw== 86 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@types/jquery@^3.3.31": 6 | version "3.3.31" 7 | resolved "https://registry.npmjs.org/@types/jquery/-/jquery-3.3.31.tgz#27c706e4bf488474e1cb54a71d8303f37c93451b" 8 | integrity sha512-Lz4BAJihoFw5nRzKvg4nawXPzutkv7wmfQ5121avptaSIXlDNJCUuxZxX/G+9EVidZGuO0UBlk+YjKbwRKJigg== 9 | dependencies: 10 | "@types/sizzle" "*" 11 | 12 | "@types/lodash@^4.14.137": 13 | version "4.14.146" 14 | resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.146.tgz#de0d2c8610012f12a6a796455054cbc654f8fecf" 15 | integrity sha512-JzJcmQ/ikHSv7pbvrVNKJU5j9jL9VLf3/gqs048CEnBVVVEv4kve3vLxoPHGvclutS+Il4SBIuQQ087m1eHffw== 16 | 17 | "@types/sizzle@*": 18 | version "2.3.2" 19 | resolved "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz#a811b8c18e2babab7d542b3365887ae2e4d9de47" 20 | integrity sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg== 21 | 22 | "@types/three@^0.103.2": 23 | version "0.103.2" 24 | resolved "https://registry.npmjs.org/@types/three/-/three-0.103.2.tgz#f7d49130001c551941a0ded757def810579aafc4" 25 | integrity sha512-zhtf0Qs5wLJpIn1+VWCpzSgpKayj/GSWZ6woiuz09FW59KEDeLpnBkYz6lbblVpRmGdlnG8nd0unaASshOvcXw== 26 | dependencies: 27 | three "*" 28 | 29 | any-event@^0.4.1: 30 | version "0.4.1" 31 | resolved "https://registry.npm.taobao.org/any-event/download/any-event-0.4.1.tgz#538a2c5ae66f587a85cc48c0438c021b2c61d352" 32 | integrity sha1-U4osWuZvWHqFzEjAQ4wCGyxh01I= 33 | 34 | any-touch@^0.4.4: 35 | version "0.4.4" 36 | resolved "https://registry.npm.taobao.org/any-touch/download/any-touch-0.4.4.tgz#50bf2b01accdea2726dcbc3e25d1c4a5c6adfffa" 37 | integrity sha1-UL8rAazN6icm3Lw+JdHEpcat//o= 38 | dependencies: 39 | any-event "^0.4.1" 40 | 41 | three@*: 42 | version "0.110.0" 43 | resolved "https://registry.npmjs.org/three/-/three-0.110.0.tgz#8719591de1336269113ee79f4268ba247b2324f8" 44 | integrity sha512-wlurH8XBO9Sd5VIw8nBa+taLR20kqaI4e9FiuMh6tqK8eOS2q2R+ZoUyufbyDTVTHhs8GiTbv0r2CMLkwerFJg== 45 | 46 | typescript@^3.5.2: 47 | version "3.6.2" 48 | resolved "https://registry.npmjs.org/typescript/-/typescript-3.6.2.tgz#105b0f1934119dde543ac8eb71af3a91009efe54" 49 | integrity sha512-lmQ4L+J6mnu3xweP8+rOrUwzmN+MRAj7TgtJtDaXE5PMyX2kCrklhg3rvOsOIfNeAWMQWO2F1GPc1kMD2vLAfw== 50 | --------------------------------------------------------------------------------