TypeScript入門教程

TypeScript簡介

定義

總所周知,JavaScript語言並不是一門面向對象的語言,而是一種解釋性的函數式編程語言。在前端Web還不是很複雜的情況下,使用JavaScript是可以應付各種需求的,但當前端頁面變的越來越複雜時,JavaScript就顯得比較力不從心了,而TypeScript就是爲了解決這個情況而誕生的語言。
TypeScript是面向對象的語言同時支持許多面向對象的特性,因此可以使用它創建出更加強壯和易於擴展的程序。同時,TypeScript 擴展了 JavaScript 的語法,所以任何現有的 JavaScript 程序可以不加改變的在 TypeScript 下工作。

根據維基百科的定義:TypeScript是一種由微軟開發的自由和開源的編程語言,它是JavaScript的一個嚴格超集,並添加了可選的靜態類型和基於類的面向對象編程。

TypeScript的優勢

可以看到,越來越多的前端框架開始使用TypeScript,那麼它究竟有哪些優點呢?下面羅列一些常見的優點:

  • 更多的規則和類型限制,讓代碼預測性更高,可控性更高,易於維護和調試。
  • 對模塊、命名空間和麪向對象的支持,更容易組織代碼開發大型複雜程序。
  • TypeScript 的編譯步驟可以捕獲運行之前的錯誤。
  • Angular2+ 和 Ionic2+ 默認使用TypeScript,同時Vue.js和React.js等一些流行的前端框架也開始支持TypeScript。
  • ...

這裏寫圖片描述

環境搭建

俗話說,“工欲善其事,必先利其器”,學習一門新的語言和技術必須先了解其開發環境。

安裝TypeScript

TypeScript提供了兩種主要的方式獲取TypeScript工具:

  • 通過npm(Node.js包管理器)
  • 安裝Visual Studio的TypeScript插件

最新版的Visual Studio 2017和Visual Studio 2015 Update 3默認包含了TypeScript,如果你的Visual Studio還不支持TypeScript,可以使用Visual Studio下載頁面鏈接來獲取安裝插件。同時,針對使用npm的用戶,可以使用下面的命令來安裝TypeScript工具。

npm install -g typescript

除了上面兩種方式外,我們還可以使用TypeScript提供的在線環境來體驗TypeScript的魅力:http://www.typescriptlang.org...

創建TypeScript文件

打開編輯器,將下面的代碼輸入到greeter.ts文件裏。

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

let user = "jack ma";

document.body.innerHTML = greeter(user);

編譯代碼

TypeScript使用.ts作爲擴展名,但是這段代碼僅僅是JavaScript而已,想要運行這段代碼,還需要編譯上面的代碼。在命令行上,運行TypeScript編譯器:

tsc greeter.ts

輸出結果爲一個greeter.js文件,它包含了和輸入文件中相同的JavsScript代碼。此時,我們就可以運行這段代碼了。

類型註解

TypeScript裏的類型註解是一種輕量級的爲函數或變量添加約束的方式。 在這個例子裏,我們希望 greeter函數接收一個字符串參數,那麼我們可以這麼做:

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

let user = [0, 1, 2];

document.body.innerHTML = greeter(user);

重新編譯,會看到一個錯誤:

greeter.ts(7,26): error TS2345: Argument of type 'number[]' is not assignable to parameter of type 'string'.

接口

讓我們開發這樣一個示例:使用一個接口,它描述了具有firstName和lastName字段的對象。在TypeScript中,如果兩個類型其內部結構兼容,那麼這兩種類型兼容。這使我們實現一個接口,僅僅只需必要的結構形狀,而不必有明確的implements子句。

interface Person {
    firstName: string;
    lastName: string;
}

function greeter(person: Person) {
    return "Hello, " + person.firstName + " " + person.lastName;
}

let user = { firstName: "Jane", lastName: "User" };

document.body.innerHTML = greeter(user);

