TypeScript学习

132 阅读8分钟

一、认识TypeScript

1.什么是TypeScript?

TypeScript是微软开发的开源编程语言。 它是JavaScript的超集,它可以编译成普通、干净、完整的JavaScript代码

2.TypeScript的优点

  1. JavaScript从设计之初就没有考虑类型的约束问题,TypeScript为JavaScript提供类型检查
  2. 在编码期间提供类型检测错误,为开发人员创建了一个更高效的编码和调试过程

3.TypeScript的安装

npm install typescript -g // 全局安装typescript
npm install ts-node -g // node环境执行typescript
tsc --version // 查看版本

二、TypeScript中的数据类型

1.number

const num: number = 1
const num: number = '1' // 报错
const num: number = 0xffff // 支持16进制 
const num: number = 0b0002 // 支持二进制
const num: number = 0o117 // 支持八进制
const num: number = NaN
const num: number = Infinity

2.string

const str: string = 'hello world'

3.boolean

const isValue: boolean = false
const isValue: boolean = true

4.undefined和null

const unde: undefined = undefined
const ul: null = null

undefined和null是所有类型的子类型,可以把undefined和null赋值给其他类型,如果在tsconfig.json中开启了'strictNullChecks': true,undefined和null只能赋值给它们各自的类型

5.object

const obj: object = {}
const obj: Object = {}

6.bigint

const obj: object = {}

7.symbol

const s: symbol = Symbol()

8.void

void表示一个没有返回值的函数

fucntion bar(name: string): void {
	console.log(name)
}
bar('zhangsan')

9.any

在TypeScript中,任何类型的值都可以复制给any,any也可以赋值给任意类型

项目中,尽可能的使用any类型

let num: any = 30
num = 'str'
num = true  // 不报错

10.unknow

unknow表示不知道的类型,在获取接口数据的时候,数据类型未知时,就可以使用unknow,想用时利用断言

const num: unknow = 5
num.toFixed() // 报错,num为未知类型
(num as number).toFixed()

11.never

never类型表示的是那些永不存在的值的类型

12.元组(tuple)

多种元素的组合

const info: [string, number, boolean] = ['1', 5, true]

13.枚举类型(enum)

// 整数枚举会从0开始依次递增
enum Src {
  Left,
  Right,
  Top,
  Bottom
}

三、类型运算

1.类型别名(Type)

type IDType = srting | number
function getID(id: IDType) {}

2.联合类型(Union Type)

  • 联合类型是由两个或者多个其他类型组成的类型
  • 表示可以是这些类型中的任何一个值
  • 联合类型中的每一个类型被称之为联合成员
function getID(id: number | string) {
	if(typeOf id === 'string') {
    console.log(id.toUpperCase())
  } else {
    console.log(id)
  }
}
getID('5')
getID(5)
getID(true) // 报错

3.可选类型(?)

一个参数一个可选类型的时候,它其实类似于是这个参数类型 | undefined的联合类型

function foo(name?: string) {
  console.log(name)
}
foo()
foo('zhangsan')

4.交叉类型(&)

交叉类型是由两个及以上的成员类型组成,成员类型通过&符号连接

type F = {
  name: string
}
type P = {
  age: number
}

type T = F & P
const person: T = {
  name: '135',
  age: 18
}

5.类型收窄

type IDType = number | string
// 利用typeof
function getID(id: IDType) {
  if(typeof id === 'string') {
    console.log(id)
  } else {
    console.log(id)
  }
}
// 利用instanceof
function getTime(time: string | Date){
  if(time instanceof Date) {
    console.log(time.toUTCString())
  } else {
    console.log(time)
  }
}

6.类型断言(as)

类型断言

有时TypeScript无法获取具体的类型信息,这就需要使用类型断言,TypeScript只允许类型断言转换为更具体或者不太具体的类型

const el = document.getElementById('app') as HTMLImageElement

非空类型断言:

function foo(str?: string) {
  console.log(str!.length) // 非空断言
}

