• 声明文件可以让我们不需要将 js 重构为 ts,只需要加上声明文件就可以使用系统
  • 类型声明在编译的时候都会被删除,不会影响真正的代码
  • 关键字 declare 表示声明的意思,我们可以用它来做出各种声明
  • declare 只是声明,不需要实现。

普通类型声明

types/index.d.ts
  1. // 通过 declare 来声明变量,这个声明只是为了告诉ts有这个变量,避免报错,没有任何意义
  2. // 声明变量
  3. declare let book:string;
  4. declare var book2:string;
  5. declare const book3:string;
  6. declare function add(x:number, y:number):number; //声明方法
  7. declare class Person {}; //声明类
  8. // 声明 接口 和 类型
  9. declare interface Animal {name:string}; //同名接口可以自动合并
  10. declare interface Animal {age:number};
  11. declare type Sum = (x:number, y:number) => number;

src/index.ts
  1. // 有了声明之后,下面这些就不会报错了
  2. console.log(book);
  3. console.log(book2);
  4. console.log(book3);
  5. add(1, 2);
  6. new Person();
  7. let a1:Animal = {name: 'jack', age: 1};
  8. 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, ];

  1. 也可以同时使用`declare` `const`
  2. ```typescript
  3. declare const enum Seasons {
  4. Spring,
  5. Summer,
  6. Autumn,
  7. Winter,
  8. }
  9. let seasons = [
  10. Seasons.Spring,
  11. Seasons.Summer,
  12. Seasons.Autumn,
  13. Seasons.Winter
  14. ];
  15. //编译为
  16. var seasons = [
  17. 0 /* Spring */,
  18. 1 /* Summer */,
  19. 2 /* Autumn */,
  20. 3 /* Winter */
  21. ];

namespace

  • 如果一个全局变量包括了很多子属性,可能使用 namespace
  • 在声明文件中的 namespace 表示一个全局变量中包含很多子属性
  • 在命名空间内部不需要使用 declare 声明属性或方法,也不需要加export 导出
    1. declare namespace $${
    2. function ajax(url:string,settings:any):void; //这里不用加export也可以
    3. let name:string;
    4. namespace fn {
    5. function extend(object:any):void;
    6. }
    7. }
    8. $$.ajax('/api/users',{});
    9. $$.fn.extend({
    10. log:function(message:any){
    11. console.log(message);
    12. }
    13. });

类型声明文件

  • 我们可以把类型声明放在一个单独的类型声明文件中,一般是根目录下 types/*
  • 可以在类型声明文件中使用类型声明
  • 文件命名规范为 *.d.ts
  • 观看类型声明文件有助于了解库的使用方式
    tsconfig.json
    1. {
    2. "compilerOptions": {
    3. /* 模块解析相关参数 */
    4. "moduleResolution": "node", /* 选择模块解析策略,有'node''classic'两种类型' */
    5. "esModuleInterop": true, /* 通过为导入内容创建命名空间,实现CommonJSES模块之间的互操作性 */
    6. "baseUrl": "./", /* 设置解析非相对模块名称的基本目录,相对模块不会受baseUrl的影响 */
    7. "paths": { /* 设置模块名称到基于baseUrl的路径映射 */
    8. "*": ["types/*"]
    9. },
    10. },
    11. /* 指定要编译的路径列表,但是和files的区别在于,这里的路径可以是文件夹,也可以是文件,
    12. 可以使用相对和绝对路径,而且可以使用通配符,
    13. 比如"./src"即表示要编译src文件夹下的所有文件以及子文件夹的文件 */
    14. "include": [
    15. "src/**/*", /* 匹配任意字符,不包括路径分隔符;** 匹配任意字符,包括路径分隔符 */
    16. "types/**/*",
    17. ],
    18. }

types/jquery.d.ts
  1. // declare module 'jquery';
  2. // declare module 'jquery' {};
  3. declare function $$(selector:string):{
  4. click():void;
  5. css(val:object):void,
  6. width(val:number):void,
  7. height(val:number):void,
  8. }
  9. // 命名空间同名可以合并;
  10. // 函数 和 命名空间 同名也可以合并
  11. declare namespace $$ {
  12. const name:string;
  13. const fn2:({extend():void})
  14. }
  15. //
  16. declare namespace $$ {
  17. namespace fn{
  18. function extend():void;
  19. }
  20. }
  21. export default $$;

src/index.ts
  1. import $$ from 'jquery';
  2. $$("#root").height(100)
  3. console.log($$.name);
  4. $$.fn.extend();
  5. $$.fn2.extend();

types/index.d.ts 处理scss 和 less 的声明报错
  1. // 配置全局ts文件
  2. // 声明 .scss和 .less文件
  3. // declare module '*.css'
  4. // declare module '*.less'
  5. // declare module '*.scss'
  6. // declare module '*.svg'
  7. declare module '*.module.scss' {
  8. const classes: { [key: string]: string };
  9. export default classes;
  10. }
  11. declare module '*.module.less' {
  12. const classes: { [key: string]: string };
  13. export default classes;
  14. }
  15. declare module '*.less' {
  16. const classes: { [key: string]: string };
  17. export default classes;
  18. }

第三方声明文件

  • 可以安装使用第三方的声明文件
  • @types是一个约定的前缀,所有的第三方声明的类型库都会带有这样的前缀
  • JavaScript 中有很多内置对象,它们可以在 TypeScript 中被当做声明好了的类型
  • 内置对象是指根据标准在全局作用域(Global)上存在的对象。这里的标准是指 ECMAScript 和其他环境(比如 DOM)的标准
  • 这些内置对象的类型声明文件,就包含在TypeScript 核心库的类型声明文件

使用jquery

  1. cnpm i jquery -S # 安装jquery库
  2. cnpm i @types/jquery -S # 安装jquery库的类型声明文件
  3. //对于common.js风格的模块必须使用 import * as 或 import jQuery = require('jquery');
  4. import * as jQuery from 'jquery';
  5. jQuery.ajax('/user/1');
  6. // 如果想要 import jQuery from 'jquery'; 需要开启 "esModuleInterop": true,

自己编写声明文件

  • 模块查找规则
  • node_modules\@types\jquery/index.d.ts
  • 我们可以自己编写声明文件并配置tsconfig.json

index.d.ts
  1. // jQuery本身是一个函数,它自身也有一些属性
  2. declare function jQuery(selector:string):HTMLElement;
  3. declare namespace jQuery {
  4. function ajax(url:string):void;
  5. }
  6. export = jQuery;

tsconfig.json
  • 如果配置了paths,那么在引入包的的时候会自动去paths目录里找类型声明文件
  • 在 tsconfig.json 中,我们通过 compilerOptions 里的 paths 属性来配置路径映射
  • paths是模块名到基于baseUrl的路径映射的列表
    1. {
    2. "compilerOptions": {
    3. /* 设置解析非相对模块名称的基本目录,相对模块不会受baseUrl的影响 */
    4. "baseUrl": "./",
    5. /* 设置模块名称到基于baseUrl的路径映射 */
    6. "paths": {
    7. "*": ["types/*"]
    8. }
    9. }
    10. }
    1. import * as jQuery from 'jquery';
    2. 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 中的 typestypings 字段指定一个类型声明文件地址
  • 在项目根目录下,编写一个 index.d.ts 文件
  • 针对入口文件(package.json 中的 main 字段指定的入口文件),编写一个同名不同后缀的 .d.ts 文件

    1. {
    2. "name": "myLib",
    3. "version": "1.0.0",
    4. "main": "lib/index.js",
    5. "types": "myLib.d.ts",
    6. }
  • 先找myLib.d.ts

  • 没有就再找index.d.ts
  • 还没有再找lib/index.d.js
  • 还找不到就认为没有类型声明了

扩展全局变量的类型

扩展局部变量的类型

  1. declare var String:StringConstructor;
  2. interface StringConstructor {
  3. new(value?:any):String;
  4. (value?:any):string;
  5. readonly prototype:String;
  6. }
  7. interface String {
  8. toString():string;
  9. }
  1. // 扩展类的原型
  2. interface String {
  3. double():string;
  4. }
  5. String.prototype.double = function(){
  6. return this+this;
  7. }
  8. // let result = 'hello'.double();
  9. // 等同于
  10. let result = new String('hello').double();
  11. console.log(result); //hellohello
  12. //扩展类的实例
  13. interface Window{
  14. myname:string
  15. }
  16. console.log(window.myname);

模块内的全局扩展

types\global\index.d.ts

  1. export {}
  2. // declare global{} 在模块里扩展全局变量
  3. declare global{
  4. interface String {
  5. double():string;
  6. }
  7. interface Window{
  8. myname:string
  9. }
  10. }

合并声明

  • 同一名称的两个独立声明会被合并成一个单一声明
  • 合并后的声明拥有两个声明的特性 | 关键字 | 作为类型使用 | 作为值使用 | | —- | —- | —- | | 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指构造函数本身,是一个值

  1. <a name="kszbW"></a>
  2. ## 合并类型声明
  3. 可以通过接口合并的特性给一个第三方为扩展类型声明<br />user.js
  4. ```typescript
  5. interface Animal {
  6. name:string;
  7. }
  8. let a1:Animal = {name:'jack', age:10};
  9. console.log(a1.name, a1.age);

