Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >TS类型定义详解:types/typeRoots/@types,以及命名空间namespace

TS类型定义详解:types/typeRoots/@types,以及命名空间namespace

原创
作者头像
周陆军博客
发布于 2023-05-07 15:07:38
发布于 2023-05-07 15:07:38
6.4K00
代码可运行
举报
文章被收录于专栏:前端博客前端博客
运行总次数:0
代码可运行

什么是声明文件?

声明文件就是给js代码补充类型标注. 这样在ts编译环境下就不会提示js文件"缺少类型".

声明变量使用关键字declare来表示声明其后面的全局变量的类型, 比如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// packages/global.d.ts
declare var __DEV__: boolean
declare var __TEST__: boolean

看过vue3源码的同学一定知道这些是vue中的变量, 上面代码表示__DEV__等变量是全局, 并且标注了他们的类型. 这样无论在项目中的哪个ts文件中使用__DEV__, 变量ts编译器都会知道他是boolean类型.

声明文件在哪里?

首先声明文件的文件名是有规范要求的, 必须以.d.ts结尾, 

为了规避一些奇怪的问题, 推荐放在根目录下.

别人写好的声明文件( @types/xxx )

当我们用 npm 等包管理工具安装第三方包的时候,有些包并不是 TypeScript 编写的,自然也不会导出 TypeScript 声明文件。这种情况下引入了这种包,则会编译报错(没有设置 allowJS——allowJS 是 TypeScript 1.8 引进的一个编译项)

举个例子,当我们通过npm install jquery --save 安装 jquery 包并引用的时候,TypeScript 会报错。

你可以通过npm install @types/jquery安装相关声明,或者自己定义一份.d.ts 文件,并将 jquery 声明为 module。’

全世界不是 TypeScript 编写的包多了去了。 在 TypeScript 大规模应用之前,社区已经有超过 90% 的顶级 JavaScript 库,或基于 Flow 编写的库(React系)。如果没有 DefinitelyTyped 项目,这些库想要提供类型支持,无疑只有完全重构代码。这既不现实也没必要。 即使你的包是 TypeScript 编写的,如果你没有导出声明文件,也是没用的。(TypeScript 默认不会导出声明文件,只会编译输出 JavaScript 文件)。因此 TypeScript 必须对这种情况提供解决方案,而上面的两种方案:

  1. 安装 @types 
  2. 自己 declare module)就是 TypeScript 官方提出的,

我的推荐是尽量使用 @types 下的声明,实在没有,再使用第二种方法。 值得一提的是,并不是所有的包都可以通过这种方式解决的, 能解决的是 DefinitelyTyped 组织已经写好定义的包, 好消息是比较流行的包基本都有。 如果你想查一个包是否在 @type 下,可以访问 https://microsoft.github.io/TypeSearch/ 托管在github 上的 Definitely Typed(下文统一简称DT)项目是 Github 年度 octoverse 报告 上的常客,是贡献者数最多的前十个仓库之一——号称 GitHub review 数量之最的项目。 具体查看《[翻译] DefinitelyTyped 的自动化管理改造: https://juejin.cn/post/6977281038263255054》

TypeScript 经过了一系列的摸索,先后提出了 tsd(已废弃)、typings(已废弃),最终在 TypeScript 2.0 的时候重新整理了类型定义,提出了 DefinitelyTyped

鉴于 DefinitelyTyped 的作用,我们说 DefinitelyTyped 让 TypeScript 再次伟大也不为过。

DefinitelyTyped 就是让你把 "类型定义文件(*.d.ts)",发布到 npm 中,配合编辑器(或插件),就能够检测到 JS 库中的静态类型。

类型定义文件的以 .d.ts 结尾,里面主要用来定义类型。

类型定义

我们可以使用 type 用来定义类型变量:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 基本类型
type UserName = string

// 类型赋值
type WebSite = string
type Tsaid = WebSite

