【study】Typescript进阶之路

610 阅读11分钟

一.Typescript的安装

1.npm/cnpm安装
// Typescript的安装
npm install -g typescript

// Typescript检验安装是否成功
tsc -v
// 安装成功会输出版本号 例如:Version 4.3.5
2.yarn安装
// 安装yarn
npm install -g yarn

// Typescript的安装
yarn global add typescript

// Typescript检验安装是否成功
tsc -v
// 安装成功会输出版本号 例如:Version 4.3.5

二.实现一个简单的'你好 ts'

1.目录结构

image.png

2.实现一个简单的'你好 ts'
// index.ts

console.log('你好 ts')
// ts 编译 es5
var str:string = "你好 ts"
3.ts变成js的手动输入命令
tsc index.ts  // 会在多次运行这个命令之后,自动生成一个index.js文件并更新
4.ts变成js的热更新配置\color{red}{ts变成js的热更新配置}
  • 空白文件夹下输入:tsc --init(会自动生成一个tsconfig.json文件)
  • 在tsconfig.json文件下,改outDir为outDir:'./js'
  • 新建index.ts文件
let str:string = "你好 TS"
let str1:string = "你好啊"
  • 点击终端——运行任务——typescript——tsc:监视-tscconfig.json
  • 自动生成了index.js的目录结构

image.png

  • 每次更改index.ts保存之后会自动更新到index.js里面

三.Typescript的类型介绍

1.布尔类型 (boolean) true false
var flag:boolean = true
flag = false
2.数字类型 (number)
var a:number = 123
console.log(a)
var b:number = 456.88 // ts里面number包括浮点类型
console.log(b)

image.png

3.字符串类型(string)
let str:string = "你好 TS"
console.log(str)
4.数组类型
// 第一种定义数组的方式
let arr1:number[] = [1,2,3,4] // 在ts里面无论使用var还是let,转成js都会变成var
let arr2:string[] = ["1","2","3","4"] // 字符串可以用单引号,也可以用双引号,建议用双引号

// 第二种定义数组的方式
let arr3:Array<number> = [11,22,33,44]
let arr4:Array<string> = ["11","22","33","44"]
5.元组类型(tuple) 属于数组的一种
// 第一种方式
let arr1:[string,number,boolean] = ['12',34,false]

// 第二种any的方式
let arr2:any[] = ['111',number,false]
6.枚举类型(enum)
// Flag    1表示true    -1表示false
 enum Flag  {success=1, error=-1} // 如果这里不赋值,则在打印的时候会打印出索引index值,如果前一个有赋值,后一个没赋值,则后一个的index是前一个赋值+1
 var f:Flag = Flag.success
 console.log(f)
 // 会打印出1
7.任意类型(any)
var nums:any = 123 // 可以写成任意类型
// 任意类型的用处
var oBox = document.getElement('box') //此时会报错,写法应该如下
var oBox:any = document.getElement('box') // 加上任意类型在ts里面才不会报错
oBox.style.color = 'red'
8.null 和 undefined 其他 (never类型) 数据类型的子类型
var other1:undefined;
var other2:number | undefined; // 可以使用并且,赋值number和不赋值都可
var other3:null;
// 一个元素可能是number,可能是null,可能是undefined
var other4 : number | null | undefined ;
9.void表示没有任何类型,一般用于定义方法的时候没有返回值
function run ():void {
   console.log('run') 
}
run()
10.never类型:是其他类型(包括null 和 undefined) 的子类型,代表从不会出现的值
// 这意味着声明never的变量只能被never类型所赋值
var never1:undefined;
never1 = undefined

var never2:null;
never2 = null

var a:never;
a(()=>{
   throw new Error('错误')
})()

四.Typescript中的函数

1.Typescript定义函数的两种方式
// 第一种:函数声明法
function run(): string {   // string表示返回值类型是string
  return 'run'
}
run()

// 第二种:函数表达式
var run2 = function (): number {  // number表示返回值类型是number
  return 123
}
run2()

2.Typescript定义传参的两种方式
// 第一种:函数声明法
function getinfo(name: string, age: number): string {
  return `${name} --- ${age}`
}
getinfo('Typescript', 20)

