如何理解ArkTS不支援structural typing

威哥爱编程發表於2024-11-21

大家好,我是 V 哥,今天有粉絲問 V 哥,ArkTS不支援structural typing 是什麼意思?ArkTS不支援介面嗎?

V哥把問題整理一下,分享給大家,尤其對TypeScript不熟的小夥伴,看到官網這句表述,是不是也是一頭霧水,不知所措,前端小夥伴就不用說了,出門右轉吧,因為對你來說,這是個很簡單的問題。

首先 關於 structural typing(結構化型別) 是 TypeScript 中的特性,我們先來介紹一下。

TypeScript 中的結構化型別(Structural Typing)特性

TypeScript 是一種靜態型別的超集於 JavaScript 的程式語言,它引入了型別系統來增強程式碼的可維護性和可讀性。TypeScript 的型別系統基於結構化型別(Structural Typing),這意味著型別的相容性和等價性是基於型別實際的結構或定義來確定的,而不是基於其名稱或宣告位置,這與像 C# 或 C 這樣的語言中的命名型別系統(Nominal Typing)不同。

結構化型別的核心原則

  1. 形狀相容性(Shape Compatibility):如果兩個型別具有相同的屬性和方法,並且這些成員的型別也匹配,那麼這兩個型別就是相容的,無論它們的名稱是什麼。
  2. 允許額外屬性(Extra Properties Are Allowed):一個物件可以擁有在型別定義中沒有的額外屬性,但仍然可以被賦值給該型別。
  3. 引數雙變(Parameter Bivariance):對於函式型別,只要函式簽名中的其他部分匹配,就可以允許引數型別有額外的特定性或更廣泛的通用性。

看個程式碼案例

基本物件相容性

interface Point2D {
    x: number;
    y: number;
}

interface Point3D {
    x: number;
    y: number;
    z: number;
}

const point2D: Point2D = { x: 1, y: 2 };
const point3D: Point3D = { x: 1, y: 2, z: 3 };

// 將 Point3D 賦值給 Point2D
const anotherPoint2D: Point2D = point3D; // 允許,因為 Point3D 包含了 Point2D 所需的所有屬性
console.log(anotherPoint2D); // 輸出: { x: 1, y: 2, z: 3 }

我們看到在這個例子中,Point3D 包含了 Point2D 所需的所有屬性(xy),並且還有一個額外的 z 屬性。TypeScript 允許這種賦值,因為它是基於結構的。

函式相容性

函式型別的相容性也遵循結構化型別規則,函式的相容性取決於其引數和返回型別。只要函式簽名中的其他部分匹配,就可以允許引數型別有額外的特定性或更廣泛的通用性。

type Sum = (a: number, b: number) => number;
const sum: Sum = (a, b) => a + b;

const extendedSum = (a: number, b: number, c: number) => a + b + c;

// 將 extendedSum 賦值給 newSum
const newSum: Sum = (a: number, b: number) => extendedSum(a, b, 0);
console.log(newSum(1, 2)); // 輸出: 3

我們看到在這個例子中,extendedSum 函式接受三個引數,但我們只使用了前兩個引數來建立一個新的 Sum 型別的函式 newSum。TypeScript 允許這種賦值,因為 newSum 的結構與 Sum 相容。

結構化型別的優勢

  1. 靈活性:能夠靈活地分配具有相同結構的不同型別。
  2. 與 JavaScript 的相容性:TypeScript 的結構化型別非常適合 JavaScript 動態和靈活的特性,使得與現有 JavaScript 程式碼庫的整合更加容易。
  3. 減少樣板程式碼:不需要顯式的介面或型別宣告,因為相容性取決於結構,從而減少了樣板程式碼。
  4. 增強程式碼可重用性:結構化型別允許具有相容結構的型別可以互換使用,促進了更模組化和可維護的程式碼。

ArkTS 不支援結構化型別

瞭解了 TypeScript 是如何支援結構化型別後,咱們就可以很好理解 ArkTS中不支援結構化型別是什麼意思了,那在 ArkTS中,不同物件是怎麼相容型別的呢?

在ArkTS中,物件的型別相容性不是基於物件的結構(如屬性和方法的集合)來確定的,而是基於介面或類的名義型別系統。

