小楠楠 發表於 2025-5-30 17:26:00

记录---xx.d.ts文件有什么用,为什么不引入都能生效?

<h1 data-id="heading-0">🧑‍💻 写在开头</h1>
<p>点赞 + 收藏 === 学会🤣🤣🤣</p>
<h2 data-id="heading-0">背景简介</h2>
<p>在前端项目开发中,我们常常会遇到这样一种情况:项目中引入了某个第三方库,但它没有提供内置的 TypeScript 类型声明。此时,如果你在 TypeScript 项目中直接使用这个库,编译器就会报错。</p>
<div>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202505/2149129-20250530172053341-423393245.png" alt="" loading="lazy"></p>
<p>&nbsp;也许你很聪明,知道在项目中的<code>shims-vue.d.ts</code>中通过declare声明此模块,来解决报错。</p>
</div>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202505/2149129-20250530172111304-1930771969.png" alt="" loading="lazy"></p>
<div>
<div>
<p>但你是否想过这些问题:</p>
<ul>
<li>为什么在<code>.shims-vue.d.ts</code>文件中写一个declare声明就能解决报错?</li>
<li>.d.ts文件明明没有显示引入,为什么就生效了?</li>
<li>任意xxx.d.ts文件声明为什么都能发挥作用吗?</li>
</ul>
<p>其实,能够写一行 <code>declare module 'xxx'</code> 就让 TypeScript 编译器“闭嘴”,看似简单,背后却隐藏着 TypeScript 类型系统的一些核心机制。</p>
<h2 data-id="heading-1"><code>.d.ts</code> 文件的作用</h2>
<p><code>.d.ts</code> 文件是 TypeScript 世界中的“翻译器”,它不负责运行代码,而是负责<strong>描述代码的结构、类型、接口、模块</strong>,使得 TypeScript 编译器“明白”你在干什么,从而让类型检查、提示、自动补全等功能得以正常运作。</p>
<ul>
<li><strong>为 JavaScript 代码或第三方库补充类型</strong></li>
</ul>
<p>当你引入一个 <strong>没有类型定义</strong> 的库时,比如老旧的 JS 库或某些 npm 包没有内置 TypeScript 类型,会导致 TS 编译器报错。这时你可以写一个 <code>.d.ts</code> 文件,手动声明它的类型:</p>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:bash;gutter:true;">// types/jquery.d.ts
declare var $: any;</pre>
</div>
<ul>
<li>为非代码资源声明模块(如 SVG、CSS、JSON 等)</li>
</ul>
<p>在项目中引入非代码资源时,TypeScript 默认是无法识别的,比如:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:bash;gutter:true;">import logo from './logo.svg';</pre>
</div>
会报错:找不到模块。这时就需要一个&nbsp;<code>.d.ts</code>&nbsp;文件来告诉 TS 这是什么类型:<br>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:bash;gutter:true;">// types/shims-svg.d.ts
declare module '*.svg' {
const content: string;
export default content;
}</pre>
</div>
<ul>
<li>定义全局变量或类型</li>
</ul>
<p>比如&nbsp;<code>VITE_APP_VERSION</code>&nbsp;是在构建时注入的变量,你可以这样声明:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:bash;gutter:true;">// types/global.d.ts
declare const VITE_APP_VERSION: string;</pre>
</div>
<p>这样就可以在任何文件中使用,不报错、还能获得类型提示。</p>
<ul>
<li>定义环境变量、全局命名空间等复杂类型结构</li>
</ul>
<p>例如:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:bash;gutter:true;">interface Window {
myGlobalAPI: () =&gt; void;
}

declare namespace MyLib {
type Options = {
    debug: boolean;
};
}</pre>
</div>
<h4 data-id="heading-2">补充已有模块的类型信息(模块扩展)</h4>
<p>你可以给已有模块添加自定义类型,不需要修改原始库代码:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:bash;gutter:true;">// types/vue-router.d.ts
import 'vue-router';

