TypeScript --- 基础

1. 简述

JavaScript 为基础构建的语言, 扩展并添加类型, 可以在任何支持 JavaScript 的平台中执行, TypeScript 不能被 JS解析器 直接执行, 需要编译成 JS,然后再运行

2. 环境搭建

1. Node.js

2. TypeScript

npm i -g typescript

3. 开发环境配置

1. 编译配置

1. 自动编译

1. 自动编译文件

# -w 会自动监视文件的变化,并在文件发生变化,并保存时对文件自动重新编译
tsc main.js -w

2. 自动编译项目

如果直接使用 tsc 命令, 可以自动的将当前项目下的所有 ts 文件编译为 js 文件, 但是需要在根目录下创建一个 ts 的配置文件 tsconfig.json

{
  "include": [     // 需要编译的文件
    "src/**/*",
    "tests/**/*"
  ],
  "exclude": [],    // 不需要编译的文件
  "extends": [],     // 集成的配置文件 / 配置文件们
  "files": [
    "src/main.ts"     // 指定需要被编译的文件, 一般不会用这个
  ],
}

执行监听命令

tsc -w

2. 编辑器配置

tsconfig.json

{
  "include": [     // 需要编译的文件
    "src/**/*",
    "tests/**/*"
  ],
  "compilerOptions": {   // 编译器的选项
    "target": "ES6",    // 编译成对应的 ES 的版本, 可选值: ES3(默认值)、ES5 ES6/ES2015、ES7/ES2016、ES2017、ES2018、ES2019、ES2020、ESNext
    // "lib": [],       // 指定代码运行时所包含的库(宿主环境), 一般不改,可选值: ES5 ES6/ES2015、ES7/ES2016、ES2017、ES2018、ES2019、ES2020、ESNext、DOM、WebWorker、ScriptHost....
    "module": "system",    // 指定需要使用的模块化规范, 可选项: 'none', 'commonjs', 'amd', 'system', 'umd', es6/es2015, 'es2020', 'es2022', 'esnext', 'node16', 'nodenext', 'preserve'
    "outDir": "./dist",   // 指定编译后的文件存储位置
    "outFile": "./dist/app.js",  // 将所有js代码合并为一个文件, 必须指定 module 为 amd 或 system, 一般不用, 会使用打包工具来实现这个功能
    "strict": true,     // 所有严格检查的总开关,如果为true, 下面一些配置项可以不用配置
    "allowJs": false,   // 是否同时对 js 文件也进行编译, 一般用来将第三方的js文件一起编译
    "checkJs": false,    // 是否开启 检查 js 代码是否符合 ts 规范
    "removeComments": true,  // 是否移除注释
    "noEmit": false,     // 是否生成编译后的文件, 一般用于检查代码是否符合 ts 规范, 而不编译文件
    "noEmitOnError": true,  // 当代码发生错误时, 是否不生成编译文件
    "alwaysStrict": true,  // 是否设置编译后的文件开启严格模式
    "noImplicitAny": true, // 是否关闭未指定的类型自动指定为any
    "noImplicitThis": true, // 是否关闭使用不明确类型的this
    "strictNullChecks": true,  // 是否严格检查空值
  }
}

2. 整合 webpack

1. 初始化

在项目根目录下生成 package.json 文件

npm init -y

2. 环境安装

cnpm i -D webpack webpack-cli typescript ts-loader html-webpack-plugin webpack-dev-server clean-webpack-plugin 

3. 配置 webpack

项目根目录下创建 src/index.ts

function sum(a: number, b: number): number {
    return a + b;
}

console.log(sum(2, 3));

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

const path = require('path');
const HTMLWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin')

