「课件」原创 => TypeScript 基础【转载请标明出处】

Zero 章

声明:我本人是跟 小满老师,学习的 TS。整个过程学的那叫一个痛苦~ 但是我仍然感谢小满!本课件,或多或少会参考小满老师的课件,如有侵权,我会下架!但本课件里面,更多的是我自己的理解和感悟,我实在不希望被告抄袭 ~

小满zs‘s typeScript

0.1 前置知识

  • HTML、CSS、JS(默认是 ES6 规范) 入门程度 基础知识

  • ES6 ~ ES8

  • 主机上部署了 Node.js(使用过 NVM 和 NRM)

TS 的原理 => TS 作者自己定义的语法,然后编写的 文件内容,最后翻译为 JS 文件。所以归根到底还是 JS

0.2 安装 TS 和 ts-node

npm i typescript -g

npm i ts-node -g # 使用 ts-node *.ts 就可以执行 ts文件 

0.3 简单构建 TS 项目

tsc --init # 生成 tsconfig.json => ts 编译配置文件

npm init -y # 生成 package.json 管理第三方库的配置文件 也可以用 npm install typescript -D

npm i @types/node -D # 生成 node_modules 目录 Node.js 的类型定义文件,它们是 TypeScript 社区为 Node.js 编写的类型声明文件。@types/node 包含了 Node.js 标准库的类型定义文件,-D 选项表示将 @types/node 安装为开发依赖(devDependency),因为类型定义文件通常只在开发阶段用到,不会被打包到生产环境中。当然了,如果你还用到其它的第三方库,你还是得 npm i xxx 自行安装,这玩意没人管你。
  • tsconfig.json 内容修改

"strict": false, 这是一个坑儿 ~,首先我们需要知道的是 ts 其实最后是通过一些模板然后翻译为 js 的,所以最好最好是 不要开启 严格检测。js "自由" 是最好的!否则一些 明明在 ts 上是很正常的操作,翻译后可能会提示一些错误。

0.4 tsconfig.json

tsconfig.json => ts 编译配置文件

  • 开发时 => 常用配置
// 常用配置
{
  /*
      tsconfig.json是ts编译器的配置文件,ts可以根据它的信息来对待吗进行编译 可以再tsconfig中写注释
      include : 用来指定哪些文件需要被编译
      exclude : 用来指定哪些文件不需要被编译 :默认node_module
      extends : 用来指定继承的配置文件
      files   : 用来指定被编译的文件列表,只有编译少量文件才使用
      compilerOptions : 编译器的选项是配置文件中非常重要也是非常复杂的配置选项
  */
  "include":[
    // ** : 任意目录 , * : 任意文件
    "./src/**/*"
  ],
  "exclude": [
    "./src/hello/**/*"
  ],
  // "extends": "./configs/base",
  "files": [
    "1.ts",
    // "2.ts"
  ],
  "compilerOptions": {
    // 用来指定 ES 版本 ESNext : 最新版。 'ES3', 'ES5', 'ES6'/'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ESNext'
    "target": "ES2020",
    // 指定要使用模块化的规范 : 'None', 'CommonJS', 'AMD', 'System', 'UMD', 'ES6'/'ES2015', 'ES2020' or 'ESNext'
    "module": "ESNext",
    // 用来指定项目中要使用的库 'ES5', 'ES6', 'ES2015', 'ES7', 'ES2016', 'ES2017', 'ES2018', 'ESNext', 'DOM', 'DOM.Iterable',
    //                          'WebWorker', 'ScriptHost', 'ES2015.Core', 'ES2015.Collection', 'ES2015.Generator', 'ES2015.Iterable', 
    //                          'ES2015.Promise', 'ES2015.Proxy', 'ES2015.Reflect', 'ES2015.Symbol', 'ES2015.Symbol.WellKnown', 
    //                          'ES2016.Array.Include', 'ES2017.object', 'ES2017.Intl', 'ES2017.SharedMemory', 'ES2017.String', 
    //                          'ES2017.TypedArrays', 'ES2018.Intl', 'ES2018.Promise', 'ES2018.RegExp', 'ESNext.AsyncIterable', 
    //                          'ESNext.Array', 'ESNext.Intl', 'ESNext.Symbol'
    // 运行在浏览器中不用设置,运行在node或其他中才需要设置
    // "lib":[],
    // 用来指定编译后文件的存放位置
    "outDir":"./dist",
    // 将代码合并为一个文件,设置之后所有的全局作用域中的代码会合并到同一个文件中 但是只能在  'amd' and 'system' 中才能使用
    // "outFile": "./dist/app.js",
    // 是否对js文件进行编译,默认false
    "allowJs": false,
    // 是否检查js代码是否符合语法规范,默认false
    "checkJs": false,
    // 是否移除注释,默认false
    "removeComments":false,
    // 是否不生成编译后文件,默认false
    "noEmit": false,
    // 当有错误时是否生成文件,默认false
    "noEmitOnError": false,
    // 是否生成sourceMap,默认false  这个文件里保存的,是转换后代码的位置,和对应的转换前的位置。有了它,出错的时候,通过断点工具可以直接显示原始代码,而不是转换后的代码。
    "sourceMap":false,

    // 所有的严格检查的总开关,默认false
    "strict": false,
    // 编译后的文件是否开启严格模式,默认false
    "alwaysStrict": false,
    // 不允许隐式的any,默认false(允许)
    "noImplicitAny": false,
    // 不允许隐式的this,默认false(允许)
    "noImplicitThis": false,
    // 是否严格的检查空值,默认false 检查有可能为null的地方
    "strictNullChecks": true,
    // 是否严格检查bind、call和apply的参数列表,默认false  检查是否有多余参数
    "strictBindCallApply":false,
    // 是否严格检查函数的类型,
    "strictFunctionTypes":false,
    // 是否严格检查属性是否初始化,默认false
    "strictPropertyInitialization":false,

    // 是否检查switch语句包含正确的break,默认false
    "noFallthroughCasesInSwitch":false,
    // 检查函数没有隐式的返回值,默认false
    "noImplicitReturns":false,
    // 是否检查检查未使用的局部变量,默认false
    "noUnusedLocals":false,
    // 是否检查未使用的参数,默认false
    "noUnusedParameters":false,

    // 是否检查不可达代码报错,默认false   true,忽略不可达代码 false,不可达代码将引起错误
    "allowUnreachableCode":false
  }
}
// 最全配置下方参考:
// https://www.tslang.cn/docs/handbook/compiler-options.html

