typescript 实战 typescript is

admin2024-10-07  11

TypeScript是什么?

TypeScript 是 JavaScript 的一个超集,支持 ECMAScript 6 标准。
TypeScript 由微软开发的自由和开源的编程语言。
TypeScript 设计目标是开发大型应用,它可以编译成纯 JavaScript,编译出来的 JavaScript 可以运行在任何浏览器上。

TypeScript增加了什么?

  • 类型批注和编译时类型检查
  • 类型推断
  • 类型擦除
  • 接口
  • 枚举
  • Mixin
  • 泛型编程
  • 名字空间
  • 元组
  • Await

以下功能是从 ECMA 2015 反向移植而来:

  • 模块
  • lambda 函数的箭头语法
  • 可选参数以及默认参数

JavaScript 与 TypeScript 的区别

TypeScript 是 JavaScript 的超集,扩展了 JavaScript 的语法,因此现有的 JavaScript 代码可与 TypeScript 一起工作无需任何修改,TypeScript 通过类型注解提供编译时的静态类型检查。

TypeScript 可处理已有的 JavaScript 代码,并只对其中的 TypeScript 代码进行编译。

typescript 实战 typescript is,typescript 实战 typescript is_typescript,第1张

TypeScript开发环境搭建

  1. 下载安装Node.js
  2. 使用npm全局安装TypeScript
    npm install -g typescrit
  3. 创建一个ts文件
  4. 使用tsc对ts文件进行编译
    tsc xxx.ts

基本类型

  • 类型声明
  • 类型声明是TS非常重要的一个特点
  • 通过类型声明可以指定TS中变量(参数、形参)的类型
  • 指定类型后,当为变量赋值时,TS编译器会自动检查值是否符合类型声明,符合则赋值,否则报错
  • 简而言之,类型声明给变量设置了类型,使得变量只能存储某种类型的值
  • 语法:
let 变量:类型;
let 变量:类型 = 值
function fn(参数:类型, 参数:类型):类型{...}
  • 自动类型判断
  • TS拥有自动的类型判断机制
  • 当对变量的声明和赋值是同时进行的,TS编译器会自动判断变量的类型
  • 所以如果你的变量声明和赋值是同时进行的,可以省略类型声明
  • 类型

类型

例子

描述

number

1, -33, 2.5

任意数字

string

‘hi’, “hi”, hi

任意字符串

boolen

true, false

布尔值true或false

字面量

其本身

限制变量的值就是该字面量的值,定义赋值之后就不能再改变

any

*

任意类型,相当于对该变量关闭了TS的类型检测,可以直接赋值给其他变量

unknown

*

类型安全的any,不能直接赋值给其他变量

void

空值(undefined)

没有值(或undefined)

never

没有值

不能是任何值

object

{key:value}

任意的js对象

array

[1, 2, 3]

任意的js数组

tuple元组

[4, 5]

元素,TS新增的类型,固定长度的数组

enum

enum{A, B}

枚举,TS中新增的类型

额外知识点:
类型断言——可以用来告诉解析器变量的实际类型

语法
变量 as 类型
<类型> 变量

TypeScript编译

编译监听

TS目前不能在浏览器上直接运行,需要转化为响应的js文件。则通过 tsc xxx.ts 命令进行编译,但是我们不可能每改一次代码执行一次这个命令,所以需要对文件进行监听。

//命令行代码:
tsc xxx.ts -w   // w watch的缩写

但是前面这种写法只能监听一个TS文件,监听多个文件只能开启多个终端,这不符合我们的日常开发需求。所以我们真正开发时采用以下的方法。

  • 首先在根目录创建tsconfig.json配置文件,手动创建或通过 tsc - - init自动创建
  • 运行tsc 编译所有在目录下的ts文件
  • 运行tsc -m 监听所在目录下的所有ts文件。

tsconfig.json 文件配置

  1. include
    用来定义需要排除在外的元素。
tsconfig.json

{	
//用来指定哪些ts文件要被编译
	"include":[ 
		'./scr/**/*', //**表示任意目录, *表示任意文件
	]
}
  1. exclude
    用来指定哪些文件不需要被编译。 如果没有特殊指定, "exclude"默认情况下会排除node_modules,bower_components,jspm_packages和目录。
tsconfig.json 