module.exports = {
    // 指定入口文件
    entry: "./src/index.ts",

    // 打包配置
    output: {
        // 指定打包后的文件所在目录
        path: path.resolve(__dirname, "./dist"),

        // 打包后的文件名
        filename: "bundle.js",
    },

    // 指定 webpack 打包时要使用的模块
    module: {
        // 指定加载的规则
        rules: [
            {
                // test 指定的是规则生效的文件, 正则表达式
                test: /\.ts$/,
                // 要使用的 loader
                use: "ts-loader",
                // 要排除的文件
                exclude: /node_modules/,
            }
        ]
    },

    // 配置 webpack 插件
    plugins: [
        new HTMLWebpackPlugin({
            // 设置模版路径
            template: path.resolve(__dirname, "./src/index.html"),
            title: "自定的 title"
        }),   // webpack 管理 html 页面的插件
        new CleanWebpackPlugin(),  // 每次打包前先清除 dist 文件夹下的所有打包文件
    ],

    // 用来配置引用模块, 哪些模块可以作为模块使用
    resolve: {
        extensions: [".ts", ".js"]
    }
}

4. 指定 ts 的编辑规范

根目录下创建 tsconfig.json

{
  "compilerOptions": {
    "module": "ES2015",
    "target": "ES2015",
    "strict": true
  }
}

5. 添加自定义命令

pageage.json 文件的 scripts 配置项中添加 build start 自定义命令

{
  "scripts": {
    "build": "webpack",  // 打包命令
    "start": "webpack server --open chrome.exe"   // 代码热修改
  },
}

6. 执行命令打包

npm run build
npm run start

7. 配置自动生成 HTML

8. 配置 热更新 服务

9. 低版本兼容插件

1. 模块下载

cnpm i -D @babel/core @babel/preset-env babel-loader core-js

2. 配置

webpack.config.js

module.exports = {
    // 指定 webpack 打包时要使用的模块
    module: {
      output: {
        	// 配置打包环境
        	environment: {
            	arrowFunction: false,   // 指定不用箭头函数
                const: false  // 指定不使用 const 声明变量
        	}
        },
        // 指定加载的规则
        rules: [
            {
                test: /\.ts$/,
                // 要使用的 loader
                use: [{
                    // 指定加载器
                    loader: "babel-loader",
                    // 设置 babel
                    options: {
                        presets: [
                            [
                                "@babel/preset-env",    // 指定环境插件
                                // 配置信息
                                {
                                    targets: {
                                        "chrome": "88",   // 要兼容的目标浏览器
                                        "ie": "11"
                                    },
                                    "corejs": "3",   // 指定 core.js 的版本
                                    "useBuiltIns": "usage",  // 使用 core.js 的方式, usage 表示按需加载
                                }
                            ]

                        ],

                    }
                }, "ts-loader"],
                // 要排除的文件
                exclude: /node_modules/,
            }
        ]
    },
}

3. 整合 less / sass

1. 下载模块

npm i -D less less-loader css-loader style-loader 

npm i -D sass sass-loader css-loader style-loader

2. 配置

webpack.config.js

module.exports = {
    module: {
        // 指定加载的规则
        rules: [
            // 添加关于处理 less / sass 的处理规则
            {
                test: /.less$/,
                use: [
                    "style-loader",
                    "css-loader",
                    "less-loader"     // loader 是从下往上执行的
                ]
            }
        ]
    },
}

3. 低版本兼容插件

npm i -D postcss postcss-loader postcss-preset-env

webpack.config.js

module.exports = {
    module: {
        // 指定加载的规则
        rules: [
            // 添加关于处理 less / sass 的处理规则
            {
                test: /.less$/,
                use: [
                    "style-loader",
                    "css-loader",
                    // 引入 postcss 并配置
                    {
                        loader: "postcss-loader",
                        options: {
                            postcssOptions: {
                                plugins: [
                                    [
                                        "postcss-preset-env",
                                        {
                                            browsers: "last 2 versions"   // 兼容最近两个版本的浏览器
                                        }
                                    ]
                                ]
                            }
                        }
                    },
                    "less-loader"     // loader 是从下往上执行的
                ]
            }
        ]
    },
}

4. hello world

main.ts

console.log("hello ts")

编译

tsc main.ts

5. 数据类型

1.类型声明

TS 中 类型声明是一个非常重要的特点, 可以通过类型声明指定 TS 中变量(参数、形参)的类型, 指定类型后,当为变量赋值时, TS 编辑器会自动检查值是否符合类型声明, 符合则赋值, 否则报错, 简而言之类型声明给变量设置了类型, 使得变量只能存储某种类型的值

