怎么使用TypeScript

今天就跟大家聊聊有關(guān)怎么使用TypeScript,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。

創(chuàng)新互聯(lián)建站主要從事成都網(wǎng)站設(shè)計(jì)、網(wǎng)站制作、網(wǎng)頁設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)趙縣,十多年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專業(yè),歡迎來電咨詢建站服務(wù):18982081108

1、類繼承

在ES5中,我們一般通過函數(shù)或者基于原型的繼承來封裝一些組件公共的部分方便復(fù)用,然而在TypeScript中,我們可以像類似Java語言中以面向?qū)ο蟮姆绞绞褂妙惱^承來創(chuàng)建可復(fù)用的組件。我們可以通過class關(guān)鍵字來創(chuàng)建類,并基于它使用new操作符來實(shí)例化一個(gè)對(duì)象。為了將多個(gè)類的公共部分進(jìn)行抽象,我們可以創(chuàng)建一個(gè)父類并讓子類通過extends關(guān)鍵字來繼承父類,從而減少一些冗余代碼的編寫增加代碼的可復(fù)用性和可維護(hù)性。示例如下:

class Parent {
 readonly x: number;
 constructor() {
 this.x = 1;
 }
 
 print() {
 console.log(this.x);
 }
}

class Child extends Parent {
 readonly y: number;
 constructor() {
 // 注意此處必須優(yōu)先調(diào)用super()方法
 super();
 this.y = 2;
 }
 
 print() {
 // 通過super調(diào)用父類原型上的方法,但是方法中的this指向的是子類的實(shí)例
 super.print();
 console.log(this.y);
 }
}

const child = new Child();
console.log(child.print()) // -> 1 2

在上述示例中,Child子類中對(duì)父類的print方法進(jìn)行重寫,同時(shí)在內(nèi)部使用super.print()來調(diào)用父類的公共邏輯,從而實(shí)現(xiàn)邏輯復(fù)用。class關(guān)鍵字作為構(gòu)造函數(shù)的語法糖,在經(jīng)過TypeScript編譯后,最終會(huì)被轉(zhuǎn)換為兼容性好的瀏覽器可識(shí)別的ES5代碼。class在面向?qū)ο蟮木幊谭妒街蟹浅3R?,因此為了弄清楚其背后的?shí)現(xiàn)機(jī)制,我們不妨多花點(diǎn)時(shí)間來看下經(jīng)過編譯轉(zhuǎn)換之后的代碼是什么樣子的(當(dāng)然這部分已經(jīng)比較熟悉的同學(xué)可以直接跳過)。

var __extends = (this && this.__extends) || (function () {
 var extendStatics = function (d, b) {
 extendStatics = Object.setPrototypeOf ||
 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
 return extendStatics(d, b);
 }
 return function (d, b) {
 extendStatics(d, b);
 function __() { this.constructor = d; }
 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
 };
})();
var Parent = /** @class */ (function () {
 function Parent() {
 this.x = 1;
 }
 Parent.prototype.print = function () {
 console.log(this.x);
 };
 return Parent;
}());
var Child = /** @class */ (function (_super) {
 __extends(Child, _super);
 function Child() {
 var _this = 
 // 注意此處必須優(yōu)先調(diào)用super()方法
 _super.call(this) || this;
 _this.y = 2;
 return _this;
 }
 Child.prototype.print = function () {
 // 通過super調(diào)用父類原型上的方法,但是方法中的this指向的是子類的實(shí)例
 _super.prototype.print.call(this);
 console.log(this.y);
 };
 return Child;
}(Parent));
var child = new Child();
console.log(child.print()); // -> 1 2

以上就是轉(zhuǎn)換后的完整代碼,為了方便對(duì)比,這里將原來的注釋信息保留,仔細(xì)研究這段代碼我們會(huì)發(fā)現(xiàn)以下幾個(gè)要點(diǎn):

