欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

TypeScript 類型兼容(逆變、協(xié)變、雙向協(xié)變和不變)的實(shí)現(xiàn)

 更新時(shí)間:2025年08月18日 09:44:15   作者:Moment  
在TypeScript中,類型系統(tǒng)支持逆變、協(xié)變、雙向協(xié)變和不變四種型變概念,用于處理類型兼容性,具有一定的參考價(jià)值,感興趣的可以了解一下

在 TypeScript 中,類型系統(tǒng)支持“逆變(Contravariance)”、“協(xié)變(Covariance)”、“雙向協(xié)變(Bivariance)”和“不變(Invariance)”的概念,這些概念主要用于理解類型之間的兼容性,尤其是在函數(shù)參數(shù)和返回值之間的關(guān)系。

類型安全和型變

首先,TypeScript 通過(guò)在 JavaScript 上添加靜態(tài)類型系統(tǒng)來(lái)實(shí)現(xiàn)類型安全。類型安全意味著我們可以在編譯時(shí)檢測(cè)到可能的類型錯(cuò)誤,防止它們?cè)谶\(yùn)行時(shí)導(dǎo)致程序崩潰。例如,TypeScript 不允許將一個(gè) number 類型的值賦給一個(gè) boolean 類型的變量,也不允許調(diào)用某個(gè)對(duì)象上不存在的方法。

let isDone: boolean = true;
isDone = 1; // Error: Type '1' is not assignable to type 'boolean'.

let currentDate: Date = new Date();
currentDate.exec(); // Error: Property 'exec' does not exist on type 'Date'.

在這兩個(gè)例子中,TypeScript 的類型檢查機(jī)制會(huì)在編譯時(shí)報(bào)告錯(cuò)誤,從而保證了類型的正確性和代碼的可靠性。

然而,完全嚴(yán)格的類型安全有時(shí)會(huì)導(dǎo)致不便。例如,當(dāng)我們處理子類型和父類型時(shí),TypeScript 允許一些 變通,以便在保證類型安全的前提下提供更大的靈活性。這種 變通 被稱為型變(Variance)。

在類型系統(tǒng)中,當(dāng)類型 A 和類型 B 存在繼承關(guān)系時(shí),使用 A 的地方是否也能使用 B。型變?cè)诤瘮?shù)的參數(shù)和返回值中表現(xiàn)尤為明顯。型變通常包括以下四種類型:

  • 協(xié)變(Covariance):如果 A 是 B 的子類型,那么 F<A> 也是 F<B> 的子類型。換句話說(shuō),F<A> 可以賦值給 F<B>。協(xié)變通常用于函數(shù)的返回值類型。
  • 逆變(Contravariance):如果 A 是 B 的子類型,那么 F<B>F<A> 的子類型。換句話說(shuō),F<B> 可以賦值給 F<A>。逆變通常用于函數(shù)的參數(shù)類型。

雙向協(xié)變(Bivariance):TypeScript 中的一種特殊情況,允許函數(shù)參數(shù)類型既可以協(xié)變也可以逆變,雖然這種情況理論上不安全,但它是為了保持實(shí)際代碼的兼容性。

不變(Invariance):F<A> 既不是 F<B> 的子類型,也不是其超類型。換句話說(shuō),F<A>F<B> 是完全不兼容的類型。

協(xié)變

在 TypeScript 中,協(xié)變 主要是指在處理泛型、函數(shù)類型、數(shù)組等結(jié)構(gòu)時(shí),允許子類型被視為其父類型。簡(jiǎn)言之,協(xié)變?cè)试S子類型可以賦值給父類型,這是最常見(jiàn)的類型兼容性規(guī)則。

泛型默認(rèn)是協(xié)變的,這就以為著如果 B 是 A 的子類型,那么 T<B> 也是 T<A> 的子類型。

例如,假設(shè)我們有一個(gè)泛型接口 Box<T>

class Animal {}
class Dog extends Animal {}

interface Box<T> {
  value: T;
}

let animalBox: Box<Animal>;
let dogBox: Box<Dog> = { value: new Dog() };

animalBox = dogBox; // 合法,協(xié)變

他們相互賦值是不會(huì)錯(cuò)的,雖然這倆類型不一樣,但是依然是類型安全的。

在這個(gè)例子中,Box<Dog> 可以賦值給 Box<Animal>,因?yàn)?Dog 是 Animal 的子類型。這種關(guān)系在 TypeScript 的類型系統(tǒng)中是允許的,因?yàn)樗蠀f(xié)變的規(guī)則。

除了在類中,函數(shù)和數(shù)組中也都是可以協(xié)變的:

class Animal {}
class Dog extends Animal {}

type AnimalFactory = () => Animal;
type DogFactory = () => Dog;

let createAnimal: AnimalFactory;
let createDog: DogFactory = () => new Dog();

createAnimal = createDog; // 合法,協(xié)變

class Animal {}
class Dog extends Animal {}

let dogs: Dog[] = [new Dog(), new Dog()];
let animals: Animal[];

animals = dogs; // 合法,協(xié)變

協(xié)變?cè)?TypeScript 中非常重要,因?yàn)樗试S類型系統(tǒng)具有更大的靈活性。協(xié)變?cè)试S你在不違反類型安全的情況下,靈活地使用繼承結(jié)構(gòu)中的子類型和父類型。

在使用泛型數(shù)據(jù)結(jié)構(gòu)時(shí),協(xié)變?cè)试S我們?cè)诓挥绊戭愋桶踩那闆r下,處理更廣泛的類型。例如,List<Dog> 可以在 List<Animal> 的上下文中使用,因?yàn)?Dog 是 Animal 的子類型。協(xié)變使得函數(shù)可以返回更具體的類型而不影響函數(shù)的兼容性,這有助于設(shè)計(jì)靈活且類型安全的接口。例如,你可以在接口的實(shí)現(xiàn)中返回一個(gè)更具體的類型,而不需要更改接口本身的簽名。

雖然協(xié)變提供了很多靈活性,但是在某些情況下也可能會(huì)帶來(lái)問(wèn)題,例如數(shù)組的協(xié)變可能會(huì)導(dǎo)致運(yùn)行時(shí)錯(cuò)誤,例如在使用 push 操作的時(shí)候,它可能會(huì)破壞數(shù)組的類型一致性,從而導(dǎo)致運(yùn)行時(shí)報(bào)錯(cuò):

class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}

let animals: Animal[] = [new Dog()];
animals.push(new Cat()); // 合法,但可能不是預(yù)期的行為

在這個(gè)例子中,animals 原本是 Dog[] 類型,但由于數(shù)組的協(xié)變,Cat 實(shí)例被推入了這個(gè)數(shù)組,這可能會(huì)導(dǎo)致一些不一致的問(wèn)題。

逆變

在 TypeScript 中,逆變 是指,如果類型 A 是類型 B 的父類型(A <: B),那么在函數(shù)參數(shù)的位置上,類型 B 可以賦值給類型 A。換句話說(shuō),逆變?cè)试S父類型的函數(shù)參數(shù)賦值給子類型的函數(shù)參數(shù)。

在函數(shù)參數(shù)中的逆變,我們可以考慮以下類繼承結(jié)構(gòu):

class Animal {
  speak() {
    console.log("Animal sound");
  }
}

class Dog extends Animal {
  speak() {
    console.log("Bark");
  }
}

假設(shè)我們有兩個(gè)函數(shù)類型,一個(gè)接受 Animal 作為參數(shù),另一個(gè)接受 Dog 作為參數(shù):

type AnimalHandler = (animal: Animal) => void;
type DogHandler = (dog: Dog) => void;

在逆變的情況下,AnimalHandler 可以賦值給 DogHandler。這是因?yàn)?Dog 是 Animal 的子類型,因此處理 Animal 的函數(shù)也可以處理 Dog。

let handleAnimal: AnimalHandler = (animal: Animal) => {
  animal.speak();
};

let handleDog: DogHandler;

handleDog = handleAnimal; // 合法,逆變

在這個(gè)例子中,handleAnimal 可以處理任何 Animal,包括 Dog。因此,我們可以將 handleAnimal 賦值給 handleDog,這在類型系統(tǒng)中是安全的。

逆變的直觀理解是,你可以將處理較寬泛類型的函數(shù)賦值給處理更具體類型的函數(shù)。如果一個(gè)函數(shù)能夠處理 Animal,那么它肯定能夠處理 Dog,因?yàn)?Dog 是 Animal 的一種特殊化形式。

在 TypeScript 中,型變通常用于描述類型在不同上下文中的傳遞方式。逆變與協(xié)變是相對(duì)的:

  • 協(xié)變:子類型可以賦值給父類型。例如,Dog 是 Animal 的子類型,那么 Dog 類型的值可以賦值給 Animal 類型的變量。
  • 逆變:父類型可以賦值給子類型。例如,Animal 是 Dog 的父類型,那么接受 Animal 參數(shù)的函數(shù)可以賦值給接受 Dog 參數(shù)的函數(shù)。

在 React 中,事件處理函數(shù)是逆變的一種典型應(yīng)用。當(dāng)你在一個(gè)通用的事件處理器中使用更廣泛的事件類型時(shí),逆變可以確保你的事件處理器適用于子類型事件。