TypeScript支持JavaScript的新特性,比如支持基於類的面向對象編程。讓我們創建一個Student類,它帶有一個構造函數和一些公共字段。

class Student {
    fullName: string;
    constructor(public firstName, public middleInitial, public lastName) {
        this.fullName = firstName + " " + middleInitial + " " + lastName;
    }
}

interface Person {
    firstName: string;
    lastName: string;
}

function greeter(person : Person) {
    return "Hello, " + person.firstName + " " + person.lastName;
}

let user = new Student("Jane", "M.", "User");

document.body.innerHTML = greeter(user);

重新運行tsc greeter.ts,你會看到生成的JavaScript代碼和原先的一樣。 TypeScript裏的類只是JavaScript裏常用的基於原型面向對象編程的簡寫。

語法特性

和JavaScript相比,TypeScript帶來了諸多語法上的變化,下面就其比較重要的羅列如下。

字符串特性

多行字符串

使用``包裹跨行的字符串,示例:

var html = `<div>
<span></span>
</div>`

字符串模板

可以在多行字符串中使用模板,示例:

var names = 'daocheng';
function getImg() {
  return '<i></i>'
}

var html = `<div>${names}
<span>${getImg()}</span>
<div>
`

自動拆分字符串

function getData(template, name, age) {
    console.log(template);
    console.log(name);
    console.log(age);
}

var names = 'daocheng';
var age = 23;
getData`你好,我的名字是${names},我今年${age}歲了`

參數

參數類型

Typescript中的參數類型包括:
boolean/number/string/array/tuple/enum/any/(null和undefined)/ void /never。
其中元祖(tuple)、枚舉、任意值、void類型和never是有別於Javascript的特有類型。

類型聲明與默認參數

在Typescritpt中聲明變量,需要加上類型聲明,如boolean或string等。通過靜態類型約束,在編譯時執行類型檢查,這樣可以避免一些類型混用的低級錯誤。示例:

var names = 'daocheng';
function getData(name: stirng, age: number): boolean {
    return true
}

Typescript還支持初始化默認參數。如果函數的某個參數設置了默認值,當該函數被調用時,如果沒有給這個參數傳值或者傳值爲undefined時,這個參數的值就是設置的默認值。示例:

function max(x: number, y: number = 4): number {
    return x > y ? x : y;
}
let result1 = max(2); //正常
let result2 = max(2, undefined); //正常
let result3 = max(2, 4, 7); //報錯
let result4 = max(2, 7); //正常

可選參數

在javascript裏,被調用函數的每個函數都是可選的,而在typescript中,被調用的每個函數的每個參數都是必傳的。在編譯時,會檢查函數每個參數是否傳值。簡而言之,傳遞給一個函數的參數個數必須和函數定義的參數個數一致。例如:

function max(x: number, y: number) {
    if(y){
        return x > y ? x : y;
    } else {
        return x
    }
}
let result1 = max(2);
let result2 = max(2, 4, 7); //報錯
let result3 = max(2, 4);
//注意:可選參數必須放在默認參數後面

函數

剩餘函數

當需要同時操作多個參數,或者並不知道會有多少參數傳遞進來時,就需要用到Typescript 裏的剩餘參數。示例:

function sum(x: number, ...restOfNumber: number[]){
    let result = x;
    restOfNumber.forEach(value => result += value);
    return result;
}
let result1 = sum(1, 2, 3, 4, 5, 6);
console.log(result1);

let result2 = sum(2);
console.log(result2);

let result3 = sum(2, 5);
console.log(result3);

generator函數

控制函數的執行過程,可以手動的干預函數執行。示例:

function getPrice(stock) {
    while (1) {
        yield Math.random() * 100;
    }
}
var priceGenerator = getPrice('dcc');
var limitPrice = 51;
var price = 100;
while (price > limitPrice) {
    price = priceGenerator.next().value;
    console.log(`this generator return ${price}`);
}
console.log(`buying at ${price}`);