1) 子類Child的構(gòu)造函數(shù)中super()方法被轉(zhuǎn)換成了var _this = _super.call(this) || this,這里的_super指的就是父類Parent,因此這句代碼的含義就是調(diào)用父類構(gòu)造函數(shù)并將this綁定到子類的實(shí)例上,這樣的話子類實(shí)例便可擁有父類的x屬性。因此為了實(shí)現(xiàn)屬性繼承,我們必須在子類構(gòu)造函數(shù)中調(diào)用super()方法,如果不調(diào)用會(huì)編譯不通過。

2) 子類Child的print方法中super.print()方法被轉(zhuǎn)換成了_super.prototype.print.call(this) ,這句代碼的含義就是調(diào)用父類原型上的print方法并將方法中的this指向子類實(shí)例,由于在上一步操作中我們已經(jīng)繼承到父類的x屬性,因此這里我們將直接打印出子類實(shí)例的x屬性的值。

3) extends關(guān)鍵字最終被轉(zhuǎn)換為__extends(Child, _super)方法,其中_super指的是父類Parent,為了方便查看,這里將_extends方法單獨(dú)提出來進(jìn)行研究。

var __extends = (this && this.__extends) || (function () {
 var extendStatics = function (d, b) {
 extendStatics = Object.setPrototypeOf ||
 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
 return extendStatics(d, b);
 }
 return function (d, b) {
 // 第一部分
 extendStatics(d, b);
 
 // 第二部分
 function __() { this.constructor = d; }
 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
 };
})();

在以上代碼中,主要可以分為兩個(gè)部分來進(jìn)行理解,第一部分為extendStatics(d, b)方法,第二部分為該方法后面的兩行代碼。

第一部分:

在extendStatics方法內(nèi)部雖然代碼量相對(duì)較多,但是不難發(fā)現(xiàn)其實(shí)還是主要為了兼容ES5版本的執(zhí)行環(huán)境。在ES6中新增了Object.setPrototypeOf方法用于手動(dòng)設(shè)置對(duì)象的原型,但是在ES5的環(huán)境中我們一般通過一個(gè)非標(biāo)準(zhǔn)的__proto__屬性來進(jìn)行設(shè)置,Object.setPrototypeOf方法的原理其實(shí)也是通過該屬性來設(shè)置對(duì)象的原型,其實(shí)現(xiàn)方式如下:

Object.setPrototypeOf = function(obj, proto) {
 obj.__proto__ = proto;
 return obj;
}

extendStatics(d, b)方法中,d指子類Child,b指父類Parent,因此該方法的作用可以解釋為:

// 將子類Child的__proto__屬性指向父類Parent
Child.__proto__ = Parent;

可以將這行代碼理解為構(gòu)造函數(shù)的繼承,或者叫靜態(tài)屬性和靜態(tài)方法的繼承,即屬性和方法不是掛載到構(gòu)造函數(shù)的prototype原型上的,而是直接掛載到構(gòu)造函數(shù)本身,因?yàn)樵贘S中函數(shù)本身也可以作為一個(gè)對(duì)象,并可以為其賦予任何其他的屬性,示例如下:

function Foo() {
 this.x = 1;
 this.y = 2;
}

Foo.bar = function() {
 console.log(3);
}

Foo.baz = 4;
console.log(Foo.bar()) // -> 3
console.log(Foo.baz) // -> 4

因此當(dāng)我們?cè)谧宇怌hild中以Child.someProperty訪問屬性時(shí),如果子類中不存在就會(huì)通過Child.__proto__尋找父類的同名屬性,通過這種方式來實(shí)現(xiàn)靜態(tài)屬性和靜態(tài)方法的路徑查找。

第二部分:

在第二部分中僅包含以下兩行代碼:

function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());

其中d指子類Child,b指父類Parent,這里對(duì)于JS中實(shí)現(xiàn)繼承的幾種方式比較熟悉的同學(xué)可以一眼看出,這里使用了寄生組合式繼承的方式,通過借用一個(gè)中間函數(shù)__()來避免當(dāng)修改子類的prototype上的方法時(shí)對(duì)父類的prototype所造成的影響。我們知道,在JS中通過構(gòu)造函數(shù)實(shí)例化一個(gè)對(duì)象之后,該對(duì)象會(huì)擁有一個(gè)__proto__屬性并指向其構(gòu)造函數(shù)的prototype屬性,示例如下:

function Foo() {
 this.x = 1;
 this.y = 2;
}

const foo = new Foo();
foo.__proto__ === Foo.prototype; // -> true

對(duì)于本例中,如果通過子類Child來實(shí)例化一個(gè)對(duì)象之后,會(huì)產(chǎn)生如下關(guān)聯(lián):

const child = new Child();
child.__proto__ === (Child.prototype = new __());
child.__proto__.__proto__ === __.prototype === Parent.prototype;

// 上述代碼等價(jià)于下面這種方式
Child.prototype.__proto__ === Parent.prototype;

因此當(dāng)我們?cè)谧宇怌hild的實(shí)例child對(duì)象中通過child.someMethod()調(diào)用某個(gè)方法時(shí),如果在實(shí)例中不存在該方法,則會(huì)沿著__proto__繼續(xù)往上查找,最終會(huì)經(jīng)過父類Parent的prototype原型,即通過這種方式來實(shí)現(xiàn)方法的繼承。

基于對(duì)以上兩個(gè)部分的分析,我們可以總結(jié)出以下兩點(diǎn):

// 表示構(gòu)造函數(shù)的繼承,或者叫做靜態(tài)屬性和靜態(tài)方法的繼承,總是指向父類
1. Child.__proto__ === Parent;

// 表示方法的繼承,總是指向父類的prototype屬性
2. Child.prototype.__proto__ === Parent.prototype;

2、訪問修飾符

TypeScript為我們提供了訪問修飾符(Access Modifiers)來限制在class外部對(duì)內(nèi)部屬性的訪問,訪問修飾符主要包含以下三種:

  • public:公共修飾符,其修飾的屬性和方法都是公有的,可以在任何地方被訪問到,默認(rèn)情況下所有屬性和方法都是public的。

  • private:私有修飾符,其修飾的屬性和方法在class外部不可見。

  • protected:受保護(hù)修飾符,和private比較相似,但是其修飾的屬性和方法在子類內(nèi)部是被允許訪問的。

我們通過一些示例來對(duì)幾種修飾符進(jìn)行對(duì)比:

class Human {
 public name: string;
 public age: number;
 public constructor(name: string, age: number) {
 this.name = name;
 this.age = age;
 }
}

const man = new Human('tom', 20);
console.log(man.name, man.age); // -> tom 20
man.age = 21;
console.log(man.age); // -> 21

在上述示例中,由于我們將訪問修飾符設(shè)置為public,因此我們通過實(shí)例man來訪問name和age屬性是被允許的,同時(shí)對(duì)age屬性重新賦值也是允許的。但是在某些情況下,我們希望某些屬性是對(duì)外不可見的,同時(shí)不允許被修改,那么我們就可以使用private修飾符:

class Human {
 public name: string;
 private age: number; // 此處修改為使用private修飾符
 public constructor(name: string, age: number) {
 this.name = name;
 this.age = age;
 }
}

const man = new Human('tom', 20);
console.log(man.name); // -> tom
console.log(man.age);
// -> Property 'age' is private and only accessible within class 'Human'.

我們將age屬性的修飾符修改為private后,在外部通過man.age對(duì)其進(jìn)行訪問,TypeScript在編譯階段就會(huì)發(fā)現(xiàn)其是一個(gè)私有屬性并最終將會(huì)報(bào)錯(cuò)。

注意:在TypeScript編譯之后的代碼中并沒有限制對(duì)私有屬性的存取操作。

編譯后的代碼如下:

var Human = /** @class */ (function () {
 function Human(name, age) {
 this.name = name;
 this.age = age;
 }
 return Human;
}());
var man = new Human('tom', 20);
console.log(man.name); // -> tom
console.log(man.age); // -> 20

使用private修飾符修飾的屬性或者方法在子類中也是不允許訪問的,示例如下:

class Human {
 public name: string;
 private age: number;
 public constructor(name: string, age: number) {
 this.name = name;
 this.age = age;
 }
}

class Woman extends Human {
 private gender: number = 0;
 public constructor(name: string, age: number) {
 super(name, age);
 console.log(this.age);
 }
}