0.5 Hello TypeScript

  • 新建 hello.ts
// 变量:类型约束 => 初识 TS 就要从这里开始 ~
let str:string = "Hello TypeScript";
console.log(str);

"约束" 一词,在 TS 中展现的淋淋尽致!它比一些强类型语言更加的突出,甚至是 奇奇怪怪

  • 手动编译
tsc # 直接就把当前目录下 *.ts 文件全编译了 ~
  • 监听编译
tsc -w *.ts # 监听 这个 ts文件 一旦改动就会自动编译为 js 文件
  • 执行 ts 文件 或 js 文件
ts-node *.ts / *.js

0.6 命名空间 namespace

我们在开发的时候,肯定是要去避免 不同文件中 同名的冲突的。所以有了 namespace 命名空间这个东西!

namespace index {
    // export 是导出的作用,使得其它地方可以用到 里面的元素
    export let a = 1
    export const add = (a: number, b: number) => a + b
    namespace index2 {
        // 支持嵌套
    }
}

// namespace 同名也支持 合并
namespace index {
    export let b = 2
}

console.log(index.a) // 只有 export a, 才能调用 a
console.log(index.b)

namespace 不难理解(只要你是科班出身的、学过 C++ 入门刚开始基础的部分,就会觉得这东西很熟悉。)

0.6.1 解决多文件内容上 => 命名冲突

  • demo.ts
export namespace demo {
    export let a = 1
    export let b = 2
}

export namespace vxAPP {
    export const sendMsg = (msg:string) => {
        console.log(msg);
    }
}
  • test.ts
import {demo, vxAPP} from "./demo"; // 导入这个文件里面的 demo 命名空间
import a = demo.a // 这种是直接导入这个 a,相当于简化了放到了 a 里面
console.log(a)
vxAPP.sendMsg("Hello namespace")
const sendMsg = (msg:string) => {
        console.log(msg);
}
sendMsg("hello.ts")

0.7 import

import 在这里,还可以导入 第三方库。这个不陌生 吧 ~

import 变量名 from '第三方库'
import {库内元素A, 库内元素B} from '第三方库'
import $ from "JQuery"; // 欸~ 这不就导入 JQuery 了嘛 ~

我们还要介绍一个,现在不推荐 使用的 三斜线指令

0.7.1 不推荐的三斜线指令

首先要使用 三斜线指令 是通过注释的方式,然后实现 import 的效果,我们可以把好几个文件融合生成 一个 JS 文件。然后还可以把生成内容里面的 注释给干掉。

  • 使用前提

在使用三斜线指令的时候,把它俩 添加到 tsconfig.json 里面去。才可以使用哟。。

 "outFile": "./js/index.js",  /*只生成一个文件 index.js*/                                
 "removeComments": true,      
  • index2.ts
namespace A {
    export const fn = () => 'a'
}
  • index3.ts
namespace A {
    export const fn2 = () => 'b'
}
  • index.ts
///<reference path="./index2.ts" />
///<reference path="./index3.ts" />
 
 
console.log(A);

首先,大家千万别激动。这玩意现在已经不建议用了 ~ ~

它呢,必须得编译到 一个 js 文件里!并且也只能 去调用 生成的 js 文件。

/// 是以一种注释的方式 实现 import 的效果,在编译器处理的时候,会将它当成预处理指令。并且因为它是 注释,所以也可以不去显示它。

编译之后,就运行 ts-node .\js\index.js 即可!

0.7.2 声明文件 d.ts(等同于C语言的 .h 文件)

当使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全、接口提示等功能。为啥会有这东西呢?因为我们 ts 不就是规范和约束 js 的吗?那么js 的一些库,人家压根就没有约束呀。。所以你得专门写一个 d.ts 声明文件,对这个库里面的内容做约束。

