首先介紹一下什麼是TypeScript ,與JavaScript的區別,及優缺點javascript
首先JavaScript 是一門很是靈活的編程語言:html
TypeScript 的類型系統,在很大程度上彌補了 JavaScript 的缺點。前端
通常來講,在大型項目中,後期維護成本比前期開發成本要多得多,因此團隊規範化尤其重要,包括編碼規範,方法調用規範等,而TS能夠經過代碼的方式,約束團隊開發,這樣纔有利於後期維護及擴展,從而達到高效的開發java
兩個最重要的特性——類型系統
、適用於任何規模
。es6
優點:強大的IDE支持,支持類型檢測,容許爲變量指定類型,語法檢測,語法提示web
缺點:有必定的學習成本,須要理解 接口,泛型,類,枚舉類型等前端可能不是很熟悉的知識點。typescript
接口(Interfaces):能夠用於對``對象的形狀Shape`進行描述npm
泛型(Generics):在定義函數,接口或類時,不預先指定具體的類型,而是在使用時在指定類型的一種特性編程
類(Classes):定義了一件事物的抽象特色,包括屬性和方法json
若想使用TS進行開發,首先必需要搭建搭建TypeScript開發環境
安裝: npm install -g typescript
,全局安裝,能夠在任意位置執行tsc
版本:tsc -v
編譯:tsc 文件名.ts
TS 中,使用:
爲變量指定類型,:
先後的空格無關緊要。TS 只會在編譯時
對類型進行靜態檢查
,如發現有錯誤,編譯時就會報錯。而在運行時,與普通的 JavaScript 文件同樣,不會對類型進行檢查。
編譯時即便報錯,仍是會生成編譯結果,仍然可使用編譯以後的文件,若想在報錯時終止 js文件的生成,能夠在 tsconfig.json 中配置 noEmitOnError 便可。
類型系統按照類型檢查的時機
分類,能夠分爲動態類型
和靜態類型
。
動態類型
:是指在運行時纔會進行類型檢查,類型錯誤每每會致使運行時錯誤。JavaScript 是一門解釋型語言,沒有編譯階段,因此它是動態類型,如下代碼在運行時纔會報錯:
// test.js let foo = 1; foo.split(' '); // TypeError: foo.split is not a function 運行時會報錯(foo.split 不是一個函數)
靜態類型
:是指編譯階段就能肯定每一個變量的類型,類型錯誤每每會致使語法錯誤。TypeScript 在運行前須要先編譯爲 JavaScript,而在編譯階段就會進行類型檢查,因此 TypeScript 是靜態類型,如下代碼在編譯階段就會報錯:
// test.ts let foo: number = 1; foo.split(' '); // Property 'split' does not exist on type 'number'. 編譯時報錯(數字沒有 split 方法),沒法經過編譯
類型系統按照是否容許隱式類型轉換
分類,能夠分爲強類型
和弱類型
。
如下代碼在 JS或 TS 中均可以正常運行,運行時數字 1 會被隱式類型轉換爲字符串 '1',加號 + 被識別爲字符串拼接,因此打印出結果是字符串 '11'。
console.log(1 + '1'); // 11
TS 是徹底兼容 JS的,並不會修改 JS 運行時的特性,因此它們都是弱類型
。雖然 TS 不限制加號兩側的類型,可是能夠藉助類型系統,以及 ESLint 代碼檢查,來限制加號兩側必須同爲數字或同爲字符串。會在必定程度上使得 TypeScript 向強類型更近一步了——固然,這種限制是可選的。
這樣的類型系統體現了 TypeScript 的核心設計理念:在完整保留 JavaScript 運行時行爲的基礎上,經過引入靜態類型系統來提升代碼的可維護性,減小可能出現的 bug。
布爾值、數值、字符串、null、undefined,爲變量指定類型,且變量值需與類型一致
let flag: boolean = false let num: number = 15 let str: string = 'abc' var a: null = null var b: undefined = undefined // 編譯經過
使用構造函數創造的對象不是原始數據類型,事實上 new XXX() 返回的是一個 XXX對象:
let flag:boolean=new Boolean(false) // Type 'Boolean' is not assignable to type 'boolean'. let num: number = new Number(15) // Type 'Number' is not assignable to type 'number'. let str: string = new String('abc') // Type 'String' is not assignable to type 'string'. // 編譯經過
可是能夠直接調用 XXX 也能夠返回一個 xxx 類型:
let flag: boolean = Boolean(false) let num : number = Number(15) let str : string = String('abc') // 編譯經過
使用 number
定義數值類型:
let decLiteral: number = 6; let hexLiteral: number = 0xf00d; let binaryLiteral: number = 0b1010; // ES6 中的二進制表示法 let octalLiteral: number = 0o744; // ES6 中的八進制表示法 let notANumber: number = NaN; let infinityNumber: number = Infinity;
編譯結果:
var decLiteral = 6; var hexLiteral = 0xf00d; var binaryLiteral = 10; // ES6 中的二進制表示法 var octalLiteral = 484; // ES6 中的八進制表示法 var notANumber = NaN; var infinityNumber = Infinity;
ES6 中二進制和八進制數值表示法:分別用前綴0b
|0B
和0o
|0O
表示。會被編譯爲十進制數字。
使用string
定義字符串類型:
let myName: string = 'Echoyya'; let str: string = `Hello, my name is ${myName}.`; // 模板字符串
編譯結果:
var myName = 'Echoyya'; var str = "Hello, my name is " + myName + "."; // 模板字符串
ES6 中模板字符串:加強版的字符串,用反引號(`) 標識 ${expr} 用來在模板字符串中嵌入表達式。
JavaScript 沒有空值(Void)的概念,在 TS中,用 void 表示沒有任何返回值的函數:
function alertName(): void { alert('My name is Tom'); }
然而聲明一個 void 類型的變量沒什麼用,由於只能將其賦值爲 undefined 和 null:
let unusable: void = undefined;
Null 和 Undefined
let u: undefined = undefined; let n: null = null;
區別:undefined 和 null 是全部類型的子類型。也就是說 undefined 類型的變量,能夠賦值給全部類型的變量,包括 void 類型:
let num: number = undefined; let u: undefined; let str: string = u; let vo: void= u; // 編譯經過
而 void 類型的變量不能賦值給其餘類型的變量,只能賦值給 void 類型:
let u: void; let num: number = u; // Type 'void' is not assignable to type 'number'. let vo: void = u; // 編譯經過
任意值(Any)用來表示容許賦值爲任意類型
。一個普通類型,在賦值過程當中是不被容許改變類型的,any 類型,容許被賦值爲任意類型。let str: string = 'abc'; str = 123; // Type 'number' is not assignable to type 'string'. // ----------------------------------------------------------------- let str: any = 'abc'; str = 123; // 編譯經過
任意值能夠訪問任意屬性和方法:
let anyThing: any = 'hello'; anyThing.setName('Jerry'); anyThing.setName('Jerry').sayHello(); anyThing.myName.setFirstName('Cat'); console.log(anyThing.myName); console.log(anyThing.myName.firstName); // 編譯經過
未聲明類型的變量
:若是變量在聲明時,未指定類型,那麼會被識別爲任意值類型:let something; something = 'seven'; something = 7; // 等價於 let something: any; something = 'seven'; something = 7;
若未明確指定類型,那麼 TS 會依照類型推論(Type Inference)規則
推斷出一個類型。
如下代碼雖然沒有指定類型,但編譯時會報錯:
let data = 'seven'; data = 7; // Type 'number' is not assignable to type 'string'.
事實上,它等價於:
let data: string = 'seven'; data = 7; // Type 'number' is not assignable to type 'string'.
TS會在未明確指定類型時推測出一個類型,這就是類型推論
。若是定義時未賦值,無論以後是否賦值,都會被推斷成 any 類型
:
let data; data = 'seven'; data = 7; // 編譯經過
聯合類型(Union Types)
表示取值能夠爲多種類型中的一種
,使用 |
分隔每一個類型。
let data: string | number; // 容許 data 能夠是 string 或 number 類型,但不能是其餘類型 data = 'seven'; data = 7; data = true; // Type 'boolean' is not assignable to type 'string | number'.
訪問聯合類型的屬性或方法
:當不肯定一個聯合類型的變量究竟是哪一個類型時,只能訪問此聯合類型中全部類型共有的屬性或方法:
function getLength(something: string | number): number { return something.length; } // length 不是 string 和 number 的共有屬性,因此會報錯 // Property 'length' does not exist on type 'string | number'. // Property 'length' does not exist on type 'number'.
訪問 string 和 number 的共有屬性:
function getString(something: string | number): string { return something.toString(); }
聯合類型的變量在被賦值
的時候,會根據類型推論的規則推斷出一個類型:
let data: string | number; data = 'seven'; console.log(data.length); // 5 data = 7; console.log(data.length); // 編譯時報錯 // Property 'length' does not exist on type 'number'.
line2:data 被推斷爲 string,訪問length 屬性不會報錯。
line4:data 被推斷爲 number,訪問length 屬性報錯。
在 TS中,使用接口(Interfaces)來定義對象的類型。可用於對類的一部分行爲進行抽象
之外,也經常使用於對對象的形狀(Shape)
進行描述。
interface Person { name: string; age: number; } let p1: Person = { name: 'Tom', age: 25 };
定義一個接口 Person(接口通常首字母大寫),定義一個變量 tom 類型是 Person。這樣就約束了 tom 的形狀必須和接口 Person 一致。
肯定屬性
:賦值時,定義的變量的形狀必須與接口形狀保持一致。變量的屬性比接口少或多
屬性都是不容許
的:
interface Person { name: string; age: number; } let p1: Person = { // 缺乏 age 屬性 name: 'Tom' }; // Type '{ name: string; }' is not assignable to type 'Person'. // Property 'age' is missing in type '{ name: string; }'. // ----------------------------------------------------------------- let p2 Person = { // 多餘 gender 屬性 name: 'Tom', age: 25, gender: 'male' }; // Type '{ name: string; age: number; gender: string; }' is not assignable to type 'Person'. // Object literal may only specify known properties, and 'gender' does not exist in type 'Person'.
可選屬性
:是指該屬性能夠不存在。有時不須要徹底匹配一個接口時,能夠用可選屬性,但此時仍然不容許添加未定義的屬性
interface Person { name: string; age?: number; } let p1: Person = { // 編譯經過 name: 'Tom' }; let p2: Person = { // 編譯經過 name: 'Tom', age: 25 }; let p3: Person = { // 報錯(同上) name: 'Tom', age: 25, gender: 'male' };
任意屬性
:容許一個接口有任意的屬性
interface Person { name: string; age?: number; [propName: string]: any; } let p1: Person = { name: 'Tom', gender: 'male' };
使用 [propName: string]
定義了任意屬性
的屬性名取 string 類型的值。屬性值爲任意值
注意:一旦定義了任意屬性,那麼肯定屬性和可選屬性的類型都必須是它的類型的子集:
例一:任意屬性的類型是 string,可是可選屬性 age 的值倒是 number,number 不是 string 的子屬性,因此會報錯。
interface Person { name: string; age?: number; [propName: string]: string; } let p1: Person = { name: 'Tom', age: 25, gender: 'male' }; // Property 'age' of type 'number' is not assignable to string index type 'string // Type '{ name: string; age: number; gender: string; }' is not assignable to type 'Person'. // Property 'age' is incompatible with index signature. // Type 'number' is not assignable to type 'string'
例二:一個接口中只能定義一個任意屬性。若是接口中有多個類型的屬性,能夠在任意屬性
中使用聯合類型
:
interface Person { name: string; age?: number; [propName: string]: string | number; } let p1: Person = { // 編譯經過 name: 'Tom', age: 25, gender: 'male', year:2021 };
對象中的一些字段只能在建立時被賦值,可使用 **readonly **定義只讀屬性
:
例一:使用 readonly 定義的屬性 id 初始化後,又被從新賦值,因此會報錯。
interface Person { readonly id: number; name: string; age?: number; [propName: string]: any; } let p1: Person = { id: 89757, name: 'Tom', gender: 'male' }; p1.id = 9527; // Cannot assign to 'id' because it is a read-only property.
例二:只讀的約束存在於第一次給對象賦值
的時候,而不是第一次給只讀屬性賦值時:
interface Person { readonly id: number; name: string; age?: number; [propName: string]: any; } let p2: Person = { // 第一次給對象賦值 name: 'Tom', gender: 'male' }; p2.id = 89757; // Property 'id' is missing in type '{ name: string; gender: string; }' but required in type 'Person' 對 p2 賦值時,沒有給 id 賦值 // Cannot assign to 'id' because it is a read-only property. id 是隻讀屬性
在 TS 中,有多種定義數組類型的方式。
最簡單的方法是使用類型 + 方括號
來表示數組:
let arr: number[] = [1, 1, 2]; // 數組元素中不容許出現其餘的類型 let arr1: number[] = [1, '1', 2]; // 報錯:Type 'string' is not assignable to type 'number'.
數組的一些方法的參數也會根據數組在定義時約定的類型進行限制:
let arr2: number[] = [1, 1, 2, 3, 5]; arr2.push('8'); //報錯:Argument of type '"8"' is not assignable to parameter of type 'number'.
使用數組泛型(Array Generic) Array<elemType>
來表示數組:
let arr3: Array<number> = [1, 1, 2, 3, 5];
泛型涉及內容較多,後期有時間會在整理一篇文章,敬請關注!
以前介紹了使用接口表示對象的類型,一樣接口也能夠用來描述數組:
interface NumberArray { [index: number]: number; } let arr: NumberArray = [1, 1, 2, 3, 5];
NumberArray 表示:索引的類型是數字,值的類型也是數字,這樣即可以表示一個數字類型的數組,雖然接口也能夠描述數組,可是通常不會這麼作,由於這種方式較複雜。有一例外,就是經常使用來表示類數組。
類數組(Array-like Object)不是數組類型,好比 arguments,其實是一個類數組,不能用普通數組的方式來描述,而應該用接口:
function sum() { let args: number[] = arguments; } // 報錯:Type 'IArguments' is missing the following properties from type 'number[]': pop, push, concat, join, and 24 more.
接口描述類數組
:除了約束索引的類型是數字,值的類型也必須是數字以外,也約束了它還有 length 和 callee 兩個屬性。
function sum() { let args: { [index: number]: number; length: number; callee: Function; } = arguments; }
而事實上經常使用的類數組都有本身的接口定義,如 IArguments, NodeList, HTMLCollection 等:
function sum() { let args: IArguments = arguments; } //其中 IArguments 是 TypeScript 中定義好了的類型,它實際上就是: interface IArguments { [index: number]: any; length: number; callee: Function; }
一個比較常見的作法是,用 any 表示數組中容許出現任意類型:
let list: any[] = ['Echoyya', 25, { website: 'https://www.cnblogs.com/echoyya/' }, false];
後續筆記敬請期待~~~