类型定义文件是帮助TS文件,去理解引入的JS库里的内容的。因为这些JS库里,没有TS要求的类型的概念。
我们写代码的时候,通常会遇到一个场景,引入一个类库,发现这个库是用js代码写的,然后ts里用这样的类库就会有问题,导致我们需要安装类库的类型定义文件。安装这样的类型定义文件是非常简单的,但是类型定义文件是怎么写的呢? 原理是什么呢?
- 我们先在index.html里引入 jquery文件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.js"></script>
<script src="./page.ts"></script>
</head>
<body>
</body>
</html>
- 然后我们可以在page.ts里调用jquery的方法,我们会发现,虽然可以运行,但是编辑器会飘红,告诉我们需要安装jquery 的类型文件。因为jquery 是JS写的。
npm i --save-dev @types/jquery
$(function(){
alert(123);
})
现在我们不安装别人写的jquery 的类型定义文件,我们自己写一个类型定义文件 jquery.d.ts。帮助page.ts去理解jquery的JS语法。
- 我们调用的
$
是一个函数,接收一个参数也是一个函数,$
函数返回值是void。参数这个函数返回值也是void
$(function(){
alert(123);
})
- 我们可以这样写,
通过declare var定义全局变量
;.d.ts 文件中的顶级声明必须以 "declare" 或 "export" 修饰符开头
//定义全局变量
declare var $: (params:()=>void) => void;
- 我们刚才把
$
定义为一个全局变量。还可以把$
定义为一个全局函数
// 定义全局函数
declare function $(params:()=>void): void;
函数重载
,对一个函数名字,可以写多个全局函数声明。也就是说一个函数,可以有多种形式。
- 当我们调用函数接收的参数是一个函数。而不是字符串
$(function(){
$('body').html('<div>123</div>');
})
- 我们可以再次定义一个$全局函数声明,这次接收的参数是一个字符串,返回的是对象,对象里有一个属性html,这属性的类型也是函数,接收一个字符串参数,返回一个对象
declare function $(params: string): {
html: (html: string) => {};
};
interface JqueryInstance {
html:(html:string)=>JqueryInstance
}
// 当传函数的时候,返回值是空
declare function $(readyFunc: () => void): void;
// 当传一个选择器的时候,返回值是一个juqery对象
declare function $(selector: string): JqueryInstance;
函数重载的另外一种写法.这种写法利用了interface 的一种特性
,就是在接口里定义两个函数的实现的时候,实际上就是一种函数的重载。表示Jquery 可以有两种实现方式。
interface Jquery {
(readyFunc: () => void): void;
(selector: string): JqueryInstance;
}
当然,如果只是声明了Jquery 接口,那么$
符号是不可以用的,所以可以定义一下$的类型。
interface Jquery {
(readyFunc: () => void): void;
(selector: string): JqueryInstance;
}
declare var $: Jquery;
如何对对象进行类型定义,以及如何对类进行类型定义,以及命名空间的嵌套
// new $.fn.init();
declare namespace $ {
namespace fn {
class init {}
}
}
$(function(){
$('body').html('<div>123</div>');
new $.fn.init();
})
总之,当我们引入外部的一个库,这个库可能会有一些全局的变量、方法、对象无法被typescript无法识别这些东西,我们就可以通过全局的声明的语法让typescript理解库里的这些东西。让我们在typescript里使用这些变量、方法、对象的时候不再报错。
- 定义全局变量 用
declare var
- 定义全局函数 用
declare function
- 定义全局对象 用
declare namespace
- 定义全局函数重载的时候,有了两种方式
declare function $(readyFunc: () => void): void;
declare function $(params: string): JqueryInstance;
// interface Jquery {
// (readyFunc: () => void): void;
// (selector: string): JqueryInstance;
// }
- 如果想要$即是函数又是对象的时候,用interface 就不合适了,还是需要用declare
declare function $(readyFunc: () => void): void;
declare function $(params: string): JqueryInstance;
declare namespace $ {
namespace fn {
class init {}
}
}
ES6的方式,如何通过.d.ts文件,声明一些模块。
- 首先安装一下jquery
npm install jquery --save
.然后在page.ts里引入,会发现报错。原因是 我们的jquery.d.ts文件,并不是模块化声明的。
- 下边我们模块化声明一下 jquery.d.ts。注意 declare 后边跟 module
// ES6 模块化
// 定义一个模块叫做jquery 和通过 import引入的 jquery要一致
declare module 'jquery' {
interface JqueryInstance {
html:(html:string)=>JqueryInstance
}
// 混合类型
function $(readyFunc: () => void): void;
function $(params: string): JqueryInstance;
namespace $ {
namespace fn {
class init {}
}
}
}
- 这个时候,通过 import引入的 方法的引用还是会报错。原因是,当我们用模块化定义d.ts文件的时候,记得在最后一定要把外部要用的东西
通过export导出去
declare module 'jquery' {
interface JqueryInstance {
html:(html:string)=>JqueryInstance
}
// 混合类型
function $(readyFunc: () => void): void;
function $(params: string): JqueryInstance;
namespace $ {
namespace fn {
class init {}
}
}
export = $
}
- 这样,我们定义了一个jquery的模块,里边有关于jquery的混合类型的定义,最终我们把我们引入后,就可以拿到$上各种方法和命名空间。
这种是ES6的模块化方式,还有AMD的等等。