我们使用 import 导入第三方库里面东西的时候,有时候会爆红。而有的并不会(在 npm i @types/node -D 之后,就已经安装了很多标准库 和对应的声明文件),但是但是,有些库是没写声明文件的。这就导致了我们 import 会爆红。因为它没有声明。

  • 解决方案:
    • npm i --save-dev @types/express 直接给你自动给的写很多的声明文件,它们都在 node_modules 目录下的 @types 里面
    • 自己手写 d.ts(一般都是为自己写的第三方库,编写声明文件,让ts的使用者能够直接使用

大家感兴趣的话,可以去 看一些 d.ts 的文件内容,然后自行学习。咱们一个干后端的 ~ 学这 J8 干嘛。。

0.8 declare 全局声明

declare 其实也是 d.ts 声明文件要用到的重要关键字!

声明全局(包括我们学过的所有的东西,都可以用它声明全局)

declare let a:number // 声明全局的 a 变量
// 此时局部变量的作用域就很明显了,它只能出现在 {} 这种代码块的内容,即与 直接显现在文档里面的 全局变量分开才能发挥其局部的作用。
{
    let a = 10
    console.log(a);
}
a = 20
console.log(a);

第一章·TS 类型

1.1 内置类型

这里给大家先复习一下 JS 中的两种对象。

对象字面量 {}:

let obj = {};
  • 这种方式创建的对象是一个普通的 JavaScript 对象,也称为对象字面量或对象字面量语法。它是一种简洁创建对象的方式。
  • 这种方式创建的对象没有原型链或构造函数,它们仅仅是简单的键值对集合。
  • 适合用于创建临时的简单对象作为数据结构

类 Class:

class A {}
let a = new A();
  • 使用类和构造函数创建的对象具有更多的功能性。类可以有构造函数和方法,允许面向对象的编程方式。
  • 类可以被继承,可以拥有属性和方法,可以实现接口等。
  • 使用类创建的对象通常用于更复杂的应用程序,需要更多的功能和结构化的代码组织。

因此,选择使用对象字面量还是类取决于你的需求。如果你只是需要一个简单的数据容器或者临时存储一些键值对,那么对象字面量可能是更好的选择。但是如果你需要创建一个具有更多功能和结构的对象,那么类可能更适合。


**TS 内置类型 => **

  • 顶级类型:any(任意类型)、unknown(未知类型)

    unkonwn 类型只能赋值给自身类型的变量 或 any 类型的变量,其它类型不行!但是 any 类型随便。

    unkonwn 类型很傻逼,它也没办法.属性和调用方法

  • Object 是祖宗类型 等价于 {} 它俩只是换个写法、而 object 只能存储 引用类型 (如:[]``{}``() => {} 箭头函数 ...)

  • Number、String、Boolean(包装类是吧,哈哈 ~)

  • number、string、boolean(基本类型)

  • never: 一般表达的是 有问题的状态!实际开发中,一般都是用不到的。也就拿它 做个爆红的提示

1.2 interface 接口

如果不用 class 去创建类模版,然后不采取 new 引用类型() 的方式去创建对象的话,这个 js 对象本身就是个 类似于临时类的存在,只不过会直接返回 该临时类的 对象罢了。

那么既然它是临时的类,就也可以用 interface 进行规范,毕竟 interface 是接口嘛,而此时的用法就是 把 interface 作为类型来用。

interface B {
    xxx:string,
}
interface A extends B {
    name:string,
    age:number,
    gender?:string, // 该属性可以有,也可以没有,并不强制性要求存在
    readonly f:() => boolean // 声明一个函数返回的是 boolean 值,readonly 修饰后,那么被 A 规范的对象,关于改方法 就不能随便改了
    [propName:string]:any // 这东西叫索引签名, propName 是 string 类型的, 但是 value 值 是 any 任意类型,这样的话,我们就可以 允许用 A 规范的 对象在声明的时候,可以在 name 和 age 的下面 随便声明任何类型的东西
}

let a:A = {
    xxx: "我是B接口继承过来的",
    name: "MQy",
    age: 88,
    a:1,
    b:2,
    c:3
}

1.2.1 interface 规范函数

interface 开始魔幻起来了 ~ 大家千万要抗住!

interface Fn {
    (name:string, age:number):number[]
} // 可以理解为 函数式接口,只有一个方法
let f:Fn = (name, age) => {
    return [1]
}
console.log(f('mqy', 18)[0]);

1.2.2 interface 规范数组

interface X { // 可以当成数据结构
    name: string 
} // 如果是一个属性,那就属于该属性了

let arr:X[] = ["小满", "胡萝卜"]
  • 二维数组定义
// 第一种风格
let arr:number[][] = [[1],[2],[3]]
// 第二种风格
let arr:Array<Array<number>> = [[1],[2],[3]]

1.2.3 剩余参数(本质是数组)

function a(...args:any[]) {
    console.log(args)
}

a(1,2,3,4,5)

// IArguments 可以直接拿到 更加详细的 args 剩余参数的数据
function b(...args:string[]) {
    let a:IArguments = arguments
    console.log(a)
}

b("1","2","3")

1.3 联合类型和交叉类型

  • 联合类型:可能有多个类型需要接收!支持不同类型。
let a:number | string = 12345
a = "2222"

// 后端程序员偷懒 返回 boolean 数据为 0 或 1
let fn = function (retBoolean:number | boolean) boolean {
    return !!retBoolean; // !! 两个感叹号就可以 把 0 1 强转为 true 和 false,如果本身就是 boolean 类型,那么就不会 去进行转换!
}
  • 交叉类型:就是把两个类型合并在一起了,我们传参的时候可能需要 用 {} js 对象这种 形式把这俩类型需要的数据全部包裹上。
interface People {
    name: string,
    age: number
}

interface Man {
    gender: number
}

const mqy = (man:People & Man): void => {
    console.log(man);
}

mqy({
    name: "mqy",
    age: 22,
    gender: 1
})

1.4 类型断言

如果我们使用了 联合类型,那么就有可能并不知道 它到底是什么类型的。而我们在使用一些 方法和属性的时候,就可能会报错。这时候,我们就要对它 进行断言!

断言它可能就是这个类型,对的!所以我们可以使用这个类型的方法和属性。

let fn = function(num:number | string):void {
    // 如果不是这个类型的话,那就可能不对劲了
    // if(typeof num == 'string') // 不用断言的话,那就得这么做,当然实际开发其实是推荐这样做的!
    console.log((num as string).length);  // 用 as 断言它就是 string
    // console.log(<string>num.length); // 这样也是 断言的一种写法 <断言类型>
}

fn("13213");

1.5 回顾 class 类

如果可以的话,肯定是推荐 声明一个 class 类模版,然后 new 一个对象。而不是说 直接临时的类然后返回一个 js 普通对象。

// 类实现 接口
interface CarInterface {
    // 作用域修饰符我们就不讲了吧 ~
    private engine:string
    public disp:() => void
}
class Car implements CarInterface{ 
    engine:string; // 字段 
    
    constructor(engine:string) { // 构造函数 
        this.engine = engine 
    }  
    
    disp():void { // 方法 
        console.log("发动机为 :   "+this.engine) 
    } 
}

let sss:CarInterface = new Car("Engine 1")
let ssss:Car = new Car("Engine 1")

// 类的继承
class Shape { 
   Area:number 
   
   constructor(a:number) { 
      this.Area = a 
   } 
} 
 
class Circle extends Shape { 
   disp():void { 
      console.log("圆的面积:  "+this.Area) 
   } 
}
  
var obj = new Circle(223); 
obj.disp()

// 方法重写
class PrinterClass { 
   doPrint():void {
      console.log("父类的 doPrint() 方法。") 
   } 
} 
 
class StringPrinter extends PrinterClass { 
   doPrint():void { 
      super.doPrint() // 调用父类的函数
      console.log("子类的 doPrint()方法。")
   } 
}
// static 修饰符
class StaticMem {  
   static num:number; 
   
   static disp():void { 
      console.log("num 值为 "+ StaticMem.num) 
   } 
} 
 
StaticMem.num = 12     // 初始化静态变量
StaticMem.disp()       // 调用静态方法

// 访问修饰符
定义了两个变量 str1 和 str2,str1 为 public,str2 为 private,
实例化后可以访问 str1,如果要访问 str2 则会编译错误。

class Encapsulate { 
   str1:string = "hello" 
   private str2:string = "world" 
}
 
var obj = new Encapsulate() 
console.log(obj.str1)   // 可访问 
console.log(obj.str2)   // 编译错误, str2 是私有的

1.6 对象合并和类的合并

  • 对象合并(Object.assign)
interface Name {
    name:string
}

interface Age {
    age: number
}

interface Gender {
    gender: number
}

let peopleName:Name = {
    name: "mqy"
}

let peopleAge:Age = {
    age: 22
}

let peopleGender:Gender = {
    age: 1
}

const people = Object.assign(peopleName, peopleAge, peopleGender)
  • 类的合并
class A {
    type:boolean = false;
    changeType() {
        this.type = !this.type
    }
}

class B {
    name:string = "张三";
    getName():string {
        return this.name;
    }
}

// 把类融合,可以用 implements 实现 A, B 类
class C implements A, B {
    type:boolean
    changeType:() => void();
    name: string;
    getName:() => string
}

// 还有更好的方法 Object.getOwnPropertyNames() 可以获取对象自身的属性,除了它继承来的属性。。我们都能拿到

// 我们可以 定义方法 f() 然后遍历生成一下,其实这属于超纲的内容,小demo算是
function f(mergeClass:any, itemClass:any[]){
    itemClass.forEach(item => {
     Object.getOwnPropertyNames(item.prototype).forEach(name => {
            mergeClass.prototype[name] = item.prototype[name]
        })
    })
}

1.7 抽象类

abstract class temp {
    name:string
    constructor (name?:string){ 
        this.name = name; 
    }
    getName():string { // 普通方法可以有实现
        return this.name; 
    }
    abstract init(name: string): void // 只能由继承它的子类去实现
    
}

1.8 元祖类型

let arr:[number, string] = [1,2] // ts 中的元祖类型 规范的是 每个元素的类型,并不是 python 里面的不可更改.
let arr:[x:number, y?:boolean] = [1] // 这样子就是第二个元素可以不用非得填进去了
  • 可以用 const readonly 实现 python 元祖的效果
const let arr:readonly [number, string] = [1,2] // 这样的话就是 python 的元祖了

1.9 type 类型

在 TypeScript 中,type 关键字用于创建类型别名(Type Aliases),可以用来定义任意的类型。类型别名允许我们给现有类型起一个新名字,使得代码更加清晰易读,并且方便维护和重用。

但真实开发中,常用它来 编写数据结构的类型

type Point = {
  x: number;
  y: number;
};

const point: Point = { x: 10, y: 20 };

所以我一直都感觉它是类似于 typedef strcut 的存在!

  • 类型推论和类型别名

类型推论是 js 本身就有的功能,不解释。

类型别名其实不就是 某些类型赋值给 type 类型的变量。

type nArr = number[]
let str:nArr = [1,2,3]
type strOrnum = number | string
type ret = 1 extends never ? never : number // 如果 1 的父类是 never 的话 

1.10 enum 枚举类型

enum Color { // 比 JAVA 的枚举类 简单多了
    red = "red",
    green = "green",
    blue = "blue"
}
// enum 前面的修饰符 只能是 const 锁变量为常量修饰符

console.log(Color.red);
console.log(Color.green);
console.log(Color.blue);
let key = Color['red']; // 这样的话就可以 存储 enum 里面的枚举名
console.log(key)

1.11 never 类型

never 一般表达的是 有问题的状态!实际开发中,一般都是用不到的。也就拿它 做个爆红的提示

type dada = '唱' | '跳' | 'rap' | '篮球'

function ffff(value:dada) {
    switch(value) {
        case "唱": {
            break;
        }
        case "跳": {
            break;
        }
        case "rap": {
            break;
        }
        default: {
            const error:never = value; // 这也被 称为 兜底逻辑
            // 就是如果 你的 value 添加了一个新的类型 那么你没加入 case 判断,这里就会爆红
            break
        }
    }
}

1.12 symbol 类型

Symbol 是 JavaScript 语言中的一种新特性,它用于创建唯一标识符。在 ES6(ECMAScript 2015)中引入了 Symbol 类型,它是 JavaScript 的第七种基本数据类型。与之前的六种基础数据类型不同,Symbol 不是内置的对象或类,而是一种原始数据类型。它的主要用途是在对象的键中使用,以避免属性名称的冲突。

由 ES6 新增的 [变量名] 语法,就可以让 Symbol 变量去充当 标识符。但是 它并不能由 for-each 便利获取到

so,实际开发几乎用不到。。是一个很多此一举的类型。谁真的闲的用它作为属性名呀。。

const level:symbol = Symbol("level");
let student = {
    name: "小明",
    age: 12,
    [level]: "优秀"
}

console.log(student);
for(let x in student){
    
    // console.log(x); // 肯定取不到 symbol 那个属性
    console.log(x);
}

// 可以取到 Symbol
Object.getOwnPropertySymbols(student);
// 但由于我们需要 同时能取到 Symbol 和 普通属性,所以 ES6 新增了方法,并且很简短
console.log(Reflect.ownKeys(student));
  • Symbol 注意事项

  • 我们都知道在 Javascript中函数跟对象没差儿,都是地址嘛,是引用类型,而Symbol 这个东西很明显是一个函数()对吧。好好好!那么为啥Symbol就是原始类型呢?

答:官方发明这个玩意的时候,直接不让其 new 创建对象,即构造方法的 作用消失。那么它自然也就不能是引用类型,只能是 原始类型。在用 Symbol 作为参数的时候,传递的也只是值,而非引用对象。

  • 为什么 Symbol() 里面的值一样,但是两个变量 === 返回 false ?

首先,Symbol() 里面的值,我们称之为 "描述值",它是不会影响 Symbol 的唯一性的!

Symbols 的唯一性:指的是通过内部的 Symbol 注册表来实现的。在 JavaScript 引擎内部,存在一个全局的 Symbol 注册表,用于存储所有创建的 Symbol。可以看下面的这个注册表 的简易图:

Symbol Registry
+-------------------------------------------+
| Symbol("level", SymbolId: 123) |
| Symbol("level", SymbolId: 456) |
| Symbol("other", SymbolId: 789) |
+-------------------------------------------+

很显然,虽然你是相同的描述,但注册的确确实实是 两个,ID 是不一样的!

所以也就是两个不同的 Symbol,它们只不过是 描述值一样而已。

  • Symbol.for() 注册表中寻找 Symbol 实例(找到第一个就不会再去找)如果没找到,就会自动创建一个
console.log(Symbol.for("level") === Symbol.for("level")) // 这个肯定返回 true

1.13 回顾 set、map

let set:Set<number> = new Set([1,1,1,1,2,3,4]);
console.log(set);
let map:Map<any,any> = new Map();
let Arr = [1,2,3]
map.set(Arr, 'asd')
console.log(map.get(Arr));

第二章·TS 函数

2.1 默认值与可空null 参数

function ffff(num?:number, str?:string){ // 这样子写就是这个参数可以传递过来 null 空的
    if(num != null && str != null){
        console.log(num + str);
    }
}

ffff(12,"sss");
function add(a: number = 0, b: number = 0): number{ // 参数可以有默认值
    return a + b
}

console.log(add())

2.2 this 的类型可以被定义

如果我们想使用 this关键字,那么我们在函数的参数列表中,第一个参数必须要定义 this 的类型,然后 我们在 函数当中才能使用 this

interface Obj {
    user:number[]
    add:(this:Obj, num:number) => void
}

let obj:Obj = {
    user:[1,2,3],
    add: function(this:Obj, num:number) {
        this.user.push(num)
    }
}

obj.add(4)
console.log(obj.user)

就是 ts 规定,如果你真的想要用到 this 关键字,那么请你 把 this 声明和写在第一个参数上。并且规范它的类型。

2.3 函数声明与函数重载

如果光写函数头部,而不写具体的函数体实现,那么我们认为这就是 函数声明!!学过 C/C++ 应该清楚无比~

在 ts 中,同名函数是会重载的,而我们引入了 函数声明之后 + 重载 就会有一些新奇的用法!!!

即,可以先声明这些同名函数,然后用 一个 函数实现 涵盖好几个的函数声明。ts 还真能做到

// 假设有一个名为 User 的类
class User {
    constructor(public id: number, public name: string) {}
}

// 定义一个函数,根据不同的参数类型,返回不同的值
function processInput(input: number): number;
function processInput(input: string): string;
function processInput(input: User): string;

// 实现函数重载
function processInput(input: number | string | User): number | string {
    if (typeof input === 'number') {
        return input * 2;
    } else if (typeof input === 'string') {
        return input.toUpperCase();
    } else {
        return `User ID: ${input.id}, Name: ${input.name}`;
    }
}

// 使用函数
console.log(processInput(123)); // 输出:246
console.log(processInput('hello')); // 输出:HELLO
console.log(processInput(new User(1, 'Alice'))); // 输出:User ID: 1, Name: Alice

2.4 内置对象

2.4.1 ECMAScript 的内置对象

JavaScript 中有很多内置对象,它们可以直接在 TypeScript 中当做定义好了的类型。

内置对象是指根据标准在全局作用域(Global)上存在的对象。这里的标准是指 ECMAScript 和其他环境(比如 DOM)的标准。

常用内置对象:Number Date RegExp(正则表达式) Error(错误信息) XMLHttprequest(xhr 异步请求)

什么叫内置对象呢,就是 TS 本身默认给我们内置 提供好的 类型规范,就是它给我们写的 interface。然后让我们以这些为类型,去 new 这个对象 对的,还出现了 new 关键字。。所以有的时候我就觉得 TS 挺奇怪的

let b: Boolean = new Boolean(1);
let e: Error = new Error('Error occurred');
let d: Date = new Date();
let r: RegExp = /[a-z]/;
let local:Storage = localStorage
let lo:Location = location
let promise:Promise<string> = new Promise((resolove) => {resolove("mqy")});
let cookie:string = document.cookie

2.4.2 DOM 和 BOM 的内置对象

DocumentHTMLElementEventNodeList NodeListOf<元素类型>等。TypeScript 中会经常用到这些类型:

let body: HTMLElement = document.body; // HTML 文档里面的标签or元素
let allDiv: NodeList = document.querySelectorAll('div'); // 如果该元素很多很多,那就用 NodeList 类型包裹

let div:NodeListOf<HTMLDivElement | HTMLElement> = document.querySelectorAll("div"); 

document.addEventListener('click', function(e: MouseEvent) { // 还可以添加事件监听,直接咔一下都不需要咱们 html 文档上标注
  // Do something
});

2.5 回顾 Pormise

在 TypeScript 中,Promise 是用于处理异步操作的对象。它表示一个可能在未来某个时间点完成或失败的操作,并返回操作结果或错误。

Promise 可以有三种状态:

  1. pending(进行中):Promise 的初始状态,表示操作正在进行中。
  2. fulfilled(已完成):表示操作成功完成。
  3. rejected(已拒绝):表示操作失败。

创建 Promise 对象时,需要传递一个执行器函数(executor function),它接受两个参数:resolve 和 reject。通过调用 resolve 函数,可以将 Promise 状态从 pending 变为 fulfilled,并传递操作结果。通过调用 reject 函数,可以将 Promise 状态从 pending 变为 rejected,并传递操作失败的原因。

Promise 提供了链式调用的机制,可以通过 then 方法注册成功状态的处理函数通过 catch 方法注册失败状态的处理函数。这样可以对异步操作进行更加清晰和结构化的处理。

下面是一个使用 Promise 的示例:

function fetchData(): Promise<string> {
    // 新建一个异步对象,它创建的时候 接收的是一个 函数,这个函数参数有 resolve,reject
    // 这俩参数其实 也是函数,我们把参数是函数的情况 称为 回调函数
    return new Promise<string>((resolve, reject) => {
        // 执行的是一个定时器
        setTimeout(() => {
            const data = 'Data fetched successfully';
            if(data){
                resolve(data); // 模拟操作成功
            }else{
                reject(new Error('Failed to fetch data')); // 模拟操作失败
            }
        }, 2000); // 定时器
    })
}

function errorF(error:Error):void{
    console.log(error);
}
// 然后我们在调用这个 异步对象的时候,就可以用 then 和 catch 两个.方法 来绑定 resolve,reject 这两个参数
fetchData().then((data) => { // .then 里面的函数就是 resolve 的实参
    console.log(data);
}).catch(errorF)

第三章·回顾 => 生成器、迭代器和解构

3.1 生成器

function* g() { // 哟~ 你跟 function 长这么像?
    yield Promise.resolve(114514); // 元素 1
    yield 1;
    yield "1314";
}

let i = g(); // 把生成器拿到了
console.log(i.next());
console.log(i.next());
console.log(i.next());

3.2 迭代器

迭代器就是为对象封装了一个表,表里面存储的是能够顺序找到 每个元素的索引或地址。反正就是肯定能找到。

然后迭代器的使用类似于 生成器,都是通过 .next() 就可以拿到下一个元素。

当对象有 Symbol.iterator 方法的时候,我们就认为它是可以迭代的。一些内置的类型 Array Set Map String 等。就可以直接使用 for..of 语法糖 来进行迭代遍历。

3.2.1 **for..in 与 for..of **

for..in迭代的是对象的 键 的列表,而for..of则迭代对象的键对应的值。

let list = {4,5,6}

for(let i in list){
    console.log(i); // 键
}

// for..of 更关注与迭代对象的值
for(let x of list) {
    console.log(x); // 值
}

3.2.2 for..of 简易原理

// for..of 差不多意思去实现的
const each = (obj:any) => {
    let It:any = obj[Symbol.iterator]() // 拿到这个方法
    let next:any = It.next() // 这个方法会返回一个对象,该对象呢还有 next() 方法
    while(!next.done){ // 如果是 false 则一直循环
        console.log(next.value)
        next = It.next()
    }
}

function args(...arr:any) {
    // let map:Map<string,number> = new Map();
    // map.set("xx",11);
    // map.set("aa",22);
    for(let x of arguments){
        console.log(x)
    }
    each(arguments)
}

args(1,2,3,4);

3.2.3 实现 for..of 遍历普通对象

下面的这种玩法,可能会对你理解迭代器有更深的领悟。当然 ~ 我这个菜鸡也没看过 这块底层的代码。

// 额。。每个人的 实现方式不同,不喜勿喷
class xxnxx {
    max:number
    current:number
    arr:Array<any>
    constructor(forOF:any){
        this.max = 0;
        this.current = 0;
        this.arr = new Array();
        for(let i in forOF){
            this.arr.push(i);
            this.max++;
        }
    }
    next(){
        if(this.current == this.max){
            return {
                value: undefined,
                done: true
            }
        }
        return {
            value: this.arr[this.current++],
            done: false
        }
    }
}

let forOF = {
    name:"小明",
    age: 18,
    [Symbol.iterator](){ 
        return new xxnxx(this);
    }
}

for(let i of forOF){
    console.log(i);
}

3.3 解构

let [a,b,c] = [4,5,6] // 底层是使用的迭代器
console.log(a,b,c)
let arr = [1,2,3,4]
let x = {...arr};  // 其实这个底层也是使用了迭代器
console.log(x);

第四章·泛型

4.1 快速入门

你可以把泛型理解为预确定类型,就是在这个类型没有确定之前,我们需要写一个占位符来进行占位。等待着类型来真正的确定它!

function tempxxx<T1>(a:T1,b:T1):Array<T1>{
    return [a, b];
}

console.log(tempxxx(1,2));
console.log(tempxxx(true,false))

function add(T, K)(a:T, b:K):Array<T | K>{ // 这个元素可能是 T 类型也可能是 K 类型
    return [a,b]
}

// 类型别名
type A<T> = string | number | T
let a:A<boolean> = true
let a:A<null> = null

// interface 
interface Data<T> {
    msg: T
}

// 泛型也有默认的确定类型
function add<T = number, K = number>(a:T, b:k):Array<T | k> {
    return [a,b]
}

add(false, '1');

4.2 泛型约束

我们其实可以用 extends 来告诉泛型 T 你的父亲只能是 xxx,也就是你的待确定类型只能是谁的儿子。

function add<T extends number>(a:T, b:T){
    return a + b;
} // 这样子写 它就不会标红,因为它明确的知道 你也就是 number 的儿子或者 number 了,加法是没问题的。

4.3 keyof

用来提取对象属性作为字面量类型

有时候我们开发一些框架,可能会有这样的需求。其实 我们学 ts 或 js 没必要学这么细致,但是讲课肯定都得讲。

// 有时候我们是要对泛型进行 对象属性的约束
// 而 keyof 操作符可以接受一个对象类型作为参数,返回该对象属性名组成的字面量联合类型
// 毕竟这样子写 "name" | "age" | ...  是非法的,会爆红,而且很麻烦。。
let obj = {
    name: "mqy",
    age: 18
}

/*function ob1<T extends object, K extends "name" | "age">(obj:T, key:K){
    return obj[key]; // 这样的约束就是不允许的,会爆红
}*/
function ob<T extends object, K extends keyof T> (obj:T, key:K) {
    return obj[key];
}
console.log(ob(obj,"name"));

4.4 自动遍历声明属性

interface Data {
    name: string
    age: number
}

// type 其实可以有类型别名的作用,也可以是类似于结构体,我们可以自己设计一个类型
type Options <T extends object> = {
    // 这种遍历生成 的方式,也算是一种奇怪的玩法,可以作为了解
    // Key in keyof T 就是遍历 name, age 这些属性名
    readonly [Key in keyof T]:T[key]
}

type B = Options<Data> // 你看,它就自己生成了。
/* 这个结构体类型就生成了
{
    name: string
    age: number
}
*/

第五章·Decorator 装饰器(类似于反射机制)

5.1 装饰类

Ts 的装饰器,可以作用在 属性、类、方法、参数... 上面,作用的地方很多。作用之后,就可以操作 目标。

  • "experimentalDecorators": true, 开启 装饰类的支持!

const classDecorator: ClassDecorator = (target:Function) => {
    target.prototype.getVal = function <T>(val: T): T {
        return val;
    }
}

@classDecorator
class A {
    constructor() {

    }
}

const a = new A();
console.log((a as any).getVal(10)); // 输出:10

5.2 装饰器提层用法(装饰工厂)

这种用法也被称为 装饰工厂

那么我们就 举个模拟工厂模式的例子

class B{
    name:string;
}
// new() 类型表示一个可以实例化的构造函数的类型。
const decoratorFactory = (Obj:new() => any): ClassDecorator => {
    return (target: Function) => {
        target.prototype.getObj = () => {
            return new Obj();
        }
    }
}

const classDecorator: ClassDecorator = (target:Function) => {
    target.prototype.getVal = function <T>(val: T): T {
        return val;
    }
}

// 装饰器,这名字起的。是不是就是因为 像装饰品一样,装饰多个呀
@classDecorator
@decoratorFactory(B)
class BFactory {
    
}

const bFactory = new BFactory();
let b = (bFactory as any).getObj();
b.name = "mqy";
console.log(b);
console.log((bFactory as any).getVal(123))

5.3 装饰属性、参数和方法

ts 装饰器如果是 装饰属性、参数和方法,则更加偏向于 AOP 编程思想,就是在 目标诞生的时候,我们要做些什么。

  • 装饰属性
const MyPropertyDecorator:PropertyDecorator = (target: any, propertyKey: string) => {
    console.log("我仅仅想输出");
    console.log(propertyKey, target);
    
}

class MyClass {
    @MyPropertyDecorator
    myProperty: string;

    constructor(myProperty: string) {
        this.myProperty = myProperty;
    }
}

const instance = new MyClass("example");
  • 装饰方法:MethodDecorator
  • 装饰参数:ParameterDecorator

5.4 reflect-metadata(支持反射功能的库)

reflect-metadata 是一个用于在 TypeScript 中提供反射功能的库,它允许你在运行时访问类型和成员的元数据信息。这对于实现一些高级功能,比如依赖注入、ORM(对象关系映射)等非常有用。但实际上也只不过是在效果上跟反射机制一样,属于是强制实现出来的。它是弄了一个东西叫 元数据,然后附加在类或属性上面,然后你对这个元数据进行一些操作。那玩来玩去都是搞这个元数据,其实对人家 本身没影响。除非你利用这些元数据去玩一些操作。

以下是一个简单的示例,演示了如何在 TypeScript 中使用 reflect-metadata 库:

首先,确保你已经安装了 reflect-metadata 库,可以通过以下命令安装:

npm install reflect-metadata
import "reflect-metadata";

// 定义一个装饰器,用于将元数据附加到类上
function MyClassMetadata(metadata: string) {
    return function(target: any) {
        Reflect.defineMetadata("myClassMetadata", metadata, target);
    };
}

// 定义一个装饰器,用于将元数据附加到属性上
function MyPropertyMetadata(metadata: string) {
    return function(target: any, propertyKey: string) {
        Reflect.defineMetadata("myPropertyMetadata", metadata, target, propertyKey);
    };
}

// 应用装饰器并添加元数据到类和属性上
@MyClassMetadata("This is class metadata")
class MyClass {
    @MyPropertyMetadata("This is property metadata")
    myProperty: string;
}


// 获取属性的元数据
const propertyMetadata = Reflect.getMetadata("myPropertyMetadata", MyClass.prototype, "myProperty");
console.log("Property metadata:", propertyMetadata);

第六章·TS 构建项目

6.1 rollup 构建

安装依赖

1. 全局安装rollup npm install rollup -g

2. 安装TypeScript npm install typescript -D

3. 安装TypeScript 转换器 npm install rollup-plugin-typescript2 -D

4. 安装代码压缩插件 npm install rollup-plugin-terser -D

5. 安装rollupweb服务 npm install rollup-plugin-serve -D

6. 安装热更新 npm install rollup-plugin-livereload -D

7. 引入外部依赖 npm install rollup-plugin-node-resolve -D

8. 安装配置环境变量用来区分本地和生产  npm install cross-env -D

9. 替换环境变量给浏览器使用 npm install rollup-plugin-replace -D

创建 public 目录存放前端界面文件、src 目录 存放 ts 文件

  • index.html
<!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>
    <script src="../lib/index.js"></script>
</body>
</html>
  • main.ts
let a:number = 1;
console.log(a)
let str:string = "hello world"
console.log(str)

修改 package.json 内容

{
  "name": "rollupTs",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "cross-env NODE_ENV=development  rollup -c -w",
    "build":"cross-env NODE_ENV=produaction  rollup -c"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "cross-env": "^7.0.3",
    "rollup-plugin-livereload": "^2.0.5",
    "rollup-plugin-node-resolve": "^5.2.0",
    "rollup-plugin-replace": "^2.2.0",
    "rollup-plugin-serve": "^1.1.1",
    "rollup-plugin-terser": "^7.0.2",
    "rollup-plugin-typescript2": "^0.36.0",
    "typescript": "^5.3.3"
  }
}

