ArkTS-接口、泛型、空安全

接口

接口聲明引入新類型。接口是定義代碼協(xié)定的常見方式。

任何一個(gè)類的實(shí)例只要實(shí)現(xiàn)了特定接口,就可以通過該接口實(shí)現(xiàn)多態(tài)。

接口通常包含屬性和方法的聲明

interface Style {
  color: string // 屬性
}
interface AreaSize {
  calculateAreaSize(): number // 方法的聲明
  someMethod(): void;     // 方法的聲明
}

實(shí)現(xiàn)接口的類示例:

// 接口:
interface AreaSize {
  calculateAreaSize(): number // 方法的聲明
  someMethod(): void;     // 方法的聲明
}

// 實(shí)現(xiàn):
class RectangleSize implements AreaSize {
  private width: number = 0
  private height: number = 0
  someMethod(): void {
    console.log('someMethod called');
  }
  calculateAreaSize(): number {
    this.someMethod(); // 調(diào)用另一個(gè)方法并返回結(jié)果
    return this.width * this.height;
  }
}
  • 接口屬性

接口屬性可以是字段、getter、setter或getter和setter組合的形式。

屬性字段只是getter/setter對(duì)的便捷寫法。以下表達(dá)方式是等價(jià)的:

interface Style {
  color: string
}

interface Style {
  get color(): string
  set color(x: string)
}

實(shí)現(xiàn)接口的類也可以使用以下兩種方式:

interface Style {
  color: string
}

class StyledRectangle implements Style {
  color: string = ''
}
interface Style {
  color: string
}

class StyledRectangle implements Style {
  private _color: string = ''
  get color(): string { return this._color; }
  set color(x: string) { this._color = x; }
}
  • 接口繼承

接口可以繼承其他接口,如下面的示例所示:

interface Style {
  color: string
}

interface ExtendedStyle extends Style {
  width: number
}

繼承接口包含被繼承接口的所有屬性和方法,還可以添加自己的屬性和方法。
</br>
</br>
</br>

泛型類和接口

  • 類和接口可以定義為泛型,將參數(shù)添加到類型定義中,如以下示例中的類型參數(shù)Element:
class CustomStack<Element> {
  public push(e: Element):void {
    // ...
  }
}
  • 要使用類型CustomStack,必須為每個(gè)類型參數(shù)指定類型實(shí)參:
let s = new CustomStack<string>();
s.push('hello');
  • 編譯器在使用泛型類型和函數(shù)時(shí)會(huì)確保類型安全。參見以下示例:
let s = new CustomStack<string>();
s.push(55); // 將會(huì)產(chǎn)生編譯時(shí)錯(cuò)誤
  • 泛型約束

泛型類型的類型參數(shù)可以被限制只能取某些特定的值。例如,MyHashMap<Key, Value>這個(gè)類中的Key類型參數(shù)必須具有hash方法。

interface Hashable {
  hash(): number
}
class MyHashMap<Key extends Hashable, Value> {
  public set(k: Key, v: Value) {
    let h = k.hash();
    // ...其他代碼...
  }
}
  • 泛型函數(shù)

使用泛型函數(shù)可編寫更通用的代碼。比如返回?cái)?shù)組最后一個(gè)元素的函數(shù):

function last(x: number[]): number {
  return x[x.length - 1];
}
last([1, 2, 3]); // 3

如果需要為任何數(shù)組定義相同的函數(shù),使用類型參數(shù)將該函數(shù)定義為泛型:

function last<T>(x: T[]): T {
  return x[x.length - 1];
}

現(xiàn)在,該函數(shù)可以與任何數(shù)組一起使用。

在函數(shù)調(diào)用中,類型實(shí)參可以顯式或隱式設(shè)置:

// 顯式設(shè)置的類型實(shí)參
last<string>(['aa', 'bb']);
last<number>([1, 2, 3]);

// 隱式設(shè)置的類型實(shí)參
// 編譯器根據(jù)調(diào)用參數(shù)的類型來確定類型實(shí)參
last([1, 2, 3]);
  • 泛型默認(rèn)值

