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文件,那么我们用到的关于其中的内容,就不应该被编译,不知道能不能理解这句话,但这确实是我最大的感悟了。

相关推荐
zqx_77 分钟前
随记 前端框架React的初步认识
前端·react.js·前端框架
惜.己24 分钟前
javaScript基础(8个案例+代码+效果图)
开发语言·前端·javascript·vscode·css3·html5
什么鬼昵称1 小时前
Pikachu-csrf-CSRF(get)
前端·csrf
长天一色1 小时前
【ECMAScript 从入门到进阶教程】第三部分:高级主题(高级函数与范式,元编程,正则表达式,性能优化)
服务器·开发语言·前端·javascript·性能优化·ecmascript
NiNg_1_2341 小时前
npm、yarn、pnpm之间的区别
前端·npm·node.js
秋殇与星河1 小时前
CSS总结
前端·css
BigYe程普2 小时前
我开发了一个出海全栈SaaS工具,还写了一套全栈开发教程
开发语言·前端·chrome·chatgpt·reactjs·个人开发
余生H2 小时前
前端的全栈混合之路Meteor篇:关于前后端分离及与各框架的对比
前端·javascript·node.js·全栈
程序员-珍2 小时前
使用openapi生成前端请求文件报错 ‘Token “Integer“ does not exist.‘
java·前端·spring boot·后端·restful·个人开发
axihaihai2 小时前
网站开发的发展(后端路由/前后端分离/前端路由)
前端