可以看到 type 其实可以定义各种格式的类型,也可以和其他类型进行组合。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 对象
type User = {
  name: string;
  age: number;
  website: WebSite;
}

// 方法
type say = (age: number) => string

// 类
class TaSaid {
  website: string;
  say: (age: number) => string;
}

当然,我们也可以使用 interface 定义我们的复杂类型,在 TS 中我们也可以直接定义 interface:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
interface Application {
    init(): void
    get(key: string): object
}

interface 和 type(或者说 class) 很像。

  • type 的含义是定义自定义类型,当 TS 提供给你的基础类型都不满足的时候,可以使用 type 自由组合出你的新类型,
  • interface 应该是对外输出的接口

type 不可以被继承,但 interface 可以:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
interface BaseApplication {
    appId: number
}

export interface Application extends BaseApplication {
  init(): void
    get(key: string): object
}

declare

declare的最广为人知的用处就是给第三方js库来做类型定义,让typescript明白js引入的用法,

declare 可以创建 *.d.ts 文件中的变量,declare 只能作用域最外层:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
declare var foo: number;
declare function greet(greeting: string): void;
declare namespace tasaid {
  // 这里不能 declare
  interface blog {
    website: 'http://tasaid.com'
  } 
}

基本上顶层的定义都需要使用 declare, class 也是:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
declare class User {
  name: string
}

namespace

为防止类型重复,使用 namespace 用于划分区域块,分离重复的类型,顶层的 namespace 需要 declare 输出到外部环境,子命名空间不需要 declare。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 命名空间
declare namespace Models {
  type A = number
  // 子命名空间
  namespace Config {
    type A = object
    type B = string
  }
}

type C = Models.Config.A

TypeScript 是怎么找定义的

什么情况会找不到定义而报类似上面举的例子的错误

包类型定义的查找

就好像 node 的包查找是先在当前文件夹找 node_modules,在它下找递归找,如果找不到则往上层目录继续找,直到顶部一样, TypeScript 类型查找也是类似的方式。

具体来说就是:

  • TypeScript 编译器先在当前编译上下文找 jquery 的定义。
  • 如果找不到,则会去 node_modules 中的@types (默认情况,目录可以修改,后面会提到)目录下去寻找对应包名的模块声明文件。