1. 变量

先声明再赋值

let userName: string
userName = "小明"
userName = 0      // 此行代码会报错, 因为 userName 的类型是 string 无法使用 number 进行赋值

声明的同时赋值

let userName: string = "小明"  // 声明变量的类型
let userAddress = "北京市"  // 如果变量的声明和赋值时同时进行的, TS 会自动检测变量的类型

userAddress = 0   // 此行代码会报错, 因为 userName 的类型是 string 无法使用 number 进行赋值

2. 函数

function sum(a: number, b: number): number {
    return a + b
}

sum(1, 2)

2. number

任意数字


3. string

任意字符串

4. boolean

布尔值 true 或 false

5. 字面量

限制变量的值就是该字面量的值

let s1: "male" | "female"   // 可以使用 | 来连接多个类型

s1 = "male"
s1 = "female"
s1 = "hello"   // s1 只能 "male" 或 "female"


let c: boolean | string     // 联合类型

c = true
c = "hello"
c = 10   // c 只能是 布尔值 或 字符串

6. any(不推荐)

任意类型

let a: any
let b     // 声明变量, 如果不指定类型, TS 解析器会自动给该变量指定为 any 类型

a = 10 
a = "hello"
a = true

7. unknown(推荐)

类型安全的 any, 在变量的类型不确定的时候使用

let a: unknown

a = 10 
a = "hello"
a = true


let d
let c: string
c = d   // 不会报错, any类型可以赋值给任意类型

c = a   // 会报错, unknown 类型不可以赋值给其他类型的数据


// 类型断言, 当 a 的类型是 string 时, 才会赋值给c
c = a as string
c = <string>s

8. void

没有值(或 undefined)

// 没有返回值, 默认是void
function fn():void {
    console.log(1)
}

9. never

不返回任何值

function fn():never {
    throw new Error("报错了")
}

10. object

任意对象, 但是 JS 中一切皆对象, 所以需要变换使用方法

let j: {name: string} & { age: number}
j = {name: "小明", age: 18}

function fn(): { name: string } {
    return {
        name: "小明"
    }
}


function fn(): { name: string } {
    return {
        name: "小明",
        age: 10       // 报错
    }
}


// name 是必选项 
// age后加了?,则表示age是可选项
function fn(): { name: string, age?: number } {
    return {
        name: "小明",
        age: 10
    }
}


// name 是必选项 
// 其他都是必选项
function fn(): { name: string, [propName: string]:unknown } {
    return {
        name: "小明",
        age: 10,
        address: "北京市",
        hobby: {
            hobby1: "篮球"
        }
    }
}


// 限制返回值必须是一个对应类型的函数
function fn(): (a: number, b: number) => number {
    let a = 1
    let b = 2
    return function (a, b) {
        return a + b
    }
}

11. array

任意数组

let a: string[]    // 字符串数组

let b: number[]    // 数值类型数组

let c: Array<number>  // 数值类型数组

let d: { [propName: string]: unknown }[]   // 任意对象类型数组

d = [{
    name: "小明",
    age: 10
}]

12. tuple

固定长度的数组

let t: [number,number]

t = [1,2]

t = [1,2,3]  // 报错, 长度必须为2

13. enum

枚举类型

// 声明枚举
enum Gender {
    "Male",
    "Female"
}

let u: { name: string, gender: Gender }

u = {
    name: "小明",
    gender: Gender.Male
}

if (u.gender === Gender.Male){
    console.log(1)
}

14. 类型别名

type o_t_t = 1 | 2 | 3

let a: o_t_t
let b: o_t_t

6. 面向对象

1. 类( class )

定义规则

class 类名 {
    属性名: 类型;
    
    // 构造函数
    constructor(参数: 类型) {
        this.属性名 = 参数
    }
    
    方法名() {
        
    }
}

**示例: **

class Person {

    // 实例属性
    name: string;
    age: number;

    // 类属性 (静态属性), 只能通过 Person.len 来访问
    static len: number = 18;