// 第二种:函数表达式
var getinfo2 = function (name: string, age: number): string {
  return `${name} --- ${age}`
}
getinfo2('Typescript', 200)
3.Typescript没有返回值的方法
function norun(): void {
  console.log('run')
}
norun()
4.Typescript方法的可选参数
  • ES5里面的方法的实参和形参可以不一样,但是TS中必须一样,如果不一样就需要配置可选参数
  • 注意:可选参数必须配置到参数的最后面
function getinfo(name: string, age?: number): string {  // ?表示age是可选参数,即可传可不传
  if (age) {
    return `${name} --- ${age}`
  } else {
    return `${name} --- 年龄保密`
  }
}
alert(getinfo('Typescript'))
5.Typescript方法的默认参数
  • ES5里面没法设置默认参数,ES6和TS都可以设置默认参数
function getinfo(name: string, age: number = 20): string {
  return `${name} --- ${age}`

}
alert(getinfo('Typescript'))    // 不传的话默认是20
alert(getinfo('Typescript', 30)) // 传值的话会取传值30
6.Typescript方法的剩余参数
// 求和一般写法
function sum(a: number, b: number, c: number, d: number): number {
  return a + b + c + d
}
alert(sum(1,2,3,4))

// 三点运算符,接收新参传过来的值
// 第一种写法:
function sum(...result: number[]): number {
  var sum = 0;
  for (var i = 0; i < result.length; i++) {
    sum += result[i]
  }
  return sum
}
alert(sum(1, 2, 3, 4, 5, 6))

// 第二种写法:
function sum(a:number,...result: number[]): number { // 将a赋值给sum,剩余赋值给result,结果是一样的
  var sum = a;
  for (var i = 0; i < result.length; i++) {
    sum += result[i]
  }
  return sum
}
alert(sum(1, 2, 3, 4, 5, 6))
7.Typescript方法的函数重载
  • java中的方法的重载:重载指的是两个或者两个以上的同名函数,但是他们的参数不一样,这时会出现函数重载的情况
  • ts中的重载:通过为同一个函数提供多个函数类型的定义来实现多种功能的目的
  • ts为了兼容ES5以及ES6,重载的写法和java中有区别
// ES5中出现同名方法,下面的会替换掉上面的方法
function fun(config: any): any {}
function fun(config: any, value: any): any {}

// ts中的重载:参数不一样
function getinfo(name: string): string;
function getinfo(age: number): number;
function getinfo(str: any): any {
  if (typeof str === 'string') {
    return '我叫:' + str
  } else {
    return '我的年龄是:' + str
  }
}
alert(getinfo('张三'))
alert(getinfo(20))

// ts中的重载:参数一样
function getinfo(name: string): string;
function getinfo(name: string,age: number): string;
function getinfo(name: any,age?: any): any {
  if(age) {
    return '我叫:'+name +'----'+ '我的年龄:'+age
  }else {
    return '我叫:'+name
  }
}
alert(getinfo('张三'))
alert(getinfo('TS重载',20))
8.Typescript方法的箭头函数
  • 箭头函数里面的this指向上下文
var fun = ((a:string,b:number):string => {
  return a + b
})
alert(fun('箭头函数',6))

五.Typescript中的类

1.复习ES5里面的继承
// 要被继承的函数
function Person(name, age) {
   this.name = name;
   this.age = age;
}
Person.prototype.work = function () {
   console.log('我喜欢工作!!!')
}

