- 声明文件可以让我们不需要将 js 重构为 ts,只需要加上声明文件就可以使用系统
- 类型声明在编译的时候都会被删除,不会影响真正的代码
- 关键字 declare 表示声明的意思,我们可以用它来做出各种声明
- declare 只是声明,不需要实现。
普通类型声明
types/index.d.ts
// 通过 declare 来声明变量,这个声明只是为了告诉ts有这个变量,避免报错,没有任何意义// 声明变量declare let book:string;declare var book2:string;declare const book3:string;declare function add(x:number, y:number):number; //声明方法declare class Person {}; //声明类// 声明 接口 和 类型declare interface Animal {name:string}; //同名接口可以自动合并declare interface Animal {age:number};declare type Sum = (x:number, y:number) => number;
src/index.ts
// 有了声明之后,下面这些就不会报错了console.log(book);console.log(book2);console.log(book3);add(1, 2);new Person();let a1:Animal = {name: 'jack', age: 1};let s1:Sum = (x, y) => x+y;
外部枚举
- 外部枚举是使用
declare enum定义的枚举类型 - 外部枚举用来描述已经存在的枚举类型的形状 ```typescript declare enum Seasons { Spring, Summer, Autumn, Winter }
let seasons = [ Seasons.Spring, Seasons.Summer, Seasons.Autumn, Seasons.Winter ];
//编译为 //declare 定义的类型只会用于编译时的检查,编译结果中会被删除。 var seasons = [ Seasons.Spring, Seasons.Summer, Seasons.Autumn, ];
也可以同时使用`declare` 和 `const````typescriptdeclare const enum Seasons {Spring,Summer,Autumn,Winter,}let seasons = [Seasons.Spring,Seasons.Summer,Seasons.Autumn,Seasons.Winter];//编译为var seasons = [0 /* Spring */,1 /* Summer */,2 /* Autumn */,3 /* Winter */];
namespace
- 如果一个全局变量包括了很多子属性,可能使用 namespace
- 在声明文件中的
namespace表示一个全局变量中包含很多子属性 - 在命名空间内部不需要使用 declare 声明属性或方法,也不需要加export 导出
declare namespace $${function ajax(url:string,settings:any):void; //这里不用加export也可以let name:string;namespace fn {function extend(object:any):void;}}$$.ajax('/api/users',{});$$.fn.extend({log:function(message:any){console.log(message);}});
类型声明文件
- 我们可以把类型声明放在一个单独的类型声明文件中,一般是根目录下 types/*
- 可以在类型声明文件中使用类型声明
- 文件命名规范为
*.d.ts - 观看类型声明文件有助于了解库的使用方式
tsconfig.json
{"compilerOptions": {/* 模块解析相关参数 */"moduleResolution": "node", /* 选择模块解析策略,有'node'和'classic'两种类型' */"esModuleInterop": true, /* 通过为导入内容创建命名空间,实现CommonJS和ES模块之间的互操作性 */"baseUrl": "./", /* 设置解析非相对模块名称的基本目录,相对模块不会受baseUrl的影响 */"paths": { /* 设置模块名称到基于baseUrl的路径映射 */"*": ["types/*"]},},/* 指定要编译的路径列表,但是和files的区别在于,这里的路径可以是文件夹,也可以是文件,可以使用相对和绝对路径,而且可以使用通配符,比如"./src"即表示要编译src文件夹下的所有文件以及子文件夹的文件 */"include": ["src/**/*", /* 匹配任意字符,不包括路径分隔符;** 匹配任意字符,包括路径分隔符 */"types/**/*",],}
types/jquery.d.ts
// declare module 'jquery';// declare module 'jquery' {};declare function $$(selector:string):{click():void;css(val:object):void,width(val:number):void,height(val:number):void,}// 命名空间同名可以合并;// 函数 和 命名空间 同名也可以合并declare namespace $$ {const name:string;const fn2:({extend():void})}//declare namespace $$ {namespace fn{function extend():void;}}export default $$;
src/index.ts
import $$ from 'jquery';$$("#root").height(100)console.log($$.name);$$.fn.extend();$$.fn2.extend();
types/index.d.ts 处理scss 和 less 的声明报错
// 配置全局ts文件// 声明 .scss和 .less文件// declare module '*.css'// declare module '*.less'// declare module '*.scss'// declare module '*.svg'declare module '*.module.scss' {const classes: { [key: string]: string };export default classes;}declare module '*.module.less' {const classes: { [key: string]: string };export default classes;}declare module '*.less' {const classes: { [key: string]: string };export default classes;}
第三方声明文件
- 可以安装使用第三方的声明文件
- @types是一个约定的前缀,所有的第三方声明的类型库都会带有这样的前缀
- JavaScript 中有很多内置对象,它们可以在 TypeScript 中被当做声明好了的类型
- 内置对象是指根据标准在全局作用域(Global)上存在的对象。这里的标准是指 ECMAScript 和其他环境(比如 DOM)的标准
- 这些内置对象的类型声明文件,就包含在TypeScript 核心库的类型声明文件中
使用jquery
cnpm i jquery -S # 安装jquery库cnpm i @types/jquery -S # 安装jquery库的类型声明文件//对于common.js风格的模块必须使用 import * as 或 import jQuery = require('jquery');import * as jQuery from 'jquery';jQuery.ajax('/user/1');// 如果想要 import jQuery from 'jquery'; 需要开启 "esModuleInterop": true,
自己编写声明文件
- 模块查找规则
- node_modules\@types\jquery/index.d.ts
- 我们可以自己编写声明文件并配置
tsconfig.json
index.d.ts
// jQuery本身是一个函数,它自身也有一些属性declare function jQuery(selector:string):HTMLElement;declare namespace jQuery {function ajax(url:string):void;}export = jQuery;
tsconfig.json
- 如果配置了
paths,那么在引入包的的时候会自动去paths目录里找类型声明文件 - 在 tsconfig.json 中,我们通过
compilerOptions里的paths属性来配置路径映射 paths是模块名到基于baseUrl的路径映射的列表{"compilerOptions": {/* 设置解析非相对模块名称的基本目录,相对模块不会受baseUrl的影响 */"baseUrl": "./",/* 设置模块名称到基于baseUrl的路径映射 */"paths": {"*": ["types/*"]}}}
import * as jQuery from 'jquery';jQuery.ajax();
npm声明文件可能的位置
- node_modules/jquery/package.json
- “types”:”types/xxx.d.ts”
- node_modules/jquery/index.d.ts
- node_modules/@types/jquery/index.d.ts
- typings\jquery\index.d.ts
查找声明文件
- 如果是手动写的声明文件,那么需要满足以下条件之一,才能被正确的识别
- 给
package.json中的types或typings字段指定一个类型声明文件地址 - 在项目根目录下,编写一个
index.d.ts文件 针对入口文件(package.json 中的 main 字段指定的入口文件),编写一个同名不同后缀的
.d.ts文件{"name": "myLib","version": "1.0.0","main": "lib/index.js","types": "myLib.d.ts",}
先找myLib.d.ts
- 没有就再找index.d.ts
- 还没有再找lib/index.d.js
- 还找不到就认为没有类型声明了
扩展全局变量的类型
扩展局部变量的类型
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;}// let result = 'hello'.double();// 等同于let result = new String('hello').double();console.log(result); //hellohello//扩展类的实例interface Window{myname:string}console.log(window.myname);
模块内的全局扩展
types\global\index.d.ts
export {}// declare global{} 在模块里扩展全局变量declare global{interface String {double():string;}interface Window{myname:string}}
合并声明
- 同一名称的两个独立声明会被合并成一个单一声明
合并后的声明拥有两个声明的特性 | 关键字 | 作为类型使用 | 作为值使用 | | —- | —- | —- | | class | yes | yes | | enum | yes | yes | | interface | yes | no | | type | yes | no | | function | no | yes | | var,let,const | no | yes |
类既可以作为类型使用,也可以作为值使用,接口只能作为类型使用 ```typescript interface A{ name:string; } interface A{ age:number; } type AAA = A; let aaa:AAA = {name:’jack’, age:10}; //ok // let bbb = A; //error,接口类型不能用作值
// 声明一个类的时候 // 得到2个类型(构造函数的类型,实例的类型) class Person { name:string; } let p:Person = {name: ‘jack’}; //作为类型使用 let c:any = Person; //作为值使用,Person指构造函数本身,是一个值
<a name="kszbW"></a>## 合并类型声明可以通过接口合并的特性给一个第三方为扩展类型声明<br />user.js```typescriptinterface Animal {name:string;}let a1:Animal = {name:'jack', age:10};console.log(a1.name, a1.age);
types/animal/index.d.ts
interface Animal {age:number;}
使用命名空间扩展类
可以使用 namespace 来扩展类,用于表示内部类
class Form {username: Form.Item = '';password: Form.Item = new Form.Item();}// Item为Form的内部类namespace Form {export class Item {}}let item:Form.Item = new Form.Item();console.log(item); //Item {}
使用命名空间扩展函数
可以使用 namespace 来扩展函数
function greeting(name: string): string {return greeting.words+name;}namespace greeting {export let words = "Hello,";}console.log(greeting('jack')) //Hello,jack
使用命名空间扩展枚举类型
enum Color {red = 1,yellow = 2,blue = 3,}namespace Color {export const green = 4;export const purple = 5;}console.log(Color.green); //4
扩展store
import { createStore, Store } from 'redux';type StoreExt = Store & {ext: string}let store: StoreExt = createStore(state => state);store.ext = 'hello';
生成声明文件
把TS编译成JS后丢失类型声明,我们可以在编译的时候自动生成一份JS文件
{"compilerOptions": {"declaration": true, /* Generates corresponding '.d.ts' file.*/}}
类型声明实战
- events
index.js ```typescript import { EventEmitter } from ‘events’cnpm i events
let e = new EventEmitter(); e.on(‘message’, (txt:string) => { console.log(txt); }) e.emit(‘message’, ‘hello’);
types/events/index.d.ts```typescriptexport type Listener = (...args:any[]) => void;export type Type = string|symbol;export class EventEmitter {static defaultMaxListener:number;emit(type:Type, ...args:any[]):void;on(type:Type, listener: Listener):this;addListener(type:Type, listener:Listener):this;once(type:Type, listener:Listener):this;}