{
	"exclude":[
	"./src/banner/**/*"
	]
}
  1. extends
    继承。这个有点类似js 的引入。extends是tsconfig.json文件里的顶级属性(与compilerOptions,files,include,和exclude一样)。 extends的值是一个字符串,包含指向另一个要继承文件的路径。
tsconfig.json

{
	"extends":’./config/base‘  //继承config/base.json文件
}
  1. files
    指定一个包含相对或绝对文件路径的列表。
官网案例

{
	 "files": [
        "core.ts",
        "sys.ts",
        "types.ts",
        "scanner.ts",
        "parser.ts",
        "utilities.ts",
        "binder.ts",
        "checker.ts",
        "emitter.ts",
        "program.ts",
        "commandLineParser.ts",
        "tsc.ts",
        "diagnosticInformationMap.generated.ts"
    ]
}
  1. compilerOptions (重点)
    编译选项是配置文件重非常重要也比较复杂的配置选项。
    在compilerOptions中包含多个子选项,用来完成对编译的配置。
东西太多 无法一一列举  详情需访问官网 或者初始化配置 tsc --init 来查看

'compilerOptions':{
   
	"target" :"ES5",        	//用来设置编译的ECMAscript版本+
    "module":"es6",				//指定要使用的模块化规范
    "lib":[ ], 	    			//lib用来指定项目中要使用的库
    "outDir":"./dist:",			//用来指定编译后文件所在的目录
    "outFile":"./dist/app.js"	//将编译的代码合并到一个文件上
	"allowJs":true,   			//是否对JS文件进行编译
	"removeComments":true		//是否移除注释
	"noEmit":false 				//不生成编译后的文件
	"noEmitOnError":true		//当有错误时不编译文件	
	
	"strict":true				//所有严格检查的总开关 这个开启 底下的四个默认为true 推荐为true
	"alwaysStrict":false  		//设置是否为编译后的文件开启严格模式
	"noImpLicitAny":true,		//不允许隐式的any类型(就是必须声明)
	"noImplicitThis":true,		//不允许不明确类型的this 方法中的this需指定在参数中 this:any  
	"strictNullChecks":true   	//严格检查空值 如果有可能存在null则报错
	     
}

Webpack打包TypeScript

  1. 在项目目录下 通过命令行生成package.json
npm init -y //生成package.json文件
npm install -D webpack webpack-cli typescript ts-loader
  1. 在package.json文件的scripts字段中添加运行webpack
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack"
},
  1. 在项目目录下创建webpack.config.js配置文件
//引入node 路径包
const path = require('path')

//导出所有webpack配置
module.exports = {

    //指定路口文件
    entry: "./src/index.ts",

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

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

    },

    //指定webpack打包时使用的模块
    module: {

        //指定要加载的规则
        rules: [

            { //指定规则生效的文件
                test: /\.ts$/,
                //使用ts-loader打包ts文件
                use: "ts-loader",
                //设置不需要打包的文件
                exclude: /node_modules/
            }
        ]
    }
}
  1. 项目根目录创建tsconfig.json配置文件
{
    //这是自己配置 
    "compilerOptions": {
        "module": "ES2015",
        "target": "ES2015",
        "strict": true
    }
}
  1. 打包编译只需运行 npm run bulid
  2. html-webpack-plugin插件
    打包完成我们需要在html页面显示时,需要安装这个插件,可以动态生成html页面。(以前是手动创建html文件,用script标签手动引入)
npm i -D html-webpack-plugin

之后需在webpack.config.js中配置

const htmlWebpackPlugin = require('html-webpack-plugin')
   //导出所有webpack配置
module.exports = {
   //配置webpack插件
    plugins: [
        new htmlWebpackPlugin()
    ]
}
  1. webpack配置模块格式
    有的时候我们不同模块之间需要互相引入某些值,例如模块A需要引入模块B的值,但是如果两者都是TS文件,则无法执行(webpack默认不支持TS模块引入),但是可以通过相关的配置,设置允许TS文件格式进行模块导入。
    需要在webpack.config.js文件中添加字段:
module.exports = {

   //用来设置引用模块格式
    resolve: {
        extensions: ['.ts', '.js']  //表示允许.ts 和.js文件进行模块化操作(例如模块的引入 导出)
    }
    
 }
  1. 安装webpack的babel插件
npm i -D @babel/core @babel/preset-env  babel-loader core.js

安装完之后需要在webpack.config.js中进行babel的配置:

