10.类型声明
为什么要用声明文件
(1)如果文件使用 TS 编写,在编译成S 文件时可以自动生成声明文件,并在发布的时候将.d.ts 文件一起发布,我们无需编写声明文件。
(2)当我们在 TS 文件中引入使用第三方库的类型或使用 集成库 时,比: axs 库,ES6 库的 Map 型 ,这些用 开发,不能获取 TS 一样的 举型提示,需要一个声明文件来帮助库的使用者来获取库的类型提示。
声明文件中只对类型定义,不能进行赋值和实现
定义和使用声明文件
- 声明文件可以让我们不需要将typescript重构为TS,只需要加上声明文件就可以使用系统
- 类型声明在编译的时候都会被删除,不会影响真正的代码
- 关键字 declare 表示声明的意思,我们可以用它来做出各种声明:
typescript
declare var/let/const // 声明全局变量
declare function // 声明全局方法
declare class // 声明全局类
declare enum // 声明全局枚举类型
declare namespace // 声明(含有子属性的)全局对象
interface/type // 声明全局类型
12.1 普通类型声明
typescript
declare let name: string; //变量
declare let age: number; //变量
declare function getName(): string; //方法
declare class Animal { name: string } //类
console.log(name, age);
getName();
new Animal();
export default {};
声明jQuery对象
typescript
declare const $: (selector: string) => { //变量
click(): void;
width(length: number): void;
};
$('#root').click();
console.log($('#root').width);
12.2 外部枚举
- 外部枚举是使用
declare enum
定义的枚举类型 - 外部枚举用来描述已经存在的枚举类型的形状
typescript
declare enum Seasons {
Spring,
Summer,
Autumn,
Winter
}
let seasons = [
Seasons.Spring,
Seasons.Summer,
Seasons.Autumn,
Seasons.Winter
];
declare
定义的类型只会用于编译时的检查,编译结果中会被删除。上例的编译结果如下
typescript
var seasons = [
Seasons.Spring,
Seasons.Summer,
Seasons.Autumn,
Seasons.Winter
];
也可以同时使用declare
和 const
typescript
declare const enum Seasons {
Spring,
Summer,
Autumn,
Winter
}
let seasons = [
Seasons.Spring,
Seasons.Summer,
Seasons.Autumn,
Seasons.Winter
];
编译结果
typescript
var seasons = [
0 /* Spring */,
1 /* Summer */,
2 /* Autumn */,
3 /* Winter */
];
12.3 namespace命名空间
- 如果一个全局变量包括了很多子属性,可能使用namespace
- 在声明文件中的
namespace
表示一个全局变量包含很多子属性 - 在命名空间内部不需要使用 declare 声明属性或方法
typescript
declare namespace ${
function ajax(url: string, settings: any): void;
let name: string;
namespace fn {
function extend(object:any): void;
}
}
$.ajax('/api/users',{});
$.fn.extend({
log:function(message:any){
console.log(message);
}
});
export {};
类似于js的模块声明
js
declare module "JQueryModule"{
namespace $ {
function ajax(url: string, settings: any): void;
let name: string;
namespace fn {
function extend(object: any): void;
}
}
export = $
}
另一个ts模块使用:
import $ from'JQueryModule';
$.ajax('/api/users',{});
$.fn.extend({
log:function(message:any){
console.log(message);
}
});
12.4 类型声明文件
- 我们可以把类型声明放在一个单独的类型声明文件中
- 可以在类型声明文件中使用类型声明
- 文件命名规范为:
*.d.ts
- 观看类型声明文件有助于了解库的使用方式
12.4.1 jquery.d.ts
typings/jquery.d.ts
ts
declare const $:(selector:string)=>{
click():void;
width(length:number):void;
}
12.4.2 tsconfig.typescripton
tsconfig.typescripton
typescript
{
"compilerOptions": {
"module": "commontypescript",
"target": "ES2015",
"outDir":"lib"
},
"include": [
"src/**/*",
"typings/**/*"
]
}
12.4.3 test.typescript
src\test.ts
typescript
$('#button').click();
$('#button').width(100);
export {};
12.5 第三方声明文件
- 可以安装使用第三方的声明文件
- @types是一个约定的前缀,所有的第三方声明的类型库都会带有这样的前缀
- JavaScript 中有很多内置对象,它们可以在 TypeScript 中被当做声明好了的类型
- 内置对象是指根据标准在全局作用域(Global)上存在的对象。这里的标准是指 ECMAScript 和其他环境(比如 DOM)的标准
- 这些内置对象的类型声明文件,就包含在TypeScript 核心库的类型声明文件中
12.5.1 使用jquery
typescript
cnpm i jquery -S
//对于common.typescript风格的模块必须使用 import * as
import * as jQuery from 'jquery';
jQuery.ajax('/user/1');
12.5.2 安装声明文件
typescript
cnpm i @types/jquery -S
12.5.3 自己编写声明文件
- 模块查找规则
- node_modules@types\jquery/index.d.ts
- 我们可以自己编写声明文件并配置
tsconfig.typescripton
12.5.3.1 index.d.ts
typescript
types\jquery\index.d.ts
declare function jQuery(selector:string):HTMLElement;
declare namespace jQuery{
function ajax(url:string):void
}
export default jQuery;
12.5.3.2 tsconfig.typescripton
- 如果配置了
paths
,那么在引入包的的时候会自动去paths
目录里找类型声明文件 - 在 tsconfig.typescripton 中,我们通过
compilerOptions
里的paths
属性来配置路径映射 paths
是模块名到基于baseUrl
的路径映射的列表
typescript
{
"compilerOptions": {
"baseUrl": "./",// 使用 paths 属性的话必须要指定 baseUrl 的值
"paths": {
"*":["types/*"]
}
}
import $ from "jquery";
$.ajax('get');
12.5.4 npm声明文件可能的位置
- node_modules/jquery/package.typescripton
- "types":"types/xxx.d.ts"
- node_modules/jquery/index.d.ts
- node_modules/@types/jquery/index.d.ts
- typings\jquery\index.d.ts
12.5.5 查找声明文件
- 如果是手动写的声明文件,那么需要满足以下条件之一,才能被正确的识别
- 给
package.typescripton
中的types
或typings
字段指定一个类型声明文件地址 - 在项目根目录下,编写一个
index.d.ts
文件 - 针对入口文件(package.typescripton 中的 main 字段指定的入口文件),编写一个同名不同后缀的
.d.ts
文件
json
{
"name": "myLib",
"version": "1.0.0",
"main": "lib/index.typescript",
"types": "myLib.d.ts",
}
`
- 先找myLib.d.ts
- 没有就再找index.d.ts
- 还没有再找lib/index.d.typescript
- 还找不到就认为没有类型声明了
12.6 扩展全局变量的类型
12.6.1 扩展局部变量类型
typescript
declare var String: StringConstructor;
interface StringConstructor {
new(value?: any): String;
(value?: any): string;
readonly prototype: String;
}
interface String {
toString(): string;
}
//扩展类的原型
interface String {
double():string;
}
String.prototype.double = function(){
return this+'+'+this;
}
console.log('hello'.double());
//扩展类的实例
interface Window{
myname:string
}
console.log(window.myname);
//export {} 没有导出就是全局扩展
12.6.2 模块内全局扩展
types\global\index.d.ts
typescript
declare global{
interface String {
double():string;
}
interface Window{
myname:string
}
}
export {}
12.7 合并声明
- 同一名称的两个独立声明会被合并成一个单一声明
- 合并后的声明拥有原先两个声明的特性
关键字 | 作为类型使用 | 作为值使用 |
---|---|---|
class | yes | yes |
enum | yes | yes |
interface | yes | no |
type | yes | no |
function | no | yes |
var,let,const | no | yes |
- 类既可以作为类型使用,也可以作为值使用,接口只能作为类型使用
typescript
class Person{
name:string=''
}
let p1:Person;//作为类型使用
let p2 = new Person();//作为值使用
interface Animal{
name:string
}
let a1:Animal;
let a2 = Animal;//接口类型不能用作值
12.7.1 合并类型声明
- 可以通过接口合并的特性给一个第三方为扩展类型声明
use.typescript
typescript
interface Animal{
name:string
}
let a1:Animal={name:'zhufeng',age:10};
console.log(a1.name);
console.log(a1.age);
//注意不要加export {} ,这是全局的
types\animal\index.d.ts
typescript
interface Animal{
age:number
}
12.7.2 使用命名空间扩展类
我们可以使用 namespace 来扩展类,用于表示内部类
typescriptclass Form { username: Form.Item=''; password: Form.Item=''; } //Item为Form的内部类 namespace Form { export class Item {} } let item:Form.Item = new Form.Item(); console.log(item);
12.7.3 使用命名空间扩展函数
- 我们也可以使用
namespace
来扩展函数
typescript
function greeting(name: string): string {
return greeting.words+name;
}
namespace greeting {
export let words = "Hello,";
}
console.log(greeting('zhufeng'))
12.7.4 使用命名空间扩展枚举类型
typescript
enum Color {
red = 1,
yellow = 2,
blue = 3
}
namespace Color {
export const green=4;
export const purple=5;
}
console.log(Color.green)
12.7.5 扩展Store
typescript
import { createStore, Store } from 'redux';
type StoreExt = Store & {
ext: string
}
let store: StoreExt = createStore(state => state);
store.ext = 'hello';
12.8 生成声明文件
- 把TS编译成typescript后丢失类型声明,我们可以在编译的时候自动生成一份typescript文件
json
{
"compilerOptions": {
"declaration": true, /* Generates corresponding '.d.ts' file.*/
}
}
12.9 类型声明实战
typescript
npm link
npm link zf-events
12.9.1 index.typescript
typescript
import { EventEmitter } from "zf-events";
console.log(EventEmitter.defaultMaxListeners);
var e = new EventEmitter();
e.on('message', function (text:string) {
console.log(text)
})
e.emit('message', 'hello');
12.9.2 index.d.ts
typescript
export type Listener = (...args: any[]) => void;
export type Type = string | symbol
export class EventEmitter {
static defaultMaxListeners: number;
emit(type: Type, ...args: any[]): boolean;
addListener(type: Type, listener: Listener): this;
on(type: Type, listener: Listener): this;
once(type: Type, listener: Listener): this;
}