    // 只读实例属性
    readonly hobby: string = "篮球"
    // 只读类属性
    static readonly money: number = 18000


    // 构造函数
    constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }

    // 实例方法
    sayHello() {
        return `${this.name} say Hello!`;
    }

    // 类方法
    static sayHi() {
        return `Person sayHi!`;
    }
}

const p1 = new Person("小明", 18)

// 打印实例属性
console.log(p1.name, p1.age)

// 调用实例方法
console.log(p1.sayHello());

// 调用类属性
console.log(Person.len)

// 调用类方法
console.log(Person.sayHi)

编译

tsc index.ts

2. 属性

1. 公共属性

class Person {
    // public 关键字 定义的属性或方法, 可以在任意位置访问, 默认 age 就是公共属性
    public name: string
    age: number

    constructor(name: string, age: number) {
        this.name = name
        this.age = age
    }

}

const p = new Person("孙悟空", 18)
console.log(p)

// 属性是在对象中设置的, 所以属性是可以被任何的修改的
// 属性可以被修改, 会导致对象中的数据变得非常不安全
p.name = "猪八戒"
p.age = -22   // 年龄不可能会是负数
console.log(p)

上面的简写方式: 构造函数中声明公共属性

class Person {
    // 不用再用 this.name = name 赋值了, 内部自动完成
    constructor(public name: string, public age: number) {
    }
}

const p = new Person("孙悟空", 18)
console.log(p)

// 属性是在对象中设置的, 所以属性是可以被任何的修改的
// 属性可以被修改, 会导致对象中的数据变得非常不安全
p.name = "猪八戒"
p.age = -22   // 年龄不可能会是负数
console.log(p)

2. 私有属性

class Person {

    // 使用 private 关键字声明的属性 只可以被当前类的内部访问
    // 一般使用 _ 来表示私有属性
    private _name: string
    
    // 使用 private 关键字声明的属性 只可以被 当前类 及其子类 的内部访问
    protected _age: number

    constructor(name: string, age: number) {
        this._name = name
        this._age = age
    }

    getAge(): number {
        return this._age
    }

    setAge(num: number): void {
        // 可以在逻辑中控制 age 的合法性
        if (num <= 0) return
        this._age = num
    }
}

const p = new Person("孙悟空", 18)

// 外部无法通过 _name 或 _age 来修改私有属性
p._name = "猪八戒"

// 调用类的内部方法, 修改私有属性
p.getName()
p.setAge(20)
console.log(p)

**注意: **

TS 中有这个语法, 但是编译成 JS 后仍然会被修改, 是因为 JS 不支持私有属性, 需要修改 tsconfig.json 的配置

{
    "compilerOptions": {
        "noEmitOnError": true   // 确保当 TS 语法有错误时, 不会编译成 JS 文件
    }
}

3. getter 和 setter

class Person {
    private _name: string;
    private _age: number;

    constructor(name: string, age: number) {
        this._name = name;
        this._age = age;
    }

    // 设置 getter
    get name() {
        return this._name;
    }

    // 设置 setter
    set name(name: string) {
        this._name = name;
    }
}


const person = new Person("小明", 18)

// 读 name, 自动执行 getter
console.log(person.name);

// 设置 name 自动执行 setter
person.name = "孙悟空"

3. 继承

1. 未使用继承

class Dog {
    name: string;
    age: number;

    constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }

    sayHello() {
        console.log("汪汪汪!")
    }
}

class Cat {
    name: string;
    age: number;

    constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }

    sayHello() {
        console.log("喵喵喵!")
    }
}


const dog = new Dog("旺财", 3)
dog.sayHello()

const cat = new Cat("小白", 2)
cat.sayHello()

编译

tsc index.ts

2. 使用继承

class Animal {
    name: string;
    age: number;

    constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }

    sayHello(): void {
        console.log('动物在叫')
    }
}

// 使用 关键字 extends 来继承 Animal 父类
class Dog extends Animal {

    // Dog 独有的方法
    run() {
        console.log(`${this.name} 在跑`)
    }