module.exports = {
	//告诉webpack不使用箭头函数
	//因为webpack打包时会将代码块打包到一个自调用函数中,该函数内部使用箭头函数
	//而IE不支持
	evironment:{
		arrowFunction:false
	},
	   //指定webpack打包时使用的模块
    module: {

        //指定要加载的规则
        rules: [

            { //指定规则生效的文件
                test: /\.ts$/,
                //使用ts-loader打包ts文件
                use: [
                    //配置babel
                    {
                        //指定加载器
                        loader: "label-loader",
                        //设置babel
                        options: {
                            //设置预定义的环境
                            presets: [
                                [
                                    //指定环境的插件
                                    "@babel/preset-env",
                                    {
                                        //要兼容浏览器版本
                                        targets: {
                                            "ie": "11"
                                        },
                                        //指定corejs版本
                                        "corejs": "4",
                                        // 使用corejs的方式
                                        "useBuiltIns": "usage" //按需加载
                                    }
                                ]
                            ]
                        }

                    },
                    "ts-loader",
                ],
                //设置不需要打包的文件
                exclude: /node_modules/
            }
        ]
    },
}

Typescript进阶

接口

在 TypeScript 中,我们使用接口(Interfaces)来定义对象的类型。

什么是接口

在面向对象语言中,接口(Interfaces)是一个很重要的概念,它是对行为的抽象,而具体如何行动需要由类(classes)去实现(implement)。 TypeScript 中的接口是一个非常灵活的概念,除了可用于对类的一部分行为进行抽象以外,也常用于对「对象的形状(Shape)」进行描述。

例子:

interface Person {
  name: string
  age: number
}

let tom: Person = {
  name: 'Tom',
  age: 25
}

上面的例子中,我们定义了一个接口 Person,接着定义了一个变量 tom,它的类型是 Person。这样,我们就约束了 tom 的形状必须和接口 Person 一致。 接口一般首字母大写。有的编程语言中会建议接口的名称加上 I 前缀。 定义的变量比接口少了一些属性是不允许的:

interface Person {
  name: string
  age: number
}

let tom: Person = {
  name: 'Tom'
}
// Property 'age' is missing in type '{ name: string }' but required in type 'Person'.

多一些属性也是不允许的:

interface Person {
  name: string
  age: number
}

let tom: Person = {
  name: 'Tom',
  age: 25,
  gender: 'male'
}

// Type '{ name: string age: number gender: string }' is not assignable to type 'Person'.
// Object literal may only specify known properties, and 'gender' does not exist in type 'Person'.

可见, 赋值的时候,变量的形状必须和接口的形状保持一致。

可选属性

有时我们希望不要完全匹配一个形状,那么可以用可选属性:

interface Person {
  name: string
  age?: number
}

let tom: Person = {
  name: 'Tom'
}
interface Person {
  name: string
  age?: number
}

let tom: Person = {
  name: 'Tom',
  age: 25
}

可选属性的含义是该属性可以存在,可以不存在。

任意属性

有时候我们希望一个接口允许有任意的属性,可以使用如下方式:

interface Person {
  name: string
  age?: number
  [propName: string]: any
}

let tom: Person = {
  name: 'Tom',
  gender: 'male'
}

使用 [propName: string] 定义了任意属性取 string 类型的值。 需要注意的是,一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集:

interface Person {
  name: string
  age?: number
  [propName: string]: string
}

let tom: Person = {
  name: 'Tom',
  age: 25,
  gender: 'male'
}

// Property 'age' of type 'number | undefined' is not assignable to string index type 'string'.
// Type '{ name: string age: number gender: string }' is not assignable to type 'Person'.
// Property 'age' is incompatible with index signature.
// Type 'number' is not assignable to type 'string'.

上例中,任意属性的值允许是 string,但是可选属性 age 的值却是 number,number 不是 string 的子属性,所以报错了。
另外,在报错信息中可以看出,此时 { name: ‘Tom’, age: 25, gender: ‘male’ } 的类型被推断成了 { [x: string]: string | number name: string age: number gender: string },这是联合类型和接口的结合。

只读属性

有时候我们希望对象中的一些字段只能在创建的时候被赋值,那么可以用 readonly 定义只读属性:

interface Person {
  readonly id: number
  name: string
  age?: number
  [propName: string]: any
}

let tom: Person = {
  id: 89757,
  name: 'Tom',
  gender: 'male'
}

tom.id = 9527
// Cannot assign to 'id' because it is a read-only property.

