TypeScript中extends的正確打開方式詳解
前言
最近完整地看了一遍TypeScript的官方文檔,發(fā)現(xiàn)文檔中有一些知識點沒有專門講解到,或者是講解了但卻十分難以理解,因此就有了這一系列的文章,我將對沒有講解到的或者是我認為難以理解的知識點進行補充講解,希望能給您帶來一點幫助。
tips:配合官方文檔食用更佳
這是本系列的第二篇TypeScript中extends的正確打開方式,在TypeScript中我們經(jīng)常見到extends這一關(guān)鍵字,我們可能第一時間想到的就是繼承,但除了繼承它其實還有其他的用法,接下來讓我們來一一獲得extends的正確打開方式。
extends第一式:繼承
作為眾人皆知的extends關(guān)鍵字,其最出名的使用方式便是繼承。
類繼承類
首先讓我們來用extends實現(xiàn)一下類的繼承。
class Animal {
public name;
constructor(name: string) {
this.name = name;
}
eat(food: string) {
console.log(`${this.name}正在吃${food}`);
}
}
class Sheep extends Animal {
constructor(name: string) {
super(name);
}
miemie() {
console.log("別看我只是一只羊,羊兒的聰明難以想象~");
}
}
let lanyangyang = new Sheep("懶羊羊");
lanyangyang.eat("青草蛋糕");
// 懶羊羊正在吃青草蛋糕
lanyangyang.miemie();
//別看我只是一只羊,羊兒的聰明難以想象~
首先我們定義了一個Animal類,該類有name屬性以及eat方法。然后又定義了一個繼承Animal類的Sheep類,該類在父類name屬性以及eat方法基礎(chǔ)上又新增了一個miemie方法。
接口繼承接口
extends不僅能夠用于類與類之間的繼承上,還能夠用于接口與接口之間的繼承。接下來我們來實現(xiàn)一下接口之間的繼承。
interface IAnimal{
name:string;
eat:(food:string)=>void;
}
interface ISheep extends IAnimal{
miemie:()=>void;
}
let lanyangyang:ISheep={
name:'懶羊羊',
eat(food:string){
console.log(`${this.name}正在吃${food}`);
},
miemie() {
console.log("別看我只是一只羊,羊兒的聰明難以想象~");
}
}
lanyangyang.eat("青草蛋糕");
// 懶羊羊正在吃青草蛋糕
lanyangyang.miemie();
//別看我只是一只羊,羊兒的聰明難以想象~
我們定義了一個IAnimal接口,然后用通過extends繼承IAnimal定義了ISheep接口,則實現(xiàn)ISheep接口的變量lanyangyang必須要有父接口的name屬性以及實現(xiàn)eat方法,并且還要實現(xiàn)本身的miemie方法。
現(xiàn)在我們通過extends實現(xiàn)了類與類之間的繼承、接口與接口之間的繼承,那么類與接口之間是否能互相繼承呢?答案是可以。
接口繼承類
首先我們使用extends來實現(xiàn)接口繼承類。
class Animal {
public name;
constructor(name: string) {
this.name = name;
}
eat(food: string) {
console.log(`${this.name}正在吃${food}`);
}
static run(){
console.log(`${this.name} is running`)
}
}
interface ISheep extends Animal{
miemie:()=>void;
}
let lanyangyang:ISheep={
name:'懶羊羊',
eat(food:string){
console.log(`${this.name}正在吃${food}`);
},
miemie() {
console.log("別看我只是一只羊,羊兒的聰明難以想象~");
}
}
lanyangyang.eat("青草蛋糕");
// 懶羊羊正在吃青草蛋糕
lanyangyang.miemie();
//別看我只是一只羊,羊兒的聰明難以想象~
在接口繼承類時,可以把類看作一個接口,但是類中的靜態(tài)方法是不會繼承過來的。我們在實現(xiàn)ISheep接口的變量lanyangyang必須要有類Animal的name屬性以及實現(xiàn)eat方法,并且還要實現(xiàn)本身的miemie方法。但是我們不必實現(xiàn)類Animal的靜態(tài)方法run。
是不是覺得還會有下一個標(biāo)題類繼承接口,對不起,這個真沒有!類繼承接口使用的關(guān)鍵字變成了implements。
extends第二式:三元表達式條件判斷
extends還有一個比較常見的用法就是在三元表達式中進行條件判斷,即判斷一個類型是否可以分配給另一個類型。這里根據(jù)三元表達式中是否存在泛型判斷結(jié)果還不一致,首先我們介紹普通的三元表達式條件判斷。
普通的三元表達式條件判斷
帶有extends的三元表達式如下:
type TypeRes=Type1 extends Type2? Type3: Type4;
這里表達的意思就是如果類型Type1可被分配給類型Type2,則類型TypeRes取Type3,否則取Type4。那怎么理解類型Type1可被分配給類型Type2呢??
我們可以這樣理解:類型為Type1的值可被賦值給類型為Type2的變量。可以具體分為一下幾種情況:
Type1和Type2為同一種類型。Type1是Type2的子類型。Type2類型兼容類型Type1。 接下來我們分情況進行驗證。
情況一:Type1和Type2為同一種類型。
type Type1=string; type Type2=Type1; type Type3=true; type Type4=false; type TypeRes=Type1 extends Type2? Type3: Type4; // true
這里Type1和Type2為同一種類型。因此Type1可被分配給Type2。因此TypeRes類型最后取為true。
情況二:Type1是Type2的子類型。
class Animal {
public name;
constructor(name: string) {
this.name = name;
}
eat(food: string) {
console.log(`${this.name}正在吃${food}`);
}
}
class Sheep extends Animal {
constructor(name: string) {
super(name);
}
miemie() {
console.log("別看我只是一只羊,羊兒的聰明難以想象~");
}
}
type Type1=Sheep;
type Type2=Animal;
type Type3=true;
type Type4=false;
type TypeRes=Type1 extends Type2? Type3: Type4;
// true
這里Sheep類繼承自Animal,即Type1是Type2的子類型。因此Type1可被分配給Type2。因此TypeRes類型最后取為true。
情況三: Type2類型兼容類型Type1。
首先還是拋出一個問題,什么是類型兼容??這個問題可以從官方文檔中得到答案,大家可以戳類型兼容性詳細了解!
所謂 Type2類型兼容類型Type1,指得就是Type1類型的值可被賦值給類型為Type2的變量。 舉個栗子:
type Type1={
name:string;
age:number;
gender:string;
}
type Type2={
name:string;
age:number;
}
type Type3=true;
type Type4=false;
type TypeRes=Type1 extends Type2? Type3: Type4;
// true
由于類型Type1擁有至少與Type2相同的屬性,因此Type2是兼容Type1的。也就是說Type1類型的值可被賦值給類型為Type2的變量。
let kenny1:Type1={
name:'kenny',
age:26,
gender:'male'
}
let kenny2:Type2=kenny;
// no Error
因此TypeRes類型最后取為true。
再舉個栗子,以函數(shù)的兼容性為例,
type Type1=(a:number)=>void; type Type2=(a:number,b:string)=>void; type Type3=true; type Type4=false; type TypeRes=Type1 extends Type2? Type3: Type4; // true
當(dāng)函數(shù)參數(shù)不同時,看Type2是否兼容Type1,就要看Type1的每個參數(shù)必須能在Type2里找到對應(yīng)類型的參數(shù)。 注意的是參數(shù)的名字相同與否無所謂,只看它們的類型。
這里Type1第一個number類型的參數(shù)是可以在Type2中找到,即Type1類型的函數(shù)可被賦值給類型為Type2的變量。
let fn1:Type1=(a:number)=>{}
let kenny2:Type2=fn1;
// no Error
因此TypeRes類型最后取為true。
帶有泛型的三元表達式條件判斷
我們先來一個舉一個不帶泛型的栗子,大家可以想想結(jié)果是什么。
type Type1=string|number; type Type2=string; type Type3=true; type Type4=false; type TypeRes=Type1 extends Type2? Type3: Type4;
沒錯,由于Type1是Type2的父類型,因此Type1是不可分配給Type2的,因此TypeRes類型最后取為false。
但當(dāng)我們加上泛型之后呢,再來看一個栗子。
type Type1=string|number; type Type2=string; type Type3=true; type Type4=false; type Type5<T>=T extends Type2? Type3: Type4; type TypeRes<Type1> // boolean
這里TypeRes類型最后就不是false了,而變成boolean。這是為什么呢?
原來再使用泛型時,若extends左側(cè)的泛型具體取為一個聯(lián)合類型時,就會把聯(lián)合類型中的類型拆開,分別帶入到條件判斷式中進行判斷,最后把結(jié)果再進行聯(lián)合。上述的栗子中結(jié)果可以這么來看,
(string extends string?true:false)|(number extends string?true:false) true | false boolean
在高級類型中有很多類型實現(xiàn)便用到了這一特性。
比如Exclude<T, U> -- 從T中剔除可以賦值給U的類型。
type T1 = "a" | "b" | "c" | "d"; type T2 = "a" | "c" | "f" type ExcludeT1T2=Exclude<T1,T2> //"b"|"d"
該類型的類型實現(xiàn)為
type Exclude<T, U> = T extends U ? never : T;
當(dāng)T為聯(lián)合類型時,會自動分發(fā)條件,對T中的所有類型進行遍歷,判斷其是否可以分配給類型U,如果是的話便返回never類型,否則返回其原來的類型。最后再將其進行聯(lián)合得到一個結(jié)果聯(lián)合類型。
由于never類型與其他類型聯(lián)合最終得到的還是其他類型,因此便可以從類型T中剔除掉可以賦給U的類型。
那有沒有辦法讓泛型三元表達式中extends和普通的extends作用相同?有!只需要給泛型加一個[]。栗子如下:
type Type1=string|number; type Type2=string; type Type3=true; type Type4=false; type Type5<T>=[T] extends Type2? Type3: Type4; type TypeRes<Type1> // false
extends第三式:泛型約束
首先我們來回答一下什么是泛型?簡單來說,泛型就是一種類型變量,普通的變量代表一個任意的值,而不是一個特定的值,我們可以把任何值賦給變量,而類型變量代表一個任意的類型,而不是一個特定的類型,我們可以把任何類型賦給類型變量。它是一種特殊的變量,只用于表示類型而不是值。
那如果我們不想讓泛型表示任意類型時,該怎么辦?這時我們就可以使用extends對泛型進行約束,讓泛型表示滿足一定條件的類型。接下來,我們使用extends進行泛型的約束。
interface ISheep{
name:string;
eat:(food:string)=>void;
miemie:()=>void;
}
function eatAndMiemie<T extends ISheep>(sheep:T):void{
sheep.eat("青草蛋糕");
sheep.miemie();
}
eatAndMiemie(
{
name: "懶羊羊",
eat(food:string){
console.log(`${this.name}正在吃${food}`);
},
miemie() {
console.log("別看我只是一只羊,羊兒的聰明難以想象~");
}
run() {console.log(`${this.name}正在奔跑`)};
}
)
// 懶羊羊正在吃青草蛋糕
//別看我只是一只羊,羊兒的聰明難以想象~
這里我們便對泛型T進行了約束,其必須至少要擁有ISheep的name屬性及eat、miemie方法,另外T中若有其他的屬性及方法,則不作限制。這里我們便通過extends對泛型T進行了約束。
其實泛型約束中的extends也是起到了三元表達式中類型分配的作用,其中T extends ISheep表示泛型T必須可以分配給類型Isheep。
以上就是TypeScript中extends的正確打開方式詳解的詳細內(nèi)容,更多關(guān)于TypeScript extends打開方式的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JavaScript設(shè)計模式之單例模式應(yīng)用場景案例詳解
這篇文章主要為大家介紹了JavaScript中單例模式的應(yīng)用場景案例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-05-05
微信小程序 ecshop地址三級聯(lián)動實現(xiàn)實例代碼
這篇文章主要介紹了微信小程序 ecshop地址3級聯(lián)動實現(xiàn)實例代碼的相關(guān)資料,需要的朋友可以參考下2017-02-02
JavaScript?Canvas實現(xiàn)噪點濾鏡回憶童年電視雪花屏
這篇文章主要為大家介紹了JavaScript?Canvas實現(xiàn)噪點濾鏡回憶童年電視雪花屏,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-09-09
詳解JavaScript中數(shù)組的相關(guān)知識
這篇文章主要介紹了JavaScript中中數(shù)組的相關(guān)知識,是JS入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下2015-07-07
JS時間分片技術(shù)解決長任務(wù)導(dǎo)致的頁面卡頓
旨在把一個運行時間比較長的任務(wù)分解成一塊一塊比較小的任務(wù),分塊去執(zhí)行,因為超過 50ms 的任務(wù)就會被認為是 long task,用戶就能感知到渲染卡頓和交互的卡頓,所以我們可以縮短函數(shù)的連續(xù)執(zhí)行時間2022-07-07
微信小程序上滑加載下拉刷新(onscrollLower)分批加載數(shù)據(jù)(二)
這篇文章主要介紹了微信小程序上滑加載下拉刷新(onscrollLower)分批加載數(shù)據(jù)的相關(guān)資料,需要的朋友可以參考下2017-05-05