可选链

  • 可选链使用可选链操作符?.
  • 它的作用是当对象的属性不存在时,会短路,直接返回undefined,如果存在,那么才会继续执行
  • 可选链操作是ESMAScript提出的特性,但是和TypeScript一起使用更完美
type Person = {
  name: string
  age: number
  friend?: {
    name: string
    age: number
  }
}
const info: Person = {
  name: 'zhangSan',
  age: 125,
  friend: {
    name: 'ds',
    age: 50
  }
}

console.log(info.friend?.name) // ds

??和!!运算符

??操作符:

  • 它是ES11增加的新特性
  • 控制合并操作符(??)是一个逻辑操作符,当操作符的左侧是null或者undefined时,返回其右侧操作数,否则返回左侧操作数

!!操作符:

  • 将一个其他类型转换成boolean类型
  • 类似于Boolean(变量)的方式

7.字面量类型

字面量类型的意义,必须结合联合类型

let num: 5 | 6 | 7 = 5

四、TypeScript函数类型

在JavaScript开发中,函数时重要的组成部分,并且函数可以作为一等公民(可以作为参数,也可以作为返回值进行传递)

1.函数的定义和参数

type FnType = (num1: number, num2: number) => number
const add: FnType = (n1, n2) => { return n1 + n2 }
// 函数的剩余参数
function sum(...nums: , num){}

2.函数的返回值类型

不写函数的返回值类型时,默认为void,表示没有返回值或返回值为undefined,也可以指定函数的返回值类型

function fn(str: string): void {}
function fn(str: string): string {
  return name
}

3.函数的可选参数

type FnType = (num1: number, num2?: number) => void
const add: FnType = (n1, n2) => {
  if(typeof n2 === 'number') {
    console.log(n1 +n2)
  }
}

4.函数重载

函数的名称相同,但是参数不同的几个参数就是函数重载

// 函数重载是多个函数声明,一个函数实现,最终实现的函数需要满足上面所有声明的约束
function foo(a: string, b: string): string;
function foo(a: number, b: number): number; // 函数声明
function foo(a: number | string, b: number | string): any { // 函数实现
  if(a === 'string' && b === 'string') {
    return a + b
  } else if(a === 'number' && b === 'number') {
    return a + b
  }
}
foo(5,6)
foo('d','das')

五、TypeScript中的类

1.类的定义

TypeScript中通过class创建一个类,可以在类中定义成员属性和方法

class Person {
  name: string
  age: number

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

const p = new Person('zhangsan', 18)

2.类的继承

class Person {
  name: string
  age: number

  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }
}
class Student extends Person {
  sNo: number
  constructor(name: string, age: number, sNo: number) {
    super(name, age)
    this.sNo = sNo
  }
  eating() {
    console.log('eating')
  }
}
const s = new Student('zhangsan', 18, 18)
console.log(s)

3.类的多态

class Animal {
  sport() {
    console.log('sport')
  }
}
class Dog extends Animal {
  sport() {
    console.log('talk')
  }
}
class Fish extends Animal {
  sport() {
    console.log('running')
  }
}

// 多态的目的是为了写出更加具备通用性的代码
function makeSport(animals: Animal[]) {
  animals.forEach(animal => {
    animal.sport()
  })
}

makeSport([new Dog(), new Fish()]) //talk running

4.类的成员修饰符

  • public修饰的是在任何地方可见、公有的属性或方法,默认编写的属性就是public的
class Person {
  public name: string = ''
}
const p = new Person()
  • private修饰的是仅在同一类中可见、私有的属性或方法
class Person {
  private name: string = ''
  getName() {
    return this.name
  }
}
const p = new Person()
p.getName()
  • protected修饰的是仅在类自身及子类中可见、受保护的属性或方法
class Person {
  protected name: string = ''
}
class Student extends Person {
  getName() {
    return this.name
  }
}

5.只读属性 readonly