const woman = new Woman('Alice', 18);
// -> Property 'age' is private and only accessible within class 'Human'.

在上述示例中由于在父類Human中age屬性被設(shè)置為private,因此在子類Woman中無法訪問到age屬性,為了讓在子類中允許訪問age屬性,我們可以使用protected修飾符來對(duì)其進(jìn)行修飾:

class Human {
 public name: string;
 protected age: number; // 此處修改為使用protected修飾符
 public constructor(name: string, age: number) {
 this.name = name;
 this.age = age;
 }
}

class Woman extends Human {
 private gender: number = 0;
 public constructor(name: string, age: number) {
 super(name, age);
 console.log(this.age);
 }
}

const woman = new Woman('Alice', 18); // -> 18

當(dāng)我們將private修飾符用于構(gòu)造函數(shù)時(shí),則表示該類不允許被繼承或?qū)嵗?,示例如下?/p>

class Human {
 public name: string;
 public age: number;
 
 // 此處修改為使用private修飾符
 private constructor(name: string, age: number) {
 this.name = name;
 this.age = age;
 }
}

class Woman extends Human {
 private gender: number = 0;
 public constructor(name: string, age: number) {
 super(name, age);
 }
}

const man = new Human('Alice', 18);
// -> Cannot extend a class 'Human'. Class constructor is marked as private.
// -> Constructor of class 'Human' is private and only accessible within the class declaration.

當(dāng)我們將protected修飾符用于構(gòu)造函數(shù)時(shí),則表示該類只允許被繼承,示例如下:

class Human {
 public name: string;
 public age: number;
 
 // 此處修改為使用protected修飾符
 protected constructor(name: string, age: number) {
 this.name = name;
 this.age = age;
 }
}

class Woman extends Human {
 private gender: number = 0;
 public constructor(name: string, age: number) {
 super(name, age);
 }
}

const man = new Human('Alice', 18);
// -> Constructor of class 'Human' is protected and only accessible within the class declaration.

另外我們還可以直接將修飾符放到構(gòu)造函數(shù)的參數(shù)中,示例如下:

class Human {
 // public name: string;
 // private age: number;
 
 public constructor(public name: string, private age: number) {
 this.name = name;
 this.age = age;
 }
}

const man = new Human('tom', 20);
console.log(man.name); // -> tom
console.log(man.age);
// -> Property 'age' is private and only accessible within class 'Human'.

3、接口與構(gòu)造器簽名

當(dāng)我們的項(xiàng)目中擁有很多不同的類時(shí)并且這些類之間可能存在某方面的共同點(diǎn),為了描述這種共同點(diǎn),我們可以將其提取到一個(gè)接口(interface)中用于集中維護(hù),并使用implements關(guān)鍵字來實(shí)現(xiàn)這個(gè)接口,示例如下:

interface IHuman {
 name: string;
 age: number;
 walk(): void;
}

class Human implements IHuman {
 
 public constructor(public name: string, public age: number) {
 this.name = name;
 this.age = age;
 }

 walk(): void {
 console.log('I am walking...');
 }
}

上述代碼在編譯階段能順利通過,但是我們注意到在Human類中包含constructor構(gòu)造函數(shù),如果我們想在接口中為該構(gòu)造函數(shù)定義一個(gè)簽名并讓Human類來實(shí)現(xiàn)這個(gè)接口,看會(huì)發(fā)生什么:

interface HumanConstructor {
 new (name: string, age: number); 
}

class Human implements HumanConstructor {
 
 public constructor(public name: string, public age: number) {
 this.name = name;
 this.age = age;
 }

 walk(): void {
 console.log('I am walking...');
 }
}
// -> Class 'Human' incorrectly implements interface 'HumanConstructor'.
// -> Type 'Human' provides no match for the signature 'new (name: string, age: number): any'.

