TypeScript相關(guān)知識(shí)

使用 TypeScript 的好處


JavaScript 已經(jīng)很棒了,你或許會(huì)懷疑,我真的需要學(xué)習(xí) TypeScript 嗎?從技術(shù)層面上來(lái)說(shuō),成為一位出色的開(kāi)發(fā)者確實(shí)不需要學(xué)習(xí) TypeScript,大多數(shù)人沒(méi)有學(xué)習(xí) TypeScript 也做的很好。但是,工作中使用 TypeScript 確實(shí)有許多好處:

  • 基于靜態(tài)類(lèi)型,用 TypeScript 編輯代碼有更高的預(yù)測(cè)性,更易糾錯(cuò)。
  • 由于模塊,命名空間和強(qiáng)大的面向?qū)ο缶幊讨С?,使?gòu)建大型復(fù)雜應(yīng)用程序的代碼庫(kù)更加容易。
  • TypeScript在編譯為JavaScript的過(guò)程中,在它到達(dá)運(yùn)行時(shí)間前可以捕獲所有類(lèi)型的錯(cuò)誤,并中斷它們的執(zhí)行。
  • Angular 2 框架就是用 TypeScript 編寫(xiě)的,同時(shí)推薦開(kāi)發(fā)人員在項(xiàng)目中也使用這種語(yǔ)言。

安裝TypeScript


安裝 TypeScript 最簡(jiǎn)單的方式就是通過(guò) npm。使用以下命令行,可以全局安裝 TypeScript 包,然后就可以在所有項(xiàng)目中使用TypeScript編譯器了:

npm install -g typescript

打開(kāi)終端然后運(yùn)行 tsc -v 命令來(lái)查看是否正確安裝了 TypeScript.

tsv -v

Version 3.8.1

第一個(gè)例子


新建一個(gè) greeter.ts文件

function greeter(person) {
    return "Hello, " + person;
}

let user = "Jane User";

document.body.innerHTML = greeter(user);

編譯成 JavaScript


TypeScript 是 寫(xiě)在 .ts 文件(或者 JSX的.tsx)里,不能直接在瀏覽器端運(yùn)行,需要首先轉(zhuǎn)譯為vanilla.js。這個(gè)編譯的過(guò)程可以有多種實(shí)現(xiàn)方式:

  • 在終端上運(yùn)行前面提到的命令行工具 tsc
  • 直接在 Visual Studio 或者其他 IDE 和文本編輯器上(操作)。
  • 使用自動(dòng)化構(gòu)建工具,
tsc greeter.ts

*這行命令是把 TypeScript 文件 index.ts編譯為 JavaScript 版本的 greeter.js。如果 greeter.js 已經(jīng)存在的話會(huì)被覆蓋。

也可以通過(guò)列出所有的文件或者使用通配符來(lái)一次編譯多個(gè)文件:

#Will result in separate .js files: main.js worker.js.
 
tsc main.ts worker.ts
 
#Compiles all .ts files in the current folder. Does NOT work recursively.
 
tsc *.ts

靜態(tài)類(lèi)型


TypeScript 一個(gè)很獨(dú)特的特征是支持靜態(tài)類(lèi)型。意思就是可以聲明變量的類(lèi)型,(因此)編譯器就可以確保賦值時(shí)不會(huì)產(chǎn)生類(lèi)型錯(cuò)誤。如果省略了類(lèi)型聲明,TypeScript 將會(huì)從代碼中自動(dòng)推測(cè)出正確的類(lèi)型。

Number

//number
let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;

String

let color: string = "blue";
color = 'red';

也可以混合使用

let fullName: string = `Bob Bobbington`;
let age: number = 37;
let sentence: string = `Hello, my name is ${ fullName }.

I'll be ${ age + 1 } years old next month.`;

Array

let list: number[] = [1, 2, 3];

let list: Array<number> = [1, 2, 3];

Tuple

可以在一個(gè)數(shù)組中分別定義數(shù)據(jù)的類(lèi)型

// Declare a tuple type
let x: [string, number];
// Initialize it
x = ["hello", 10]; // OK
// Initialize it incorrectly
x = [10, "hello"]; // Error

Enum

來(lái)自JavaScript的標(biāo)準(zhǔn)數(shù)據(jù)類(lèi)型集的一種有用的補(bǔ)充則是枚舉。就像C#這樣的語(yǔ)言中,枚舉是一種為數(shù)值集提供更友好名稱(chēng)的方法。

enum Color {Red, Green, Blue}
let c: Color = Color.Green;
enum Color {Red = 1, Green = 2, Blue = 4}
let c: Color = Color.Green;

枚舉的一個(gè)便利的功能是,您還可以從數(shù)值轉(zhuǎn)到枚舉中該值的名稱(chēng)。例如,如果我們的值為2,但不確定上面的Color枚舉中映射到了什么,我們可以查找相應(yīng)的名稱(chēng):

enum Color {Red = 1, Green, Blue}
let colorName: string = Color[2];

console.log(colorName); // Displays 'Green' as its value is 2 above

Any