// 继承的函数
function Fun(name, age, sex) {
   Person.call(this, name, age)   // 对象冒充继承
   this.sex = sex
}
Fun.prototype = new Person()    // 原型链继承
Fun.prototype.constructor = Fun   // 让其指向自己
Fun.prototype.study = function () {
   console.log('我喜欢学习!!!')
}
let tljx = new Fun('棠梨煎雪', 18, '女')
console.log(tljx)
tljx.work()
tljx.study()
2.复习ES6里面的继承
class Person {
   constructor(name, age) {
      this.name = name
      this.age = age
    }
   work() {
     console.log('我喜欢工作!!!')
    }
}
class Fun extends Person {
    constructor(name, age, sex) {
      super(name, age)
      this.sex = sex
    }
    study() {
      console.log('我喜欢学习!!!')
    }
}
let tljx = new Fun('棠梨煎雪', 18, '女')
console.log(tljx)
tljx.work()
tljx.study()
3.Typescript中的
class Person {
  name:string //属性 前面省略了public关键词
  constructor(name:string) { // 构造函数 实例化类的时候触发的方法
    this.name = name;
  }
  run():void {
    alert(this.name)
  }
}
var p = new Person('棠梨煎雪')
p.run()
class Person {
  name:string //属性 前面省略了public关键词
  constructor(name:string) { // 构造函数 实例化类的时候触发的方法
    this.name = name;
  }
  getName():string {
    return this.name
  }
  setName(name:string):void {
    this.name = name
  }
}
var p = new Person('棠梨煎雪')
alert(p.getName())
p.setName('Typescript')
alert(p.getName())
4.Typescript的继承
class Person {
  name: string
  age: number
  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }
  work(): void {
    console.log('我喜欢工作!!!')
  }
}
class Fun extends Person {
  sex: string
  constructor(name: string, age: number, sex: string) {
    super(name, age)
    this.sex = sex
  }
  study(): void {
    console.log('我喜欢学习!!!')
  }
}
let tljx = new Fun('棠梨煎雪', 18, '女')
console.log(tljx)
tljx.work()
tljx.study()
5.Typescript的继承探讨
  • 父类的方法和子类的方法一致,那么是会用父类,还是子类呢?
class Person {
  name: string
  age: number
  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }
  work(): void {
    console.log('我喜欢工作!!!')
  }
}
class Fun extends Person {
  sex: string
  constructor(name: string, age: number, sex: string) {
    super(name, age)
    this.sex = sex
  }
  work(): void {
    console.log('我喜欢学习!!!')
  }
}
let tljx = new Fun('棠梨煎雪', 18, '女')
console.log(tljx)
tljx.work() // 我喜欢学习!!!
6.Typescript类里面的修饰符
  • Typescript里面定义属性的时候,给我们提供了以下三种修饰符
  • public:共有,在类里面,子类,和类外面都可以访问
  • protected:保护类型,在类里面,子类里面可以访问,在类的外部没法访问
  • private:私有,在类的里面可以访问,子类,类外部都没法访问
  • 属性不加修饰符,默认是共有public
7.Typescript类静态方法
  • 在实例方法前面直接加上static即可
class Person {
  name: string
  age: number
  static sex="女" // 静态方法
  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }
  static work(): void { // work就是静态方法 这里无法直接调用类的属性
    console.log('我喜欢工作!!!')
    console.log('我喜欢工作!!!' + Person.sex)
  }
}
let tljx = new Person('棠梨煎雪', 18)
console.log(tljx)
tljx.work() // 我喜欢学习!!!
8.Typescript的多态方法
  • 父类定义一个方法不去实现,让继承他的子类去实现,每一个子类有不同的表现
  • 多态属于继承
9.Typescript的抽象方法
  • Typescript中的抽象类,它是提供其他类继承的基类,不能直接被实例化
  • 用abstract关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体实现并且必须在派生类中实现
  • abstract抽象方法只能放在抽象类里面
  • 抽象类和抽象方法用来定义标准
  • 抽象类不能被实例化
abstract class Person {
  name: string
  age: number
  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }
  abstract eat(): any;
}
// var a = new Person()  // 抽象类不能被实例化
class Dog extends Person {
  // 抽象类的子类必须实现抽象类里面的抽象方法
  constructor(name: string, age: number){
    super(name,age)
  }
  eat(): any {
     console.log(this.name + '爱吃饭')
  }
}
var dog = new Dog('wangwang',12)
dog.eat()

六.Typescript中的接口

1.属性类接口
  • 对json的约束
interface nameJson {
  firstName: string;  // 注意;结束
  secondName: string
}
function printName(name: nameJson): void {
  console.log(name.firstName + '-------' + name.secondName)
}

// 第一种写法
printName({
  // obj:20, 写的和定义nameJson不一一对应会报错
  firstName: '棠梨',
  secondName: '煎雪'
})