析構表達式

析構表達式又稱解構,是ES6的一個重要特性,Typescript在1.5版本中開始增加了對結構的支持,所謂結構,就是將聲明的一組變量與相同結構的數組或者對象的元素數值一一對應。分數組解構([])和對象解構({})兩種。

數組解構

let imput = [1, 2];
let [first, second] = input;
console.log(first); //相當於inputp[0]
console.log(second); //相當於input[1]
function f([first, second]) {
    console.log(first + second)
}
f{[1, 3]}   //結果是4

let [first, ...rest] = [1, 2, 3, 4];
console.log(first); //1
console.log(second); //[2,3,4]

對象解構

let test = {
    x: 0,
    y: 0,
    width: 15,
    heights: {
        height1: 10,
        height2: 20
    }
};
let { x, y: myY, width, heights: {height2} } = test;
console.log(x, myY, width, height2); //輸出:0,10,15,20

箭頭表達式

用來聲明匿名函數,消除傳統匿名函數的this指針問題。例如:

function Test1(names: string) {
    this.names = names;
    setInterval(function() {
        console.log('my name is ' + this.names);
    }, 1000)
}
function Test2(names: string) {
    this.names = names;
    setInterval(() => {
        console.log('my names is ' + this.names)
    }, 1000)
}

var a = new Test1('daocheng'); //undefined
var b = new Test2('daocheng'); //daocheng

循環

typescritpt中涉及三種高級循環方式:forEach()、for in、for of。

forEach

var myArray = [1, 2, 3, 4];
myArray.name = 'daocheng';

myArray.forEach(value => console.log(value)); //結果爲1,2,3,4
//特點:不支持break,會忽略(name)

for in

var myArray = [1, 2, 3, 4];
myArray.name = 'daocheng';

for (var n in myArray ) {
    console.log(n)
}   //結果爲1,2,3,4
//特點: 循環的結果是對象或者數組的鍵值。可以break

for of

var myArray = [1, 2, 3, 4];
myArray.name = 'daocheng';

for (var n of myArray) {
    console.log(n)
}   //結果是1,2,3,4
//特點:忽略屬性,可以打斷。當循環爲字符串時,會把字符串中每個字符打出來

傳統的JavaScript程序使用函數和基於原型(Prototype)繼承來創建可重用的“類”,這對於習慣了面向對象編程的開發者來說不是很友好,Typescript中可以支持基於類(class)的面向對象編程。

類的聲明

class Car {
    engine: string,
    constructor(engine: string) { 
        this.engine = engine;
    }
    drive(distanceInMeters: number = 0) { 
        console.log(`aaa is running` + this.engine)
    }
}

let car = new Car('petrol');
car.drive(100)

類的封裝、繼承、多態

封裝、繼承、多態是面向對象的三大特性。上面的例子把汽車的行爲寫到一個類中,即所謂的封裝。在Typescript中,使用extends關鍵字可以方便的實現。例如:

繼承

繼承就是類與類之間一種特殊與一般的關係,可以理解成“is a”的關係。在繼承關係中,子類可以無條件的繼承父類的方法和屬性。

class Car {
    engine: string;
    constructor(engine: string) {
        this.engine = engine;
    }
    drive(distanceInMeter: number = 0){
        console.log(`A car runs ${distanceInMeter}m
        powered by` + this.engine)
    }
}

class MotoCar extends Car {
    constructor(engine: string) {
        super(engine)
    }
}

let tesla = new MotoCar('electricity');
tesla.drive();
//其中子類MotoCar的實例對象tesla調用了父類Car的drive()方法。

多態

多態就是通過對傳遞的參數判斷來執行邏輯,即可實現一種多態處理機制。

class Car {
    engine: string;
    constructor(engine: string) {
        this.engine = engine;
    }
    drive(distanceInMeter: number = 0){
        console.log(`A car runs ${distanceInMeter}m
        powered by` + this.engine)
    }
}