types/animal/index.d.ts

  1. interface Animal {
  2. age:number;
  3. }

使用命名空间扩展类

可以使用 namespace 来扩展类,用于表示内部类

  1. class Form {
  2. username: Form.Item = '';
  3. password: Form.Item = new Form.Item();
  4. }
  5. // Item为Form的内部类
  6. namespace Form {
  7. export class Item {}
  8. }
  9. let item:Form.Item = new Form.Item();
  10. console.log(item); //Item {}

使用命名空间扩展函数

可以使用 namespace 来扩展函数

  1. function greeting(name: string): string {
  2. return greeting.words+name;
  3. }
  4. namespace greeting {
  5. export let words = "Hello,";
  6. }
  7. console.log(greeting('jack')) //Hello,jack

使用命名空间扩展枚举类型

  1. enum Color {
  2. red = 1,
  3. yellow = 2,
  4. blue = 3,
  5. }
  6. namespace Color {
  7. export const green = 4;
  8. export const purple = 5;
  9. }
  10. console.log(Color.green); //4

扩展store

  1. import { createStore, Store } from 'redux';
  2. type StoreExt = Store & {
  3. ext: string
  4. }
  5. let store: StoreExt = createStore(state => state);
  6. store.ext = 'hello';

生成声明文件

把TS编译成JS后丢失类型声明,我们可以在编译的时候自动生成一份JS文件

  1. {
  2. "compilerOptions": {
  3. "declaration": true, /* Generates corresponding '.d.ts' file.*/
  4. }
  5. }

类型声明实战

  • events
    1. cnpm i events
    index.js ```typescript import { EventEmitter } from ‘events’

let e = new EventEmitter(); e.on(‘message’, (txt:string) => { console.log(txt); }) e.emit(‘message’, ‘hello’);

  1. types/events/index.d.ts
  2. ```typescript
  3. export type Listener = (...args:any[]) => void;
  4. export type Type = string|symbol;
  5. export class EventEmitter {
  6. static defaultMaxListener:number;
  7. emit(type:Type, ...args:any[]):void;
  8. on(type:Type, listener: Listener):this;
  9. addListener(type:Type, listener:Listener):this;
  10. once(type:Type, listener:Listener):this;
  11. }