TypeScript 學習(二)

挖坑埋神經病發表於2018-10-18

倉庫地址: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() {}
}
複製程式碼

相關文章