所以,如果兩個物件沒有實現相同的介面或繼承自相同的類,即使它們具有相同的公共API,它們也被視為完全不同的型別‌。

在ArkTS中,物件的型別相容性是基於介面或類的名義型別系統(Nominal Typing System),而不是基於物件的結構(如屬性和方法的集合)來確定的。這意味著,只有當一個物件的型別與另一個型別完全相同時,它們才被認為是相容的。這與結構化型別系統(Structural Typing System),如TypeScript中所採用的,有所不同,在結構化型別系統中,如果兩個物件具有相同的形狀(即相同的屬性和方法),它們就被認為是相容的,即使它們的型別名稱不同。

程式碼案例解釋

1. 型別相容性基於介面或類的宣告

在ArkTS中,如果有兩個類,即使它們的屬性和方法相同,但如果它們沒有相同的類宣告,它們將不被認為是相容的。

// 定義兩個類,它們具有相同的屬性和方法
class Animal {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    speak() {
        console.log(`The animal says something.`);
    }
}

class Dog {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    speak() {
        console.log(`Woof woof!`);
    }
}

// 在結構化型別系統中,以下賦值是允許的,因為兩個物件具有相同的形狀
// let animal: Animal = new Dog(); // 錯誤,在ArkTS中不允許

// 在ArkTS中,上述賦值將導致錯誤,因為Animal和Dog是不同的類,即使它們具有相同的屬性和方法。

2. 介面之間的相容性

在ArkTS中,介面之間的相容性也是基於名義型別系統的。即使兩個介面具有相同的屬性和方法,它們也被認為是不同的型別。

// 定義兩個介面,它們具有相同的屬性和方法
interface IFly {
    fly(): void;
}

interface IBird {
    fly(): void;
}

// 在結構化型別系統中,以下賦值是允許的,因為兩個介面具有相同的形狀
// let flyObject: IFly = { fly: () => console.log("Flying") } as IBird; // 錯誤,在ArkTS中不允許

// 在ArkTS中,上述賦值將導致錯誤,因為IFly和IBird是不同的介面,即使它們具有相同的方法。

3. 類和介面之間的相容性

在ArkTS中,類可以實現介面,但類和介面之間的相容性是基於名義型別系統的。

// 定義一個介面
interface IPrint {
    print(): void;
}

// 定義一個類,實現上述介面
class Printer implements IPrint {
    print(): void {
        console.log("Printing...");
    }
}

// 在ArkTS中,即使Printer實現了IPrint介面,它們也被認為是不同的型別。
// let printObject: IPrint = new Printer(); // 正確

// 但是,如果嘗試將IPrint賦值給Printer型別,將導致錯誤。
// let printerObject: Printer = { print: () => console.log("Printing") } as IPrint; // 錯誤

再強調一下,在ArkTS中,型別相容性是基於介面或類的名義型別系統,而不是基於物件的結構。這意味著,只有當兩個型別的宣告完全相同時,它們才被認為是相容的。這與TypeScript中基於結構的型別相容性形成了對比。

那為什麼 ArkTS 會不支援結構化型別呢,V 哥的分析應該是考慮以下幾個方面:

  1. 潛在的意外相容性:在結構化型別下,即使概念上不同,型別也可能被認為是相容的,這可能導致錯誤。
  2. 有限的反射:TypeScript 沒有支援反射,這意味著程式設計師不能使用結構進行執行時型別檢查。
  3. 型別安全與靈活性:結構化型別提供了靈活性,但如果開發者不完全理解其含義,可能會犧牲型別安全。
  4. 除錯複雜性:由於型別相容性的隱式性質,除錯可能更加困難,難以追蹤和修復問題。
  5. 效能考慮‌:在某些情況下,名義型別系統可能在執行時具有更好的效能,因為它們可以在編譯時進行更多的檢查‌.

最後

不管官網是基於怎樣的考慮,華為的大佬們應該是做充分的思考後的決定,當然也沒有說死,後續會根據實際場景和反饋,看否是重新啟用結構化型別,好了,到此你應該可以完全理解了,歡迎關注威哥愛程式設計,鴻蒙開天闢地,你我相伴同行。

相關文章