上例中,使用 readonly 定义的属性 id 初始化后,又被赋值了,所以报错了。

传统方法中,JavaScript 通过构造函数实现类的概念,通过原型链实现继承。而在 ES6 中,我们终于迎来了 class。

TypeScript 除了实现了所有 ES6 中的类的功能以外,还添加了一些新的用法。

类的概念

这里对类相关的概念做一个简单的介绍。

  • 类(Class):定义了一件事物的抽象特点,包含它的属性和方法
  • 对象(Object):类的实例,通过 new 生成
  • 面向对象(OOP)的三大特性:封装、继承、多态
  • 封装(Encapsulation):将对数据的操作细节隐藏起来,只暴露对外的接口。外界调用端不需要(也不可能)知道细节,就能通过对外提供的接口来访问该对象,同时也保证了外界无法任意更改对象内部的数据
  • 继承(Inheritance):子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特性
  • 多态(Polymorphism):由继承而产生了相关的不同的类,对同一个方法可以有不同的响应。比如 Cat 和 Dog 都继承自
    Animal,但是分别实现了自己的 eat 方法。此时针对某一个实例,我们无需了解它是 Cat 还是 Dog,就可以直接调用 eat
    方法,程序会自动判断出来应该如何执行 eat 存取器(getter & setter):用以改变属性的读取和赋值行为
  • 修饰符(Modifiers):修饰符是一些关键字,用于限定成员或类型的性质。比如 public 表示公有属性或方法
  • 抽象类(Abstract Class):抽象类是供其他类继承的基类,抽象类不允许被实例化。抽象类中的抽象方法必须在子类中被实现
  • 接口(Interfaces):不同类之间公有的属性或方法,可以抽象成一个接口。接口可以被类实现(implements)。一个类只能继承自另一个类,但是可以实现多个接口

属性和方法

class Animal {
  constructor(public name) {
  this.name = name
  }
  sayHi() {
  return `My name is ${this.name}`
  }
}

let a = new Animal('Jack')
console.log(a.sayHi()) // My name is Jack

类的继承

class Cat extends Animal {
  constructor(name) {
  super(name) // 调用父类的 constructor(name)
  console.log(this.name)
  }
  sayHi() {
  return 'Meow, ' + super.sayHi() // 调用父类的 sayHi()
  }
}

let c = new Cat('Tom') // Tom
console.log(c.sayHi()) // Meow, My name is Tom

存取器

使用 getter 和 setter 可以改变属性的赋值和读取行为:

class Animal {
  constructor(name) {
  this.name = name
  }
  get name() {
  return 'Jack'
  }
  set name(value) {
  console.log('setter: ' + value)
  }
}

let a = new Animal('Kitty') // setter: Kitty
a.name = 'Tom' // setter: Tom
console.log(a.name) // Jack

静态方法

使用 static 修饰符修饰的方法称为静态方法,它们不需要实例化,而是直接通过类来调用:

class Animal {
  static isAnimal(a) {
  return a instanceof Animal
  }
}

let a = new Animal('Jack')
Animal.isAnimal(a) // true
a.isAnimal(a) // TypeError: a.isAnimal is not a function

实例属性

ES6 中实例的属性只能通过构造函数中的 this.xxx 来定义,ES7 提案中可以直接在类里面定义:

class Animal {
  name = 'Jack'

  constructor() {
  // ...
  }
}

let a = new Animal()
console.log(a.name) // Jack

静态属性

ES7 提案中,可以使用 static 定义一个静态属性:

class Animal {
  static num = 42

  constructor() {
  // ...
  }
}

console.log(Animal.num) // 42

TypeScript 中类的用法

public private 和 protected

TypeScript 可以使用三种访问修饰符(Access Modifiers),分别是 public、private 和 protected。

  • public 修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是 public 的
  • private 修饰的属性或方法是私有的,不能在声明它的类的外部访问
  • protected 修饰的属性或方法是受保护的,它和 private 类似,区别是它在子类中也是允许被访问的

抽象类

abstract 用于定义抽象类和其中的抽象方法。

什么是抽象类?

首先,抽象类是不允许被实例化的;其次,抽象类中的抽象方法必须被子类实现:

abstract class Animal {
  public name
  public constructor(name) {
  this.name = name
  }
  public abstract sayHi()
}

let a = new Animal('Jack')

// Cannot create an instance of an abstract class.