然而TypeScript會(huì)編譯出錯(cuò),告訴我們錯(cuò)誤地實(shí)現(xiàn)了HumanConstructor接口,這是因?yàn)楫?dāng)一個(gè)類實(shí)現(xiàn)一個(gè)接口時(shí),只會(huì)對(duì)實(shí)例部分進(jìn)行編譯檢查,類的靜態(tài)部分是不會(huì)被編譯器檢查的。因此這里我們嘗試換種方式,直接操作類的靜態(tài)部分,示例如下:

interface HumanConstructor {
 new (name: string, age: number); 
}

interface IHuman {
 name: string;
 age: number;
 walk(): void;
}

class Human implements IHuman {
 
 public constructor(public name: string, public age: number) {
 this.name = name;
 this.age = age;
 }

 walk(): void {
 console.log('I am walking...');
 }
}

// 定義一個(gè)工廠方法
function createHuman(constructor: HumanConstructor, name: string, age: number): IHuman {
 return new constructor(name, age);
}

const man = createHuman(Human, 'tom', 18);
console.log(man.name, man.age); // -> tom 18

在上述示例中通過額外創(chuàng)建一個(gè)工廠方法createHuman并將構(gòu)造函數(shù)作為第一個(gè)參數(shù)傳入,此時(shí)當(dāng)我們調(diào)用createHuman(Human, 'tom', 18)時(shí)編譯器便會(huì)檢查第一個(gè)參數(shù)是否符合HumanConstructor接口的構(gòu)造器簽名。

4、聲明合并

在聲明合并中最常見的合并類型就是接口了,因此這里先從接口開始介紹幾種比較常見的合并方式。

4.1 接口合并

示例代碼如下:

interface A {
 name: string;
}

interface A {
 age: number;
}

// 等價(jià)于
interface A {
 name: string;
 age: number;
}

const a: A = {name: 'tom', age: 18};

接口合并的方式比較容易理解,即聲明多個(gè)同名的接口,每個(gè)接口中包含不同的屬性聲明,最終這些來自多個(gè)接口的屬性聲明會(huì)被合并到同一個(gè)接口中。

注意:所有同名接口中的非函數(shù)成員必須唯一,如果不唯一則必須保證類型相同,否則編譯器會(huì)報(bào)錯(cuò)。對(duì)于函數(shù)成員,后聲明的同名接口會(huì)覆蓋掉之前聲明的同名接口,即后聲明的同名接口中的函數(shù)相當(dāng)于一次重載,具有更高的優(yōu)先級(jí)。

4.2 函數(shù)合并

函數(shù)的合并可以簡(jiǎn)單理解為函數(shù)的重載,即通過同時(shí)定義多個(gè)不同類型參數(shù)或不同類型返回值的同名函數(shù)來實(shí)現(xiàn),示例代碼如下:

// 函數(shù)定義
function foo(x: number): number;
function foo(x: string): string;

// 函數(shù)具體實(shí)現(xiàn)
function foo(x: number | string): number | string {
 if (typeof x === 'number') {
 return (x).toFixed(2);
 }
 
 return x.substring(0, x.length - 1);
}

在上述示例中,我們對(duì)foo函數(shù)進(jìn)行多次定義,每次定義的函數(shù)參數(shù)類型不同,返回值類型不同,最后一次為函數(shù)的具體實(shí)現(xiàn),在實(shí)現(xiàn)中只有在兼容到前面的所有定義時(shí),編譯器才不會(huì)報(bào)錯(cuò)。

注意:TypeScript編譯器會(huì)優(yōu)先從最開始的函數(shù)定義進(jìn)行匹配,因此如果多個(gè)函數(shù)定義存在包含關(guān)系,則需要將最精確的函數(shù)定義放到最前面,否則將始終不會(huì)被匹配到。

4.3 類型別名聯(lián)合

類型別名聯(lián)合與接口合并有所區(qū)別,類型別名不會(huì)新建一個(gè)類型,只是創(chuàng)建一個(gè)新的別名來對(duì)多個(gè)類型進(jìn)行引用,同時(shí)不能像接口一樣被實(shí)現(xiàn)(implements)和繼承(extends),示例如下:

type HumanProperty = {
 name: string;
 age: number;
 gender: number;
};

type HumanBehavior = {
 eat(): void;
 walk(): void;
}

