什么是 TypeScript
TypeScript 是 JavaScript 的類型的超集,它可以編譯成純 JavaScript。
安裝 TypeScript
命令行工具安裝:
npm install -g typescript
編譯一個 TypeScript 文件:
tsc hello.ts
原始數據類型/ 任意值
為每一個變量提前申明該類型,則該變量取值只能為申明的類型,否則報錯
如果一個變量可以任意取值,可以通過any 申明該變量為任意值
原始數據類型包括:布爾值(boolean)、數值(number)、字符串(string)、null
、undefined、Symbol(ES6)共六種
申明為number類型的變量,后面可以取值類型為 number 、null、undefined,會默認進行隱式轉換
例子:
let isDone: boolean = false; //申明該值為 boolean 類型 let decLiteral: number = 6; let notANumber: number = NaN; // 進行隱式轉換 let infinityNumber: number = Infinity; // 進行隱式轉換 let num: number = undefined; // 進行隱式轉換 let myName: string = 'Xcat Liu'; let u: undefined = undefined; let n: null = null; let anytest: any = 123; //申明該值為任意類型 anytest = true; anytest = '任意值' // 內置對象類型申明 let b: Boolean = new Boolean(1); // 申明為 Boolean 類型 let e: Error = new Error('Error occurred'); //申明為錯誤類型 let d: Date = new Date(); // 申明為 時間類型 let r: RegExp = /[a-z]/; //申明為正則類型
let body: HTMLElement = document.body;
let allDiv: NodeList = document.querySelectorAll('div');
document.addEventListener('click', function(e: MouseEvent) { // Do something });
提前申明變量的類型也可以用在functiuon函數傳參,用於規定該參數的類型
在 TypeScirpt 中,可以用 void
表示沒有任何返回值的函數
例子:
function sayHello(person: string) { // 在函數出入的參數后面申明類型 return 'Hello, ' + person; } function test(): void { //void 表示該函數沒有返回值 alert('Hello Wrold'); }
聯合類型
聯合類型表示取值可以為多種類型中的一種。使用 |
分隔每個類型。
let test: string | number; //申明 test變量的類型為string 或者 number其中一種 test = 'seven'; //test的取值可以為其中一種類型 test = 7;
當不確定一個使用了聯合類型的變量是哪種類型時,只能訪問此聯合類型的所有類型里共有的屬性或方法
function getString(something: string | number) { //申明傳入的參數為string或者number類型 return something.toString(); // 只能調用string和number共有的方法 }
錯誤例子:
function getLength(something: string | number) { return something.length; // number類型不存在length }
對象的類型——接口 (interfaces)
即提前定義一個對象的類型規則,用於申明一個對象時進行類型匹配
interface Person { //使用interface定義一個對象的類型,取名Person 首字母一般大寫 name: string; //申明該對象每個值的取值類型 age: number; } let xcatliu: Person = { // 申明一個xcatliu對象,使用Person規則進行匹配驗證 name: 'Xcat Liu', age: 25, };
使用Interfaces來定義對象的類型,定義了類型的對象,定義的變量不允許比類型少,也不能多,結構必須一模一樣
錯誤例子:
interface Person { name: string; age: number; } let xcatliu: Person = { //不能比定義的Person類型的變量少 name: 'Xcat Liu', }; let xcatliu: Person = { name: 'Xcat Liu', age: 25, website: 'http://xcatliu.com', /不能比定義的Person類型的變量多 };
如果我們希望不要完全匹配一個 類型,可以使用可選屬性: 可選屬性的含義是該屬性可以不存在。使用 ? 但是仍然不允許添加未定義的屬性
interface Person { name: string; age?: number; //在屬性后面加上? 表示該屬性可有可無 } let xcatliu: Person = { name: 'Xcat Liu', };
有時候我們希望一個接口允許有任意的屬性,即可以任意添加屬性個數,使用 任意屬性 [propName: string]
一旦定義了任意屬性,那么確定屬性和可選屬性都必須是它的子屬性 任意屬性的取值類型為 any ,否則會跟可選屬性沖突
interface Person { name: string; [propName: string]: any; // 表示可以任意添加屬性個數 ,添加的屬性類型為 any } let xcatliu: Person = { name: 'Xcat Liu', website: 'http://xcatliu.com', //任意添加的屬性 websit2: 'http://xcatliu.com', //任意添加的屬性 };
對象中的一些字段只能在創建的時候被賦值,那么可以用 readonly
定義只讀屬性,在剛剛創建對象的時候賦值,后面不允許改變該屬性值。也不能在創建的時候未定義值
readonly 定義在屬性的前面,用空格區分
interface Person { readonly id: number; //使用 readonly 定義只讀屬性 name: string; } let xcatliu: Person = { id: 89757, name: 'Xcat Liu', }; xcatliu.id = 9527; //報錯 不能改變該屬性值
數組的類型
數組的類型用於定義申明一個數組時的格式,申明格式類型的數組不能使用其他類型的方法
對象中的接口定義類型方法也同樣適用於數組
let testArr: number[] = [1,2,5,4,8] //申明一個數組的類型為 number let fibonacci: string[] = ["1","2"]; //申明一個數組的類型為 string let list: any[] = ['Xcat Liu', 25, { website: 'http://xcatliu.com' }]; // 申明一個數組的類型為任意類型 let fibona: Array<number> = [ 1, 2, 3, 5]; //也可以使用數組泛型Array<elemType> 來表示數組 interface NumberArray { //使用接口定義一個數組的類型,表示該數組取值必須為 string 類型 [index: number]: string; } let fi: NumberArray = ["a","1"]; interface NumberArr { //使用接口定義一個數組的類型,表示該數組取值必須為 number 類型 [index: number]: number; } let fi2: NumberArr = [1, 1, 2, 3, 5];
類數組不是數組類型,比如 arguments ,不能使用數組的類型定義方式
常見的類數組都有自己的接口定義,如 IArguments
, NodeList
, HTMLCollection
等
function sum() { let args: IArguments = arguments; // arguments是類數組類型,使用 IArguments }
函數的類型
定義函數的類型,對函數傳入的參數和返回值做一定的限制
輸入多余的(或者少於要求的)參數,是不被允許的,
function sum(x: number, y: number): number { //申明一個函數sum 限制其傳入的參數為 number類型,返回的參數為 number類型 return x + y; } sum(1, 2);
如果是一個函數表達式,,那么需要對左右兩邊進行類型的限制
//申明了一個 mySum函數,申明其類型為number let mySum: (x: number, y: number) => number = function (x: number, y: number): number { return x + y; };
函數的類型定義同樣可以使用對象的接口
interface SearchFunc { //使用接口申明一個類型,名稱為 SearchFunc (source: string, subString: string): boolean; //申明該函數的傳入值都為 string 返回值為 boolean } let mySearch: SearchFunc; //申明一個函數,使用 SearchFunc 類型規則 mySearch = function(source: string, subString: string) { return source.search(subString) !== -1; }
函數的類型也可以定義可選的傳入參數, 在參數后面使用 ?
可選參數必須接在必需參數后面
function buildName(firstName: string, lastName?: string) { // lastName 為可選參數 if (lastName) { return firstName + ' ' + lastName; } else { return firstName; } } let xcatliu = buildName('Xcat', 'Liu'); let xcat = buildName('Xcat');
TypeScript 會將添加了默認值的參數識別為可選參數 使用默認值的可選參數不限制位置
function buildName(firstName: string, lastName: string = 'Liu') { // lastName 為可選參數 return firstName + ' ' + lastName; } let xcatliu = buildName('Xcat', 'Liu'); let xcat = buildName('Xcat');
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(''); } }
ES6 中,可以使用 ...rest
的方式獲取函數中的剩余參數,items
是一個數組。可以用數組的類型來定義它:
function push(array: any[], ...items: any[]) { //...items表示剩余參數,是一個數組,可以申明為任意類型的數組 items.forEach(function(item) { array.push(item); }); } let a = []; push(a, 1, 2, 3);
類型斷言 <類型>值
值 as 類型
類型斷言可以用來繞過編譯器的類型推斷,手動指定一個值的類型
TypeScript在使用聯合類型時,默認不能引用不確定類型的方法,只能引用共有的方法,某些時刻,我們需要使用類型斷言,即申明此時的屬性為某個類型
類型斷言不是類型轉換,斷言成一個聯合類型中不存在的類型是不允許的 斷言的類型必須是聯合類型中的某一個
function getLength(something: string | number): number { if ((<string>something).length) { // 默認不能使用 length屬性,使用類型斷言 <string> 將此時的something申明為 string類型 return (<string>something).length; } else { return something.toString().length; } }
類型別名
類型別名用來給一個類型起個新名字。使用 type
type Name = string; // 使用 type 將string類型起名為 Name type NameResolver = () => string; //另一種寫法 type NameOrResolver = Name | NameResolver; //使用該別名 function getName(n: NameOrResolver): Name { if (typeof n === 'string') { return n; } else { return n(); } }
字符串字面量類型
字符串字面量類型用來約束取值只能是某幾個字符串中的一個。同樣使用 type
type test = 'click'|'scroll'|'mousemove' // 使用 type 規定test變量為三個值其中一個 function handleEvent(ele: Element, event: test) { //使用test類型,傳入的值為規定值的其中一個 // do something }
handleEvent(document.getElementById('hello'), 'scroll');
元組
數組合並了相同類型的對象,而元組(Tuple)合並了不同類型的對象。
簡單理解,在數組類型中,要么規定數組中的每個值都為某種類型,要么都為任意類型。使用元組,可以依次給每個值指定類型
let xcatliu: [string, number] = ['Xcat Liu', 25];
我們也可以在定義數組類型后,通過索引依次賦值,也可以只賦值其中一項
當直接對元組類型的變量進行初始化或者賦值的時候,需要提供所有元組類型中指定的項。
let xcatliu: [string, number]; //先定義每個值的類型 xcatliu[0] = 'Xcat Liu'; // 通過索引賦值 xcatliu[1] = 25; xcatliu[0].slice(1); //可以通過索引調用對應類型的方法 xcatliu[1].toFixed(2);
let xcatliu: [string, number] = ['Xcat Liu'];
//報錯,需要全部賦值
當我們去訪問數組未定義的下標或者對其進行賦值時,它的類型為已存在元素的類型組成的聯合類型,能夠調用的方法為聯合類型共有的方法
let xcatliu: [string, number]; xcatliu = ['Xcat Liu', 25, 'http://xcatliu.com/']; // 第三個值的類型為 string|number xcatliu.push(true); // 報錯 true 是 boolean 類型 console.log(xcatliu[2].slice(1)); // 報錯 slice不是 string|number 共有的方法
TypeScript 中類的用法 public private 和 protected
public
修飾的屬性或方法是公有的,可以在任何地方被訪問到,默認所有的屬性和方法都是public
的private
修飾的屬性或方法是私有的,不能在聲明它的類的外部訪問protected
修飾的屬性或方法是受保護的,它和private
類似,區別是它在子類中也是允許被訪問的
class Animal { public name; //表示公共的,在任何地方都可以被訪問 private age; //表示私有的,不允許被訪問,也不能繼承 protected height; //表示受保護的,只能通過繼承訪問 public constructor(name) { this.name = name; } } var a = new Animal('Jack'); console.log(a.name); console.log(a.age); //報錯,不允許被訪問 console.log(a.height); // 報錯,只能通過繼承訪問
abstract
用於定義抽象類和其中的抽象方法。
抽象類是不允許被實例化的,即不能通過new去實例化,只能被繼承
抽象類中的抽象方法必須被子類實現,即使用abstract
定義為抽象方法,那么必須在子類實現
abstract class Animal { public name; public constructor(name) { this.name = name; } public abstract sayHi(); //抽象方法 } class Cat extends Animal { public sayHi() { // 繼承 Animal類后實現定義的抽象方法 console.log(`Meow, My name is ${this.name}`); } } let cat = new Cat('Tom');
類實現接口 implements
不同類之間可以有一些共有的特性,這時候就可以把特性提取成接口(interfaces),用 implements
關鍵字來實現。
interface Alarm { //使用 interface 定義了一個接口,里面是一個 alert 方法 alert(); } class Door { } class SecurityDoor extends Door implements Alarm { // SecurityDoor繼承了 Door類並且引用了 Alarm 接口的方法 alert() { console.log('SecurityDoor alert'); } } class Car implements Alarm { // Car類 引用了Alarm 接口的方法 alert() { console.log('Car alert'); } }
一個類可以同時引用多個接口
interface Alarm { alert(); } interface Light { lightOn(); lightOff(); } class Car implements Alarm, Light { alert() { console.log('Car alert'); } lightOn() { console.log('Car light on'); } lightOff() { console.log('Car light off'); } }
接口與接口之間也可以相互繼承:
interface Alarm {
alert();
}
interface LightableAlarm extends Alarm {
lightOn();
lightOff();
}