當前位置:
首頁 > 知識 > 從 JavaScript 到 TypeScript

從 JavaScript 到 TypeScript


本文首發在我的個人博客:http://muyunyun.cn/posts/66a54fc2/

文中的案例代碼已經上傳到 TypeScript

TypeScript 並不是一個完全新的語言, 它是 JavaScript 的超集,為 JavaScript 的生態增加了類型機制,並最終將代碼編譯為純粹的 JavaScript 代碼。

TypeScript 簡介

TypeScript 由 Microsoft(算上 Angular 2 的話加上 Google)開發和維護的一種開源編程語言。 它支持 JavaScript 的所有語法和語義,同時通過作為 ECMAScript 的超集來提供一些額外的功能,如類型檢測和更豐富的語法。下圖顯示了 TypeScript 與 ES5,ES2015,ES2016 之間的關係。

從 JavaScript 到 TypeScript

使用 TypeScript 的原因

JavaScript 是一門弱類型語言,變數的數據類型具有動態性,只有執行時才能確定變數的類型,這種後知後覺的認錯方法會讓開發者成為調試大師,但無益於編程能力的提升,還會降低開發效率。TypeScript 的類型機制可以有效杜絕由變數類型引起的誤用問題,而且開發者可以控制對類型的監控程度,是嚴格限制變數類型還是寬鬆限制變數類型,都取決於開發者的開發需求。添加類型機制之後,副作用主要有兩個:增大了開發人員的學習曲線,增加了設定類型的開發時間。總體而言,這些付出相對於代碼的健壯性和可維護性,都是值得的。

此外,類型注釋是 TypeScript 的內置功能之一,允許文本編輯器和 IDE 可以對我們的代碼執行更好的靜態分析。 這意味著我們可以通過自動編譯工具的幫助,在編寫代碼時減少錯誤,從而提高我們的生產力。

對 TypeScript 的簡介到此,接下來對其特有的知識點進行簡單概括總結,(網上很多教程實際上把 ES6, ES7 的知識點也算進 ts 的知識點了,當然這沒錯~)

數據類型String 類型

一個保存字元串的文本,類型聲明為 string。可以發現類型聲明可大寫也可小寫,後文同理。

let name: string = "muyy"
let name2: String = "muyy"

Boolen 類型

boolean是 true 或 false 的值,所以 let isBool3: boolean = new Boolean(1)就會編譯報錯,因為 new Boolean(1) 生成的是一個 Bool 對象。

let isBool1: boolean = false

Number 類型

let number: number = 10;

Array 類型

數組是 Array 類型。然而,因為數組是一個集合,我們還需要指定在數組中的元素的類型。我們通過 Arrayortype語法為數組內的元素指定類型

let arr:number = [1, 2, 3, 4, 5];
let arr2:Array = [1, 2, 3, 4, 5];

let arr3:string = ["1","2"];
let arr4:Array = ["1","2"];

Enums 類型

列出所有可用值,一個枚舉的默認初始值是0。你可以調整一開始的範圍:

enum Role {Employee = 3, Manager, Admin}
let role: Role = Role.Employee
console.log(role) // 3

Any 類型

any 是默認的類型,其類型的變數允許任何類型的值:

let notSure:any = 10;
let notSure2:any = [1,"2",false];

Void 類型

JavaScript 沒有空值 Void 的概念,在 TypeScirpt 中,可以用 void 表示沒有任何返回值的函數:

function alertName: void {
console.log("My name is muyy")
}

函數為函數定義類型

我們可以給每個參數添加類型之後再為函數本身添加返回值類型。 TypeScript能夠根據返回語句自動推斷出返回值類型,因此我們通常省略它。下面函數 add, add2, add3 的效果是一樣的,其中是 add3 函數是函數完整類型。

function add(x: string, y: string): string{
return "Hello TypeScript";
}

let add2 = function(x: string, y: string): string{
return "Hello TypeScript";
}

let add3: (x: string, y: string) => string = function(x: string, y: string): string{
return "Hello TypeScript";
}

可選參數和默認參數

JavaScript 里,每個參數都是可選的,可傳可不傳。 沒傳參的時候,它的值就是 undefined 。 在 TypeScript 里我們可以在參數名旁使用?實現可選參數的功能。 比如,我們想讓 lastname 是可選的:

function buildName(firstName: string, lastname?: string){
console.log(lastname ? firstName + "" + lastname : firstName)
}