type Human = HumanProperty & HumanBehavior;

let woman: Human = {
 name: 'tom',
 age: 18,
 gender: 0,
 eat() {
 console.log('I can eat.');
 },
 walk() {
 console.log('I can walk.');
 }
}

class HumanComponent extends Human {
 constructor(public name: string, public age: number, public gender: number) {
 this.name = name;
 this.age = age;
 this.gender = gender;
 }
 
 eat() {
 console.log('I can eat.');
 }
 
 walk() {
 console.log('I can walk.');
 }
}
// -> 'Human' only refers to a type, but is being used as a value here.

5、keyof 索引查詢

在TypeScript中的keyof有點(diǎn)類似于JS中的Object.keys()方法,但是區(qū)別在于前者遍歷的是類型中的字符串索引,后者遍歷的是對(duì)象中的鍵名,示例如下:

interface Rectangle {
 x: number;
 y: number;
 width: number;
 height: number;
}

type keys = keyof Rectangle;
// 等價(jià)于
type keys = "x" | "y" | "width" | "height";

// 這里使用了泛型,強(qiáng)制要求第二個(gè)參數(shù)的參數(shù)名必須包含在第一個(gè)參數(shù)的所有字符串索引中
function getRectProperty<T extends object, K extends keyof T>(rect: T, property: K): T[K] {
 return rect[property];
}

let rect: Rectangle = {
 x: 50,
 y: 50,
 width: 100,
 height: 200
};

console.log(getRectProperty(rect, 'width')); // -> 100
console.log(getRectProperty(rect, 'notExist'));
// -> Argument of type '"notExist"' is not assignable to parameter of type '"width" | "x" | "y" | "height"'.

在上述示例中我們通過使用keyof來限制函數(shù)的參數(shù)名property必須被包含在類型Rectangle的所有字符串索引中,如果沒有被包含則編譯器會(huì)報(bào)錯(cuò),可以用來在編譯時(shí)檢測(cè)對(duì)象的屬性名是否書寫有誤。

6、Partial 可選屬性

在某些情況下,我們希望類型中的所有屬性都不是必需的,只有在某些條件下才存在,我們就可以使用Partial來將已聲明的類型中的所有屬性標(biāo)識(shí)為可選的,示例如下:

// 該類型已內(nèi)置在TypeScript中
type Partial<T> = {
 [P in keyof T]?: T[P]
};

interface Rectangle {
 x: number;
 y: number;
 width: number;
 height: number;
}

type PartialRectangle = Partial<Rectangle>;
// 等價(jià)于
type PartialRectangle = {
 x?: number;
 y?: number;
 width?: number;
 height?: number;
}

let rect: PartialRectangle = {
 width: 100,
 height: 200
};

在上述示例中由于我們使用Partial將所有屬性標(biāo)識(shí)為可選的,因此最終rect對(duì)象中雖然只包含width和height屬性,但是編譯器依舊沒有報(bào)錯(cuò),當(dāng)我們不能明確地確定對(duì)象中包含哪些屬性時(shí),我們就可以通過Partial來聲明。

7、Pick 部分選擇

在某些應(yīng)用場(chǎng)景下,我們可能需要從一個(gè)已聲明的類型中抽取出一個(gè)子類型,在子類型中包含父類型中的部分或全部屬性,這時(shí)我們可以使用Pick來實(shí)現(xiàn),示例代碼如下:

// 該類型已內(nèi)置在TypeScript中
type Pick<T, K extends keyof T> = {
 [P in K]: T[P]
};

interface User {
 id: number;
 name: string;
 age: number;
 gender: number;
 email: string;
}

type PickUser = Pick<User, "id" | "name" | "gender">;
// 等價(jià)于
type PickUser = {
 id: number;
 name: string;
 gender: number;
};

let user: PickUser = {
 id: 1,
 name: 'tom',
 gender: 1
};

在上述示例中,由于我們只關(guān)心user對(duì)象中的id,name和gender是否存在,其他屬性不做明確規(guī)定,因此我們就可以使用Pick從User接口中揀選出我們關(guān)心的屬性而忽略其他屬性的編譯檢查。

