遇到問題:
開發超五card過程中,使用TypeScript寫代碼,因爲項目中需要引入moment,來處理日期格式問題。像往常一樣下載引入:
tnpm i moment --save
import * as moment from 'moment';
引入後發現有紅色波浪線,但是頁面並沒有報錯
前端第三方庫大多都是非 TypeScript 庫,基本上都是使用 JS 編寫的,在 TS 中使用非 TS 編寫的第三方庫,需要有個 xx.d.ts 聲明文件。引入聲明文件後,就能獲得對應的代碼補全、接口提示等功能。這裏的報錯就是缺少聲明文件引起的。
解決方案:
一,npm包
首先,查找下社區有沒有引入模塊的聲明文件。通過 https://www.npmjs.com/ 網址進行搜索。一般依賴的格式爲 @types/xxxx 。例如:@types/jQuery,@types/lodash等等。如果可以搜索到,就直接安裝。npm install @types/lodash --save-dev。一般情況下會自動查找node?_modules/@types中的聲明文件。
二,某些模塊自帶聲明文件
有些模自帶聲明文件。判斷依據是 package.json 中有 types 字段,或者有一個 index.d.ts 聲明文件。這種模式不需要額外安裝其他包,是最爲推薦的,所以以後我們自己創建 npm 包的時候,最好也將聲明文件與 npm 包綁定在一起。
例如:和 moment 等等。node_modules中找到moment模塊,打開可以看到自帶了聲明文件,但是還是會有報錯的問題。這裏可能是momet的聲明文件有點問題。
對於moment的聲明文件,我的解決方法是這樣的:
npm i typings 項目中會多個typings文件夾 typings install dt~moment --global --save
剛剛下載的moment的聲明文件被放在typings下面
index.d.ts文件內容:
這是一種引入.d.ts文件的方式。
修改tsconfig.json配置文件,include屬性中新增一個typings元素,這樣如果node_modules中找不到聲明文件,就會去typings中查找。
{
"compilerOptions": {
"baseUrl": ".",
"target": "es6",
"noImplicitThis": true,
"noImplicitUseStrict": true,
"types": ["mini-types"]
},
"include": [
"src/**/*",
"typings", // 新增
]
}
這樣報錯就沒有了,鼠標放在引入的moment上面,可以很清楚的看到提示信息:
三,自己寫聲明文件
如果以上兩種方式都不能解決問題,就需要自己寫聲明文件了。
比如在項目中引入一個第三方工具庫:
import startApp from '@alipay/merchant-mini-utils/startApp';
這個工具沒有自己的聲明文件。
可以在typings/index.d.ts文件中寫自己的聲明語句,擴展全局模塊:
declare module "@alipay/merchant-mini-utils/startApp" {
type Params = {
appId: string;
startMultApp: string;
redirect: boolean;
success: () => void;
fail: () => void
};
export default function startApp(url: string, params?: Params): void
}
在文件引入的地方可以看到提示信息,一眼就知道函數的使用方法,需要傳遞什麼參數。
這裏留有一個問題,感覺這樣寫不太對,未完待續。。。。
聲明文件的書寫規範
聲明文件的新語法有:
declare var
聲明全局變量declare function
聲明全局方法declare class
聲明全局類declare enum
聲明全局枚舉類型declare namespace
聲明(含有子屬性的)全局對象interface
和type
聲明全局類型export
導出變量export namespace
導出(含有子屬性的)對象export default
ES6 默認導出export =
commonjs 導出模塊export as namespace
UMD 庫聲明全局變量,UMD 固定語法。declare global
擴展全局變量declare module
擴展模塊/// <reference />
三斜線指令
一 聲明全局變量
- 文件內容定義了一個變量:
var a = 10;
引入方式:
<script src="./index.js"></script>
引入之後,變量a便可以全局使用,這個時候就需要在inde.d.ts文件中聲明一個全局變量:
declare var a:number;
這樣就可以在ts文件直接使用這個全局變量了。
二 聲明全局函數和帶有子屬性的全局對象
- 文件內容定義了一個函數,函數也是對象,帶有自己的靜態屬性:
function globalFunc(params){
console.log(params);
};
globalFunc.version = "v1.0.0";
globalFunc.dosomething = function(){
console.log("I am globalFunc dosomething");
};
<script src="./globalFunc.js"></script>
聲明文件:
declare function globalFunc(params:globalFunc.params):void
//類型兼容性
declare namespace globalFunc{
const version : string;
interface params{
[key:string]:any
}
function dosomething():void;
}
注意,在聲明文件中聲明的類型,接口都會是全局的,儘量放在局部環境中去聲明。
declare function 聲明全局函數
declare namespace 聲明(含有子屬性的)全局對象
三 擴展全局變量
上面函數文件引入html後,會成爲一個全局的函數。如果我在index.ts文件中想要對這個全局函數進行擴展:
globalFunc.add = ()=>{};
//報錯如下
//Property 'add' does not exist on type 'typeof globalFunc'.
就需要增加聲明:
declare global {
namespace globalFunc{
function add() :void
}
}
globalFunc.add = ()=>{};
Augmentations for the global scope can only be directly nested in external modules or ambient module declarations.
全局作用域的擴展只能直接嵌套在外部模塊或環境模塊聲明中
可以理解爲 declare global
直接作用在模塊中,也就是必須出現 import 或者 export 這些文件裏面,這個文件纔不會被當成一個全局的 TS 腳本,而是模塊。所以,我們可以導出一個空對象,用來告訴編譯器這是一個模塊的聲明文件。
// declare global 擴展全局變量
export {}
declare global {
namespace globalFunc{
function add() :void
}
}
globalFunc.add = ()=>{};
console.log(globalFunc.add());
通過 declare global 可以擴展全局變量。
四:擴展全局模塊
項目中引入了moment模塊,通過這種方式引入:import moment from "moment"
如果想對moment對象擴展一個方法:
moment.getDate = function(){
const date = new Date();
return Number(date);
};
就需要增加聲明文件:
declare module "moment" {
export function getDate():number;
}
通過 declare module 可以直接擴展第三方模塊。