ArkTS不支持結(jié)構(gòu)化類型(Structural Typing)詳解

2024-12-04 09:39 更新

大家好,我是 V 哥,今天有粉絲問 V 哥,ArkTS不支持structural typing 是什么意思?ArkTS不支持接口嗎?

輸入圖片說明

輸入圖片說明

V哥把問題整理一下,分享給大家,尤其對(duì)TypeScript不熟的小伙伴,看到官網(wǎng)這句表述,是不是也是一頭霧水,不知所措,前端小伙伴就不用說了,出門右轉(zhuǎn)吧,因?yàn)閷?duì)你來說,這是個(gè)很簡單的問題。

首先 關(guān)于 structural typing(結(jié)構(gòu)化類型) 是 TypeScript 中的特性,我們先來介紹一下。

TypeScript 中的結(jié)構(gòu)化類型(Structural Typing)特性

TypeScript 是一種靜態(tài)類型的超集于 JavaScript 的編程語言,它引入了類型系統(tǒng)來增強(qiáng)代碼的可維護(hù)性和可讀性。TypeScript 的類型系統(tǒng)基于結(jié)構(gòu)化類型(Structural Typing),這意味著類型的兼容性和等價(jià)性是基于類型實(shí)際的結(jié)構(gòu)或定義來確定的,而不是基于其名稱或聲明位置,這與像 C# 或 C 這樣的語言中的命名類型系統(tǒng)(Nominal Typing)不同。

結(jié)構(gòu)化類型的核心原則

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

看個(gè)代碼案例

基本對(duì)象兼容性

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; // 允許,因?yàn)?Point3D 包含了 Point2D 所需的所有屬性
console.log(anotherPoint2D); // 輸出: { x: 1, y: 2, z: 3 }

我們看到在這個(gè)例子中,Point3D 包含了 Point2D 所需的所有屬性(xy),并且還有一個(gè)額外的 z 屬性。TypeScript 允許這種賦值,因?yàn)樗腔诮Y(jié)構(gòu)的。

函數(shù)兼容性

函數(shù)類型的兼容性也遵循結(jié)構(gòu)化類型規(guī)則,函數(shù)的兼容性取決于其參數(shù)和返回類型。只要函數(shù)簽名中的其他部分匹配,就可以允許參數(shù)類型有額外的特定性或更廣泛的通用性。

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

我們看到在這個(gè)例子中,extendedSum 函數(shù)接受三個(gè)參數(shù),但我們只使用了前兩個(gè)參數(shù)來創(chuàng)建一個(gè)新的 Sum 類型的函數(shù) newSum。TypeScript 允許這種賦值,因?yàn)?newSum 的結(jié)構(gòu)與 Sum 兼容。

結(jié)構(gòu)化類型的優(yōu)勢

  1. 靈活性:能夠靈活地分配具有相同結(jié)構(gòu)的不同類型。
  2. 與 JavaScript 的兼容性:TypeScript 的結(jié)構(gòu)化類型非常適合 JavaScript 動(dòng)態(tài)和靈活的特性,使得與現(xiàn)有 JavaScript 代碼庫的集成更加容易。
  3. 減少樣板代碼:不需要顯式的接口或類型聲明,因?yàn)榧嫒菪匀Q于結(jié)構(gòu),從而減少了樣板代碼。
  4. 增強(qiáng)代碼可重用性:結(jié)構(gòu)化類型允許具有兼容結(jié)構(gòu)的類型可以互換使用,促進(jìn)了更模塊化和可維護(hù)的代碼。

ArkTS 不支持結(jié)構(gòu)化類型

了解了 TypeScript 是如何支持結(jié)構(gòu)化類型后,咱們就可以很好理解 ArkTS中不支持結(jié)構(gòu)化類型是什么意思了,那在 ArkTS中,不同對(duì)象是怎么兼容類型的呢?

在ArkTS中,對(duì)象的類型兼容性不是基于對(duì)象的結(jié)構(gòu)(如屬性和方法的集合)來確定的,而是基于接口或類的名義類型系統(tǒng)。

所以,如果兩個(gè)對(duì)象沒有實(shí)現(xiàn)相同的接口或繼承自相同的類,即使它們具有相同的公共API,它們也被視為完全不同的類型?。