8、never 永不存在

never表示的是那些永不存在的值的類型,比如在函數(shù)中拋出異?;蛘邿o限循環(huán),never類型可以是任何類型的子類型,也可以賦值給任何類型,但是相反卻沒有一個(gè)類型可以作為never類型的子類型,示例如下:

// 函數(shù)拋出異常
function throwError(message: string): never {
 throw new Error(message);
}

// 函數(shù)自動(dòng)推斷出返回值為never類型
function reportError(message: string) {
 return throwError(message);
}

// 無限循環(huán)
function loop(): never {
 while(true) {
 console.log(1);
 }
}

// never類型可以是任何類型的子類型
let n: never;
let a: string = n;
let b: number = n;
let c: boolean = n;
let d: null = n;
let e: undefined = n;
let f: any = n;

// 任何類型都不能賦值給never類型
let a: string = '123';
let b: number = 0;
let c: boolean = true;
let d: null = null;
let e: undefined = undefined;
let f: any = [];

let n: never = a;
// -> Type 'string' is not assignable to type 'never'.

let n: never = b;
// -> Type 'number' is not assignable to type 'never'.

let n: never = c;
// -> Type 'true' is not assignable to type 'never'.

let n: never = d;
// -> Type 'null' is not assignable to type 'never'.

let n: never = e;
// -> Type 'undefined' is not assignable to type 'never'.

let n: never = f;
// -> Type 'any' is not assignable to type 'never'.

9、Exclude 屬性排除

與Pick相反,Pick用于揀選出我們需要關(guān)心的屬性,而Exclude用于排除掉我們不需要關(guān)心的屬性,示例如下:

// 該類型已內(nèi)置在TypeScript中
// 這里使用了條件類型(Conditional Type),和JS中的三目運(yùn)算符效果一致
type Exclude<T, U> = T extends U ? never : T;

interface User {
 id: number;
 name: string;
 age: number;
 gender: number;
 email: string;
}

type keys = keyof User; // -> "id" | "name" | "age" | "gender" | "email"

type ExcludeUser = Exclude<keys, "age" | "email">;
// 等價(jià)于
type ExcludeUser = "id" | "name" | "gender";

在上述示例中我們通過在ExcludeUser中傳入我們不需要關(guān)心的age和email屬性,Exclude會(huì)幫助我們將不需要的屬性進(jìn)行剔除,留下的屬性id,name和gender即為我們需要關(guān)心的屬性。一般來說,Exclude很少單獨(dú)使用,可以與其他類型配合實(shí)現(xiàn)更復(fù)雜更有用的功能。

10、Omit 屬性忽略

在上一個(gè)用法中,我們使用Exclude來排除掉其他不需要的屬性,但是在上述示例中的寫法耦合度較高,當(dāng)有其他類型也需要這樣處理時(shí),就必須再實(shí)現(xiàn)一遍相同的邏輯,不妨我們?cè)龠M(jìn)一步封裝,隱藏這些底層的處理細(xì)節(jié),只對(duì)外暴露簡(jiǎn)單的公共接口,示例如下:

// 使用Pick和Exclude組合實(shí)現(xiàn)
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

interface User {
 id: number;
 name: string;
 age: number;
 gender: number;
 email: string;
}

// 表示忽略掉User接口中的age和email屬性
type OmitUser = Omit<User, "age" | "email">;
// 等價(jià)于
type OmitUser = {
 id: number;
 name: string;
 gender: number;
};

let user: OmitUser = {
 id: 1,
 name: 'tom',
 gender: 1
};

看完上述內(nèi)容,你們對(duì)怎么使用TypeScript有進(jìn)一步的了解嗎?如果還想了解更多知識(shí)或者相關(guān)內(nèi)容,請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝大家的支持。

文章標(biāo)題:怎么使用TypeScript
本文路徑:http://muchs.cn/article0/jopdio.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站收錄、外貿(mào)建站、網(wǎng)頁設(shè)計(jì)公司、網(wǎng)站營(yíng)銷、網(wǎng)站排名、微信小程序

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

h5響應(yīng)式網(wǎng)站建設(shè)