上面的例子中,我们定义了一个类 Cat 继承了抽象类 Animal,但是没有实现抽象方法 sayHi,所以编译报错了。

下面是一个正确使用抽象类的例子:

abstract class Animal {
  public name
  public constructor(name) {
  this.name = name
  }
  public abstract sayHi()
}

class Cat extends Animal {
  public sayHi() {
  console.log(`Meow, My name is ${this.name}`)
  }
}

let cat = new Cat('Tom')

泛型

泛型定义

泛型就是解决 类 接口 方法的复用性、以及对不特定数据类型的支持

泛型函数

// 只能返回string类型的数据
function getData(value:string):string{
    return value
}

// 可返回string和number类型的数据 (代码冗余)
function getData1(value:string):string{
    return value
}
function getData2(value:number):number{
    return value
}

// any 相当于放弃了类型检测
function getData3(value:any):any{
    return value
}

// 实现传入什么类型返回什么类型
// T 表示泛型,具体什么类型是调用这个方法的时候绝定的(也可以是其它字母)
function getData4<T>(value:T):T{ // 指定了返回的类型
    // 传入什么 返回什么
    return value
}
getData4<number>(123) //使用
getData4<string>("xxx") 

// 返回任意类型
function getData5<T>(value:T):any{  
    return "dd"
}
getData5<number>(123) //使用
getData5<string>("dd")

泛型类

比如有个最小堆算法, 需要同时支持返回数字和字符串2种类型
只支持 number类型

class MinClass{
    public list:number[] = [];
    add(num:number){
        this.list.push(num)
    }

    min():number{ // 求最小数
        var minNum = this.list[0]
        for(var i = 0; i < this.list.length; i++){
            if(minNum > this.list[i]){
                minNum = this.list[i]
            }
        }
        return minNum
    }
}

var m = new MinClass()
m.add(22)
m.add(2)
m.add(66)
console.log(m.min()) //2

可支持多类型 对不特定数据类型的支持

class MinClass<T>{
    public list:T[] = [];
    add(num:T):void{
        this.list.push(num)
    }

    min():T{ // 求最小数
        var minNum = this.list[0]
        for(var i = 0; i < this.list.length; i++){
            if(minNum > this.list[i]){
                minNum = this.list[i]
            }
        }
        return minNum
    }
}

var m = new MinClass<string>() //实例化类 并且制定了类的 T代表类型
m.add("1")
m.add("12")
m.add("23")
console.log(m.min()) //1

把类当做参数的泛型类

class User{
	name: string | undefined;
	pwd: string | undefined
}
class MysqlDb{
    add(user:user):boolean{
        return true
    }
}
var u = new User()
u.name = "u-name"
u.pwd = "u-123"

var sql = new MysqlDb()
sql.add(u)

泛型类

class User{
	name: string | undefined;
	pwd: string | undefined
}
class MysqlDb<T>{
    add(user:T):boolean{
        return true
    }
}
var u = new User()
u.name = "u-name"
u.pwd = "u-123"

var sql = new MysqlDb<User>()
sql.add(u)

参数传对象

class User{
	name: string | undefined;
    pwd: string | undefined;
    constructor(params:{
        name: string | undefined,
        pwd: string | undefined
    }){
        this.name = params.name
        this.pwd = params.pwd
    }
}
class MysqlDb<T>{
    add(user:T):boolean{
        return true
    }
}
var u = new User({
    name:"u-name",
    pwd:"u-123"
})

var sql = new MysqlDb<User>()
sql.add(u)

泛型接口

函数接口

interface ConFigFu{  // 函数类型接口
    (value1:string,value2:string):string
}

var setData:ConFigFu = function(value1:string,value2:string):string{
    return value1 + value2
}

setData("xx","yy")

泛型接口
第一种写法

interface ConFigFu{  
    <T>(value1:T,value2:T):T
}

var setData:ConFigFu = function<T>(value1:T,value2:T):T{
    return value1
}

setData<string>("xx","yy")

第二种写法

interface ConFigFu<T>{  
    (value1:T,value2:T):T
}

var setData = function<T>(value1:T,value2:T):T{
    return value1
}

var getdata:ConFigFu<string> = setData

getdata("xx","yy")


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明原文出处。如若内容造成侵权/违法违规/事实不符,请联系SD编程学习网:675289112@qq.com进行投诉反馈,一经查实,立即删除!