ts中的declare, .d.ts, namespace的基本用法和理解

关于ts中declare和.d.ts的使用

前言

TypeScript作为一门强类型的语言,其中变量的类型,对象的属性都受到了严格的约束,对于属性的读或者写都需要在事先声明的情况下进行操作。

同时,TypeScript最后都会被编译成JavaScript来执行,所以本文的特性都是在不会被编译成JavaScript代码的前提下去使用,也就是ts自己的游戏,和JavaScript无关,这句话后面就可以理解了

在本文中你可以学到declare, namespace, .d.ts的基本用法,如果有错误希望大佬指出

declare定义

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

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

总而言之有两个要点:

  1. 在当前文件中使用
  2. 只能定义,而不能有具体实现(例如你可以declare一个变量,一个方法,但你不可以在declare的同时对其赋值,或者定义的操作)

用法:

ini 复制代码
declare let x:number;
x = 1;

如果 declare 关键字没有给出变量的具体类型,那么变量类型就是any

php 复制代码
declare function sayHello(
  name:string
):void;
sayHello('张三');

.d.ts文件

与ts文件的区别:

  1. 其中语句的开头必须是export或者declare
  2. 其声明的内容在所有ts文件中都可以直接使用,无需import

例如我们声明一个接口后在另一个文件使用,大概是这样的操作:

typescript 复制代码
// interfaceA.ts
export interface A{
    name:string;
}
​
// main.ts
import {A} from "./interfaceA.ts"
​
let instanceA:A;

你可能会说,这样也可以啊,挺常规的操作啊,但如果我有几十个文件都用到了这个接口的类型,那我就需要import几十次,这就不太善咯,于是出现了.d.ts文件,.d.ts文件中的内容可以在所有文件中直接使用,并且我们不需要在任何地方对.d.ts文件进行引用,只需要其存在我们ts编译时能检查到的路径内即可,所以我们就可以出现下面的操作

typescript 复制代码
// interfaceA.d.ts
declare interface A{
    name:string;
}
​
// main.ts
let instanceA:A;
​
// test1.ts
let instanceA:A;
​
// test2.ts
let instanceA:A;
​
// test3.ts
let instanceA:A;
​
......

这样都不会报错了,这还有什么好处呢,在我们使用一些库,这些库可能并不是用ts编写的,所以我们无法获取到其中的类型声明,那么我们在使用其中属性的时候,编译就会出现报错的情况,假设我们使用JQuery(假设这个库没使用ts编写),那么我们按照如下操作的时候可能会出现错误

javascript 复制代码
$(".element")

那么我们可以通过类似于安装JQuery的类型包

例如:(应该是这样的操作,之前看另外一篇文章有提到过,明白我的意思就好)

npm install @types/jquery

那么我们就可以这么声明:

typescript 复制代码
// index.d.ts
import JQuery from "@types/jquery"
​
declare const $:JQuery;

当多个.d.ts文件变量污染的时候怎么办?

namespace的使用

namespace(命名空间)相当于一个独立的作用域,在其中我们可以正常的编写TypeScript代码,就像我们直接在文件里面编写一样,例如:

csharp 复制代码
namespace nsA {
  const name: string = "nsA";
  function sayHello(): void{
    console.log("Hello from " + name);
  }
}
​
namespace nsB {
  const name: string = "nsB";
  function sayHello(): void{
    console.log("Hello from " + name);
  }
}

但是外部怎么使用呢?

所以我们需要将外部可能用到的命名空间内部的变量export出来,这样就保证了内部的细节不会被修改,例如:

typescript 复制代码
namespace nsA {
  const name: string = "nsA";
  export function sayHello(): void{
    console.log("Hello from " + name);
  }
}
​
namespace nsB {
  const name: string = "nsB";
  export function sayHello(): void {
    console.log("Hello from " + name);
  }
}
​
nsA.sayHello(); // Hello from nsA
nsB.sayHello(); // Hello from nsB

同样,namespace也被export出去,在别的文件中通过import引入,然后当作对象使用就好,这里就不作赘述了,可以自行了解namespace的用法。

在.d.ts文件中的namespace
typescript 复制代码
// index.d.ts
declare namespace nsB {
  interface B{
    name:string;
  }
}
​
// main.ts
// 无需引入命名空间nsB
const instanceB:nsB.B = {
  name:"haha"
}
​
console.log(instanceB.name); // 输出:haha

注意,其中不能有具体实现,只能有声明

而且在.d.ts中声明的namespace是不需要export的,其默认都会export出来,所以我们可以直接使用

解决办法

所以通过上述解释,相信如何解决.d.ts变量污染的问题就得到解决了,我们只需要通过使用命名空间包裹起来的方式,就可以防止上述问题

回到开始的话(这一段非常重要!!是很多文章都没有提到的点)

还记得我们开头说过,ts的特性大多数都是只能用在ts身上,这是ts自己的游戏吗?

为什么在上述例子我没有像前面在nsB中声明一个sayHello函数,虽然我们在main.ts文件中使用nsB.sayHello()也不会报错,但是这是不被允许的,因为在编译成JavaScript代码后,这段代码依然会保留,但是我们并没有定义或者引入已经定义的nsB,nsB是undefined,在严格模式下是不允许访问的,而上述例子,instanceB后面的类型声明是不会被编译成JavaScript代码的,所以可以直接使用

declare的其它用法

我们还可以通过declare module的方式来声明模块,这样在引入一些js库的时候,就不会报相关属性可能不存在的错误,例如:

javascript 复制代码
// haha.d.ts
declare module haha{
  export function xiaoxin():void;
}
​
// main.d.ts
import {xiaoxin} from 'haha'

但注意,回到最开始的话,这是ts自己的游戏,我们要保证引入的haha是存在的,只不过其中导出的声明不够清晰而已。

最后的话

花了几个小时才弄懂这个declare,namespace,.d.ts之间的关系,在学ts的过程中,我觉得最难的不是什么语法啥的,更多的是要明白ts所做的许多工作都是为了编译阶段进行的检查,而不是执行时,ts中自己的特性,我们大多数都只能用在ts自己身上,有些代码不会被编译成js文件,例如.d.ts文件,那么我们用到的关于其中的内容,就不应该被编译,不知道能不能理解这句话,但这确实是我最大的感悟了。

相关推荐
小白白一枚11139 分钟前
css实现div被图片撑开
前端·css
薛一半1 小时前
PC端查看历史消息,鼠标向上滚动加载数据时页面停留在上次查看的位置
前端·javascript·vue.js
@蒙面大虾1 小时前
CSS综合练习——懒羊羊网页设计
前端·css
MarcoPage1 小时前
第十九课 Vue组件中的方法
前端·javascript·vue.js
.net开发1 小时前
WPF怎么通过RestSharp向后端发请求
前端·c#·.net·wpf
**之火2 小时前
Web Components 是什么
前端·web components
顾菁寒2 小时前
WEB第二次作业
前端·css·html
前端宝哥2 小时前
10 个超赞的开发者工具,助你轻松提升效率
前端·程序员
你好龙卷风!!!2 小时前
vue3 怎么判断数据列是否包某一列名
前端·javascript·vue.js
兔老大的胡萝卜2 小时前
threejs 数字孪生,制作3d炫酷网页
前端·3d