【Typescript】基础知识快速入门

241 阅读7分钟

vue3源码是用typescript编写的,如果ts语法不熟悉看起来还是非常难过的。先来复习下ts相关知识。

typescript简介

  • 以JavaScript为基础构建的语言
  • 是JavaScript一个超集
  • 可以在任何支持JavaScript的平台中执行
  • 扩展了JavaScript并增加了类型
  • 不能被JS解析器直接执行,需要先将TS编译为js

ts开发环境搭建

  • 安装node

  • 使用npm安装typescript

    npm install -g typescript

  • 创建一个ts,命令行进入文件目录使用tsc xx.ts 进行编译,然后执行node xx.js或者 安装ts-node npm install -g ts-node, 执行 ts-node xx.ts

ts的类型声明

  • 类型声明就是给变量设置了类型,使得变量只能存储某种类型的值
  • 如果你的变量的声明和赋值是同时进行的,那么可以省略掉类型声明
  • 常用的类型包括:number、string、boolean、字面量、any、unknow、void、never、object、array、tuple、enum

例如:基础静态类型

const count: number = 123;
const myName:string = 'hi';

一般基础静态类型可以省略,因为ts可以自动推断识别。

对象静态类型:

const person: {
    name: string,
    age: number
} = {
    name: 'hhh',
    age: 18
}

const person:tring[] = ['arr', 'sss']

ts编译选项配置

增加tsconfig.json可以通过增加配置对代码进行编译,如果项目根目录有配置文件,会使用配置文件的配置进行编译。

{
    // 用来指定哪些ts文件需要被编译
    // ** 表示任意目录  *表示任意文件
    include:[
        './src/**/*
    ],
    // 不需要编译的文件目录,默认值['node_modules']
    exclude: [
        './src/hello/**/*'
    ],
    // 只能文件
    // files
    // 编译的配置
    "compilerOptions": {
        // target 要编译的模板,默认es3,兼容性比较好,es3、es5、es6 es2015 es2016 es2017 es2018 es2019 es 2010
        "target":"ES6" // 编译为es6
        // module 指定模块化解决方案, none commonjs amd system umd es6 es2015 es2020 esnext
        "module": "es2015"   // 推荐es2015
        // lib 项目中使用的库,有默认值默认不需要改
        // "lib": ["dom"]
        // outDir 编译后文件所在目录
        "outDir": "./dist",
        // 将代码合并为一个文件,所有的全局作用域代码合并为同一个文件
        "outFile": "./dist/app.js"
        // 是否对js进行编译
        "allowJs": false,
        // 是否检查js代码是否符合代码规范
        "checkJs: false,
        // 是否移除注释
        "removeComments":true,
        // 不生成编译后的文件
        "noEmit": false,
        // 当有错误时不生成编译文件
        "noEmitOnError": true,

        // 代码检查相关配置
        // 所有严格检查总开关
        "strict": true
        // alwaysStrict 编译后的文件是否使用严格模式
        "alwaysStrict": true,
        // 不允许隐式any类型
        "noImplictAny": true,
        // 不允许不明确类型this
        "noImplictThis": true,
        // 严格的检查空值
        "strictNullChecks": true
    }
}

webpack打包ts代码

ts很少单独使用,一般要和打包工具例如webpack一起使用,从零搭建流程:

第一阶段

  • 安装相关依赖:
npm init
npm i -d webpack webpack-cli typescript ts-loader
  • 创建webpack.config.js
  • 创建tsconfig.json
{
    "compilerOptions": {
        "module": "ES2015",
        "target": "ES2015",
        "strict": true
    }
}
  • 在package.json中增加build命令
  • 执行npm run build 即可打包

第二阶段

  • 自动生成html,并引入相关资源,需要引入html-webpack-plugin npm i -d html-webpack-plugin
  • 内置服务器,实现自动刷新 npm i -d webpack-dev-server
  • 清空dist目录 npm i -d clean-webpack-plugin
  • 增加reslove配置,用来指定引用模块 resolve: { extensions: ['.ts', '.js'] }

第三阶段

  • 为了更好的兼容性,使用babel转换 npm i -D @babel/core @babel/preset-env babel-loader core-js

其中core-js是用来兼容语法的,例如promise在ie中没有,会自定义一个promise,配置useage会按需引入

webpack打包后会生成一个立即生成箭头函数,可以通过output的配置environment 不使用箭头函数和const语法

 output: {
        path: path.resolve(__dirname, 'dist'),
        filename: "bundle.js",
        // 兼容性配置
        environment: {
            arrowFunction: false,
            const: false
        }
    },
  • 识别less 安装 npm i -D less less-loader css-loader style-loader

  • 样式兼容性处理 npm i -D postcss postcss-loader postcss-preset-env

通过以上安装,完整的webpakc.config.js如下:

const path = require('path');
const htmlWebpackPlugin = require('html-webpack-plugin');
// 引入清空文件插件
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
// webpack所有配置信息
module.exports = {
    mode: 'production',
    entry: "./src/index.ts",
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: "bundle.js",
        // 兼容性配置
        environment: {
            arrowFunction: false,
            const: false
        }
    },

    // 指定webpack打包时要使用的模块
    module: {
        // 指定要加载的规则
        rules: [
            {
                test: /\.ts$/,
                use: [
                    {
                        loader: 'babel-loader',
                        options: {
                            presets: [
                                [
                                    "@babel/preset-env",
                                    {
                                        targets: {
                                            "chrome": "58",
                                            "ie": "11"
                                        },
                                        "corejs": "3",
                                        "useBuiltIns": "usage"
                                    }
                                ]
                            ]
                        }
                    },
                    'ts-loader'
                ],
                // 要排除的文件
                exclude: /node_modules/
            },
            {
                test: /\.less$/,
                use: [
                    "style-loader",
                    "css-loader",
                    // 引入postcss
                    {
                        loader: "postcss-loader",
                        options: {
                            postcssOptions: {
                                plugins: [
                                    [
                                        "postcss-preset-env",
                                        {
                                            browsers: 'last 2 version'
                                        }
                                    ]
                                ]
                            }
                        }
                    },
                    "less-loader"
                ]
            }
        ]
    },
    plugins: [
        new CleanWebpackPlugin(),
        new htmlWebpackPlugin({
            template: './src/index.html'
        })
    ],
    // 用来设置引用模块
    resolve: {
        extensions: ['.ts', '.js']
    }
}

面向对象

一切操作都要通过对象来进行。对象包含两个部分: 属性方法.

class Person {
    // 定义属性
    name:string = 'meng';
    // 在属性前使用static,是类型属性
    
    age: number = 18;
    // 只能访问不能修改
    readonly name: string = 'mmmm';
    // 方法
    sayHello() {
        console.log('hello');
    }
}

const per = new Person();
console.log(per.name)

构造函数

class Dog {
    name:string;
    age:number;
    // 在类里面不能直接赋值,需要构造函数
    constrctor(name:string, age:number) {
        // this就是当前创建的那个对象
        this.name = name;
        this.age = age;
    }

    bark() {
        // this表示当前调用方法的对象
        console.log(this.name);
    }
}

const dog = new Dog('小黑', 2);
dog.bark();

继承

class Animal {
    name:string;
    age:number;
    // 构造函数会在对象创建时调用
    constrctor(name:string, age:number) {
        // this就是当前创建的那个对象
        this.name = name;
        this.age = age;
    }

    bark() {
        // this表示当前调用方法的对象
        console.log(this.name);
    }
}

// Animal是父类,Dog为子类,拥有父类的所有属性和方法
// 子类可以继续添加方法和属性
class Dog extends Animal {
    run() {
        console.log(this.name)
    }
    // 如果定义和父类相同的方法,会覆盖父类(方法重写)
    sayHello() {
        console.log('wangwang)
    }
}

class Cat extends Animal {

}

const dog = new Dog('wangcai', 4);
const cat = new Cat('mimi', 2);

super关键字

super就是当前类的父类,由于子类会覆盖父类的构造函数,可以通过super关键字来执行父类的构造函数。

class Father {
    constructor(name:string) {
        this.name  = name;
    }

    sayHello() {
        console.log('动物再叫');
    }
}

class Child extends Father {
    constructor(name: string,age: number) {
        // 调用父类的构造函数
        super(name);
        this.age = age;
    }
    sayHello() {
        super.sayHello();
    }
}

抽象类

以abstract开头的类是抽象类,和其他类关系不大,只是不能用来创建对象; 抽象类是专门用来继承的,抽象类可以添加抽象方法

abstract class Animal {
    name:string;
    constructor(name:string) {
        this.name = name;
    }
    abstract sayHello():void;
}

class Cat extends Animal {
    sayHello() {
        console.log('cat miaomiao' )
    }
}

接口

用来定义一个类的结构,应该包含哪些属性和方法,也可以用类型声明去使用,可以重复使用,两者都用。

接口可以在定义类的时候去限制类的结构,只定义对象的方法,而不考虑实际值 在接口中所有的方法都是抽象方法

interface myinterface {
    name: string;
    age: number;
    sayHell():void;
}

interface myinterface {
    gender:string
}

// 定义类时,可以使类去实现一个接口,实现接口就是使类满足接口的要求
class MyClass implements myinterface {
    name: string;
    constructor(name:string) {
        this.name = name;
    }
    sayHello( {
        console.log('hi all')
    })
}

属性封装

属性可以被任意修改,可能会导致数据不安全。

  • public // 公共属性
  • private // 私有属性,只能类内部修改 可以定义get方法拿到内部的属性,通过set来修改

getter和setter可以作用属性的存储器 ts设置getter方法的方式

get name() {
    // console.log('get 执行了')
    return this._name;
}

set name(value:string) {
    this._name = value;
}
  • protected 只能在当前类和当前的子类中使用
class A {
    proteceted num:number;
    constructor(num:number) {
        this.num = num;
    }
}

class B extends A {
    test() {
        console.log(this.name);
    }
}

// 可以直接将属性定义在构造函数中

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

泛型

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

function fn<T>(a: T): T {
    return a;
}

可以直接调用具有泛型的函数:

let result = fn(10) // 不指定,ts会自动对类型进行推测
let result1 = fn<string>('hello'); // 指定泛型

// 泛型可以同时指定多个
function fn2<T,K>(A:T, B:K):T {
    return a
}

fn2<number, string>(124,'hekkk');

interface Inter {
    length: number;
}
// 表示泛型T必须是Inter实现类(子类)
function fn3<T extends Inter>(a: T):number {
    return a.length
}
// 字符串包含length属性
fn3('1212')