編者薦語: 本文旨在幫助你們在閒暇時間掌握TS用法,故抽出時間,學習整理TypeScript教程,本文大部份內容來自阮一峯老師的網站,你們放心閱讀。javascript
如下內容來自於TypeScript 入門教程html
TypeScript
是 JavaScript 的一個超集,主要提供了類型系統和對 ES6 的支持,它由 Microsoft 開發,代碼開源於 GitHub 上。 根據官網翻譯成中文是前端
TypeScript 是 JavaScript 的類型的超集,它能夠編譯成純 JavaScript。編譯出來的 JavaScript 能夠運行在任何瀏覽器上。TypeScript 編譯工具能夠運行在任何服務器和任何系統上。TypeScript 是開源的。java
TypeScript 官網列舉了一些優點,不過阮一峯老師願意本身總結一下:web
類型推論
TypeScript 的命令行工具安裝方法以下:面試
npm install -g typescript
複製代碼
以上命令會在全局環境下安裝 tsc 命令,安裝完成以後,咱們就能夠在任何地方執行 tsc 命令了。
編譯一個 TypeScript 文件很簡單:typescript
tsc hello.ts
複製代碼
查看 TypeScript 的版本信息:npm
tsc -v
複製代碼
咱們約定使用 TypeScript 編寫的文件以 .ts 爲後綴,用 TypeScript 編寫 React 時,以 .tsx 爲後綴。數組
接下來讓咱們全面擁抱TypeScript偉大的語言吧~瀏覽器
JavaScript 的類型分爲兩種: 原始數據類型和對象類型。
如下部分介紹前五中原始數組類型在TypeScipt中的應用。
在 TypeScript 中,使用 boolean 定義布爾值類型:
let isDone: boolean = false;
// 編譯經過
// 後面約定,未強調編譯錯誤的代碼片斷,默認爲編譯經過
複製代碼
注意,使用構造函數 Boolean 創造的對象不是布爾值:
let createdByNewBoolean: boolean = new Boolean(1);
// Type 'Boolean' is not assignable to type 'boolean'.
複製代碼
事實上 new Boolean() 返回的是一個 Boolean 對象
:
let createdByNewBoolean: Boolean = new Boolean(1);
複製代碼
在 TypeScript 中,boolean 是 JavaScript 中的基本類型,而
Boolean
是 JavaScript 中的構造函數
。其餘基本類型(除了 null 和 undefined)同樣,再也不描述。
使用 number 定義數值類型:
let decLiteral: number = 6;
let hexLiteral: number = 0xf00d;
// ES6 中的二進制表示法
let binaryLiteral: number = 0b1010;
// ES6 中的八進制表示法
let octalLiteral: number = 0o744;
let notANumber: number = NaN;
複製代碼
編譯結果:
var decLiteral = 6;
var hexLiteral = 0xf00d;
// ES6 中的二進制表示法
var binaryLiteral = 10;
// ES6 中的八進制表示法
var octalLiteral = 484;
var notANumber = NaN;
複製代碼
其中 0b1010 和 0o744 是 ES6 中的 二進制
和 八進制
表示法,它們會被編譯爲十進制數字。
使用 string 定義字符串類型:
let myName: string = 'Tom';
let myAge: number = 25;
// 模板字符串
let sentence: string = `Hello, my name is ${myName}. I'll be ${myAge + 1} years old next month.`;
複製代碼
JavaScript 沒有空值(Void)的概念,在 TypeScript 中,能夠用 void 表示沒有任何返回值的函數:
function alertName(): void {
alert('My name is Tom');
}
複製代碼
聲明一個 void
類型的變量沒有什麼用,由於你只能將它賦值爲 undefined
和 null
:
let unusable: void = undefined;
複製代碼
如下的兩點Null和Undefined尤其須要注意一下,由於在js面試中也常問到。
在 TypeScript 中,可使用 null 和 undefined 來定義這兩個原始數據類型:
let u: undefined = undefined;
let n: null = null;
複製代碼
與
void
的區別是,undefined
和null
是全部類型的子類型
。也就是說undefined
類型的變量,能夠賦值給number
類型的變量:
// 這樣不會報錯
let num: number = undefined;
// 這樣也不會報錯
let u:undefined;
let num:number = u;
複製代碼
而
void
類型的變量不能賦值給number
類型的變量,會報錯
let u: void;
let num: number = u;
// Type 'void' is not assignable to type 'number'.
複製代碼
任意值(Any)用來表示容許賦值爲任意類型。
若是是一個普通類型,在賦值過程當中改變類型是不被容許的:
let myFavoriteNumber: string = 'seven';
myFavoriteNumber = 7;
// Type 'number' is not assignable to type 'string'.
複製代碼
但若是是 any 類型,則容許被賦值爲任意類型。
let myFavoriteNumber: any = 'seven';
myFavoriteNumber = 7;
複製代碼
在任意值上訪問任何屬性都是容許的:
let anyThing: any = 'hello';
console.log(anyThing.myName);
console.log(anyThing.myName.firstName);
複製代碼
也容許調用任何方法:
let anyThing: any = 'Tom';
anyThing.setName('Jerry');
anyThing.setName('Jerry').sayHello();
anyThing.myName.setFirstName('Cat');
複製代碼
能夠認爲,聲明一個變量爲任意值以後,對它的任何操做,返回的內容的類型都是任意值。
變量若是在聲明的時候,未指定其類型,那麼它會被識別爲任意值類型:
let something;
something = 'seven';
something = 7;
something.setName('Tom');
複製代碼
等價於
let something: any;
something = 'seven';
something = 7;
something.setName('Tom');
複製代碼
任意值必定要慎用,由於在聲明any以後。它的任何操做,返回的類型均是 any 對變量類型的檢查變爲毫無心義,同時這也就失去了TypeScripe強類型檢查語言的意義了。
模塊的引入多是由第三方庫引進,沒法確認變量的類型時候,應該用 any(不肯定性類型) - 容許賦值爲任意類型
let notSure: any = 4
notSure = 'maybe it is a string'
notSure = true
notSure.myName
notSure.getName() // 以上不會報任何錯
複製代碼
接下來說一下TypeScript與JavaScript不一樣的概念
聯合類型表示取值能夠爲多種類型中的一種。
let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
複製代碼
let myFavoriteNumber: string | number;
myFavoriteNumber = true;
// Type 'boolean' is not assignable to type 'string | number'.
// Type 'boolean' is not assignable to type 'number'.
複製代碼
聯合類型使用 | 分隔每一個類型。 這裏的 let myFavoriteNumber: string | number
的含義是,容許 myFavoriteNumber
的類型是 string
或者 number
,可是不能是其餘類型。
當 TypeScript 不肯定一個聯合類型的變量究竟是哪一個類型的時候,咱們<b>只能訪問此聯合類型的全部類型裏共有的屬性或方法:</b>
function getLength(something: string | number): number {
return something.length;
}
複製代碼
function getLength(something: string | number): number {
return something.length;
}
// Property 'length' does not exist on type 'string | number'.
// Property 'length' does not exist on type 'number'.
複製代碼
上例中, length
不是 string
和 number
的共有屬性,因此會報錯。
訪問 string 和 number 的共有屬性是沒問題的:
function getString(something: string | number): string {
return something.toString();
}
複製代碼
聯合類型的變量在被賦值的時候,會根據類型推論的規則推斷出一個類型:
let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
console.log(myFavoriteNumber.length); // 5
myFavoriteNumber = 7;
console.log(myFavoriteNumber.length); // 編譯時報錯
// Property 'length' does not exist on type 'number'.
複製代碼
上例中,第二行的 myFavoriteNumber 被推斷成了 string
,訪問它的 length
屬性不會報錯。
而第四行的 myFavoriteNumber 被推斷成了 number
,訪問它的 length
屬性時就報錯了。
在 TypeScript 中,咱們使用接口 (Interfaces)
來定義對象的類型。
在面嚮對象語言中,接口(Interfaces)是一個很重要的概念,它是對行爲的抽象,而具體如何行動須要由類(classes)去實現(implement)。
TypeScript 中的接口是一個很是靈活的概念,除了可用於 對類的一部分行爲進行抽象
之外,也經常使用於對「對象的形狀(Shape)」進行描述。
這裏注意接口裏面定義的對象要用「;」表示,與JS不一樣
interface Person {
name: string;
age: number;
}
let tom:Person = {
name: 'Tom',
age: 25
}
複製代碼
上面的例子中,咱們定義了一個接口 Person
,接着定義了一個變量 tom
,它的類型是 Person
。這樣,咱們就約束了 tom
的形狀必須和接口 Person 一致。
接口通常大寫,如 Person
定義的變量比接口少了一些屬性是不容許的:
interface Person {
name: string;
age: number;
}
let tom: Person = {
name: 'Tom'
};
// Type '{ name: string; }' is not assignable to type 'Person'.
// Property 'age' is missing in type '{ name: string; }'.
複製代碼
定義的變量比接口多一些屬性也是不容許的:
interface Person {
name: string;
age: number;
}
let tom: Person = {
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 tom:Person = {
name: 'Tom'
}
複製代碼
可選屬性的含義就是該屬性能夠不存在。
這是仍然不容許添加未定義的屬性
interface Person {
name: string;
age?: number;
}
let tom:Person = {
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;
[propName: string]: any
}
let tom: Person = {
name: 'Tom',
gender: 'male'
}
複製代碼
使用 [propName: string]
定義了任意屬性取 string
類型的值。
須要注意的是,一旦定義了任意屬性,那麼肯定屬性和可選屬性的類型都必須是它的類型的子集
錯誤寫法:
interface Person {
name: string;
age?: number;
[propName: string]: string;
}
let tom: Person = {
name: 'Tom',
age: 25,
gender: 'male'
};
複製代碼
上例中,任意屬性的值容許是 string
,可是可選屬性 age 的值倒是 number
, number
不是 string
的子屬性,因此報錯了。
一個接口中只能定義一個任意屬性。若是接口中有多個類型的屬性,則能夠在任意屬性中使用聯合類型:
正確寫法:
interface Person {
name: string;
age?: number;
[propName: string]: string | number;
}
let tom:Person = {
name: 'Tom',
age: 25,
gender: 'male'
}
複製代碼
有時候咱們但願對象中的一些字段只能在建立的時候被賦值,那麼能夠用 readonly
定義只讀屬性:
interface Person {
readonly id: number;
name: string;
age?: number;
[propName: string]: any;
}
let tom: Person = {
id: 89757,
name: 'Tom',
gender: 'male'
};
tom.id = 9527;
// Cannot assign to 'id' because it is a constant or a read-only property.
複製代碼
上例中,使用 readonly
定義的屬性 id
初始化後, 又被賦值
了,因此報錯了。
注意,只讀的約束存在於第一次給對象賦值的時候,而不是第一次給只讀屬性賦值的時候:
interface Person {
readonly id: number;
name: string;
age?: number;
[propName: string]: any;
}
let tom: Person = {
name: 'Tom',
gender: 'male'
};
tom.id = 89757;
// 兩處報錯
複製代碼
報錯信息有兩處:
只讀屬性
,因此報錯了。在 TypeScript 中,數組類型有多種定義方式,比較靈活。
表示法 --- 最簡單的數組表示法
let fibonacci: number[] = [1, 1, 2, 3, 5];
複製代碼
數組的項中不容許
出現其餘的類型:
let fibonacci: number[] = [1, '1', 2, 3, 5];
// Type 'string' is not assignable to type 'number'.
複製代碼
數組的一些方法
的參數
也會根據數組在定義時約定的類型進行限制:
let fibonacci: number[] = [1, 1, 2, 3, 5];
fibonacci.push('8');
// Argument of type '"8"' is not assignable to parameter of type 'number'.
複製代碼
上例中,push
方法只容許傳入 number
類型的參數,可是卻傳了一個 "8"
類型的參數,因此報錯了。這裏 "8"
是一個字符串字面量
類型,會在後續章節中詳細介紹。
interface NumberArray {
[index: number]: number;
}
let fibonacci: NumberArray = [1, 1, 2, 3, 5];
複製代碼
NumberArray
表示:只要索引的類型是數字時,那麼值的類型必須是數字。
雖然接口也能夠用來描述數組,可是咱們通常不會這麼作,由於這種方式比前兩種方式複雜多了。
不過有一種狀況例外,那就是它經常使用來表示類數組。
function sum() {
let args: number[] = arguments;
}
// Type 'IArguments' is missing the following properties from type 'number[]': pop, push, concat, join, and 24 more.
複製代碼
上例中,arguments
其實是一個類數組,不能用普通的數組的方式來描述,而應該用接口:
function sum(){
let args: {
[index: number]: number;
length: number;
callee: Function;
} = arguments;
}
複製代碼
在這個例子中,咱們除了約束當索引的類型是數字時,值的類型必須是數字以外,也約束了它還有 length
和 callee
兩個屬性。
事實上經常使用的類數組都有本身的接口定義,如 IArguments
, NodeList
, HTMLCollection
等:
function sum() {
let args: IArguments = arguments;
}
複製代碼
interface IArguments {
[index: number]: any;
length: number;
callee: Function;
}
複製代碼
一個比較常見的作法是,用 any
表示數組中容許出現任意類型:
let list: any[] = ['abcd', 25, { website: 'http://xcatliu.com' }]
複製代碼
在 JavaScript 中,有兩種常見的定義函數的方式——函數聲明和函數表達式:
// 函數聲明
function sum(x,y) {
return x + y;
}
// 函數表達式
let mySum = function(x, y) {
return x + y;
}
複製代碼
限制了參數的類型 和 返回值的 類型
function sum(x: number, y:number):number {
return x + y;
}
複製代碼
注意,輸入多餘的(或者少於要求的)參數,是不被容許的:
function sum(x: number, y: number): number {
return x + y;
}
sum(1, 2, 3);
function sum(x: number, y: number): number {
return x + y;
}
sum(1);
複製代碼
與接口中的可選屬性相似,咱們用 ?
表示可選的參數:
function buildName(firstName: string, lastName?: string) {
if (lastName) {
return firstName + ' ' + lastName;
} else {
return firstName;
}
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom');
複製代碼
須要注意的是,可選參數必須在
必需參數
後面。換句話說,可選參數後面不容許再出現必需參數了:
function buildName(firstName?: string, lastName: string) {
if (firstName) {
return firstName + ' ' + lastName;
} else {
return lastName;
}
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName(undefined, 'Tom');
// A required parameter cannot follow an optional parameter.
複製代碼
TypeScript 會將添加了默認值的參數識別爲可選參數: 不傳的話,默認值爲 Cat
function buildName(firstName: string, lastName: string = 'Cat') {
return firstName + ' ' + lastName;
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom');
複製代碼
此時就不受「可選參數必須接在必需參數後面」的限制了:
function buildName(firstName: string = 'Tom', lastName: string) {
return firstName + ' ' + lastName;
}
let tomcat = buildName('Tom', 'Cat');
let cat = buildName(undefined, 'Cat');
複製代碼
items 是一個數組。因此咱們能夠用數組的類型來定義它:
function push(array: any[], ...items: any[]) {
items.forEach(function(item) {
array.push(item);
});
}
let a = [];
push(a, 1, 2, 3);
複製代碼
經過對函數聲明式學習的瞭解,咱們可能會寫成這樣:
let mySum = function (x: number, y: number):number {
return x + y;
}
複製代碼
這是能夠經過編譯的,不過事實上,上面的代碼只對等號右側的匿名函數進行了類型定義,而等號左邊的 mySum,是經過賦值操做
進行類型推論而推斷出來
的。若是須要咱們手動給 mySum
添加類型,則應該是這樣:
let mySum: (x: number, y: number) => number = function( x: number, y: number): number {
return x + y
}
複製代碼
注意不要混淆了 TypeScript 中的 => 和 ES6 中的 =>。 在 TypeScript 的類型定義中,
=> 用來表示函數的定義
,左邊是輸入類型
,須要用括號
括起來,右邊是輸出類型
。
咱們也可使用接口的方式
來定義一個函數(參數
)(輸出值
)須要符合的形狀:
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
return souce.search(subString) !== -1;
}
複製代碼
採用函數表達式|接口定義函數的方式時,對等號左側進行類型限制,能夠保證之後對函數名賦值時保證參數個數、參數類型、返回值類型不變。
若是你以爲這篇內容對你還蠻有幫助,我想邀請你幫我三個小忙:
也能夠來個人我的博客:
前端時光屋:www.javascriptlab.top/