let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean
let notSure: any = 4;
notSure.ifItExists(); // okay, ifItExists might exist at runtime
notSure.toFixed(); // okay, toFixed exists (but the compiler doesn't check)

let prettySure: Object = 4;
prettySure.toFixed(); // Error: Property 'toFixed' doesn't exist on type 'Object'.

如果你知道這種類(lèi)型的某些部分,那么任何類(lèi)型都很方便,但也有例外情況。例如,您可能有一個(gè)數(shù)組,但該數(shù)組有不同類(lèi)型的混合:

let list: any[] = [1, true, "free"];

list[1] = 100;

Void

function warnUser(): void {
    console.log("This is my warning message");
}

Null and Undefined

// Not much else we can assign to these variables!
let u: undefined = undefined;
let n: null = null;

Never

// Function returning never must have unreachable end point
function error(message: string): never {
    throw new Error(message);
}

// Inferred return type is never
function fail() {
    return error("Something failed");
}

// Function returning never must have unreachable end point
function infiniteLoop(): never {
    while (true) {
    }
}

以下是一些最常用的數(shù)據(jù)類(lèi)型:

  • Number (數(shù)值類(lèi)型) – 所有數(shù)字都是數(shù)值類(lèi)型的,無(wú)論是整數(shù)、浮點(diǎn)型或者其他數(shù)值類(lèi)型都相同。
  • String(字符串類(lèi)型) – 文本類(lèi)型,就如 vanilla JS 字符串一樣可以使用單引號(hào)或者雙引號(hào)。
  • Boolean(布爾類(lèi)型) – true 或者 false,用 0 和 1 會(huì)造成編譯錯(cuò)誤。
  • Tuple (元組)– 元組類(lèi)型允許表示一個(gè)已知元素?cái)?shù)量和類(lèi)型的數(shù)組,各元素的類(lèi)型不必相同
  • Any(任意類(lèi)型) – 該類(lèi)型的變量可以設(shè)定為字符串類(lèi)型,數(shù)值類(lèi)型或者任何其他類(lèi)型。
  • Enum (枚舉) - 對(duì)JavaScript標(biāo)準(zhǔn)數(shù)據(jù)類(lèi)型的一個(gè)補(bǔ)充。 像C#等其它語(yǔ)言一樣,使用枚舉類(lèi)型可以為一組數(shù)值賦予友好的名字。
  • Arrays(數(shù)組類(lèi)型) – 有兩種語(yǔ)法:my_arr: number[];或者my_arr: Array<number>
  • Void (空類(lèi)型)- 用在不返回任何值的函數(shù)中。
  • Null 和 Undefined - TypeScript里,undefined和null兩者各自有自己的類(lèi)型分別叫做undefined和null。 和 void相似,它們的本身的類(lèi)型用處不是很大。
  • Never - 表示的是那些永不存在的值的類(lèi)型。

接口通常會(huì)根據(jù)一個(gè)對(duì)象是否符合某種特定結(jié)構(gòu)來(lái)進(jìn)行類(lèi)型檢查。通過(guò)定義一個(gè)接口我們可以命名一個(gè)特殊的組合變量,確保它們會(huì)一直一起運(yùn)行。當(dāng)轉(zhuǎn)譯成 JavaScript 時(shí),接口會(huì)消失 – 它們唯一的目的是在開(kāi)發(fā)階段里起到輔助的作用。

在下面的例子中我們定義了一個(gè)簡(jiǎn)單的接口來(lái)對(duì)一個(gè)函數(shù)自變量進(jìn)行類(lèi)型檢查:

// Here we define our Food interface, its properties, and their types.
interface Food {
    name: string;
    calories: number;
}
 
// We tell our function to expect an object that fulfills the Food interface. 
// This way we know that the properties we need will always be available.
function speak(food: Food): void{
  console.log("Our " + food.name + " has " + food.calories + " calories.");
}
 
// We define an object that has all of the properties the Food interface expects.
// Notice that types will be inferred automatically.
var ice_cream = {
  name: "ice cream", 
  calories: 200
}
 
speak(ice_cream);

類(lèi)


在搭建大型規(guī)模的應(yīng)用程序時(shí),尤其是在 Java 或 C# 當(dāng)中,許多開(kāi)發(fā)者會(huì)優(yōu)先選擇面向?qū)ο缶幊?。TypeScript 提供一個(gè)類(lèi)系統(tǒng),和 Java、C# 中的非常相似,包括了繼承,抽象類(lèi),接口實(shí)現(xiàn),setters/getters 方法等。

值得一提的是由于最新的 JavaScript 更新(ECMAScript 2015),這些類(lèi)對(duì)于 vanilla JS 來(lái)說(shuō)是原生的,并且在沒(méi)有 TypeScript 的情況下也可以使用。這兩種實(shí)現(xiàn)方式非常相似但是也有不同的地方,TypeScript 更加嚴(yán)格一些。

繼續(xù)上面的 food的 例子,這里有一個(gè)簡(jiǎn)單的TypeScript類(lèi):

class Menu {
  // Our properties:
  // By default they are public, but can also be private or protected.
  items: Array<string>;  // The items in the menu, an array of strings.
  pages: number;         // How many pages will the menu be, a number.
 
