原文鏈接: https://segmentfault.com/a/1190000009247663
這篇文章主要講怎么寫一個typescript的描述檔案(以d.ts結尾的檔案名,比如xxx.d.ts),
總結一下:從型別type角度分為:基本型別(string、number、boolean等)及其混合;復雜型別(class、function、object)及其混合(比如說又是class又是function),從代碼有效范圍分為:全域變數、模塊變數和又是全域變數又是模塊變數的,從定義檔案來說:自己寫的.d.ts檔案和擴展別人寫的.d.ts檔案,以上三個角度,應該覆寫了描述檔案的各個方面了,
2019.09.12更新說明:
1.增加了用interface的方式宣告函式,
2.增加了在使用模塊化匯入的情況下如何宣告全域變數,
2018.12.18更新說明:
1.增加了全域宣告的原理說明,
2.增加了es6的import、export對應的d.ts檔案寫法,
3.增加了d.ts檔案放置位置的說明,
發現了一個關于typescript比較好的入門教程:https://ts.xcatliu.com/basics...,這是其中的關于描述檔案的檔案,
最近開始從js轉ts了,但是要用到一些描述檔案(d.ts),常用的比如jquery等都可以通過 npm下載到別人已經寫好的npm install @types/jquery,但是還是有一些小眾的或者公司內部的公共庫或者以前寫過的公用js代碼需要自己手動寫描述檔案,
之前也從網上面也找了一些資料,但還是看的云里霧里模糊不清,經過一段摸索,將摸索的結果記錄下來,也希望可以給別人一個參考,
如果你只寫js,d.ts對你來說也是有用的,大部分編輯器能識別d.ts檔案,當你寫js代碼的時候給你智能提示,效果像這樣:
https://segmentfault.com/img/bVDReN?w=594&h=359
詳情可以看我以前寫過的一些文章:https://segmentfault.com/a/11...
通常,我們寫js的是時候有兩種引入js的方式:1,在html檔案中通過<script>標簽全域引入全域變數,2,通過模塊加載器require其他js檔案:比如這樣var j=require('jquery'),
全域型別
首先以第一種方式舉例,
變數
比如現在有一個全域變數,那對應的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里面呼叫的時候就會提示:
https://segmentfault.com/img/bVLuCP?w=579&h=122
我們上面寫的注釋,寫js的時候還可以提示,
有時候同一個函式有若干種寫法:
https://segmentfault.com/img/bVMXTD?w=431&h=107
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 宣告函式
也可以用interface去宣告函式型別:
https://segmentfault.com/img/bVbxExG?w=440&h=178
//Get是一種型別declare interface Get{
(id: string): string
(name:string,age:number):string
}
//get是Get型別的declare var get:Get
用起來長這個樣子:
https://segmentfault.com/img/bVbxExy?w=432&h=136
class
當然除了變數和函式外,我們還有類(class),
declare class Person {
static maxAge: number //靜態變數static getMaxAge(): number //靜態方法constructor(name: string, age: number) //建構式
getName(id: number): string
}
constructor表示的是構造方法:
https://segmentfault.com/img/bVMXy0?w=651&h=91
https://segmentfault.com/img/bVMXzk?w=524&h=100
其中static表示靜態的意思,用來表示靜態變數和靜態方法:
https://segmentfault.com/img/bVMXAV?w=605&h=96
https://segmentfault.com/img/bVMXAg?w=568&h=112
物件
declare namespace OOO{
}
當然了這個物件上面可能有變數,可能有函式可能有類,
declare namespace OOO{
var aaa: number | string
function getName(id: number | string): stringclass Person {
static maxAge: number //靜態變數static getMaxAge(): number //靜態方法constructor(name: string, age: number) //建構式
getName(id: number): string //實體方法
}
}
其實就是把上面的那些寫法放到這個namespace包起來的大括號里面,注意括號里面就不需要declare關鍵字了,效果:
https://segmentfault.com/img/bVMXBu?w=525&h=113
https://segmentfault.com/img/bVMXBv?w=581&h=63
https://segmentfault.com/img/bVMXBB?w=617&h=125
物件里面套物件也是可以的:
declare namespace OOO{
var aaa: number | string
// ...namespace O2{
let b:number
}
}
效果:
https://segmentfault.com/img/bVMXB5?w=547&h=67
混合型別
有時候有些值既是函式又是class又是物件的復雜物件,比如我們常用的jquery有各種用法:
new $()
$.ajax()
$()
既是函式又是物件
declare function $2(s:string): voiddeclare namespace $2{
let aaa:number
}
效果:
作為函式用:
https://segmentfault.com/img/bVMXJ8?w=273&h=85
作為物件用:
https://segmentfault.com/img/bVMXKa?w=540&h=78
也就是ts會自動把同名的namespace和function合并到一起,
既是函式,又是類(可以new出來),又是物件
// 實體方法 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): numberdeclare function People(w: string): numberdeclare class People {
/** 建構式 */
constructor(name: string, age: number)
constructor(id: number)
// 實體屬性和實體方法
name: stringage: numbergetName(): stringgetAge(): number
/** 作為物件,呼叫物件上的方法或者變數 */
static staticA(): numberstatic aaa: string
}
/** 作為物件,呼叫物件上的方法或者變數 */
declare namespace People {
export var abc: number
}
函式用function,類用class宣告,復雜物件就用namespace,這樣的對應關系簡潔明了,
效果:
作為函式使用:
https://segmentfault.com/img/bVMXNA?w=369&h=77
類的靜態方法:
https://segmentfault.com/img/bVMXNN?w=595&h=83
類的建構式:
https://segmentfault.com/img/bVMXNB?w=626&h=103
類的實體方法:
https://segmentfault.com/img/bVMXNE?w=508&h=165
模塊化的全域變數
這個是怎么回事呢,就是有時候我們定義全域變數的時候需要引入(別人寫的)檔案,比如這樣的,我想宣告個全域變數req:
https://segmentfault.com/img/bVbxEAj?w=526&h=124
由于我們當前的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
}
}
用起來長這個樣子:
https://segmentfault.com/img/bVbxEBp?w=430&h=214
其他型別(number、string blabla)就不一一舉例了,參照上面的例子去掉declare填到global的大括號下就行了,
模塊化(CommonJS)
除了上面的全域的方式,我們有時候還是通過require的方式引入模塊化的代碼,
比如這樣的效果:
https://segmentfault.com/img/bVMXOl?w=486&h=134
對應的寫法是這樣的:
declare module "abcde" {
export let a: number
export function b(): numberexport namespace c{
let cd: string
}
}
其實就是外面套了一層 module "xxx",里面的寫法和之前其實差不多,把declare換成了export,
此外,有時候我們匯出去的是一個函式本身,比如這樣的:
https://segmentfault.com/img/bVMXOU?w=358&h=110
對應的寫法很簡單,長這個樣子:
declare module "app" {
function aaa(some:number):numberexport=aaa
}
以此類推,匯出一個變數或常量的話這么寫:
declare module "ccc" {
const c:400
export=c
}
效果:
https://segmentfault.com/img/bVMXPc?w=671&h=109
ES6的模塊化方式(import export)
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)
UMD
有一種代碼,既可以通過全域變數訪問到,也可以通過require的方式訪問到,比如我們最常見的jquery:
https://segmentfault.com/img/bVMXPg?w=539&h=174
https://segmentfault.com/img/bVMXPf?w=523&h=187
其實就是按照全域的方式寫d.ts,寫完后在最后加上declare module "xxx"的描述:
declare namespace UUU{
let a:number
}
declare module "UUU" {
export =UUU
}
效果這樣:
作為全域變數使用:
https://segmentfault.com/img/bVMXPB?w=520&h=86
作為模塊加載使用:
https://segmentfault.com/img/bVMXPM?w=513&h=104
其他
有時候我們擴展了一些內置物件,比如我們給Date增加了一個format的實體方法:
https://segmentfault.com/img/bVMXRR?w=517&h=103
對應的d.ts描述檔案這樣寫:
interface Date {
format(f: string): string
}
.d.ts檔案放到哪里
經常有人問寫出來的d.ts檔案(A.d.ts)檔案放到哪個目錄里,如果是模塊化的話那就放到和原始碼(A.js)檔案同一個目錄下,如果是全域變數的話理論上放到哪里都可以————當然除非你在tsconfig.json 檔案里面特殊配置過,
typescript
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/254958.html
標籤:其他
上一篇:c#三大特征之一的多型實作
下一篇:javascript繼承