    // 父类方法的重写
    sayHello(helloStr: string): void {
        console.log(`${this.name} ${this.age} 岁,它在 ${helloStr}`)
    }
}

class Cat extends Animal {
}


const dog = new Dog("旺财", 3)
dog.sayHello("汪汪汪!")

const cat = new Cat("小白", 2)
cat.sayHello("喵喵喵!")

编译

tsc index.ts

4. super 关键字

class Animal {
    name: string;
    age: number;

    constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }

    sayHello(): void {
        console.log('动物在叫')
    }
}

class Dog extends Animal {

    run() {
        console.log(`${this.name} 在跑`)
    }

    sayHello(helloStr: string): void {
        console.log(`${this.name} ${this.age} 岁,它在 ${helloStr}`)

        // 调用父类的 sayHello()
        super.sayHello()
    }
}

class Cat extends Animal {
    hobby: string;

    // 如果子类中定义了 constructor 构造函数, 必须手动调用父类的构造函数
    constructor(name: string, age: number, hobby: string) {
        super(name, age);
        this.hobby = hobby
    }
}


const dog = new Dog("旺财", 3)
dog.sayHello("汪汪汪!")

const cat = new Cat("小白", 2, "球球")
cat.sayHello()

5. 抽象类

// 使用关键字 abstract 声明的类,就是抽象类, 用来限制父类无法被单独实例化
// 抽象类的作用就是用来被别的子类继承的
abstract class Animal {
    name: string;
    age: number;

    constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }

    // 通用方法, 在抽象类中是无意义的
    sayHello(): void {
        console.log('动物在叫')
    }

    // 使用 abstract 关键字定义的方法, 就是抽象方法
    // 抽象方法没有方法体, 只能定义在抽象类中, 子类必须对父类的抽象方法进行重写
    abstract sayHi(): void
}


class Cat extends Animal {
    sayHello(): void {
        console.log('小猫在喵喵喵')
    }
    
    // 子类必须对父类的抽象方法进行重写
    sayHi(): void{
        console.log("小猫说 Hi")
    }
    
}

const cat = new Cat("小白", 2)
cat.sayHello()

7. 接口

1. 定义

// 描述一个对象的类型
type myType = {
    name: string,
    age: number
}

const obj: myType = {
    name: "小明",
    age: 18
}

使用接口实现

// 定义一个接口, 用来定义一个类中, 包含哪些必须定义的属性和方法
interface myInterface {
    name: string
    age: number
}

// 可以重复声明两个相同名字的 interface 
interface myInterface {
    name: string
    age: number
}

const objI: myInterface = {   // 必须实现 myInterface 中声明的所有属性和方法
    name: "小明",
    age: 18,
    hobby: "篮球"
}

使用接口来限制类的结构

interface myInterface {
    name: string

    sayHello(): void
}

// 定义类来实现接口, 也就是使类满足接口提前定义好的要求
class Animal implements myInterface {
    name: string

    // 构造函数不被限制
    constructor(name: string) {
        this.name = name
    }

    sayHello() {
        console.log("hello")
    }
}

8. 泛型

在定义函数或类时, 如果遇到类型不明确时, 就可以使用泛型

// <T> 声明了 T 就是泛型
function fn<T>(a: T): T {
    return a
}

// 不指定类型, TS 可以自动对类型进行推断
fn(10)

//指定泛型, T 就是 string 类型
fn<string>("hello")

指定多个泛型

// <T> 声明了 T 就是泛型
function fn<T, K>(a: T, b: K): T {
    console.log(b)
    return a
}

//指定泛型, T 就是 string 类型
fn<number, string>(10, "hello")

指定泛型必须实现某个接口

interface Inter{
    length: number
}

// T 泛型, 必须实现了 length 这个接口
function fn<T extends Inter>(a: T): number {
    return a.length
}

fn("hello")

fn(20)

fn({length: 20})

定义类的时候指定泛型

class Person<T> {
    constructor(public firstName: T, public lastName: T) {
    }
}

const p = new Person<string>("小", "明")
posted @ 2024-07-23 22:01  河图s  阅读(9)  评论(0)    收藏  举报