@types/*模块声明文件由社区维护,通过发布到@types 空间下:https://github.com/DefinitelyTyped/DefinitelyTyped

变量类型定义的查找

和包查找类似,默认情况下变量类型定义的查找也会去 @types 下去寻找。只不过并不是直接去 @types 找,而是有一定的优先级, 这个过程类似原型链或者作用域链。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const user: User = { name: "lucifer" };
  1. Typescript 则会先在本模块查找 User 的定义。
  2. 如果找不到, 则会到全局作用域找,而这个全局默认就是指的就是 @types 下的所有类型定义。(注意目录页是可以配的)

也就是说 @types 下的定义都是全局的。当然你可以导入 @types 下导出的定义,使得它们的作用域变成你的模块内部。

typeRoots 与 types

前面说了 TypeScript 会默认引入node_modules下的所有@types声明,但是开发者也可以通过修改tsconfig.json的配置来修改默认的行为.

tsconfig.json 中有两个配置和类型引入有关。

  1. typeRoots: 用来指定默认的类型声明文件查找路径,默认为node_modules/@types, 指定typeRoots后,TypeScript 编译器会从指定的路径去引入声明文件,而不是node_modules/@types, 比如以下配置会从typings路径下去搜索声明 {  "compilerOptions": {    "typeRoots": ["./typings"]  }}
  2. types: TypeScript 编译器会默认引入typeRoot下所有的声明文件,但是有时候我们并**不希望全局引入所有定义**,而是仅引入部分模块。这种情景下可以通过types指定模块名只引入我们想要的模块,比如以下只会引入 jquery 的声明文件 {  "compilerOptions": {    "types": ["jquery"]  }}

总结就是:

  1. typeRoots 是 tsconfig 中 compilerOptions 的一个配置项,typeRoots 下面的包会被 ts 编译器自动包含进来,typeRoots 默认指向 node_modules/@types。
  2. types 和 typeRoots 一样也是 compilerOptions 的配置,指定 types 后,typeRoots 下只有被指定的包才会被引入。
  3. @types 是 npm 的 scope 命名空间,和@babel 类似,@types 下的所有包会默认被引入,你可以通过修改 compilerOptions 来修改默认策略。

集成发布

有两种主要方式用来发布类型定义文件到 npm:

  1. 与你的 npm 包捆绑在一起(内置类型定义文件)
  2. 发布到 npm 上的 @types organization

前者,安装完了包之后会自动检测并识别类型定义文件。 后者,则需要通过 npm i @types/xxxx 安装,这就是我们前面所说的 DefinitelyTyped ,用于扩展 JS 库的类型声明。

内置类型定义文件

内置类型定义就是把你的类型定义文件和 npm 包一起发布,一般来说,类型定义文件都放在包根目录的 types 目录里,例如 vue

如果你的包有一个主 .js 文件,需要在 package.json 里指定主类型定义文件。

设置 types 或 typeings 属性指向捆绑在一起的类型定义文件。 例如包目录如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
├── lib
│   ├── main.js
│   └── main.d.ts # 类型定义文件
└── package.json

pageage.json

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{
    "name": "demo",
    "author": "demo project",
    "version": "1.0.0",
    "main": "./lib/main.js",
    // 定义主类型定义文件
    "types": "./lib/main.d.ts"
}

如果主类型定义文件名是 index.d.ts 并且位置在包的根目录里,就不需要使用 types 属性指定了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
├── lib
│   └── main.js
├── index.d.ts # 类型定义文件
└── package.json

如果你发的包中,package.json 中使用了 files 字段的话(npm 会根据 files 配置的规则决定发布哪些文件),则需要手动把类型定义文件加入:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{
  "files": [
    "index.js",
    "*.d.ts"
  ]
}

如果只发二级目录的话,把类型定义文件放到对应的二级目录下即可:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import { default as App } from 'demo/app'

发布到 @types organizatio

发布到 @types organizatio 的包表示源包没有包含类型定义文件,第三方/或原作者定义好类型定义文件之后,发布到 @types 中。例如 @types/express

根据 DefinitelyTyped 的规则,和编辑器(和插件) 自动检测静态类型。

@types 下面的包是从 DefinitelyTyped 里自动发布的,通过 types-publisher 工具。

如果想让你的包发布为 @types 包,需要提交一个 pull request 到 https://github.com/DefinitelyTyped/DefinitelyTyped

在这里查看详细信息 http://definitelytyped.org/guides/contributing.html

如果你正在使用 TypeScript,而使用了一些 JS 包并没有对应的类型定义文件,可以编写一份然后提交到 @types。

发布到 @types organizatio 的包可以通过 https://microsoft.github.io/TypeSearch/ 搜索检索,使用 npm install --save-dev @types/xxxx 安装:

具体推荐阅读《向微软官方贡献 @types 包后引发的思考: https://juejin.cn/post/6923379384002805774

命名空间(namespace)是什么?

什么时候要用命名空间?

如果你发现自己写的功能(函数/类/接口等...)越来越多, 你想对他们进行分组管理就可以用命名空间, 下面先用"类"举例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
namespace Tools {
    const TIMEOUT = 100;

    export class Ftp {
        constructor() {
            setTimeout(() => {
                console.log('Ftp');
            }, TIMEOUT)
        }
    }

    export class Http {
        constructor() {
            console.log('Http');
        }
    }

    export function parseURL(){
        console.log('parseURL');
    }
}

仔细看你会发现namespace下还有export, export在这里用来表示哪些功能是可以外部访问的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Tools.TIMEOUT // 报错, Tools上没有这个属性
Tools.parseURL() // 'parseURL'

在js中命名空间其实就是一个全局对象. 如果你开发的程序想要暴露一个全局变量就可以用namespace;

参考文章:

types 和 @types 是什么? https://juejin.cn/post/6863654755248373774

JavaScript 和 TypeScript 交叉口 —— 类型定义文件(*.d.ts) https://juejin.cn/post/6844903508991295501

TypeScript系列

转载本站文章《TS类型定义详解:types/typeRoots/@types,以及命名空间namespace》, 请注明出处:https://www.zhoulujun.cn/html/webfront/ECMAScript/typescript/2021_1129_8715.html

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
一文读懂TS的(.d.ts)文件
随着前端技术的不断发展,TypeScript(简称TS)已经在逐步取代JavaScript(简称JS),尤其在以 Vue3 使用 TS 重构后, TS 更是成为前端框架编写的主力语言。
唐志远
2023/05/03
5.1K0
TypeScript 类型体操 - 实践
像 JS 引擎那些 api,还有浏览器提供的 api,这些基本是必用的,而且都有标准的。所以 TypeScript 给内置了它们的类型声明。
Cellinlab
2023/05/17
3430
TypeScript 深水区:3 种类型来源和 3 种模块语法
TypeScript 给 JavaScript 添加了一套类型语法,我们声明变量的时候可以给变量加上类型信息,这样编译阶段就可以检查出变量使用的对不对,也就是类型检查。
神说要有光zxg
2022/11/11
6960
TypeScript 深水区:3 种类型来源和 3 种模块语法
types 和 @types 是什么?
TypeScript 的学习资料非常多,其中也不乏很多优秀的文章和教程。但是目前为止没有一个我特别满意的。原因有:
lucifer210
2020/08/28
2.9K0
深入浅出 TypeScript
本文是阅读小册 「《深入浅出 TypeScript》」 的阅读笔记,对TypeScript感兴趣的同学请继续阅读吧。
chuckQu
2022/08/19
3K0
Typescript学习笔记,从入门到精通,持续记录
TypeScript 最大的优势之一便是增强了编辑器和 IDE 的功能,包括代码补全、接口提示、跳转到定义、重构等。
房东的狗丶
2023/02/17
2.1K0
深度讲解TS:这样学TS,迟早进大厂【12】:声明文件
初学者玩转 TypeScript系列,总计 21 期,点赞、收藏、评论、关注、三连支持! TS系列地址: 21篇文章带你玩转ts
江一铭
2022/09/08
5.9K0
TS4类型系统扩展
炑焽
2024/08/07
1670
TypeScript进阶(四)声明文件
TypeScript 是一种由微软开发的开源编程语言,它是 JavaScript 的超集,为 JavaScript 添加了静态类型检查和其他一些特性。TypeScript 的声明文件是一种特殊的文件,用于描述 JavaScript 库、框架或模块的类型信息。通过声明文件,我们可以在 TypeScript 中使用第三方 JavaScript 库,并获得类型检查和智能提示的好处。
can4hou6joeng4
2023/11/28
4690
TypeScript 渐进迁移指南
我之前写了一篇《如何把 Node.js 项目从 JavaScript 迁移到 TypeScript 的指南》。指南的阅读量超过了七千,不过其实当时我对 JavaScript 和 TypeScript 的了解并不深入,把重心更多地放到特定工具上,而没怎么从全局着手。最大的问题是我没有提供迁移大型项目的解决方案。
winty
2021/02/05
1.9K0
从0到1开启一个全新的TypeScript项目
本文由极客时间整理自 FreeWheel 核心业务团队高级软件工程师陈芸在 QCon+ 案例研习社的演讲《TypeScript 在 FreeWheel 核心业务团队的项目实践(上)》。 作者|陈芸 编辑|贾亚宁 热衷前端技术的小伙伴都知道 TypeScript 这几年的需求呈现指数级增长的趋势,越来越多的开源项目开始使用 TypeScript 进行重构,出于对 TypeScript 究竟好不好,好在哪里的好奇,我们也对它进行了探索与尝试。 我本次的分享主要分为以下两个部分:首先探讨一下是否要引入 TypeS
深度学习与Python
2023/03/29
7560
从0到1开启一个全新的TypeScript项目
TS in JS 实践指北
不知道有多少 TS 爱好者哀叹过这个问题:虽然我很想用 TS,奈何老大只让用 JS。
WecTeam
2019/12/26
4.6K0
TS in JS 实践指北
d.ts
经常看到d.ts,因为一个越来越广泛的应用场景是编辑器智能提示(具体见IntelliSense based on TypeScript Declaration Files):
ayqy贾杰
2019/06/12
2.9K0
TypeScript学习笔记(三)—— 编译选项、声明文件
compilerOptions ⽀持很多选项,常⻅的有 baseUrl 、 target 、 moduleResolution 和 lib 等。 compilerOptions 每个选项的详细说明如下:
张果
2022/10/04
2.7K0
TypeScript学习笔记(三)—— 编译选项、声明文件
从JavaScript迁移到TypeScript,类型声明文件自动生成与中心化管理的实践
为了解决从 JavaScript 逐步迁移到 TypeScript 过程中遇到的痛点,FreeWheel 核心业务团队评估并提出了一套由 Protobuf 文件自动化生成 TypeScript 类型声明文件的流程,支持 Protobuf 文件的变化触发类型声明文件的自动更新。所有的 TypeScript 类型声明文件以微服务为单位储存,集中维护在公司级别的 TypeScript 中心化仓库里。
深度学习与Python
2021/09/22
1.6K0
Koa/Express+TypeScript扩展类型
在我们使用Koa或者Express进行开发时会经常使用中间件进行访问权限过滤或者属性加工,很多时候我们需要把中间件的属性传递给下一个路由函数,但是因为TS类型的限制我们无法直接获取扩展的元素或者无法获取正确的类型。本文讲解了在使用Koa/Express+TypeScript开发时如何扩展中间件上的属性。
用户6256742
2022/07/11
8970
Koa/Express+TypeScript扩展类型
构建纯TypeScript应用
构建纯TypeScript应用 现在只有命令行应用的例子。 前言 现在,应用开发的趋势是命令行接口应用和Web应用。 node.js 和 typescript的崛起所以,这里讨论如何创建纯的TypeScript CLI(Command Line Interface)应用和Web server-side应用。 概念 typescript node.js node.js是一个javascript的运行环境。 npm npm是一个javascript包管理器。也是node.js的默认包管理器。 环境准备 安
绿巨人
2018/05/16
1.6K0
一些你需要掌握的 tsconfig.json 常用配置项
tsconfig.json 是用来配置 TS 编译选项的,通常位于项目的根目录位置。
前端西瓜哥
2022/12/21
1.7K0
一些你需要掌握的 tsconfig.json 常用配置项
Typescript真香秘笈
本文由 IMWeb 首发于 IMWeb 社区网站 imweb.io。点击阅读原文查看 IMWeb 社区更多精彩文章。 1. 前言 2018年Stack Overflow Developer的调研(https://insights.stackoverflow.com/survey/2018/)显示,TypeScript已经成为比JavaScript更受开发者喜爱的编程语言了。 之前我其实对于typescript没有太多好感,主要是认为其学习成本比较高,写起代码来还要多写很多类型声明,并且会受到静态类型检查
用户1097444
2022/06/29
5.8K0
Typescript真香秘笈
巧妙利用TypeScript模块声明帮助你解决声明拓展
—\ntheme: awesome-green\n—\n# 写在开头\n\n网络上大部分 Typescript 教程都在告诉大家如何使用类型体操更好的组织你的代码。\n\n但是针对于声明文件(Declaration Files)的相关内容却是少之又少。\n\n这篇文章中,我会带你着重讲述 TypeScript Declaration Files 的用法让你的 TS 功底更上一层。\n\n# TypeScript 模块解析规则\n\n在开始之前,我们先来聊聊 TS 文件的加载策略。\n\n> 掌握加载策略才会让我们实实在在的避免一些看起来毫无头绪的问题。\n\nTS 中的加载策略分为两种方式,分别为相对路径和绝对路径两种方式。\n\n## 首先我们来看看相对模块的加载方式:\n\nTypeScript 将 TypeScript 源文件扩展名(.ts、.tsx和.d.ts)覆盖在 Node 的解析逻辑上。同时TypeScript 还将使用package.jsonnamed中的一个字段types来镜像目的"main"- 编译器将使用它来查找“主”定义文件以进行查阅。\n\n比如这样一段代码:\n\nts\n// 假设当前执行路径为 /root/src/modulea\n\nimport { b } from './moduleb'\n\n\n此时,TS 对于 ./moduleb 的加载方式其实是和 node 的模块加载机制比较类似:\n\n+ 首先寻找 /root/src/moduleb.ts 是否存在,如果存在使用该文件。\n\n+ 其次寻找 /root/src/moduleb.tsx 是否存在,如果存在使用该文件。\n\n+ 其次寻找 /root/src/moduleb.d.ts 是否存在,如果存在使用该文件。\n\n+ 其次寻找 /root/src/moduleB/package.json,如果 package.json 中指定了一个types属性的话那么会返回该文件。\n\n+ 如果上述仍然没有找到,之后会查找 /root/src/moduleB/index.ts。\n\n+ 如果上述仍然没有找到,之后会查找 /root/src/moduleB/index.tsx。\n\n+ 如果上述仍然没有找到,之后会查找 /root/src/moduleB/index.d.ts。\n\n可以看到 TS 中针对于相对路径查找的规范是和 nodejs 比较相似的,需要注意我在上边已经额外加粗了。\n\nTs 在寻找文件路径时,在某些条件下是会按照目录去查找 .d.ts 的。\n\n## 非相对导入\n\n在了解了相对路径的加载方式之后,我们来看看关于所谓的非相对导入是 TS 是如何解析的。\n\n我们可以稍微回想一下平常在 nodejs 中对于非相对导入的模块是如何被 nodejs 解析的。没错,它们的规则大同小异。\n\n比如下面这段代码:\n\nts\n// 假设当前文件所在路径为 /root/src/modulea\n\nimport { b } from 'moduleb'\n\n\n+ /root/src/node_modules/moduleB.ts\n+ /root/src/node_modules/moduleB.tsx\n+ /root/src/node_modules/moduleB.d.ts\n+ /root/src/node_modules/moduleB/package.json(如果它指定了一个types属性)\n+ /root/src/node_modules/@types/moduleB.d.ts\n+ /root/src/node_modules/moduleB/index.ts\n+ /root/src/node_modules/moduleB/index.tsx\n+ /root/src/node_modules/moduleB/index.d.ts\n\ntypescript 针对于非相对导入的 moduleb 会按照以上路径去当前路径的 node_modules 中去查找,如果上述仍然未找到。\n\n此时,TS 仍然会按照 node 的模块解析规则,继续向上进行目录查找,比如又会进入上层目录 /root/node_modules/moduleb.ts ...进行查找,直到查找到顶层 node_modules 也就是最后一个查找的路径为 /node_modules/moduleB/index.d.ts 如果未找到则会抛出异常 can't find module 'moduleb'。\n\n> 上述查找规则是基于 tsconfig.json 中指定的 moduleResolution:node,当然还有 classic 不过
19组清风
2022/09/08
1.5K0
巧妙利用TypeScript模块声明帮助你解决声明拓展
相关推荐
一文读懂TS的(.d.ts)文件
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验