  // A straightforward constructor. 
  constructor(item_list: Array<string>, total_pages: number) {
    // The this keyword is mandatory.
    this.items = item_list;    
    this.pages = total_pages;
  }
 
  // Methods
  list(): void {
    console.log("Our menu for today:");
    for(var i=0; i<this.items.length; i++) {
      console.log(this.items[i]);
    }
  }
 
} 
 
// Create a new instance of the Menu class.
var sundayMenu = new Menu(["pancakes","waffles","orange juice"], 1);
 
// Call the list method.
sundayMenu.list();

只要寫(xiě)過(guò)一點(diǎn) Java 或者 C# ,就會(huì)發(fā)現(xiàn)TypeScript和它們?cè)谡Z(yǔ)法上非常相似。繼承也是一樣:

class HappyMeal extends Menu {
  // Properties are inherited
 
  // A new constructor has to be defined.
  constructor(item_list: Array<string>, total_pages: number) {
    // In this case we want the exact same constructor as the parent class (Menu), 
    // To automatically copy it we can call super() - a reference to the parent's constructor.
    super(item_list, total_pages);
  }
 
  // Just like the properties, methods are inherited from the parent.
  // However, we want to override the list() function so we redefine it.
  list(): void{
    console.log("Our special menu for children:");
    for(var i=0; i<this.items.length; i++) {
      console.log(this.items[i]);
    }
 
  }
}
 
// Create a new instance of the HappyMeal class.
var menu_for_children = new HappyMeal(["candy","drink","toy"], 1);
 
// This time the log message will begin with the special introduction.
menu_for_children.list();

泛型


泛型(Generics)是允許同一個(gè)函數(shù)接受不同類(lèi)型參數(shù)的一種模板。相比于使用 any 類(lèi)型,使用泛型來(lái)創(chuàng)建可復(fù)用的組件要更好,因?yàn)榉盒蜁?huì)保留參數(shù)類(lèi)型。

一段簡(jiǎn)單的腳本例子,傳入一個(gè)參數(shù),返回一個(gè)包含了同樣參數(shù)的數(shù)組。

// The <T> after the function name symbolizes that it's a generic function.
// When we call the function, every instance of T will be replaced with the actual provided type.
 
// Receives one argument of type T,
// Returns an array of type T.
 
function genericFunc<T>(argument: T): T[] {    
  var arrayOfT: T[] = [];    // Create empty array of type T.
  arrayOfT.push(argument);   // Push, now arrayOfT = [argument].
  return arrayOfT;
}
 
var arrayFromString = genericFunc<string>("beep");
console.log(arrayFromString[0]);         // "beep"
console.log(typeof arrayFromString[0])   // String
 
var arrayFromNumber = genericFunc(42);
console.log(arrayFromNumber[0]);         // 42
console.log(typeof arrayFromNumber[0])   // number

第一次調(diào)用函數(shù)的時(shí)候,我們將類(lèi)型手動(dòng)設(shè)置成字符串。第二次及以后再次調(diào)用的時(shí)候就不必這樣做了,因?yàn)榫幾g器會(huì)判斷傳遞過(guò)什么參數(shù)并且自動(dòng)決定哪種類(lèi)型最適合。雖然不是強(qiáng)制性的,但是由于編譯器在眾多復(fù)雜環(huán)境中確定正確類(lèi)型的時(shí)候可能會(huì)失敗,所以每次都傳入類(lèi)型是好的做法。


在開(kāi)發(fā)大型應(yīng)用時(shí),另一個(gè)重要的概念是模塊化。與一個(gè)有 10000 行代碼的文件相比,把代碼分成多個(gè)可復(fù)用組件,這樣可以幫助項(xiàng)目保持條理性和易懂性。

TypeScript 介紹了導(dǎo)入和導(dǎo)出模塊的語(yǔ)句,但是并不能解決文件間的真正連接。TypeScript 依賴于第三方函數(shù)庫(kù)來(lái)加載外部模塊:用于瀏覽器應(yīng)用程序的 require.js 和用于 Node.js 的 CommonJS。我們來(lái)看一個(gè)簡(jiǎn)單的帶有 require.js 的TypeScript 模塊例子:

我們會(huì)有兩個(gè)文件。一個(gè)是導(dǎo)出函數(shù),另一個(gè)是導(dǎo)入并調(diào)用函數(shù)。

exporter.ts

var sayHi = function(): void {
    console.log("Hello!");
}
 
export = sayHi;

importer.ts

import sayHi = require('./exporter');
sayHi();

現(xiàn)在我們需要下載 require.js,包含在一個(gè)script標(biāo)簽里 – 如何設(shè)置請(qǐng)點(diǎn)擊這里。最后一步是編譯這兩個(gè) .ts 文件。需要添加一個(gè)額外的參數(shù)來(lái)告訴 TypeScript,我們是為 require.js 創(chuàng)建模塊的(也被稱(chēng)為AMD),而不是 CommonJS。

tsc --module amd *.ts

以上就是有關(guān)TypeScript的基本知識(shí)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容