typescript快速上手的進(jìn)階類型與技術(shù)
本文講述了typescript開發(fā)的一些高級(jí)的類型與技術(shù),算是對于基礎(chǔ)知識(shí)點(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-09
TypeScript類型級(jí)別和值級(jí)別示例詳解
這篇文章主要為大家介紹了TypeScript類型級(jí)別和值級(jí)別示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02
TypeScript快速學(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-07
TypeScript Nim交替使用細(xì)節(jié)分析
這篇文章主要為大家介紹了TypeScript Nim交替使用細(xì)節(jié)分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08
type challenge刷題之(middle 部分)示例解析
這篇文章主要為大家介紹了type challenge刷題之(middle 部分)示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08
ThreeJS使用紋理貼圖創(chuàng)建一個(gè)我的世界草地方塊
這篇文章主要為大家介紹了ThreeJS使用紋理貼圖創(chuàng)建一個(gè)我的世界草地方塊的實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06