// 第二种写法
var obj = {
  obj: 20, // 可以写nameJson里没有的key值和value值
  firstName: '棠梨',
  secondName: '煎雪'
}
printName(obj)
2.可选属性接口
interface nameJson {
  firstName: string; 
  secondName?: string // 接口的可选属性
}
function printName(name: nameJson){
  console.log(name)
}
printName({
  firstName: '棠梨',
  // secondName: '煎雪'  // 可传可不传
})
3.属性接口原生AJAX封装
interface Config {
  type: string;
  url: string;
  data?: string;
  dataType: string
}

function ajax(config: Config) {
  var xhr = new XMLHttpRequest();
  xhr.open(config.type, config.url, true)
  xhr.send(config.data)
  xhr.onreadystatechange = function () {
    if (xhr.readyState === 4 && xhr.status === 200) {
      console.log("成功")
      if (config.dataType === 'json') {
        JSON.parse(xhr.responseText)
      } else {
        console.log(xhr.responseText)
      }
    }
  }
}

ajax({
  type: 'get',
  url: 'https://www.baidu.com/',  // 模拟假接口
  data: '111',
  dataType: 'json'
})
4.函数类型接口
  • 对方法传入的参数,以及返回值进行约束
  • 加密的函数类型接口
interface encrypt {
  (key:string,value:string):string
}

var md5:encrypt = function(key:string,value:string):string{
  //  模拟操作
  return key+value;
}
console.log(md5('name','棠梨煎雪'))
5.可索引接口(数组、对象的约束,不常用)
  • 数组的约束
interface Arr {
  [index: number]: string // index索引值是number,value值是string
}
// 对数组的约束
var arr: Arr = ['棠梨煎雪', '1234']
console.log(arr[1])
  • 对象的约束
interface Obj {
  [index:string]:string
}
// 对对象的约束
var obj:Obj={name:'棠梨煎雪',age:'18'}
console.log(obj)
6.类类型接口(对类的约束,和抽象类有点相似)
interface Animal {
  name: string;
  eat(str: string): void
}
class Dog implements Animal { // implements表示实现这个接口
  name: string;
  constructor(name: string) {
    this.name = name
  }
  eat() {
    console.log(this.name + '吃饭饭')
  }
}
var p = new Dog('汪汪')
p.eat()
7.接口的拓展(接口可以继承接口)
interface Animal{
  eat():void;
}
// 继承Animal接口
interface Dog extends Animal {
  work():void
}
// implements是实现接口的关键字
class Cat implements Dog {  // 实现Dog这个接口
    public name:string;
    constructor(name:string){
       this.name = name
    }
    eat () {
      console.log(this.name + '吃饭饭')
    }
    work () {
      console.log(this.name + '爱工作')
    }
}
var p = new Cat('小白')
p.eat()
p.work()
  • 可以继承类并实现接口
// 例如
class Cat extends Parent implements Dog {} 
// Cat是定义的类
// Parent:父类
// Dog:接口

七.Typescript中的泛型

1.泛型的理解
  • 泛型就是解决类,接口方法的复用性,以及对不特定数据类型的支持
2.泛型的类型
  • 只能返回string类型的数据
function getDate(value:string):string {
  return value;
}
  • 同时返回string和number类型的数据
// 第一种:any可以实现,但是any放弃了类型检查,但我们希望传入什么返回什么
function getDate(value:any):any {
  return value;
}

// 泛型,可以支持不特定的数据类型
// 要求:传入的参数和返回的参数一致
// T表示泛型,具体什么类型是调用这个方法的时候决定的
function getData<T>(value:T):T {
  return value
}
console.log(getData<number>(123)) // 定义number,必须传入number
  • 泛型类:比如有个最小的堆算法,需要同时支持返回数字和字符串两种类型