let res1 = buildName("鳴","人"); // 鳴人
let res2 = buildName("鳴"); // 鳴
let res3 = buildName("鳴", "人", "君"); // Supplied parameters do not match any signature of call target.

如果帶默認值的參數出現在必須參數前面,用戶必須明確的傳入 undefined 值來獲得默認值。 例如,我們重寫上例子,讓 firstName 是帶默認值的參數:

function buildName2(firstName = "鳴", lastName?: string){
console.log(firstName + "" + lastName)
}

let res4 = buildName2("人"); // undefined人
let res5 = buildName2(undefined, "人"); // 鳴人

傳統的JavaScript程序使用函數和基於原型的繼承來創建可重用的組件,但對於熟悉使用面向對象方式的程序員來講就有些棘手,因為他們用的是基於類的繼承並且對象是由類構建出來的。 從ECMAScript 2015,也就是ECMAScript 6開始,JavaScript程序員將能夠使用基於類的面向對象的方式。 使用TypeScript,我們允許開發者現在就使用這些特性,並且編譯後的JavaScript可以在所有主流瀏覽器和平台上運行,而不需要等到下個JavaScript版本。

class Person{
name:string; // 這個是對後文this.name類型的定義
age:number;
constructor(name:string,age:number){
this.name = name;
this.age = age;
}
print{
return this.name + this.age;
}
}

let person:Person = new Person("muyy",23)
console.log(person.print) // muyy23

我們在引用任何一個類成員的時候都用了 this。 它表示我們訪問的是類的成員。其實這本質上還是 ES6 的知識,只是在 ES6 的基礎上多上了對 this 欄位和引用參數的類型聲明。

繼承

class Person{
public name:string; // public、private、static 是 typescript 中的類訪問修飾符
age:number;
constructor(name:string,age:number){
this.name = name;
this.age = age;
}
tell{
console.log(this.name + this.age);
}
}

class Student extends Person{
gender:string;
constructor(gender:string){
super("muyy",23);
this.gender = gender;
}
tell{
console.log(this.name + this.age + this.gender);
}
}

var student = new Student("male");
student.tell; // muyy23male

這個例子展示了 TypeScript 中繼承的一些特徵,可以看到其實也是 ES6 的知識上加上類型聲明。不過這裡多了一個知識點 —— 公共,私有,以及受保護的修飾符。TypeScript 里,成員默認為 public ;當成員被標記成 private 時,它就不能在聲明它的類的外部訪問;protected 修飾符與private 修飾符的行為很相似,但有一點不同,protected 成員在派生類中仍然可以訪問。

存儲器

TypeScript 支持通過 getters/setters 來截取對對象成員的訪問。 它能幫助你有效的控制對對象成員的訪問。

對於存取器有下面幾點需要注意的: 首先,存取器要求你將編譯器設置為輸出 ECMAScript 5 或更高。 不支持降級到 ECMAScript 3。 其次,只帶有 get 不帶有 set 的存取器自動被推斷為 readonly。 這在從代碼生成 .d.ts 文件時是有幫助的,因為利用這個屬性的用戶會看到不允許夠改變它的值。

