這篇文章主要講怎麼寫一個typescript的描述文件(以d.ts結尾的文件名,好比xxx.d.ts)。
總結一下:
從類型type
角度分爲:基本類型(string、number、boolean等)及其混合;複雜類型(class、function、object)及其混合(好比說又是class又是function)。
從代碼有效範圍分爲:全局變量、模塊變量和又是全局變量又是模塊變量的。
從定義文件來講:本身寫的.d.ts
文件和擴展別人寫的.d.ts
文件。
以上三個角度,應該覆蓋了描述文件的各個方面了。javascript
2019.09.12更新說明:html
1.增長了用interface的方式聲明函數。 2.增長了在使用模塊化導入的狀況下如何聲明全局變量。
2018.12.18更新說明:java
1.增長了全局聲明的原理說明。 2.增長了es6的import、export對應的d.ts文件寫法。 3.增長了d.ts文件放置位置的說明。
發現了一個關於typescript比較好的入門教程:https://ts.xcatliu.com/basics...,這是其中的關於描述文件的文檔。jquery
最近開始從js轉ts了。可是要用到一些描述文件(d.ts),經常使用的好比jquery等均可以經過 npm下載到別人已經寫好的npm install @types/jquery
。可是仍是有一些小衆的或者公司內部的公共庫或者之前寫過的公用js代碼須要本身手動寫描述文件。es6
以前也從網上面也找了一些資料,但仍是看的雲裏霧裏模糊不清,通過一段摸索,將摸索的結果記錄下來,也但願能夠給別人一個參考。ajax
若是你只寫js,d.ts對你來講也是有用的,大部分編輯器能識別d.ts文件,當你寫js代碼的時候給你智能提示。效果像這樣:typescript
詳情能夠看我之前寫過的一些文章:https://segmentfault.com/a/11...express
一般,咱們寫js的是時候有兩種引入js的方式:
1,在html文件中經過<script>
標籤全局引入全局變量。
2,經過模塊加載器require其餘js文件:好比這樣var j=require('jquery')
。npm
首先以第一種方式舉例。json
好比如今有一個全局變量,那對應的d.ts文件裏面這樣寫。
declare var aaa:number
其中關鍵字declare
表示聲明的意思。在d.ts文件裏面,在最外層聲明變量或者函數或者類要在前面加上這個關鍵字。在typescript的規則裏面,若是一個.ts
、.d.ts
文件若是沒有用到import或者export語法的話,那麼最頂層聲明的變量就是全局變量。
因此咱們在這裏聲明瞭一個全局變量aaa,類型是數字類型(number)。固然了也能夠是string類型或者其餘或者:
declare var aaa:number|string //注意這裏用的是一個豎線表示"或"的意思
若是是常量的話用關鍵字const表示:
declare const max:200
由上面的全局變量的寫法咱們很天然的推斷出一個全局函數的寫法以下:
/** id是用戶的id,能夠是number或者string */ decalre function getName(id:number|string):string
最後的那個string表示的是函數的返回值的類型。若是函數沒有返回值能夠用void表示。
在js裏面調用的時候就會提示:
咱們上面寫的註釋,寫js的時候還能夠提示。
有時候同一個函數有若干種寫法:
get(1234) get("zhangsan",18)
那麼d.ts對應的寫法:
declare function get(id: string | number): string declare function get(name:string,age:number): string
若是有些參數無關緊要,能夠加個?
表示非必須。
declare function render(callback?:()=>void): string
js中調用的時候,回調傳不傳均可以:
render() render(function () { alert('finish.') })
也能夠用interface去聲明函數類型:
//Get是一種類型 declare interface Get{ (id: string): string (name:string,age:number):string } //get是Get類型的 declare var get:Get
用起來長這個樣子:
固然除了變量和函數外,咱們還有類(class)。
declare class Person { static maxAge: number //靜態變量 static getMaxAge(): number //靜態方法 constructor(name: string, age: number) //構造函數 getName(id: number): string }
constructor
表示的是構造方法:
其中static表示靜態的意思,用來表示靜態變量和靜態方法:
declare namespace OOO{ }
固然了這個對象上面可能有變量,可能有函數可能有類。
declare namespace OOO{ var aaa: number | string function getName(id: number | string): string class Person { static maxAge: number //靜態變量 static getMaxAge(): number //靜態方法 constructor(name: string, age: number) //構造函數 getName(id: number): string //實例方法 } }
其實就是把上面的那些寫法放到這個namespace包起來的大括號裏面,注意括號裏面就不須要declare關鍵字了。
效果:
對象裏面套對象也是能夠的:
declare namespace OOO{ var aaa: number | string // ... namespace O2{ let b:number } }
效果:
有時候有些值既是函數又是class又是對象的複雜對象。好比咱們經常使用的jquery有各類用法:
new $() $.ajax() $()
declare function $2(s:string): void declare namespace $2{ let aaa:number }
效果:
做爲函數用:
做爲對象用:
也就是ts會自動把同名的namespace和function合併到一塊兒。
// 實例方法 interface People{ name: string age: number getName(): string getAge():number } interface People_Static{ /** 構造函數 */ new (name: string, age: number): People new (id:number): People /** 做爲對象,調用對象上的方法或者變量 */ staticA():number aaa:string /** 做爲函數使用 */ (w:number):number (w:string):number } declare var People:People_Static
ts3.6增長了新功能,function聲明和class聲明能夠合併了,因此又有了新的寫法:
/** 做爲函數使用 */ declare function People(w: number): number declare function People(w: string): number declare class People { /** 構造函數 */ constructor(name: string, age: number) constructor(id: number) // 實例屬性和實例方法 name: string age: number getName(): string getAge(): number /** 做爲對象,調用對象上的方法或者變量 */ static staticA(): number static aaa: string } /** 做爲對象,調用對象上的方法或者變量 */ declare namespace People { export var abc: number }
函數用function
,類用class
聲明,複雜對象就用namespace
,這樣的對應關係簡潔明瞭。
效果:
做爲函數使用:
類的靜態方法:
類的構造函數:
類的實例方法:
這個是怎麼回事呢,就是有時候咱們定義全局變量的時候須要引入(別人寫的)文件,好比這樣的,我想聲明個全局變量req:
因爲咱們當前的d.ts文件使用了import/export語法,那麼ts編譯器就不把咱們經過declare var xxx:yyy
當成了全局變量了,那麼咱們就須要經過如下的方式聲明全局變量:
import { Request,Response} from 'express' declare global { var req: Request var res: Response namespace OOO { var a:number } }
用起來長這個樣子:
其餘類型(number、string blabla)就不一一舉例了,參照上面的例子去掉declare填到global的大括號下就好了。
除了上面的全局的方式,咱們有時候仍是經過require的方式引入模塊化的代碼。
好比這樣的效果:
對應的寫法是這樣的:
declare module "abcde" { export let a: number export function b(): number export namespace c{ let cd: string } }
其實就是外面套了一層 module "xxx"
,裏面的寫法和以前其實差很少,把declare
換成了export
。
此外,有時候咱們導出去的是一個函數自己,好比這樣的:
對應的寫法很簡單,長這個樣子:
declare module "app" { function aaa(some:number):number export=aaa }
以此類推,導出一個變量或常量的話這麼寫:
declare module "ccc" { const c:400 export=c }
效果:
declare var aaa: 1 declare var bbb: 2 declare var ccc: 3 //由於這個文件裏咱們使用了import或者export語法,因此bbb和ccc在其餘代碼裏不能訪問到,即不是全局變量 export { aaa }
使用:
import { a1, a2 } from "./A" console.log(a1) console.log(a2)
那麼對應的A.d.ts文件是這樣寫的:
declare var a1: 1 declare var a2: 2 export { a1,a2 }
固然了也能這樣寫:
export declare var a1: 1 export declare var a2: 2
不過建議以前的第一種寫法,緣由看這裏https://segmentfault.com/a/11...
固然了還有人常常問default導出的寫法:
declare var a1: 1 export default a1
使用的時候固然就是這樣用了:
import a1 from "./A"; console.log(a1)
有一種代碼,既能夠經過全局變量訪問到,也能夠經過require的方式訪問到。好比咱們最多見的jquery:
其實就是按照全局的方式寫d.ts,寫完後在最後加上declare module "xxx"
的描述:
declare namespace UUU{ let a:number } declare module "UUU" { export =UUU }
效果這樣:
做爲全局變量使用:
做爲模塊加載使用:
有時候咱們擴展了一些內置對象。好比咱們給Date增長了一個format的實例方法:
對應的d.ts描述文件這樣寫:
interface Date { format(f: string): string }
常常有人問寫出來的d.ts文件(A.d.ts)文件放到哪一個目錄裏,若是是模塊化的話那就放到和源碼(A.js)文件同一個目錄下,若是是全局變量的話理論上放到哪裏均可以————固然除非你在tsconfig.json 文件裏面特殊配置過。