typescript快速上手的進(jìn)階類型與技術(shù)
本文講述了typescript
開發(fā)的一些高級的類型與技術(shù),算是對于基礎(chǔ)知識點(diǎn)的補(bǔ)充,具體內(nèi)容包括:比如元組、枚舉類、接口、泛型相關(guān)概念等。雖說是進(jìn)階,但是內(nèi)容不算多也并不難理解。
類型別名
類型別名用來給一個(gè)類型起個(gè)新名字。
type Name = string; type NameResolver = () => string; type NameOrResolver = Name | NameResolver; function getName(n: NameOrResolver): Name { if (typeof n === 'string') { return n; } else { return n(); } }
上例中,我們使用 type
創(chuàng)建類型別名。
類型別名常用于聯(lián)合類型。
字符串字面量類型
字符串字面量類型用來約束取值只能是某幾個(gè)字符串中的一個(gè)。
type EventNames = 'click' | 'scroll' | 'mousemove'; function handleEvent(ele: Element, event: EventNames) { // do something } handleEvent(document.getElementById('hello'), 'scroll'); // 沒問題 handleEvent(document.getElementById('world'), 'dblclick'); // 報(bào)錯(cuò),event 不能為 'dblclick' // index.ts(7,47): error TS2345: Argument of type '"dblclick"' is not assignable to parameter of type 'EventNames'.
上例中,我們使用 type
定了一個(gè)字符串字面量類型 EventNames
,它只能取三種字符串中的一種。
注意,類型別名與字符串字面量類型都是使用 type
進(jìn)行定義。
元組
數(shù)組合并了相同類型的對象,而元組(Tuple)合并了不同類型的對象。
元組起源于函數(shù)編程語言(如 F#),這些語言中會(huì)頻繁使用元組。
定義一對值分別為 string
和 number
的元組:
let tom: [string, number] = ['Tom', 25];
當(dāng)賦值或訪問一個(gè)已知索引的元素時(shí),會(huì)得到正確的類型:
let tom: [string, number]; tom[0] = 'Tom'; tom[1] = 25; tom[0].slice(1); tom[1].toFixed(2);
也可以只賦值其中一項(xiàng):
let tom: [string, number]; tom[0] = 'Tom';
但是當(dāng)直接對元組類型的變量進(jìn)行初始化或者賦值的時(shí)候,需要提供所有元組類型中指定的項(xiàng)。
let tom: [string, number]; tom = ['Tom', 25];
let tom: [string, number]; tom = ['Tom']; // Property '1' is missing in type '[string]' but required in type '[string, number]'.
越界的元素
當(dāng)添加越界的元素時(shí),它的類型會(huì)被限制為元組中每個(gè)類型的聯(lián)合類型:
let tom: [string, number]; tom = ['Tom', 25]; tom.push('male'); tom.push(true); // Argument of type 'true' is not assignable to parameter of type 'string | number'.
枚舉
枚舉(Enum)類型用于取值被限定在一定范圍內(nèi)的場景,比如一周只能有七天,顏色限定為紅綠藍(lán)等。
枚舉使用 enum
關(guān)鍵字來定義:
enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};
枚舉成員會(huì)被賦值為從 0
開始遞增的數(shù)字,同時(shí)也會(huì)對枚舉值到枚舉名進(jìn)行反向映射:
enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat}; console.log(Days["Sun"] === 0); // true console.log(Days["Mon"] === 1); // true console.log(Days["Tue"] === 2); // true console.log(Days["Sat"] === 6); // true console.log(Days[0] === "Sun"); // true console.log(Days[1] === "Mon"); // true console.log(Days[2] === "Tue"); // true console.log(Days[6] === "Sat"); // true
事實(shí)上,上面的例子會(huì)被編譯為:
var Days; (function (Days) { Days[Days["Sun"] = 0] = "Sun"; Days[Days["Mon"] = 1] = "Mon"; Days[Days["Tue"] = 2] = "Tue"; Days[Days["Wed"] = 3] = "Wed"; Days[Days["Thu"] = 4] = "Thu"; Days[Days["Fri"] = 5] = "Fri"; Days[Days["Sat"] = 6] = "Sat"; })(Days || (Days = {}));
手動(dòng)賦值
我們也可以給枚舉項(xiàng)手動(dòng)賦值:
enum Days {Sun = 7, Mon = 1, Tue, Wed, Thu, Fri, Sat}; console.log(Days["Sun"] === 7); // true console.log(Days["Mon"] === 1); // true console.log(Days["Tue"] === 2); // true console.log(Days["Sat"] === 6); // true
上面的例子中,未手動(dòng)賦值的枚舉項(xiàng)會(huì)接著上一個(gè)枚舉項(xiàng)遞增。
類
傳統(tǒng)方法中,JavaScript 通過構(gòu)造函數(shù)實(shí)現(xiàn)類的概念,通過原型鏈實(shí)現(xiàn)繼承。而在 ES6 中,我們終于迎來了 class
。
TypeScript 除了實(shí)現(xiàn)了所有 ES6 中的類的功能以外,還添加了一些新的用法。
類的概念
雖然 JavaScript 中有類的概念,但是可能大多數(shù) JavaScript 程序員并不是非常熟悉類,這里對類相關(guān)的概念做一個(gè)簡單的介紹。
- 類(Class):定義了一件事物的抽象特點(diǎn),包含它的屬性和方法
- 對象(Object):類的實(shí)例,通過
new
生成 - 面向?qū)ο螅∣OP)的三大特性:封裝、繼承、多態(tài)
- 封裝(Encapsulation):將對數(shù)據(jù)的操作細(xì)節(jié)隱藏起來,只暴露對外的接口。外界調(diào)用端不需要(也不可能)知道細(xì)節(jié),就能通過對外提供的接口來訪問該對象,同時(shí)也保證了外界無法任意更改對象內(nèi)部的數(shù)據(jù)
- 繼承(Inheritance):子類繼承父類,子類除了擁有父類的所有特性外,還有一些更具體的特性
- 多態(tài)(Polymorphism):由繼承而產(chǎn)生了相關(guān)的不同的類,對同一個(gè)方法可以有不同的響應(yīng)。比如
Cat
和Dog
都繼承自Animal
,但是分別實(shí)現(xiàn)了自己的eat
方法。此時(shí)針對某一個(gè)實(shí)例,我們無需了解它是Cat
還是Dog
,就可以直接調(diào)用eat
方法,程序會(huì)自動(dòng)判斷出來應(yīng)該如何執(zhí)行eat
- 存取器(getter & setter):用以改變屬性的讀取和賦值行為
- 修飾符(Modifiers):修飾符是一些關(guān)鍵字,用于限定成員或類型的性質(zhì)。比如
public
表示公有屬性或方法 - 抽象類(Abstract Class):抽象類是供其他類繼承的基類,抽象類不允許被實(shí)例化。抽象類中的抽象方法必須在子類中被實(shí)現(xiàn)
- 接口(Interfaces):不同類之間公有的屬性或方法,可以抽象成一個(gè)接口。接口可以被類實(shí)現(xiàn)(implements)。一個(gè)類只能繼承自另一個(gè)類,但是可以實(shí)現(xiàn)多個(gè)接口
TypeScript 中類的用法
public private 和 protected
TypeScript 可以使用三種訪問修飾符(Access Modifiers),分別是 public
、private
和 protected
。
public
修飾的屬性或方法是公有的,可以在任何地方被訪問到,默認(rèn)所有的屬性和方法都是public
的private
修飾的屬性或方法是私有的,不能在聲明它的類的外部訪問protected
修飾的屬性或方法是受保護(hù)的,它和private
類似,區(qū)別是它在子類中也是允許被訪問的
下面舉一些例子:
class Animal { public name; public constructor(name) { this.name = name; } } let a = new Animal('Jack'); console.log(a.name); // Jack a.name = 'Tom'; console.log(a.name); // Tom
參數(shù)屬性
修飾符和readonly
還可以使用在構(gòu)造函數(shù)參數(shù)中,等同于類中定義該屬性同時(shí)給該屬性賦值,使代碼更簡潔。
class Animal { // public name: string; public constructor(public name) { // this.name = name; } }
readonly
只讀屬性關(guān)鍵字,只允許出現(xiàn)在屬性聲明或索引簽名或構(gòu)造函數(shù)中。
class Animal { readonly name; public constructor(name) { this.name = name; } } let a = new Animal('Jack'); console.log(a.name); // Jack a.name = 'Tom'; // index.ts(10,3): TS2540: Cannot assign to 'name' because it is a read-only property.
注意如果 readonly
和其他訪問修飾符同時(shí)存在的話,需要寫在其后面。
class Animal { // public readonly name; public constructor(public readonly name) { // this.name = name; } }
抽象類
abstract
用于定義抽象類和其中的抽象方法。
abstract class Animal { public name; public constructor(name) { this.name = name; } public abstract sayHi(); } class Cat extends Animal { public sayHi() { console.log(`Meow, My name is ${this.name}`); } } let cat = new Cat('Tom');
上面的例子中,我們實(shí)現(xiàn)了抽象方法 sayHi
,編譯通過了。
需要注意的是,即使是抽象方法,TypeScript 的編譯結(jié)果中,仍然會(huì)存在這個(gè)類,上面的代碼的編譯結(jié)果是:
var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : ((__.prototype = b.prototype), new __()); }; var Animal = (function () { function Animal(name) { this.name = name; } return Animal; })(); var Cat = (function (_super) { __extends(Cat, _super); function Cat() { _super.apply(this, arguments); } Cat.prototype.sayHi = function () { console.log('Meow, My name is ' + this.name); }; return Cat; })(Animal); var cat = new Cat('Tom');
類的類型
給類加上 TypeScript 的類型很簡單,與接口類似:
class Animal { name: string; constructor(name: string) { this.name = name; } sayHi(): string { return `My name is ${this.name}`; } } let a: Animal = new Animal('Jack'); console.log(a.sayHi()); // My name is Jack
類與接口
實(shí)現(xiàn)(implements)是面向?qū)ο笾械囊粋€(gè)重要概念。一般來講,一個(gè)類只能繼承自另一個(gè)類,有時(shí)候不同類之間可以有一些共有的特性,這時(shí)候就可以把特性提取成接口(interfaces),用 implements
關(guān)鍵字來實(shí)現(xiàn)。這個(gè)特性大大提高了面向?qū)ο蟮撵`活性。
舉例來說,門是一個(gè)類,防盜門是門的子類。如果防盜門有一個(gè)報(bào)警器的功能,我們可以簡單的給防盜門添加一個(gè)報(bào)警方法。這時(shí)候如果有另一個(gè)類,車,也有報(bào)警器的功能,就可以考慮把報(bào)警器提取出來,作為一個(gè)接口,防盜門和車都去實(shí)現(xiàn)它:
interface Alarm { alert(): void; } class Door { } class SecurityDoor extends Door implements Alarm { alert() { console.log('SecurityDoor alert'); } } class Car implements Alarm { alert() { console.log('Car alert'); } }
一個(gè)類可以實(shí)現(xiàn)多個(gè)接口:
interface Alarm { alert(): void; } interface Light { lightOn(): void; lightOff(): void; } class Car implements Alarm, Light { alert() { console.log('Car alert'); } lightOn() { console.log('Car light on'); } lightOff() { console.log('Car light off'); } }
上例中,Car
實(shí)現(xiàn)了 Alarm
和 Light
接口,既能報(bào)警,也能開關(guān)車燈。
泛型
泛型(Generics)是指在定義函數(shù)、接口或類的時(shí)候,不預(yù)先指定具體的類型,而在使用的時(shí)候再指定類型的一種特性。
首先,我們來實(shí)現(xiàn)一個(gè)函數(shù) createArray
,它可以創(chuàng)建一個(gè)指定長度的數(shù)組,同時(shí)將每一項(xiàng)都填充一個(gè)默認(rèn)值:
function createArray(length: number, value: any): Array<any> { let result = []; for (let i = 0; i < length; i++) { result[i] = value; } return result; } createArray(3, 'x'); // ['x', 'x', 'x']
這段代碼編譯不會(huì)報(bào)錯(cuò),但是一個(gè)顯而易見的缺陷是,它并沒有準(zhǔn)確的定義返回值的類型:
Array<any>
允許數(shù)組的每一項(xiàng)都為任意類型。但是我們預(yù)期的是,數(shù)組中每一項(xiàng)都應(yīng)該是輸入的 value
的類型。
這時(shí)候,泛型就派上用場了:
function createArray<T>(length: number, value: T): Array<T> { let result: T[] = []; for (let i = 0; i < length; i++) { result[i] = value; } return result; } createArray<string>(3, 'x'); // ['x', 'x', 'x']
上例中,我們在函數(shù)名后添加了 <T>
,其中 T
用來指代任意輸入的類型,在后面的輸入 value: T
和輸出 Array<T>
中即可使用了。
泛型類
與泛型接口類似,泛型也可以用于類的類型定義中:
class GenericNumber<T> { zeroValue: T; add: (x: T, y: T) => T; } let myGenericNumber = new GenericNumber<number>(); myGenericNumber.zeroValue = 0; myGenericNumber.add = function(x, y) { return x + y; };
泛型參數(shù)的默認(rèn)類型
在 TypeScript 2.3 以后,我們可以為泛型中的類型參數(shù)指定默認(rèn)類型。當(dāng)使用泛型時(shí)沒有在代碼中直接指定類型參數(shù),從實(shí)際值參數(shù)中也無法推測出時(shí),這個(gè)默認(rèn)類型就會(huì)起作用。
function createArray<T = string>(length: number, value: T): Array<T> { let result: T[] = []; for (let i = 0; i < length; i++) { result[i] = value; } return result; }
聲明合并
如果定義了兩個(gè)相同名字的函數(shù)、接口或類,那么它們會(huì)合并成一個(gè)類型:
函數(shù)的合并
我們可以使用重載定義多個(gè)函數(shù)類型:
function reverse(x: number): number; function reverse(x: string): string; function reverse(x: number | string): number | string { if (typeof x === 'number') { return Number(x.toString().split('').reverse().join('')); } else if (typeof x === 'string') { return x.split('').reverse().join(''); } }
接口的合并
接口中的屬性在合并時(shí)會(huì)簡單的合并到一個(gè)接口中:
interface Alarm { price: number; } interface Alarm { weight: number; }
相當(dāng)于:
interface Alarm { price: number; weight: number; }
到此這篇關(guān)于typescript快速上手的進(jìn)階類型與技術(shù)的文章就介紹到這了,更多相關(guān)typescript進(jìn)階技術(shù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
前端算法之TypeScript包含min函數(shù)的棧實(shí)例詳解
這篇文章主要為大家介紹了前端算法之TypeScript包含min函數(shù)的棧實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09TypeScript快速學(xué)習(xí)入門基礎(chǔ)語法
TypeScript的基礎(chǔ)語法,包括變量聲明、復(fù)合類型(數(shù)組和對象)、條件控制(if-else和switch)、循環(huán)(for和while)、函數(shù)(基礎(chǔ)和箭頭函數(shù),以及可選參數(shù))、面向?qū)ο筇匦裕杜e、接口、繼承)以及模塊開發(fā)中的導(dǎo)出和導(dǎo)入2024-07-07TypeScript Nim交替使用細(xì)節(jié)分析
這篇文章主要為大家介紹了TypeScript Nim交替使用細(xì)節(jié)分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08type challenge刷題之(middle 部分)示例解析
這篇文章主要為大家介紹了type challenge刷題之(middle 部分)示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08ThreeJS使用紋理貼圖創(chuàng)建一個(gè)我的世界草地方塊
這篇文章主要為大家介紹了ThreeJS使用紋理貼圖創(chuàng)建一個(gè)我的世界草地方塊的實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06