微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Vue3.0 前备知识 —— TypeScript:进入类型的世界

目录

一.安装 TypeScript

二.原始数据类型 和 Any

 三.Array 和 Tuple(元组)

四.interface 接口

五.函数

六.类型推论,联合类型 和 类型断言

七.Class 类

八.类与接口

九.枚举 Enums

十.泛型 Generics

十一.类型别名、交叉类型

十二.声明文件

十三.内置类型


一.安装 TypeScript

二.原始数据类型 和 Any

  • 原始数据类型(primitive values):除了 Object 以外,所有类型都是不可变的(值本身无法被改变)
  • Boolean、Number、String、Null、Undefined、BigIntSymbol
  • 注意的点:
  1. Number:可以写成二进制、八进制数字
  2. String:ts 中可以使用 es6 新增的 `模版字符串`
  3. undefined 和 null,是所有类型的子类型,也就是说 undefined 类型的变量,可以赋值给 number 类型的变量
let isDone: boolean = false

// es6 还支持2进制和8进制
let age: number = 10
let binaryNumber: number = 0b1111

// es6 新增的 `模版字符串` 也可以用
let firstName: string = 'TeaMeow'
let message: string = `Hello, ${firstName}, age is ${age}`

// undefined 和 null,是所有类型的子类型
// 也就是说 undefined 类型的变量,可以赋值给 number 类型的变量
let u: undefined = undefined
let n: null = null
let num: number = undefined
  • Any:值本身可以被改变,也可以在任何值上,访问任意属性、任意方法
  • 注意:一般不提倡用,不然会失去类型检查的意义
