TypeScript 学习(二)

250 阅读5分钟

仓库地址:github.com/YaliixxG/Ty…

接口

可选属性

接口里的属性不全是必需的。可以先定义在接口里,但是属性名字定义的时候需在后面加一个?符号。
例如一些值类型的属性,在 ts 里面是不能为空的,如果为空则会报错。但是在后面加一个?符号,则不会报错,说明这个属性可以为空,为可选属性,可有可无。

interface abc {
  color?: string

  width?: number
}

function creatSquare(config: abc): { color: string; area: number } {
  let newabc = { color: "white", area: 100 }

  if (config.color) {
    newabc.color = config.color
  }

  if (config.width) {
    newabc.area = config.width * config.width
  }

  return newabc
}

let myabc = creatSquare({ color: "black" })

只读属性

一些对象属性只能在对象刚刚创建的时候修改其值。 你可以在属性名前用readonly来指定只读属性:

interface Point {
  readonly x: number

  readonly y: number
}

你可以通过赋值一个对象字面量来构造一二个 Point。赋值后,x 和 y 再也不能被改变了。

let p1: Point = { x: 10, y: 20 }

p1.x = 5 // error

TS 有ReadonlyArray<T>类型,它与 Array相似,只是把所有可变方法去掉了,确保数组创建后再也不能被修改。

let a: number[] = [1, 2, 3, 4] //创建普通数组

let ro: ReadonlyArray<number> = a //创建一个不可变的数组,等于普通数组a

ro[0] = 12 //error

ro.push(5) //error

ro.length = 100 //error

a = ro //error

上面代码的最后一行,可以看到就算把整个 ReadonlyArray 赋值到一个普通数组也是不可以的。 但是你可以用类型断言重写:

a = ro as number[]

readonly vs const

  • 什么时候用哪一个?

    • 作为变量:const
    • 作为属性:readonly

函数类型

为了使用接口表示函数类型,我们需要给接口定义一个调用签名。 它就像是一个只有参数列表和返回值类型的函数定义。参数列表里的每个参数都需要名字和类型。

interface SearchFunc {
  //括号里类似定义参数名字和类型
  //冒号后定义的函数返回值的类型

  (source: string, subString: string): boolean
}

如何调用?

let mySearch: SearchFunc

mySearch = function(source: string, subString: string) {
  let res = source.search(subString)

  return res > -1
}

函数类型的类型检查,函数的参数名不需要与接口里定义的名字相比配,相当于形参实参的意思相似。

let mySearch: SearchFunc

mySearch = function(src: string, sub: string) {
  let res = src.search(sub)

  return res > -1
}

可索引类型

interface StringArray {
  [index: number]: string //这是一个索引签名,意思是用number类型去索引StringArray,返回值为string类型
}
  • 共支持两种索引签名
    • 字符串
    • 数字
  • 重点:数字索引的返回值必须是字符串索引返回值类型的子类型

类类型

Implements 与 Extends 的区别
  • extends,表示对父类的继承,可以实现父类,也可以调用父类初始化 this.parent().而且会覆盖父类定义的变量或者函数。
  • implements,表示对接口的实现,接口通过关键字 interface 进行定义。eg:public class S implements F,在接口 F 中对方法进行声明,在类 S 中对该方法进行实现。

实现接口

TypeScript 也能够用它来明确的强制一个类去符合某种契约。

interface ClockInterface {
  currentTime: Date
}
class Clock implements ClockInterface {
  currentTime: Date

  constructor(h: number, m: number) {}
}

也可以在接口中定义一个方法,在类里实现它,如同下面的 setTime 方法一样:

interface ClockInterface {
  currentTime: Date

  setTimeout(d: Date)
}
class Clock implements ClockInterface {
  currentTime: Date

  setTimeout(d: Date) {
    this.currentTime = d
  }

  constructor(h: number, m: number) {}
}

接口描述了类的公共部分,而不是公共和私有两部分。

类静态部分与实例部分的区别

(这一部分不是很懂)

继承接口

和类一样,接口也可以相互继承

interface Shape {
  color: string
}

interface PenStroke {
  penWidth: number
}

interface Square extends Shape, PenStroke {
  slideLength: number
}

let square = <Square>{}

square.color = "yellow"

square.penWidth = 10

square.slideLength = 5.0

混合类型

示例:一个对象可以同时作为函数和对象使用,并带有额外的属性

interface Counter {
  (start: number): string //作为函数来定义,参数为数字类型,返回值为字符串类型

  interval: number //作为对象定义

  reset(): void //定义一个方法
}

function getCounter(): Counter {
  let counter = <Counter>function(start: number) {}

  counter.interval = 123

  counter.reset = function() {}

  return counter
}

let c = getCounter()

c(10)

c.reset()

c.interval = 5.0

接口继承类

公共,私有与受保护的修饰符 public/private/protected 的具体区别
  • public:是指这个函数可以被其他的类来调用,也可以被自己类里的函数来调用。 在 TypeScript 里,成员都默认为 public。
  • protected:是指这个函数可以被继承类调用,也可以被自己类里的函数来调用。当成员被标记 private 时,它就不能再声明它的类的外部访问。
    class Animal {
      private name: string
      constructor(theName: string) {
        this.name = theName
      }
    }
    new Animal("Cat").name // 错误: 'name' 是私有的.
    
  • private:只能被自己类里的其他函数调用,其他的一概不能调用

当接口继承一个类类型时,它会继承类的成员,但是!不包括其实现。(就好像接口声明了所有类中存在的成员,但并没有提供具体实现一样)
接口继承到类的 private 和 protected 成员,那你这个接口类型就只能被这个类或其子类所实现,权限问题。

//类类型

class Control {
  private state: any //私有成员
}

//定义一个 a 接口来继承Control这个类,因为 a 接口集成后,则它包含了Control这个弗雷德所有成员,包括私有成员 state
// state作为私有成员,所以只能是Control的子类们才能实现 a 接口
//只有Control子类才能够拥有父类的私有成员 state

interface a extends Control {
  do(): void
}

//C作为子类继承Control父类,并且实现 a 接口方法

class C extends Control implements a {
  do() {}
}

// D作为子类继承Control父类

class D extends Control {
  do() {}
}

//下面这个就会报错了,因为 Image 这个类并不是 Control 的子类,所以不能用 a 接口

class Image implements a {
  do() {}
}