项目根目录下 创建 rollup.config.js

console.log(process.env);
import ts from 'rollup-plugin-typescript2'
import path from 'path'
import serve from 'rollup-plugin-serve'
import livereload from 'rollup-plugin-livereload'
import { terser } from 'rollup-plugin-terser'
import resolve from 'rollup-plugin-node-resolve'
import repacle from 'rollup-plugin-replace'
 
const isDev = () => {
    return process.env.NODE_ENV === 'development'
}
export default {
    input: "./src/main.ts",
    output: {
        file: path.resolve(__dirname, './lib/index.js'),
        format: "umd",
        sourcemap: true
    },
 
    plugins: [
        ts(),
        terser({
            compress: {
                drop_console: !isDev()
            }
        }),
        repacle({
            'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
        }),
        resolve(['.js', '.ts']),
        isDev() && livereload(),
        isDev() && serve({
            open: true,
            openPage: "/public/index.html"
        })
    ]
}

tsc --init 生成 tsconfig.json 并且配置

{
  "compilerOptions": {
    /* Visit https://aka.ms/tsconfig.json to read more about this file */
 
    /* Projects */
    // "incremental": true,                              /* Enable incremental compilation */
    // "composite": true,                                /* Enable constraints that allow a TypeScript project to be used with project references. */
    // "tsBuildInfoFile": "./",                          /* Specify the folder for .tsbuildinfo incremental compilation files. */
    // "disableSourceOfProjectReferenceRedirect": true,  /* Disable preferring source files instead of declaration files when referencing composite projects */
    // "disableSolutionSearching": true,                 /* Opt a project out of multi-project reference checking when editing. */
    // "disableReferencedProjectLoad": true,             /* Reduce the number of projects loaded automatically by TypeScript. */
 
    /* Language and Environment */
    "target": "es2016",                                  /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
    // "lib": [],                                        /* Specify a set of bundled library declaration files that describe the target runtime environment. */
    // "jsx": "preserve",                                /* Specify what JSX code is generated. */
    // "experimentalDecorators": true,                   /* Enable experimental support for TC39 stage 2 draft decorators. */
    // "emitDecoratorMetadata": true,                    /* Emit design-type metadata for decorated declarations in source files. */
    // "jsxFactory": "",                                 /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */
    // "jsxFragmentFactory": "",                         /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
    // "jsxImportSource": "",                            /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */
    // "reactNamespace": "",                             /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */
    // "noLib": true,                                    /* Disable including any library files, including the default lib.d.ts. */
    // "useDefineForClassFields": true,                  /* Emit ECMAScript-standard-compliant class fields. */
 
    /* Modules */
    "module": "ES2015",                                /* Specify what module code is generated. */
    // "rootDir": "./",                                  /* Specify the root folder within your source files. */
    // "moduleResolution": "node",                       /* Specify how TypeScript looks up a file from a given module specifier. */
    // "baseUrl": "./",                                  /* Specify the base directory to resolve non-relative module names. */
    // "paths": {},                                      /* Specify a set of entries that re-map imports to additional lookup locations. */
    // "rootDirs": [],                                   /* Allow multiple folders to be treated as one when resolving modules. */
    // "typeRoots": [],                                  /* Specify multiple folders that act like `./node_modules/@types`. */
    // "types": [],                                      /* Specify type package names to be included without being referenced in a source file. */
    // "allowUmdGlobalAccess": true,                     /* Allow accessing UMD globals from modules. */
    // "resolveJsonModule": true,                        /* Enable importing .json files */
    // "noResolve": true,                                /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */
 
    /* JavaScript Support */
    // "allowJs": true,                                  /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */
    // "checkJs": true,                                  /* Enable error reporting in type-checked JavaScript files. */
    // "maxNodeModuleJsDepth": 1,                        /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */
 
    /* Emit */
    // "declaration": true,                              /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
    // "declarationMap": true,                           /* Create sourcemaps for d.ts files. */
    // "emitDeclarationOnly": true,                      /* Only output d.ts files and not JavaScript files. */
      "sourceMap": true,                                /* Create source map files for emitted JavaScript files. */
    // "outFile": "./",                                  /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
    // "outDir": "./",                                   /* Specify an output folder for all emitted files. */
    // "removeComments": true,                           /* Disable emitting comments. */
    // "noEmit": true,                                   /* Disable emitting files from a compilation. */
    // "importHelpers": true,                            /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
    // "importsNotUsedAsValues": "remove",               /* Specify emit/checking behavior for imports that are only used for types */
    // "downlevelIteration": true,                       /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
    // "sourceRoot": "",                                 /* Specify the root path for debuggers to find the reference source code. */
    // "mapRoot": "",                                    /* Specify the location where debugger should locate map files instead of generated locations. */
    // "inlineSourceMap": true,                          /* Include sourcemap files inside the emitted JavaScript. */
    // "inlineSources": true,                            /* Include source code in the sourcemaps inside the emitted JavaScript. */
    // "emitBOM": true,                                  /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
    // "newLine": "crlf",                                /* Set the newline character for emitting files. */
    // "stripInternal": true,                            /* Disable emitting declarations that have `@internal` in their JSDoc comments. */
    // "noEmitHelpers": true,                            /* Disable generating custom helper functions like `__extends` in compiled output. */
    // "noEmitOnError": true,                            /* Disable emitting files if any type checking errors are reported. */
    // "preserveConstEnums": true,                       /* Disable erasing `const enum` declarations in generated code. */
    // "declarationDir": "./",                           /* Specify the output directory for generated declaration files. */
    // "preserveValueImports": true,                     /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
 
    /* Interop Constraints */
    // "isolatedModules": true,                          /* Ensure that each file can be safely transpiled without relying on other imports. */
    // "allowSyntheticDefaultImports": true,             /* Allow 'import x from y' when a module doesn't have a default export. */
    "esModuleInterop": true,                             /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */
    // "preserveSymlinks": true,                         /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
    "forceConsistentCasingInFileNames": true,            /* Ensure that casing is correct in imports. */
    /* Type Checking */
    "strict": true,                                      /* Enable all strict type-checking options. */
    // "noImplicitAny": true,                            /* Enable error reporting for expressions and declarations with an implied `any` type.. */
    // "strictNullChecks": true,                         /* When type checking, take into account `null` and `undefined`. */
    // "strictFunctionTypes": true,                      /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
    // "strictBindCallApply": true,                      /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */
    // "strictPropertyInitialization": true,             /* Check for class properties that are declared but not set in the constructor. */
    // "noImplicitThis": true,                           /* Enable error reporting when `this` is given the type `any`. */
    // "useUnknownInCatchVariables": true,               /* Type catch clause variables as 'unknown' instead of 'any'. */
    // "alwaysStrict": true,                             /* Ensure 'use strict' is always emitted. */
    // "noUnusedLocals": true,                           /* Enable error reporting when a local variables aren't read. */
    // "noUnusedParameters": true,                       /* Raise an error when a function parameter isn't read */
    // "exactOptionalPropertyTypes": true,               /* Interpret optional property types as written, rather than adding 'undefined'. */
    // "noImplicitReturns": true,                        /* Enable error reporting for codepaths that do not explicitly return in a function. */
    // "noFallthroughCasesInSwitch": true,               /* Enable error reporting for fallthrough cases in switch statements. */
    // "noUncheckedIndexedAccess": true,                 /* Include 'undefined' in index signature results */
    // "noImplicitOverride": true,                       /* Ensure overriding members in derived classes are marked with an override modifier. */
    // "noPropertyAccessFromIndexSignature": true,       /* Enforces using indexed accessors for keys declared using an indexed type */
    // "allowUnusedLabels": true,                        /* Disable error reporting for unused labels. */
    // "allowUnreachableCode": true,                     /* Disable error reporting for unreachable code. */
    /* Completeness */
    // "skipDefaultLibCheck": true,                      /* Skip type checking .d.ts files that are included with TypeScript. */
    "skipLibCheck": true                                 /* Skip type checking all .d.ts files. */
  }
}

