介面
可選屬性
介面裡的屬性不全是必需的。可以先定義在介面裡,但是屬性名字定義的時候需在後面加一個?
符號。
例如一些值型別的屬性,在 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() {}
}
複製程式碼