let notSure: any = 4
notSure = 'maybe it is a string'
notSure = 'boolean'
// 在任意值上访问任何属性都是允许的:
notSure.myName
// 也允许调用任何方法:
notSure.getName()

 三.Array 和 Tuple(元组

  • 数组:let arrOfNumbers: number[] = [1, 2, 3, 4]
  • 元组let user: [string, number] = ['TeaMeow', 21]
// 最简单的方法是:「类型 + 方括号」来表示数组
let arrOfNumbers: number[] = [1, 2, 3, 4]
// 数组的项中不允许出现其他类型:
// 数组的方法的参数也会根据 数组在定义时 约定的类型进行限制:
arrOfNumbers.push(3)
arrOfNumbers.push('abc')

// 元祖的表示和数组非常类似,只不过它将类型写在了里面 这就对每一项起到了限定的作用
let user: [string, number] = ['TeaMeow', 21]
// 但是写少一项 就会报错 写多一项也会有问题
user = ['molly', 20, true]

四.interface 接口

  • Duck Typing 概念:如果某个东西长得像鸭子,像鸭子一样游泳,像鸭子一样嘎嘎叫,那它就可以被看成是一只鸭子
  • 接口其实就是非原始数据类型(比如 Object)的定义模板、规范
  • 接口中可以定义 可选属性?,表示变量如果使用该接口,这个属性写不写都行
  • 接口中可以定义 只读属性readonly,表示变量如果使用该接口,这个只读属性就只能被第一次初始化进行赋值,之后不能被赋值
  • 只读属性 readonly 和 const 的区别联系:
  1. 区别:都表示除了第一次进行赋值外,无法进行二次更改
  2. 联系:readonly 用于接口中的某个属性,最后接口产生变量;const 用于常量
// 我们定义了一个接口 Person
interface Person {
  name: string;
  age: number;
}
// 接着定义了一个变量 TeaMeow,它的类型是 Person。这样,我们就约束了 TeaMeow 的形状必须和接口 Person 一致
let TeaMeow: Person ={
  name: 'TeaMeow',
  age: 20
}

// 有时希望不要完全匹配一个形状,那么可以用可选属性:
interface Person {
    name: string;
    age?: number;
}
let TeaMeow: Person = {
    name: 'Viking'
}

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

interface Person {
  readonly id: number;
  name: string;
  age?: number;
}

五.函数

  • ts 中,约定函数的输入输出函数中可以有可选参数
  • 函数本身类型中的 => 和 es6 没关系,是 ts 中关于定义函数本身类型的语法
  • 可以通过接口定义函数本身的类型
// 约定输入,约定输出
function add(x: number, y: number): number {
  return x + y
}

// 可选参数
function add(x: number, y: number, z?: number): number {
  if (typeof z === 'number') {
    return x + y + z
  } else {
    return x + y
  }
}

// 函数本身的类型
const add2: (x: number, y: number, z?:number) => number = add

// interface 描述函数类型

interface ISum {
  (x: number, y: number): number
}
const sum2: ISum = sum

六.类型推论,联合类型 和 类型断言

1.联合类型

  • 一个变量,既可能是数字,又可能是字符串的时候,就用到了 联合类型:let numberOrString: number | string 
  • 当 TypeScript 不确定一个联合类型的变量到底是哪个类型时,就只能访问此联合类型中 所有类型的 共有属性方法
  • 比如说某个联合类型的变量是 数字+字符串,那么,我们调用该变量的属性就只能调用 length、toString() 这种,数字和字符串都有的属性方法
  1. numberOrString.length 
  2. numberOrString.toString()
// 用中竖线分割两个类型
let numberOrString: number | string 
// 当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,就只能访问此联合类型的所有类型里共有的属性方法:
numberOrString.length 
numberOrString.toString()

2.类型断言

  • 可以用 as 关键字进行 类型断言,告诉 typescript 编译器:
  • 你没法判断我的代码,但是我本人很清楚,这里我就把它看作是一个 string,你可以给他用 string 的方法
// 可以用 as 关键字进行 类型断言,告诉 typescript 编译器:
// 你没法判断我的代码,但是我本人很清楚,这里我就把它看作是一个 string,你可以给他用 string 的方法
function getLength(input: string | number): number {
  // 把 input 变量看作 字符串
  const str = input as string
  if (str.length) {
    return str.length
  // 把 input 变量看作 数字
  } else { 
    const number = input as number
    return number.toString().length
  }
}

3.类型守卫

  • typescript 在不同的条件分支里面,智能缩小了范围,这样代码出错的几率就大大降低了
// typescript 在不同的条件分支里面,智能缩小了范围,这样代码出错的几率就大大降低了
function getLength2(input: string | number): number {
  if (typeof input === 'string') {
    return input.length
  } else {
    return input.toString().length
  }
}

七.Class 类

  • 面向对象的三大特点:
  1. 封装(Encapsulation):将对数据的操作细节隐藏起来,只暴露对外的接口;调用者不需要(也不可能)知道细节,就能通过对外暴露的接口来访问该对象
  2. 继承(Inheritance):子类继承父类,子类除了拥有父类的所有特性外,还有自己的特性
  3. 多态(polymorphism):由继承而产生了相关的不同的类,对同一个方法可以有不同的响应,比如:动物叫 → 猫叫、狗叫,对于 叫 这个方法,不同动物类有不同的响应
  1. public 修饰的属性方法是公有的,可以在任何地方被访问到,认所有的属性方法都是 public 的
  2. private 修饰的属性方法是私有的,不能在声明它的类的外部访问,包括它的子类也不能访问
  3. protected 修饰的属性方法是受保护的,它和 private 类似,区别是它在子类中是允许被访问的
class Animal {
  name: string;
  constructor(name: string) {
    this.name = name
  }
  run() {
    return `${this.name} is running`
  }
}
const snake = new Animal('lily')

// 继承的特性
class Dog extends Animal {
  bark() {
    return `${this.name} is barking`
  }
}

const xiaobao = new Dog('xiaobao')
console.log(xiaobao.run())
console.log(xiaobao.bark())

// 这里我们重写构造函数,注意在子类的构造函数中,必须使用 super 调用父类方法,否则会报错。
class Cat extends Animal {
  constructor(name) {
    super(name)
    console.log(this.name)
  }
  run() {
    // 在子类的构造函数中,必须使用 super 调用父类方法,否则会报错
    return 'Meow, ' + super.run()
  }
}
const maomao = new Cat('maomao')
console.log(maomao.run())

 

八.类与接口

  • 类实现某接口:implements => class Cellphone implements Radio
  • 类实现多个接口:中间需要用 , 隔开 => 实现多个接口,需要中间用 逗号 隔开
interface Radio {
  switchRadio(trigger: boolean): void;
}

interface Battery {
  checkBatteryStatus(): void;
}

// 类实现(继承)某接口 implements
class Car implements Radio {
  switchRadio(trigger) {
    return 123
  }
}
class Cellphone implements Radio {
  switchRadio() { }
}

// 实现多个接口,需要中间用 逗号 隔开
class Cellphone implements Radio, Battery {
  switchRadio() { }
  checkBatteryStatus() { }
}

九.枚举 Enums

  • 枚举成员会被赋值为:从 0 开始递增的数字:console.log(Direction.Up) // 0
  • 枚举做了反向映射,可以选出枚举中的第几项是什么:console.log(Direction[0]) // Up
// 定义枚举方向,这里的枚举成员会被赋值为:从 0 开始递增的数字
enum Direction {
  Up, // 被赋值为 0
  Down, // 被赋值为 1
  Left, // 被赋值为 2
  Right, // 被赋值为 3
}
console.log(Direction.Up) // 0

// 枚举做了反向映射,可以选出枚举中的第几项是什么
console.log(Direction[0]) // Up

// 字符串枚举
enum Direction {
  Up = 'UP',
  Down = 'DOWN',
  Left = 'LEFT',
  Right = 'RIGHT',
}
const value = 'UP'
if (value === Direction.Up) {
  console.log('go up!')
}

十.泛型 Generics

1.泛型定义

  • 泛型(Generics):在定义函数、接口或类的时候,不指定具体类型,使用时再指定类型
  • 传入某个类型的参数,返回的也是这个类型
  • 泛型也可以传入多个值,比如第一个参数传入字符串类型,第二个参数传入数字类型,返回的时候,让第二个参数先返回来,也就是返回数字,第一个参数后返回来,也就是返回字符串
function echo(arg) {
  return arg
}
const result = echo(123)
// 上面传入了数字,但是返回了 any

// 下面规定,传入 T类型参数,也需要返回 T类型结果
function echo<T>(arg: T): T {
  return arg
}
const result = echo(123)

// 泛型也可以传入多个值
// 下面规定,传入 T,U 类型参数,返回 U,T 类型参数
function swap<T, U>(tuple: [T, U]): [U, T] {
  return [tuple[1], tuple[0]]
}
// 传入字符串、数字类型的参数,返回数字、字符串类型的参数(比原来反了)
const result = swap(['string', 123])

2.泛型约束

  • 函数内使用泛型时,由于事先不知道它是哪种类型,所以不能随意的操作它的属性方法,比如不确定传入的参数是否有 length,就不能操作 length 属性
  • 泛型继承约束:T extends IWithLength
  • 继承那个接口表示,接下来传入的参数中必须包含 length 这个属性
function echoWithArr<T>(arg: T): T {
  console.log(arg.length)
  return arg
}

// 上例中,泛型 T 不一定包含属性 length,我们可以给他传入任意类型,当然有些不包括 length 属性,那样就会报错

interface IWithLength {
  length: number;
}
// 泛型继承约束,继承那个接口表示,接下来传入的参数中必须包含 length 这个属性
function echoWithLength<T extends IWithLength>(arg: T): T {
  console.log(arg.length)
  return arg
}

echoWithLength('str') // 传入字符串包含了 length属性
const result3 = echoWithLength({length: 10}) // 传入的对象包含了 length属性
const result4 = echoWithLength([1, 2, 3]) // 传入的数组包含了 length属性

3.泛型、类、接口

  • 可以给类和接口加泛型,进行约束
class Queue {
  private data = [];
  push(item) {
    return this.data.push(item)
  }
  pop() {
    return this.data.shift()
  }
}

const queue = new Queue()
queue.push(1)
queue.push('str')
console.log(queue.pop().toFixed())
console.log(queue.pop().toFixed())

//在上述代码中存在一个问题,它允许你向队列中添加任何类型的数据,当然,当数据被弹出队列时,也可以是任意类型。在上面的示例中,看起来人们可以向队列中添加string 类型的数据,但是在使用的过程中,就会出现我们无法捕捉到的错误,比如,传入字符串,无法进行取整

class Queue<T> {
  private data = [];
  push(item: T) {
    return this.data.push(item)
  }
  pop(): T {
    return this.data.shift()
  }
}
const queue = new Queue<number>()

// 泛型和 interface
interface KeyPair<T, U> {
  key: T;
  value: U;
}

let kp1: KeyPair<number, string> = { key: 1, value: "str"}
let kp2: KeyPair<string, number> = { key: "str", value: 123}

十一.类型别名、交叉类型

1.类型别名

  • 类型别名 type:就是给类型起一个别名,让它可以更方便的被重用
  • 类型别名除了支持 函数类型,也支持 联合类型,还支持 字符串字面量
// 下面的函数类型是:(x: number, y: number) => number
let sum: (x: number, y: number) => number
const result = sum(1,2)
// 将函数类型取个类型别名
type PlusType = (x: number, y: number) => number
let sum2: PlusType

// 支持联合类型
type StrOrNumber = string | number
let result2: StrOrNumber = '123'
result2 = 123

// 字符串字面量
type Directions = 'Up' | 'Down' | 'Left' | 'Right'
let toWhere: Directions = 'Up'

2.交叉类型

  • &:表示 和,比如下面:
interface IName  {
  name: string
}
// IPerson 既要包含 IName,又要包含 {} 中的内容
type IPerson = IName & { age: number }
let person: IPerson = { name: 'hello', age: 12}

 

十二.声明文件

  • xxx.d.ts 就是声明文件,用于告诉 typescript 这个是什么,比如 jQuery 是什么
  • 当我们在 ts 中使用 jQuery 时,就会出现对应的提示

十三.内置类型

const a: Array<number> = [1,2,3]

// 不同的文件中有多处定义,但是它们都是 内部定义的一部分,然后根据不同的版本或者功能合并在了一起,一个interface 或者 类多次定义会合并在一起。这些文件一般都是以 lib 开头,以 d.ts 结尾,告诉大家,我是一个内置对象类型欧
const date: Date = new Date()
const reg = /abc/

// 还可以使用 build in object,内置对象
// 比如 Math 与其他全局对象不同,Math 不是一个构造器。Math 的所有属性方法都是静态的
Math.pow(2,2)

// DOM 和 BOM 标准对象
// document 对象,返回的是一个 HTMLElement
let body: HTMLElement = document.body
// document 上面的query 方法,返回的是一个 nodeList 类型
let allLis = document.querySelectorAll('li')

// 添加事件,document 上面有 addEventListener 方法,注意这个回调函数,因为类型推断,这里面的 e 事件对象也自动获得了类型,这里是个 mouseEvent 类型,因为点击是一个鼠标事件,现在我们可以方便的使用 e 上面的方法属性。
document.addEventListener('click', (e) => {
  e.preventDefault()
})
  • Typescript 还提供了一些功能性、帮助性的类型,叫做 utility types,提供一些简洁明快的功能
  • partial,它可以把传入参数都变成 可选
  • Omit,它可以 忽略 传入参数的某个部分
interface IPerson {
  name: string
  age: number
}
// 下面表示 name、age 都得传入
let TeaMeow: IPerson = { name: 'viking', age: 20 }

// partial,它可以把传入的类型都变成可选
// 改造接口,将他用 partial 包裹,使得所有的参数都变成可选的
type IPartial = Partial<IPerson>
// 使用改造后的接口,这样 name、age 就变成可选参数了
let TeaMeow2: IPartial = { } 

// Omit,它返回的类型可以忽略传入类型的某个属性
// 改造接口,将他用 Omit 处理,使得 name 属性被忽略(可以不被传入)
type IOmit = Omit<IPerson, 'name'>
// 使用改造后的接口,这样 name 就可以被忽略了
let TeaMeow3: IOmit = { age: 20 }

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