在ArkTS中,對(duì)象的類型兼容性是基于接口或類的名義類型系統(tǒng)(Nominal Typing System),而不是基于對(duì)象的結(jié)構(gòu)(如屬性和方法的集合)來確定的。這意味著,只有當(dāng)一個(gè)對(duì)象的類型與另一個(gè)類型完全相同時(shí),它們才被認(rèn)為是兼容的。這與結(jié)構(gòu)化類型系統(tǒng)(Structural Typing System),如TypeScript中所采用的,有所不同,在結(jié)構(gòu)化類型系統(tǒng)中,如果兩個(gè)對(duì)象具有相同的形狀(即相同的屬性和方法),它們就被認(rèn)為是兼容的,即使它們的類型名稱不同。

代碼案例解釋

1. 類型兼容性基于接口或類的聲明

在ArkTS中,如果有兩個(gè)類,即使它們的屬性和方法相同,但如果它們沒有相同的類聲明,它們將不被認(rèn)為是兼容的。

// 定義兩個(gè)類,它們具有相同的屬性和方法
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!`);
    }
}


// 在結(jié)構(gòu)化類型系統(tǒng)中,以下賦值是允許的,因?yàn)閮蓚€(gè)對(duì)象具有相同的形狀
// let animal: Animal = new Dog(); // 錯(cuò)誤,在ArkTS中不允許


// 在ArkTS中,上述賦值將導(dǎo)致錯(cuò)誤,因?yàn)锳nimal和Dog是不同的類,即使它們具有相同的屬性和方法。

2. 接口之間的兼容性

在ArkTS中,接口之間的兼容性也是基于名義類型系統(tǒng)的。即使兩個(gè)接口具有相同的屬性和方法,它們也被認(rèn)為是不同的類型。

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


interface IBird {
    fly(): void;
}


// 在結(jié)構(gòu)化類型系統(tǒng)中,以下賦值是允許的,因?yàn)閮蓚€(gè)接口具有相同的形狀
// let flyObject: IFly = { fly: () => console.log("Flying") } as IBird; // 錯(cuò)誤,在ArkTS中不允許


// 在ArkTS中,上述賦值將導(dǎo)致錯(cuò)誤,因?yàn)镮Fly和IBird是不同的接口,即使它們具有相同的方法。

3. 類和接口之間的兼容性

在ArkTS中,類可以實(shí)現(xiàn)接口,但類和接口之間的兼容性是基于名義類型系統(tǒng)的。

// 定義一個(gè)接口
interface IPrint {
    print(): void;
}


// 定義一個(gè)類,實(shí)現(xiàn)上述接口
class Printer implements IPrint {
    print(): void {
        console.log("Printing...");
    }
}


// 在ArkTS中,即使Printer實(shí)現(xiàn)了IPrint接口,它們也被認(rèn)為是不同的類型。
// let printObject: IPrint = new Printer(); // 正確


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

再強(qiáng)調(diào)一下,在ArkTS中,類型兼容性是基于接口或類的名義類型系統(tǒng),而不是基于對(duì)象的結(jié)構(gòu)。這意味著,只有當(dāng)兩個(gè)類型的聲明完全相同時(shí),它們才被認(rèn)為是兼容的。這與TypeScript中基于結(jié)構(gòu)的類型兼容性形成了對(duì)比。

那為什么 ArkTS 會(huì)不支持結(jié)構(gòu)化類型呢,V 哥的分析應(yīng)該是考慮以下幾個(gè)方面:

  1. 潛在的意外兼容性:在結(jié)構(gòu)化類型下,即使概念上不同,類型也可能被認(rèn)為是兼容的,這可能導(dǎo)致錯(cuò)誤。
  2. 有限的反射:TypeScript 沒有支持反射,這意味著程序員不能使用結(jié)構(gòu)進(jìn)行運(yùn)行時(shí)類型檢查。
  3. 類型安全與靈活性:結(jié)構(gòu)化類型提供了靈活性,但如果開發(fā)者不完全理解其含義,可能會(huì)犧牲類型安全。
  4. 調(diào)試復(fù)雜性:由于類型兼容性的隱式性質(zhì),調(diào)試可能更加困難,難以追蹤和修復(fù)問題。
  5. 性能考慮?:在某些情況下,名義類型系統(tǒng)可能在運(yùn)行時(shí)具有更好的性能,因?yàn)樗鼈兛梢栽诰幾g時(shí)進(jìn)行更多的檢查?.

最后

輸入圖片說明 不管官網(wǎng)是基于怎樣的考慮,華為的大佬們應(yīng)該是做充分的思考后的決定,當(dāng)然也沒有說死,后續(xù)會(huì)根據(jù)實(shí)際場景和反饋,看否是重新啟用結(jié)構(gòu)化類型,好了,到此你應(yīng)該可以完全理解了,歡迎關(guān)注威哥愛編程,鴻蒙開天辟地,你我相伴同行。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)