// 只能返回支持数字类型的最小堆算法
class minNumber {
  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 p = new minNumber()
p.add(2)
p.add(123)
p.add(1)
alert(p.min())
// 泛型类:同时支持返回数字和字符串两种类型的最小堆算法
class minNumber<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 p1 = new minNumber<number>() // 实例化类,并且指定了类的T代表的类型是number
p1.add(2)
p1.add(123)
p1.add(1111)
alert(p1.min())

var p2 = new minNumber<string>() // 实例化类,并且指定了类的T代表的类型是string
p2.add('2')
p2.add('123')
p2.add('1111')
alert(p2.min())
3.泛型的接口
  • 函数的接口
interface Config {
  (value1:string,value2:string):string
}

var setData:Config = function(value1:string,value2:string):string {
  return value1+value2
}
console.log(setData('棠梨','煎雪'))
  • 泛型的接口
// 第一种定义泛型接口的方法
interface Config {
  <T>(value: T): T
}
var setData: Config = function <T>(value: T): T {
  return value
}
console.log(setData('棠梨煎雪'))
// 第二种定义泛型接口的方法
interface Config<T> {
  (value: T): T
}
function setData<T>(value: T): T {
  return value
}
var mysetData:Config<string> = setData
console.log(mysetData('棠梨煎雪'))
4.泛型类:把类作为参数的泛型类(约束)
// 定义一个User的类这个类的作用就是映射数据库字段
// 然后定义一个MysqlDb的类用于操作数据库
// 最后把User类作为参数传到MysqlDb中

class User {
  username: string | undefined;
  password: string | undefined
}
class MysqlDb {
  add(user: User): boolean {
    console.log(user)
    return true
  }
}
var u = new User()
u.username = "棠梨煎雪"
u.password = "123"
var s = new MysqlDb()
s.add(u)
// 使用泛型

class User{
  username: string | undefined;
  password: string | undefined;
  constructor(params:{
    username: string | undefined;
    password: string | undefined;
  }){
    this.username = params.username
    this.password = params.password
  }
}
var u = new User({
  username:'棠梨煎雪',
  password:'123456'
})
class MysqlDb<T> {
  add(user: T): boolean {
    console.log(user)
    return true
  }
}
var s = new MysqlDb<User>()
s.add(u)

八.Typescript类型,接口,类,泛型的综合应用

/*
  功能:定义一个操作数据库的库    支持Mysql  Mssql  MongoDb
  要求:Mysql  Mssql  MongoDb功能一样   都有add update delete  get方法
  注意:约束统一的规范,以及代码的重用
*/

// 泛型接口
interface User<T> {
  add(info: T): boolean;
  update(info: T, id: number): boolean;
  delete(id: number): boolean;
  get(id: number): any[]; // 得到的是任意类型的数组
}

// 定义一个操作数据库的库 Mysql
// 注意:要实现泛型接口,那么这个类也应该是一个泛型类
class MysqlDb<T> implements User<T>{
  add(info: T): boolean {
    console.log(info)
    return true
    // throw new Error("Method not implemented.");
  }
  update(info: T, id: number): boolean {
    throw new Error("Method not implemented.");
  }
  delete(id: number): boolean {
    throw new Error("Method not implemented.");
  }
  get(id: number): any[] {
    throw new Error("Method not implemented.");
  }
}

// 定义一个操作数据库的库 Mssql 
// 注意:要实现泛型接口,那么这个类也应该是一个泛型类
class MssqlDb<T> implements User<T>{
  add(info: T): boolean {
    throw new Error("Method not implemented.");
  }
  update(info: T, id: number): boolean {
    throw new Error("Method not implemented.");
  }
  delete(id: number): boolean {
    throw new Error("Method not implemented.");
  }
  get(id: number): any[] {
    throw new Error("Method not implemented.");
  }
}

// 定义一个操作数据库的库 MongoDb 
// 注意:要实现泛型接口,那么这个类也应该是一个泛型类
class MongoDbDb<T> implements User<T>{
  add(info: T): boolean {
    throw new Error("Method not implemented.");
  }
  update(info: T, id: number): boolean {
    throw new Error("Method not implemented.");
  }
  delete(id: number): boolean {
    throw new Error("Method not implemented.");
  }
  get(id: number): any[] {
    throw new Error("Method not implemented.");
  }
}

class UserDb{
  username:string | undefined;
  password:string | undefined
}

var u = new UserDb()
u.username = "棠梨煎雪"
u.password="666666"

var oMysql = new MysqlDb<UserDb>() // 类作为参数来约束数据传入的类型
oMysql.add(u)