class Jeep extends Car {
    constructor(engine: string) {
        super(engine)
    }
    drive(distanceInMeters: number = 100) {
        console.log('jeep...')
        return super.drive(distanceInMeters);
    }
}
let landRover: Car = new Jeep('petrol'); //實現多態

Jeep子類中的drive()方法重寫了Car的drive()方法,這樣drive()方法在不同的類中就具有不同的功能,這就是多態。注意:子類和派生類的構造函數中必須調用super(),它會實現父類構造方法。

參數屬性

參數屬性是通過給構造函數的參數添加一個訪問限定符來聲明。參數屬性可以方便地讓我們在一個地方定義並初始化類成員。

class Car {
    constructor(public engine: string) {}
    drive() { }
}

抽象類

Typescript有抽象類的概念,它是供其他類繼承的基類,不能直接被實例化。不同於接口,抽象類必須包含一些抽象方法,同時也可以包含非抽象的成員。抽象類中的抽象方法必須在派生類中實現。

abstract class Person {
    abstract speak(): void;
    walking(): void {
        console.log('walking');
    }
}

class Male extends Person {
    speak(): void {
        console.log('man wakling')
    }
}

接口

接口在面向對象設計中具有極其重要的作用,在Gof的23種設計模式中,基本上都可見到接口的身影。長期以來,接口模式一直是Javascript這類弱類型語言的軟肋,Typescript接口的使用方式類似於Java。
在Typescript中接口有屬性類型、函數類型、可索引類型、類類型這幾種,在Angular的開發中主要使用類類型接口,我們使用interface關鍵字定義接口並用implements關鍵字實現接口。

interfance Animal {
    name: string;
    setName();
}

class Dog implements Animal {
    name: string;
    setName() {
        console.log(this.name)
    }
    constructor() { }
}
//接口更注重功能的設計,抽象類更注重結構內容的體現

模塊

ES6中引入了模塊的概念,在TypeScript中也支持模塊的使用。使用import和export關鍵字來建立兩個模塊之間的聯繫。

裝飾器

裝飾器(Decorators)是一種特殊類型的聲明,它可以被附加到類聲明、方法、屬性或參數上。裝飾器有@符號緊接一個函數名稱,如:@expression,expression求職後必須是一個函數,在函數執行的時候裝飾器的聲明方法會被執行。裝飾器是用來給附着的主題進行裝飾,添加額外的行爲。(裝飾器屬於ES7規範)

在Typescript的源碼中,官方提供了方法裝飾器、類裝飾器、參數裝飾器、屬性裝飾器等幾種每種裝飾器類型傳入的參數大不相同。這裏我演示兩種裝飾器。例如:

function Component(component) {
    console.log('selector: ' + component.selector);
    console.log('template: ' + component.template);
    console.log('component init');
    return (target: any) => {
        console.log('component call');
        return target;
    }
}

function Directive() {
    console.log('directive init');
    return (target: any) => {
        console.log('directive call');
        return target;
    }
}

@Component({
    selector: 'person',
    template: 'person.html'
})
@Directive()
export class Person {}

let p = new Person();

C#的首席架構師以及Delphi和Turbo Pascal的創始人安德斯•海爾斯伯格參與了TypeScript的開發。Typescript是ES6的超集。添加了可選的靜態類型(注意並不是強類型)和基於類的面向對象編程。(如果對ES6熟悉那麼可以只關注類、裝飾器部分的內容。)

泛型

泛型是參數化的類型,一般用來限制集合的內容。例如:

class MinHeap<T> {
    list: T[] = [];
    
    add(element: T): void {
        //這裏進行大小比較,並將最小值放在數組頭部,功能代碼省略。
    }
    min(): T {
        return this.list.length ? this.list[0] : null
    }
}

let heap = new MinHeap<number>();
heap.add(3);
heap.add(5);
console.log(heap.min())
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章