class Hello{
private _name: string;
private _age: number;
get name: string {
return this._name;
}
set name(value: string) {
this._name = value;
}
get age: number{
return this._age;
}
set age(age: number) {
if(age>0 && age<100){ console.log("年齡在0-100之間"); // 年齡在0-100之間 return; } this._age = age; } } let hello = new Hello; hello.name = "muyy"; hello.age = 23 console.log(hello.name); // muyy

介面介面

TypeScript的核心原則之一是對值所具有的結構進行類型檢查。在TypeScript里,介面的作用就是為這些類型命名和為你的代碼或第三方代碼定義契約。

interface LabelValue{
label: string;
}

function printLabel(labelObj: LabelValue){
console.log(labelObj.label);
}

let myObj = {
"label":"hello Interface"
};
printLabel(myObj);

LabelledValue 介面就好比一個名字,它代表了有一個 label 屬性且類型為 string 的對象。只要傳入的對象滿足上述必要條件,那麼它就是被允許的。

另外,類型檢查器不會去檢查屬性的順序,只要相應的屬性存在並且類型也是對的就可以。

可選屬性

帶有可選屬性的介面與普通的介面定義差不多,只是在可選屬性名字定義的後面加一個 ? 符號。可選屬性的好處之一是可以對可能存在的屬性進行預定義,好處之二是可以捕獲引用了不存在的屬性時的錯誤。

interface Person{
name?:string;
age?:number;
}

function printInfo(info:Person){
console.log(info);
}

let info = {
"name":"muyy",
"age":23
};

printInfo(info); // {"name": "muyy", "age": 23}

let info2 = {
"name":"muyy"
};

printInfo(info2); // {"name": "muyy"}

函數類型

介面能夠描述 JavaScript 中對象擁有的各種各樣的外形。 除了描述帶有屬性的普通對象外,介面也可以描述函數類型。定義的函數類型介面就像是一個只有參數列表和返回值類型的函數定義。參數列表裡的每個參數都需要名字和類型。定義後完成後,我們可以像使用其它介面一樣使用這個函數類型的介面。

interface SearchFunc{
(source: string, subString: string): boolean;
}

let mySearch: SearchFunc;
mySearch = function(source: string,subString: string){
return source.search(subString) !== -1;
};

console.log(mySearch("鳴人","鳴")); // true
console.log(mySearch("鳴人","纓")); // false

可索引類型

與使用介面描述函數類型差不多,我們也可以描述那些能夠「通過索引得到」的類型,比如 a[10]ageMap["daniel"]。 可索引類型具有一個索引簽名,它描述了對象索引的類型,還有相應的索引返回值類型。 讓我們看如下例子:

interface StringArray{
[index: number]: string;
}

let MyArray: StringArray;
MyArray = ["是","雲","隨","風"];
console.log(MyArray[2]); // 隨

類類型

與 C# 或 Java 里介面的基本作用一樣,TypeScript 也能夠用它來明確的強制一個類去符合某種契約。

我們可以在介面中描述一個方法,在類里實現它,如同下面的 setTime方法一樣:

interface ClockInterface{
currentTime: Date;
setTime(d: Date);
}

class Clock implements ClockInterface{
currentTime: Date;
setTime(d: Date){
this.currentTime = d;
}
constructor(h: number, m: number) {}
}

繼承介面

和類一樣,介面也可以相互繼承。 這讓我們能夠從一個介面里複製成員到另一個介面里,可以更靈活地將介面分割到可重用的模塊里。

interface Shape{
color: string;
}

interface PenStroke{
penWidth: number;
}

interface Square extends Shape,PenStroke{
sideLength: number;
}

let s = {};
s.color = "blue";
s.penWidth = 100;
s.sideLength = 10;

模塊

TypeScript 與 ECMAScript 2015 一樣,任何包含頂級 import 或者 export 的文件都被當成一個模塊。

export interface StringValidator{
isAcceptable(s:string): boolean;
}

var strReg = /^[A-Za-z]+$/;
var numReg = /^[0-9]+$/;

export class letterValidator implements StringValidator{
isAcceptable(s:string): boolean{
return strReg.test(s);
}
}

export class zipCode implements StringValidator{
isAcceptable(s: string): boolean{
return s.length == 5 && numReg.test(s);
}
}

泛型

軟體工程中,我們不僅要創建一致的定義良好的 API ,同時也要考慮可重用性。 組件不僅能夠支持當前的數據類型,同時也能支持未來的數據類型,這在創建大型系統時為你提供了十分靈活的功能。 在像 C# 和 Java 這樣的語言中,可以使用泛型來創建可重用的組件,一個組件可以支持多種類型的數據。 這樣用戶就可以以自己的數據類型來使用組件。

初探泛型

如下代碼,我們給 Hello 函數添加了類型變數 T ,T 幫助我們捕獲用戶傳入的類型(比如:string)。我們把這個版本的 Hello 函數叫做泛型,因為它可以適用於多個類型。 代碼中 outputoutput2是效果是相同的,第二種方法更加普遍,利用了類型推論 —— 即編譯器會根據傳入的參數自動地幫助我們確定T的類型:

function Hello(arg:T):T{
return arg;
}

let outPut = Hello("Hello Generic");
let output2 = Hello("Hello Generic")

console.log(outPut);
console.log(outPut2);

參考資料

  • TypeScript 中文文檔
  • TypeScript
  • TypeScript for Angular 2 - Part 1 (An Introduction)

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 達人科技 的精彩文章:

C基礎:.NET環境下WebConfig的加密
Android使用OKHTTP解析JSON數據

TAG:達人科技 |