例如,考慮一個(gè)處理 MouseEvent 的事件處理器:

type MouseEventHandler = (event: React.MouseEvent<HTMLButtonElement>) => void;

如果我們有一個(gè)更通用的事件處理函數(shù),它可以處理任意的 DOM 事件,那么它可以被安全地用作處理特定的 MouseEvent:

type AnyEventHandler = (event: React.SyntheticEvent) => void;

const handleEvent: AnyEventHandler = (event) => {
  console.log(event);
};

const handleMouseEvent: MouseEventHandler = handleEvent; // 合法,逆變

在這個(gè)例子中,handleEvent 可以處理任何 SyntheticEvent,其中包括 MouseEvent,所以它可以被賦值給 handleMouseEvent。這就是逆變的一個(gè)實(shí)際應(yīng)用:更廣泛的處理函數(shù)可以用于處理更具體的事件類型。

在使用高階組件時(shí),逆變也可能發(fā)揮作用。高階組件(HOC)是接受一個(gè)組件并返回一個(gè)新組件的函數(shù)。在處理高階組件時(shí),如果傳遞給 HOC 的組件的屬性是某種類型的父類型,那么 HOC 返回的組件可以安全地接受更具體的子類型屬性。

假設(shè)你有一個(gè)高階組件 withLogging,它可以將日志功能添加到任何組件中:

function withLogging<P>(Component: React.ComponentType<P>): React.FC<P> {
  return (props: P) => {
    console.log("Rendering", Component.name);
    return <Component {...props} />;
  };
}

如果你有一個(gè)組件 DogComponent 接受 Dog 類型的屬性,那么你可以將 withLogging 應(yīng)用于 DogComponent,并返回一個(gè)同樣接受 Dog 屬性的組件:

interface Dog {
  name: string;
  breed: string;
}

const DogComponent: React.FC<Dog> = ({ name, breed }) => (
  <div>
    {name} is a {breed}
  </div>
);

const LoggedDogComponent = withLogging(DogComponent);

// 使用 LoggedDogComponent
<LoggedDogComponent name="Rex" breed="Labrador" />;

在這里,withLogging 的參數(shù)類型是泛型 P,而 Dog 是 P 的一個(gè)具體實(shí)例。由于 TypeScript 支持逆變,因此即使 P 是父類型,它也可以接受子類型 Dog 的參數(shù),這使得 LoggedDogComponent 可以安全地使用 Dog 類型的屬性。

逆變 是 TypeScript 中的一種型變,允許父類型的函數(shù)參數(shù)賦值給子類型的函數(shù)參數(shù)。它保證了類型安全,因?yàn)樘幚砀割愋偷暮瘮?shù)可以適用于子類型,而不會(huì)引發(fā)類型錯(cuò)誤。這通常用于函數(shù)參數(shù)類型的兼容性處理,確保函數(shù)的靈活性和擴(kuò)展性。

雙向協(xié)變

在類型系統(tǒng)中,協(xié)變?cè)试S子類型賦值給父類型,逆變則允許父類型賦值給子類型。雙向協(xié)變是 TypeScript 中的一種特殊行為,它允許函數(shù)參數(shù)的類型既可以協(xié)變,也可以逆變。

簡(jiǎn)單來(lái)說(shuō),雙向協(xié)變?cè)试S你在處理函數(shù)參數(shù)類型時(shí),既可以將子類型賦值給父類型,也可以將父類型賦值給子類型。

假設(shè)我們有兩個(gè)類 Animal 和 Dog,Dog 繼承自 Animal:

class Animal {
  speak() {
    console.log("Animal sound");
  }
}

class Dog extends Animal {
  speak() {
    console.log("Bark");
  }
}

現(xiàn)在我們定義兩個(gè)函數(shù)類型,一個(gè)接受 Dog 作為參數(shù),另一個(gè)接受 Animal 作為參數(shù):

type DogHandler = (dog: Dog) => void;
type AnimalHandler = (animal: Animal) => void;

根據(jù)雙向協(xié)變的規(guī)則,TypeScript 允許我們將 DogHandler 賦值給 AnimalHandler,也允許我們將 AnimalHandler 賦值給 DogHandler:

let handleDog: DogHandler = (dog: Dog) => {
  dog.speak();
};

let handleAnimal: AnimalHandler = (animal: Animal) => {
  animal.speak();
};

handleAnimal = handleDog; // 合法,協(xié)變
handleDog = handleAnimal; // 合法,逆變