泛型類型的類型參數(shù)可以設(shè)置默認(rèn)值。這樣可以不指定實(shí)際的類型實(shí)參,而只使用泛型類型名稱。下面的示例展示了類和函數(shù)的這一點(diǎn)。

class SomeType {}
interface Interface <T1 = SomeType> { }
class Base <T2 = SomeType> { }
class Derived1 extends Base implements Interface { }
// Derived1在語義上等價(jià)于Derived2
class Derived2 extends Base<SomeType> implements Interface<SomeType> { }

function foo<T = number>(): T {
  // ...
}
foo();
// 此函數(shù)在語義上等價(jià)于下面的調(diào)用
foo<number>();

空安全

默認(rèn)情況下,ArkTS中的所有類型都是不可為空的,因此類型的值不能為空。這類似于TypeScript的嚴(yán)格空值檢查模式(strictNullChecks),但規(guī)則更嚴(yán)格。

在下面的示例中,所有行都會(huì)導(dǎo)致編譯時(shí)錯(cuò)誤:

let x: number = null;    // 編譯時(shí)錯(cuò)誤
let y: string = null;    // 編譯時(shí)錯(cuò)誤
let z: number[] = null;  // 編譯時(shí)錯(cuò)誤

可以為空值的變量定義為聯(lián)合類型T | null。

let x: number | null = null;
x = 1;    // ok
x = null; // ok
if (x != null) { /* do something */ }
  • 非空斷言運(yùn)算符

后綴運(yùn)算符!可用于斷言其操作數(shù)為非空。

應(yīng)用于可空類型的值時(shí),它的編譯時(shí)類型變?yōu)榉强疹愋汀@?,類型將從T | null更改為T:

class A {
  value: number = 0;
}

function foo(a: A | null) {
  a.value;   // 編譯時(shí)錯(cuò)誤:無法訪問可空值的屬性
  a!.value;  // 編譯通過,如果運(yùn)行時(shí)a的值非空,可以訪問到a的屬性;如果運(yùn)行時(shí)a的值為空,則發(fā)生運(yùn)行時(shí)異常
}
  • 空值合并運(yùn)算符

空值合并二元運(yùn)算符??用于檢查左側(cè)表達(dá)式的求值是否等于null或者undefined。如果是,則表達(dá)式的結(jié)果為右側(cè)表達(dá)式;否則,結(jié)果為左側(cè)表達(dá)式。

換句話說,a ?? b等價(jià)于三元運(yùn)算符(a != null && a != undefined) ? a : b。

在以下示例中,getNick方法如果設(shè)置了昵稱,則返回昵稱;否則,返回空字符串:

class Person {
  // ...
  nick: string | null = null
  getNick(): string {
    return this.nick ?? '';
  }
}
  • 可選鏈

在訪問對(duì)象屬性時(shí),如果該屬性是undefined或者null,可選鏈運(yùn)算符會(huì)返回undefined。

class Person {
  nick: string | null = null
  spouse?: Person

  setSpouse(spouse: Person): void {
    this.spouse = spouse;
  }

  getSpouseNick(): string | null | undefined {
    return this.spouse?.nick;
  }

  constructor(nick: string) {
    this.nick = nick;
    this.spouse = undefined;
  }
}

說明:getSpouseNick的返回類型必須為string | null | undefined,因?yàn)樵摲椒赡芊祷豱ull或者undefined。

可選鏈可以任意長,可以包含任意數(shù)量的?.運(yùn)算符。

在以下示例中,如果一個(gè)Person的實(shí)例有不為空的spouse屬性,且spouse有不為空的nick屬性,則輸出spouse.nick。否則,輸出undefined:

說明:getSpouseNick的返回類型必須為string | null | undefined,因?yàn)樵摲椒赡芊祷豱ull或者undefined。

可選鏈可以任意長,可以包含任意數(shù)量的?.運(yùn)算符。

在以下示例中,如果一個(gè)Person的實(shí)例有不為空的spouse屬性,且spouse有不為空的nick屬性,則輸出spouse.nick。否則,輸出undefined:
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容