引言:最近筆者在學習typescript,同時應用到項目開發中,除了簡單的類型約束之外,因爲typescript仍有許多讓初學者不明確的點(官方文檔也沒有寫清楚),故筆者整理此文章供初學者學習參考。
typescript是javascript的超集,在js的基礎上,爲你的變量參數等加上類型定義,因而就變成了typescript,在項目中使用typescript會有如下幾點好處:javascript
number、string、boolean、object、null、undefined、symboljava
tuple元組指的是能夠對數組內不一樣項設置不一樣的數值類型;
any類型不指定變量具體類型;
Array<T>中T爲任意類型,也可爲自定義類型;
enum爲枚舉類型。
注:有時候爲了ts不報錯,能夠經過類型斷言繞過檢測。node
除了以上的基本類型值和ts中定義的特殊類型之外,使用者還能夠自定義類型,經過interface定義一個未實現的接口,它能夠用來限制變量所具有的數據格式,同理,class做爲構造類,也可用來變量類型,type是類型別名,有點相似interface,但其主要用於類型別名和類型合併等。typescript
interface Person{ name: string } class Person{ name: string } type Person{ name: string } const person: Person;
以上三者的約束效果是一致的。那何時用哪一個呢?關鍵在於你對於接口和類的理解,當你提煉的只是一個通用接口的時候能夠用interface,當你要提供一個能夠實例化的class給外部模塊使用的時候,你須要用class。type前面已經介紹了,主要用於類型別名或合併。因此只要你理解三者用途的區別,天然會知道何時使用哪一個了。數組
若是是普通函數,能夠直接經過interface定義:數據結構
interface testfunction{ (name: string):number } const myfunc: testfunction = ()=>{return 1;}
定義函數的傳參類型以及返回值類型。
固然你也能夠直接在函數上加約束條件,不須要單獨提取一個interface來定義函數類型:函數
const myfunc = (name: string):number{ return 1; }
若是是構造函數類的話,就不贅述了,構造函數類不一樣於普通函數。
固然,ts中容許對函數重載,以及設置可選參數,即在參數key後加個問號。學習
你能夠對class中的屬性設置爲只讀屬性,即設置readonly。
你能夠對class中的方法添加public、private、protected修飾符。
你能夠對class實現接口。類能夠繼承類,類實現接口,接口繼承接口。
你能夠實現抽象類abstract class,抽象類做爲其它派生類的基類使用。 它們通常不會直接被實例化。 不一樣於接口,抽象類能夠包含成員的實現細節。 abstract關鍵字是用於定義抽象類和在抽象類內部定義抽象方法。this
1)、public修飾符表示屬性是公開的,能夠經過實例去訪問該屬性。類屬性默認都是public屬性。
2)、private修飾符表示屬性是私有的,只有實例的方法才能訪問該屬性。
3)、protected修飾符表示屬性是保護屬性,只有實例的方法和派生類中的實例方法才能訪問到。
4)、固然除此以外,還有static修飾符,表示只能經過類自己來調用方法,不能經過實例調用。spa
interface PersonInterface{ name: string } class Person implements PersonInterface{ readonly name: string constructor({name: string} = {}){ this.name = name } public getMyName(){ this.myselfTest() this.lastTest() return this.name } static test(){ console.log(`test`) } private myselfTest(){ console.log(`my test`) } protected lastTest(){ console.log(`protected`) } } Person.test() const person = new Person() person.getMyName() //myselfTest只能在實例方法中調用 //lastTest能夠在父類和子類的實例方法中調用 class Runner extends Person{ public test(){ this.lastTest() } }
泛型泛型,就是比較空泛的類型,不明確的類型,不一樣於any,any直接把全部東西都只帶成any,不作任何的約束,泛型能夠對參數和返回值之間設置關聯約束,同時,也能夠在設置變量的數據類型的時候,傳入某種具體類型,去「具化泛型」。
函數中的使用:
function someFunction<T>(arg: T) : T { return arg; } someFunction('123') someFunction<number>(123)
接口 or type 中使用:
interface character<T>{ type: T name: string } type character<T> = { type: T name: string } const char: character<number>
經過傳入number具化泛型的約束範圍,同時,接口和type均可以再進行復用,這就是泛型在這裏起到的做用。
注:泛型還能夠添加額外的泛型約束,extend某個接口,讓ts知道這個泛型T背後可能存在的額外屬性。
ts中存在交叉類型,聯合類型,is、keyof,感受除了聯合類型用的比較多之外,其餘不多用到,不贅述。
除此以外,還有一個是ts中的聲明合併,interface的聲明能夠合併,namespace的聲明也能夠合併。因爲能夠重複聲明,以後會自動合併,因此咱們也能夠藉此來對第三方庫中的interface或namespace進行擴展。
interface Person{ name: string } interface Person{ address: string } ==> interface Person{ name: string address: string } namespace Test{ const name: string } namespace Test{ function getName(){} } ==> namespace Test{ const name: string function getName():void }
namespace是個神奇的概念,命名空間,你也能夠當作這是一個掛載點,是一個實例化的對象{},它上面掛載了一些屬性和方法。這會讓你更好的理解它,它不是一個虛擬的存在,像interface、class都只是定義了類型,並無實例化對象,可是命名空間不同,你能夠把它當作一個現實存在的實例對象。那麼接下來你會更好地理解下面這個操做:
declare function globalFunc(name: string): void declare namespace globalFunc{ const version: string function otherFunc(name: string): void }
注:declare是類型定義文件中的語法,後面會介紹到。
在這裏我declare一個全局的方法,同時我declare了一個命名空間和方法同名,其實這時候你就能夠認爲這個方法的實例對象上掛載了一些屬性和方法。畢竟在js中,函數自己也是一個對象。就像這樣子:
const globalFunc = ()=>{} globalFunc.version = '1.0.0' globalFunc.otherFunc = ()=>{}
這樣,你就能很好地理解這種寫法了。並且這種寫法十分常見。
一、當你引入一個js文件的時候,ts會去查找對應文件目錄下是否存在同名的d.ts文件,不管是相對路徑仍是絕對路徑仍是第三方模塊的引入,都是如此;
二、若是你本身在書寫一個第三方模塊,那麼直接用ts開發,編譯產物中變回自動生成模塊文件的d.ts文件;
三、在開發本身的項目或者模塊過程當中,不可避免你要引入第三方模塊,或者對全局的屬性進行擴展,這個時候你須要書寫本身項目中的全局類型定義文件,你能夠將他們統一放置在typings文件夾下,ts會自動查找項目中的d.ts文件,(且不與其餘文件存在同名的狀況下),例如你能夠在項目下建立一個global.d.ts文件,對第三方模塊或全局模塊作額外定義或擴展;
四、第三方模塊都要本身來定義嗎?非也,大部分的第三方庫都有對應的類型定義文件,若是沒有也不要擔憂,有人幫你寫好了類型定義文件,例如,在nodejs中開發的時候,引用原生模塊時,你能夠經過引入@types/node包來告訴ts原生模塊中的屬性方法,其餘第三方庫也是相似的。
五、declare必須是在全局聲明文件中使用時,纔能有效定義全局變量,若是使用了export import等,將會被斷定爲第三方模塊,而不會當作全局定義文件。
六、當你在擴展第三方模塊的時候,在你的聲明文件中,使用import * as xx from 'xx'模塊,你能獲取到裏面定義的interface、enum這種"虛擬"類型,但當你在業務代碼ts文件中引用時,只能訪問到模塊實際掛載的屬性的方法。
一個項目中的全局聲明文件多是這樣的:
//定義全局變量 declare const version: string //定義全局方法 declare function jQuery(selector: string): any; //定義全局類 declare class Person{ ... } //定義第三方模塊 declare module 'fs'{ import * as fs from 'fs' function myTestFunc(): void //擴展方法 //自定義一個interface擴展fs內部的interface interface myTestInterface extends fs.xxinterface{ name: string } //定義一個模塊上掛載的新屬性,類型爲自定義擴展後的interface const myTestName: myTestInterface }
接下來,一個模塊定義文件多是這樣的:(你能夠查閱官方文檔,裏面會有更多的模板實例,這裏會展現一個簡單的栗子並解釋)
/*當你的模塊是一個UMD模塊且是在外部加載好的時候,添加這一行,告訴ts全局有這個變量 */ export as namespace myClassLib; /*模塊所暴露的對象 */ export = MyClass; /*你的構造函數 */ declare class MyClass { constructor(someParam?: string); someProperty: string[]; myMethod(opts: MyClass.MyClassMethodOptions): number; } /*你能夠把你的類型定義放到Myclass命名空間下統一管理,也能夠供給其餘人擴展它 */ declare namespace MyClass { export interface MyClassMethodOptions { width?: number; height?: number; } }
看完了全局聲明文件、模塊聲明文件以及聲明文件注意事項,相信你對聲明文件的使用書寫以及瞭然於心!
筆者我的整理了額外的一些疑問,這裏整理了一下羅列以下:
type和interface自己均可以用來定義類型,約束變量的數據結構,可是type和interface的應用場景不一樣,須要你更好地理解這二者。type能夠做爲類型別名,也能夠再type中使用聯合類型,例如type myType = number|string,這是interface沒法實現的。而interface不只能夠定義類型,還定義一個待實現的接口,它的用處更普遍,而且在第三方模塊的擴展中,通常都是對interface進行再擴展,你沒法對type進行再擴展。
1)、抽象類要被子類繼承,接口要被類實現。
2)、抽象類中能夠聲明和實現方法,接口只能申明。
3)、接口抽象級別更高
4)、抽象類用來抽象類別,接口用來抽象功能。
declare module是定義一個模塊,module自己和namespace相似,都是一個掛載點,可是module > namespace,一個module的定義文件裏能夠含有多個namespace,namespace只是一個輔助的掛載點,在之前namespace被稱爲內部模塊,可是後來不這麼叫了,以防混淆。
三斜線指令僅可放在包含它的文件的最頂端,只可以使用在d.ts文件中。
看一下二者的使用:
/// <reference path="..." /> 用於聲明對某個路徑文件的依賴。 /// <reference types="..." />用於聲明對某個包的依賴。 例如 /// <reference types="node" /> 代表這個文件使用了@types/node/index.d.ts裏面聲明的名字
path用於約束聲明文件之間的依賴關係,types直接聲明瞭對某個包的依賴,相信你已經明白了。
感謝你看完了這篇文章,這篇文章包含了入門的基礎知識,可是若是你想了解更多有關基礎知識,還請移步官網。其次,咱們概括總結了ts中進階、難度較高的語法,同時對聲明文件的定義進行了梳理,最後咱們對常見的疑惑點進行了概括整理。但願這篇入門教程對你有所幫助~