npm run build

这个其实就是构建一下 ~,然后你就会发现 index.js 和 index.js.map 都生成了。

npm run dev

然后就运行这个项目就完事了 ~ 它就自动开监控了,就是你改什么ts啥的,都会自动改js,自动生成。

6.2 webpack 构建

安装依赖

安装webpack   npm install webpack -D

webpack4以上需要 npm install  webpack-cli -D

编译TS  npm install ts-loader -D

TS环境 npm install typescript -D

热更新服务 npm install  webpack-dev-server -D

HTML模板 npm install html-webpack-plugin -D

tsc --init

生成 tsconfig.json

新建需要的目录

  • src

    • index.ts
    let a:number = 1;
    console.log(a)
    let str:string = "hello world"
    console.log(str);
    
  • public

    • index.html
    <!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>
        <script src="../dist/index.js"></script>
    </body>
    </html>
    

webpack.config.js

const path = require('path')
const htmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
    entry: "./src/index.ts",
    mode: "development",
    output: {
        path: path.resolve(__dirname, './dist'),
        filename: "index.js"
    },
    stats: "none",
    resolve: {
        extensions: ['.ts', '.js'],
        alias: {
            '@': path.resolve(__dirname, './src')
        }
    },
    module: {
        rules: [
            {
                test: /\.ts$/,
                use: "ts-loader"
            }
        ]
    },
    devServer: {
        port: 1988,
        proxy: {}
    },
    plugins: [
        new htmlWebpackPlugin({
            template: "./public/index.html"
        })
    ]
}

修改 package.json 的内容

{
  "name": "webpackTs",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack-dev-server",
    "build":"webpack"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "html-webpack-plugin": "^5.6.0",
    "ts-loader": "^9.5.1",
    "typescript": "^5.3.3",
    "webpack": "^5.90.0",
    "webpack-cli": "^5.1.4",
    "webpack-dev-server": "^4.15.1"
  }
}

tsc --init

生成 tsconfig.json

npm run build 构建项目

npm run dev 运行项目

posted @ 2024-01-27 12:11  小哞^同^学的技术博客  阅读(7)  评论(0编辑  收藏  举报