只读属性是可以在构造器中赋值,赋值之后就不可以修改

属性本身不能修改,但是如果它是对象类型,对象中的属性是可以修改的

class Person {
  readonly name: string = '15'
  readonly friend?: string
  constructor(name: string, friend?: string) {
    this.name = name
    this.friend = friend
  }
}
const p = new Person('sdak','457')
console.log(p)

6.访问器 get/set

class Person {
  private _name: string
  constructor(name: string) {
    this._name = name
  }
  set name(newName) {
    this._name = newName
  }
  get name() {
    return this._name
  }
}
const p = new Person('sdak')
p.name = 'das'
console.log(p.name) // das

7.抽象类

调用接口时,通常会让调用者传入父类,通过多态来实现更加灵活的调用方式,但是,父类本身可能并不需要对某些方法进行具体的实现,所以父类中定义的方法,可以定义为抽象类

  • 在TypeScript中没有具体实现的方法,就是抽象方法
    • 抽象方法必须存在于抽象类中
    • 抽象类是使用abstract声明的类
  • 抽象类是不能被实例化的(不能通过new创建)
  • 抽象方法必须在子类实现,否则该类必须是一个抽象类

8.类的类型

class Person {
  name: string = '154'
}

const p = new Person()
const p1: Person =  {
  name: 'sad'
}

六、接口

1.接口的声明

通过类型(type)别名来声明对象类型

接口(interface)方式来声明对象类型

interface InfoType {
  name: string
  age: number
}
const info: InfoType = {
  name: 'ads',
  age: 18
}

2.索引类型和函数类型

// 通过interface来定义索引类型
interface User {
  [key: string] : string
}
// 定义函数类型接口
interface Fn {
  (key: string, value: string): void
}
const f: Fn = function(a: string, b: string): void {}

3.接口的继承

接口之间的继承,使用extends关键字

interface VipUser {
  name: string
  age: number
  address?: string
  sno: number
  readonly sex: string
}
interface User extends VipUser{
  message: string
  fn(): void
}

const user: User = {
  name: 'zhangsan',
  age: 18,
  // address: 'shanghai',
  sno: 1001,
  sex: 'N',
  message: 'success',
  fn: () => {
    console.log(1)
  }
}

4.接口的实现

类可以实现多个接口,利用implements关键字

interface A {
  name: string
}
interface B {
  age: number
}
class Foo implements A, B {
  name: string = '';
  age: number = 1
}

5.type与interafce的区别

如果定义的是对象类型:

  • interface可以重复的对某个接口来定义属性和方法
  • type定义的是别名,不能重复
interface A {
  name: string
}
interface A {
  age: number
}
const A: A = {
  name: 'das',
  age: 18
}
// 报错
type B = {
  name: string
  age: number
}
type B = {}

七、泛型

1.认识泛型

泛型(维基百科):泛型是程序设计语言的一种风格或范式,泛型允许程序员再强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型

在定义类、函数、接口时,不决定这些参数的类型,让调用这以参数的形式告知这里的参数应该是什么类型

function sum<Type>(num: Type) {
  return num
}
sum<number>(20)

2.泛型接口

interface Person<T1, T2> {
  name: T1
  age: T2
}

const p: Person<string, number> = {
  name: 'da',
  age: 18
}

3.泛型约束

interface ILength {
  length: number
}

function getLength<T extends ILength>(arg: T) {
  return arg.length
}

getLength('132')

八、TypeScript其他

1.TypeScript模块化

TypeScript支持两种方式来控制我们的作用域:

  • 模块化:每个文件可以是一个独立的模块,支持ES Module、CommonJS
  • 命名空间:使用namespace来声明一个命名空间

2.类型的查找

TypeScript文件:.d.ts文件用来坐类型的声明,仅仅用来做类型检测

内置类型声明:是typescript自带的,帮助我们内置JavaScript运行时的一些标准API的声明文件

外部定义类型声明、自定义类型声明:第三方types库,手写.d.ts