• TypeScript中的declare关键字


    declare关键字

    1. 简介

    declare关键字用来告诉编译器,某个类型是存在的,可以在当前文件中使用。

    作用:就是让当前文件可以使用其他文件声明的类型。比如,自己的脚本使用外部库定义的函数,编译器会因为不知道外部函数的类型定义而报错,这时就可以在自己的脚本里面使用declare关键字,告诉编译器外部函数的类型,这样编译脚本就不会因为使用了外部类型而报错。

    declare关键字可以描述变量、type或者interface命令声明的类型、class、Enum、函数、模块和命名空间。

    declare关键字最重要的特点是,它只是通知编译器某个类型是存在的,不用给出具体实现。比如只描述函数的类型,不给出函数的实现,如果不使用declare,是做不到的。

    declare只能用来描述已经存在的变量和数据结构,不能用来声明新的变量和数据结构。另外所有declare语句都不会出现在编译后的文件里面。

    2. declare variable

    可以给出外部变量的类型描述。比如当前脚本使用了其他脚本定义的全局变量x,因为当前脚本不知道它的类型,编译器会报错,而此时要是使用declare命令给出它的类型,就不会报错。

    x = 123; // 报错
    declare let x:number;
    x = 1;
    
    • 1
    • 2
    • 3

    如果declare关键字没有给出变量的具体类型,则变量类型为any。

    如果ts没有找到declare关键字给出的变量,则假定它的类型为any。

    declare 关键字只用来给出类型描述,是纯的类型代码,不允许设置变量的初始值,不涉及到值,否则会报错。

    // 报错
    declare let x:number = 1;
    
    • 1
    • 2

    3. declare function

    declare关键字可以给出外部函数的类型。

    declare function sayHello(
      name:string
    ):void;
    
    sayHello('张三');
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在ts中不能单独的声明函数类型

    // 报错
    function sayHello(
      name:string
    ):void;
    function sayHello(name) {
      return '你好,' + name;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    4. declare class

    declare可以给出class类型的描述

    declare class Animal {
      constructor(name:string);
      eat():void;
      sleep():void;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    5. declare module、declare namespace

    如果想把变量、函数、类组织在一起,可以将declare与module或namespace一起使用。

    declare namespace AnimalLib {
      class Animal {
        constructor(name:string);
        eat():void;
        sleep():void;
      }
    
      type Animals = 'Fish' | 'Dog';
    }
    
    // 或者
    declare module AnimalLib {
      class Animal {
        constructor(name:string);
        eat(): void;
        sleep(): void;
      }
    
      type Animals = 'Fish' | 'Dog';
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    declare module 和 declare namespace 里面,加不加 export 关键字都可以。

    declare namespace Foo {
      export var a: boolean;
    }
    
    declare module 'io' {
      export function readFile(filename:string):string;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    例子:使用外部库(myLib)

    declare namespace myLib {
      function makeGreeting(s:string): string;
      let numberOfGreetings: number;
    }
    
    • 1
    • 2
    • 3
    • 4

    可以为外部模块添加属性和方法时,给出新增部分的类型描述。

    这里从模块moduleA导入了Foo接口,将其重命名为Bar,并用 declare 关键字为Bar增加一个属性custom

    import { Foo as Bar } from 'moduleA';
    
    declare module 'moduleA' {
      interface Bar extends Foo {
        custom: {
          prop1: string;
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    例子:一个项目有多个模块,可以在一个模块中,对另一个模块的接口进行类型扩展。

    这里脚本a.ts定义了一个接口A,脚本b.ts为这个接口添加了属性ydeclare module './a' {}表示对a.ts里面的模块,进行类型声明,而同名 interface 会自动合并,所以等同于扩展类型。实质是新增了一个同名的接口,因为是同名会自动合并。

    // a.ts
    export interface A {
      x: number;
    }
    
    // b.ts
    import { A } from './a';
    
    declare module './a' {
      interface A {
        y: number;
      }
    }
    
    const a:A = { x: 0, y: 0 };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    使用这种语法进行模块的类型扩展时,有两点需要注意:

    (1)declare module NAME语法里面的模块名NAME,跟 import 和 export 的模块名规则是一样的,且必须跟当前文件加载该模块的语句写法(上例import { A } from './a')保持一致。

    (2)不能创建新的顶层类型。也就是说,只能对a.ts模块中已经存在的类型进行扩展,不允许增加新的顶层类型,比如新定义一个接口B

    (3)不能对默认的default接口进行扩展,只能对 export 命令输出的命名接口进行扩充。这是因为在进行类型扩展时,需要依赖输出的接口名。

    对于某些第三方模块,原始作者没有提供接口类型,这时可以在自己的脚本顶部加上下面一行命令。加上上面的命令以后,外部模块即使没有类型声明,也可以通过编译。但是,从该模块输入的所有接口都将为any类型。

    declare module "模块名";
    
    // 例子
    declare module "hot-new-module";
    
    • 1
    • 2
    • 3
    • 4

    declare module 描述的模块名可以使用通配符。模块名my-plugin-*表示适配所有以my-plugin-开头的模块名(比如my-plugin-logger)。

    declare module 'my-plugin-*' {
      interface PluginOptions {
        enabled: boolean;
        priority: number;
      }
    
      function initialize(options: PluginOptions): void;
      export = initialize;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    6. declare global

    如果要为js的原生对象添加属性和方法,可以使用declare global{}语法。只能扩充现有对象的类型描述,不能增加新的顶层类型。

    这里为js原生的String对象添加了toSmallString()方法并给出这个新增方法的类型描述。

    其中第一行的空导出语句export {},作用是强制编译器将这个脚本当作模块处理。这是因为declare global必须用在模块里面。

    export {};
    declare global {
      interface String {
        toSmallString(): string;
      }
    }
    String.prototype.toSmallString = ():string => {
      // 具体实现
      return '';
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    7. declare enum

    可以给出Enum类型描述

    declare enum E1 {
      A,
      B,
    }
    declare enum E2 {
      A = 0,
      B = 1,
    }
    declare const enum E3 {
      A,
      B,
    }
    declare const enum E4 {
      A = 0,
      B = 1,
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    8. declare module 用于类型声明文件

    可以为每个模块脚本,定义一个.d.ts文件,把该脚本用到的类型定义都放在这个文件里面。但是,更方便的做法是为整个项目,定义一个大的.d.ts文件,在这个文件里面使用declare module定义每个模块脚本的类型。

    定义

    这里url和path都是单独的模块脚本,它们的类型都定义在node.d.ts这个文件中。

    // node.d.ts
    declare module "url" {
      export interface Url {
        protocol?: string;
        hostname?: string;
        pathname?: string;
      }
      export function parse(
        urlStr: string,
        parseQueryString?,
        slashesDenoteHost?
      ): Url;
    }
    declare module "path" {
      export function normalize(p: string): string;
      export function join(...paths: any[]): string;
      export var sep: string;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    使用

    使用时,自己的脚本使用三斜杠命令,加载这个类型声明文件。

    /// 
    
    • 1

    如果没有上面这一行命令,自己的脚本使用外部模块时,就需要在脚本里面使用 declare 命令单独给出外部模块的类型。

  • 相关阅读:
    oss 部署前端项目报错 Cannot find module ‘@/views/.....‘(require和import区别)
    算法-版本号升级
    小程序提示没有找到可以构建的NPM包解决方法
    理解 期望和方差(均值/估计值)
    香港中文大学卢煜明教授荣获诺贝尔奖的“风向标”--拉斯克奖
    java计算机毕业设计汉语言类网上考试系统源码+mysql数据库+系统+LW文档+部署
    HTML5网页前端设计-作业一
    2022-30周 项目问题整理
    令人拍案叫绝的算法学习网站,算法入门到精通,算法面试冲刺资料这里都有
    SDRAM控制器——添加读写FIFO
  • 原文地址:https://blog.csdn.net/zsm4623/article/details/134058328