遇到问题:
开发超五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 可以直接扩展第三方模块。
比如在taro创建的小程序中使用typescript开发,引入第三方组件库:
import { View, Button, Text } from '@tarojs/components';
需要为这个第三方组件库写声明文件:
global.d.ts
declare module '@tarojs/components' {
export const View: React.ComponentType<any>;
export const Button: React.ComponentType<any>;
export const Text: React.ComponentType<any>;
}