declare module 'vue-router' {
interface RouteMeta {
    auth?: boolean;
}
}</pre>
</div>
<h2 data-id="heading-3">为什么写一个&nbsp;<code>declare</code>&nbsp;就能解决报错?</h2>
<p>TypeScript 是强类型语言,它在编译时会尝试为每一个变量、函数、模块、类型标识符“找到定义”。当我们引入一个没有类型声明的第三方模块时,比如:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:bash;gutter:true;">import something from 'vs-tree';</pre>
</div>
<p>如果&nbsp;<code>vs-tree</code>&nbsp;没有提供&nbsp;<code>.d.ts</code>&nbsp;文件(也就是没有类型定义),TypeScript 编译器就会报错:</p>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202505/2149129-20250530172317112-2129602949.png" alt="" loading="lazy"></p>
<p>&nbsp;但当你写上:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:bash;gutter:true;">// shims-vue.d.ts 或任何 .d.ts 文件
declare module 'vs-tree';</pre>
</div>
<div>
<div>
<p>你就是<strong>手动告诉 TypeScript</strong>:“我知道这个模块存在,不用你担心类型问题。”于是 TS 编译器不再报错,默认把它当作 <code>any</code> 类型处理。这其实是使用 <code>.d.ts</code> 的一个最基础场景:<strong>模块声明补全</strong>。</p>
<h2 data-id="heading-4">为什么 <code>.d.ts</code> 文件不引入也能生效?</h2>
<p>这要从 TypeScript 的文件识别机制说起。</p>
<p>TypeScript 在编译一个项目时,首先会加载项目根目录下的 <code>tsconfig.json</code>,它会根据其中的配置项决定:</p>
<ul>
<li>要包含哪些文件</li>
<li>要排除哪些文件</li>
<li>要使用哪些类型库(如 DOM、ESNext)</li>
<li>要如何解析模块路径(如路径别名)</li>
</ul>
<p>具体来说,<code>.d.ts</code> 文件能自动生效,主要有以下几种情况:</p>
<h3 data-id="heading-5">在 <code>tsconfig.json</code> 的 <code>include</code> 范围内</h3>
<p>只要你的 <code>.d.ts</code> 文件路径在 <code>include</code> 的匹配范围内,TS 编译器就会自动加载它:</p>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:bash;gutter:true;">{
"include": ["src", "types"]
}</pre>
</div>
<div>
<div>
<p>如你把 <code>shims-vue.d.ts</code> 放在 <code>src/</code> 或 <code>types/</code> 下,它就会自动生效。</p>
<hr>
<h3 data-id="heading-6">被编译器当作“全局声明”文件识别</h3>
<p><code>.d.ts</code> 文件中如果没有 <code>import</code>/<code>export</code>,就会被 TypeScript 当作“全局类型声明文件”(Global Declaration File),自动合并进全局作用域。这种文件中的内容对所有文件可见:</p>
</div>
<br>
<div>
<div class="cnblogs_Highlighter">
<pre class="brush:bash;gutter:true;">// types/global.d.ts
declare const __APP_VERSION__: string;</pre>
</div>
<div>
<div>
<p>你在任意 .ts 文件中都能直接使用 <code>__APP_VERSION__</code>,不需要任何引入。</p>
<hr>
<h3 data-id="heading-7">被放置在默认类型目录下(如 <code>@types</code>)</h3>
<p>TypeScript 默认会去 <code>node_modules/@types</code> 中找类型定义(社区维护的 DefinitelyTyped 类型库)。如果你把声明文件放进这个路径,甚至可以模拟 npm 包类型的形式存在。</p>
<h2 data-id="heading-8">任意 <code>xxx.d.ts</code> 文件都能生效吗?</h2>
<p>并不是,之前已经提到,只有在tsconfig.json中的 <code>include</code>中被包含,编译器才会自动加载。</p>
</div>
</div>
</div>
</div>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202505/2149129-20250530172415007-847804573.png" alt="" loading="lazy"></p>
<p>&nbsp;如上图,只有src目录下的任意<code>xx.d.ts</code>才会被自动加载。但以下情况,即使在include中声明也不会被加载。</p>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202505/2149129-20250530172438660-135607655.png" alt="" loading="lazy"></p>
<div>
<h2>本文转载于:https://juejin.cn/post/7500654211785392179</h2>
</div>
<h3 id="tid-D8HBxE">如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。</h3>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202501/2149129-20250122165814748-630765389.png" alt="" loading="lazy"></p>
</div><br><br>
来源:https://www.cnblogs.com/smileZAZ/p/18904621
頁: [1]
查看完整版本: 记录---xx.d.ts文件有什么用,为什么不引入都能生效?