在這個(gè)例子中,handleAnimal 可以處理 Animal 類型的參數(shù),而 handleDog 只處理 Dog 類型的參數(shù)。盡管從理論上來(lái)說(shuō),將 handleAnimal 賦值給 handleDog 可能存在類型安全問(wèn)題(handleAnimal 可能接受 Cat,而 handleDog 只能處理 Dog),但 TypeScript 允許這種賦值操作。

考慮一個(gè)更復(fù)雜的例子,其中我們有一個(gè)函數(shù)接受另一個(gè)函數(shù)作為參數(shù):

function processAnimal(handler: (a: Animal) => void): void {
  const animal = new Animal();
  handler(animal);
}

function processDog(handler: (d: Dog) => void): void {
  const dog = new Dog();
  handler(dog);
}

根據(jù)雙向協(xié)變的規(guī)則,processDog 可以接受 processAnimal 中的函數(shù)作為參數(shù),反之亦然:

processDog((dog: Dog) => {
  console.log(dog.speak());
}); // 合法

processAnimal((animal: Animal) => {
  console.log(animal.speak());
}); // 合法

在這里,盡管 processAnimal 接受的是 Animal 類型的參數(shù),而 processDog 只處理 Dog 類型,但 TypeScript 允許這種相互賦值。

雖然雙向協(xié)變使得代碼更靈活,但它也可能引發(fā)類型安全問(wèn)題。特別是在處理繼承關(guān)系較為復(fù)雜的情況下,可能會(huì)導(dǎo)致運(yùn)行時(shí)錯(cuò)誤。

function handleAnyAnimal(animal: Animal): void {
  console.log("Handling an animal");
}

function handleOnlyDog(dog: Dog): void {
  console.log("Handling a dog");
}

let dogHandler: (d: Dog) => void = handleAnyAnimal; // 合法,但可能不安全
dogHandler(new Dog()); // 正常
dogHandler(new Animal()); // 運(yùn)行時(shí)錯(cuò)誤,因?yàn)?Animal 不是 Dog

在這個(gè)例子中,將 handleAnyAnimal 賦值給 dogHandler 是合法的,因?yàn)?TypeScript 允許這種雙向協(xié)變。然而,當(dāng)我們嘗試傳遞一個(gè) Animal(而不是 Dog)給 dogHandler 時(shí),可能會(huì)引發(fā)運(yùn)行時(shí)錯(cuò)誤。

不變

在類型系統(tǒng)中,不變 是指某個(gè)類型不能在子類型和父類型之間相互替換。具體來(lái)說(shuō),如果你有一個(gè)泛型類型 T<A>T<B>,即使 A 是 B 的子類型,T<A> 也不能賦值給 T<B>,反之亦然。這種嚴(yán)格的類型匹配規(guī)則被稱為不變。

考慮一個(gè)簡(jiǎn)單的泛型類:

class Animal {
  name: string;
}

class Dog extends Animal {
  breed: string;
}

class Cat extends Animal {
  color: string;
}

interface Box<T> {
  content: T;
}

let animalBox: Box<Animal> = { content: new Animal() };
let dogBox: Box<Dog> = { content: new Dog() };
let catBox: Box<Cat> = { content: new Cat() };

在不變的規(guī)則下,盡管 Dog 是 Animal 的子類型,但 Box<Dog> 不能賦值給 Box<Animal>,同樣 Box<Animal> 也不能賦值給 Box<Dog>

animalBox = dogBox; // 錯(cuò)誤,Box<Dog> 不能賦值給 Box<Animal>
dogBox = animalBox; // 錯(cuò)誤,Box<Animal> 不能賦值給 Box<Dog>

這就是不變的體現(xiàn)。TypeScript 要求 Box<Animal>Box<Dog> 必須是完全一致的類型,任何嘗試在這些類型之間進(jìn)行賦值都會(huì)導(dǎo)致類型錯(cuò)誤。

不變是一種型變規(guī)則,要求類型在所有上下文中必須嚴(yán)格匹配。比如我想要一只鴨子,你不能給我一個(gè)碗對(duì)吧,碗又不能吃。

總結(jié)

本文詳細(xì)介紹了 TypeScript 類型系統(tǒng)中的四種型變概念:協(xié)變、逆變、雙向協(xié)變 和 不變,以及它們?cè)陬愋桶踩挽`活性方面的作用。

  • 協(xié)變:允許子類型賦值給父類型,常見(jiàn)于函數(shù)的返回值和數(shù)組類型。協(xié)變使得類型系統(tǒng)更加靈活,例如,List<Dog> 可以在需要 List<Animal> 的地方使用。
  • 逆變:允許父類型賦值給子類型,常見(jiàn)于函數(shù)的參數(shù)類型。逆變確保了類型系統(tǒng)的靈活性和安全性,比如將處理 Animal 的函數(shù)賦值給處理 Dog 的函數(shù)。
  • 雙向協(xié)變:一種特殊情況,允許函數(shù)參數(shù)類型既可以協(xié)變也可以逆變,盡管這可能引發(fā)類型安全問(wèn)題。雙向協(xié)變?cè)?TypeScript 中存在主要是為了保持與 JavaScript 的兼容性。
  • 不變:要求類型在所有上下文中嚴(yán)格匹配,不能在子類型和父類型之間相互替換。這種嚴(yán)格的類型檢查確保了類型系統(tǒng)的安全性,但也減少了靈活性。

理解這些型變概念能夠幫助開(kāi)發(fā)者在 TypeScript 中編寫(xiě)既靈活又安全的代碼,尤其是在處理復(fù)雜的類型關(guān)系和函數(shù)參數(shù)時(shí)。它們?yōu)槲覀兲峁┝艘粋€(gè)平衡類型安全性和代碼靈活性的工具。

到此這篇關(guān)于TypeScript 類型兼容(逆變、協(xié)變、雙向協(xié)變和不變)的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)TypeScript 類型兼容內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • javascript HTML5 canvas實(shí)現(xiàn)打磚塊游戲

    javascript HTML5 canvas實(shí)現(xiàn)打磚塊游戲

    這篇文章主要介紹了基于javascript HTML5 canvas實(shí)現(xiàn)打磚塊游戲的具體實(shí)現(xiàn)代碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-04-04
  • JavaScript關(guān)于prototype實(shí)例詳解(超重點(diǎn))

    JavaScript關(guān)于prototype實(shí)例詳解(超重點(diǎn))

    prototype是js里面給類增加功能擴(kuò)展的一種模式,這篇文章主要介紹了JavaScript關(guān)于prototype(超重點(diǎn)),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-08-08
  • 微信小程序使用slider設(shè)置數(shù)據(jù)值及switch開(kāi)關(guān)組件功能【附源碼下載】

    微信小程序使用slider設(shè)置數(shù)據(jù)值及switch開(kāi)關(guān)組件功能【附源碼下載】

    這篇文章主要介紹了微信小程序使用slider設(shè)置數(shù)據(jù)值及switch開(kāi)關(guān)組件功能,結(jié)合實(shí)例形式分析了slider組件及switch組件的功能與使用方法,并附帶源碼供讀者下載參考,需要的朋友可以參考下
    2017-12-12
  • js彈出層(jQuery插件形式附帶reLoad功能)

    js彈出層(jQuery插件形式附帶reLoad功能)

    之前的彈出層做的挺好,但是代碼結(jié)構(gòu)有問(wèn)題,這次用到了,重構(gòu)了一下,改為jQuery的插件形式,感覺(jué)還不錯(cuò),有興趣的朋友可以參考下,希望可以幫助到你
    2013-04-04
  • JavaScript日期庫(kù)date-fn.js使用方法解析

    JavaScript日期庫(kù)date-fn.js使用方法解析

    這篇文章主要介紹了JavaScript日期庫(kù)date-fn.js使用方法解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-09-09
  • js 封裝cookie操作的函數(shù)代碼

    js 封裝cookie操作的函數(shù)代碼

    這篇文章主要通過(guò)js封裝cookie的函數(shù)代碼,想用js實(shí)現(xiàn)cookies操作的朋友可以參考下
    2013-06-06
  • JavaScript關(guān)于提高網(wǎng)站性能的幾點(diǎn)建議(一)

    JavaScript關(guān)于提高網(wǎng)站性能的幾點(diǎn)建議(一)

    這篇文章主要介紹了JavaScript關(guān)于提高網(wǎng)站性能的幾點(diǎn)建議(一)的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2016-07-07
  • JavaScript 過(guò)濾關(guān)鍵字

    JavaScript 過(guò)濾關(guān)鍵字

    本文主要介紹了JavaScript過(guò)濾關(guān)鍵字的方法。具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧
    2017-03-03
  • 詳解wow.js中各種特效對(duì)應(yīng)的類名

    詳解wow.js中各種特效對(duì)應(yīng)的類名

    本篇文章主要介紹了wow.js中各種特效對(duì)應(yīng)的類名 ,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-09-09
  • 深入淺出JS的Object.defineProperty()

    深入淺出JS的Object.defineProperty()

    這篇文章主要介紹了深入淺出JS的Object.defineProperty(),文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,感興趣的小伙伴可以參考一下
    2022-06-06

最新評(píng)論