老周是公司里最会写 JavaScript 的高手,他维护的 ajax-tools 库比祖传老宅还值钱:功能齐全,但墙上没有一张电路图。新来的小陈想给老宅加装一盏智能灯,对着满墙电线一脸茫然——他真正需要的,是一张标注着“这根是火线、那根是地线”的示意图。TypeScript 的声明文件(.d.ts)就是给这类“JS 老宅”绘制的电路图。它不改变老宅的一砖一瓦,只把每个接口、每个参数、每个返回值清清楚楚标在图纸上,让 TypeScript 在开工前就能发现接错线的地方。 这份指南会带你从“看不懂图纸”到“能自己画图纸”:认识 .d.ts 的基本结构、全局声明与模块声明、接口与类型玩具、DefinitelyTyped 社区类型库,最后学会为自己的 JS 库编写并发布声明文件。学完以后,你既能安全地使用老库,也能让后来者像小陈一样,拿到图纸就能放心动工。

岗位关联

对 AI 从业者来说,掌握声明文件能让你在接入、迁移、封装各类 JS/AI 工具库时获得完整的类型保护,显著减少联调期错误,这是从“能跑”走向“工程化交付”的关键能力。

目录
  1. 可视化演示
  2. 声明文件基础
  3. 全局声明语法
  4. 模块声明语法
  5. 接口与类型声明
  6. DefinitelyTyped
  7. 编写声明文件
  8. 发布与使用
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

交互展示TypeScript声明文件.d.ts的结构语法:模块声明、全局声明和类型定义方式。

本节配有交互动效(进入视口后自动加载,约 3KB)。

文件结构:声明文件组成 declare关键字声明类型不生成代码 为JavaScript库补充类型描述信息 .d.ts = Type Definitions 让JS库在TypeScript中安全使用

声明文件基础

老宅的电路图不会画桌椅板凳,也不会画墙漆颜色,它只关心电从哪里来、开关接到哪里、线径承受多大电流。声明文件也是这样:.d.ts 文件里没有函数体、没有具体逻辑,只有一份“这里有什么接口、参数是什么、返回什么”的类型图纸。TypeScript 编译器在编译前打开这份图纸,就能知道一个 JS 库的每个角落该怎么接线,而不用去翻那堆被压缩、被混淆的源码。

为什么需要专门一张图纸,而不是直接在源码里写注释?因为很多老库是用纯 JavaScript 写的,改造源码成本高、风险大。声明文件的妙处在于它独立于源码:你可以在不触碰老宅墙体的前提下,把图纸画在另一张纸上,TS 会照着图纸检查,最终编译产出时又会把图纸整个扔掉,不影响运行时的一砖一瓦。

从数学视角看,一个 JS 库对外暴露的接口,可以看作“导出名字到类型”的对应关系:

对小陈来说,拿到这张图纸后,他不用再拿电笔逐个戳墙洞试探;对 TypeScript 来说,声明文件让它从“猜谜模式”进入“看图施工模式”。老宅还是那座老宅,但再也不会因为接错线而让整层楼跳闸。

APIJS  =  { (e:Te)  ∣  e∈exports(L),  Te∈Types }\text{API}_\text{JS} \;=\; \{\, (e : T_e) \;\mid\; e \in \text{exports}(L),\; T_e \in \text{Types} \,\}
这条公式的意思是:库 L 的对外 API,是很多个“导出项 e 配上类型 T_e”的集合。声明文件的工作,就是把这个集合逐项写清楚。
  1. API_JS代表 JS 库对外暴露的全部接口,包括函数、变量、类、枚举等。
    APIJS\text{API}_\text{JS}
  2. e 与 exports(L)e 是从 L 中导出的某个名字,它可能是一个 get 函数,也可能是一个 version 常量。
    e∈exports(L)e \in \text{exports}(L)
  3. T_e给 e 标注的类型,比如 (url: string) => Promise<Response>。
    Te∈TypesT_e \in \text{Types}
  4. 整体集合声明文件就是把这些 (e, T_e) 配对关系完整登记下来,供编译器查阅。
    { (e:Te)  ∣  e∈exports(L) }\{\, (e : T_e) \;\mid\; e \in \text{exports}(L) \,\}

全局声明语法

老宅里有些东西是“祖传公共设施”:比如每间房都能直接看到客厅的总闸,不需要先从一个模块里 import 进来。在 JS 里,这类挂在 window 或全局作用域下的库,就需要全局声明。它的语法核心是 declare 关键字——在图纸上写下“这里有一个总闸”,但不负责真的把闸装上去。

在 .d.ts 里,declare var 声明一个全局变量,declare function 声明一个全局函数,declare class 声明一个全局类,declare namespace 则像一个收纳盒,把一组相关的全局变量、函数都装进去。它们都只有类型签名,没有实现体。对编译器来说,declare 是一句承诺:“这个符号确实存在于运行环境中,请按此类型使用。”

用集合的语言描述,全局声明就是在全局命名空间中登记一个新符号:

老宅的总闸不是小陈画出来的,它本来就在那里;declare 只是把它的位置和规格告诉每一个后来施工的人。全局声明的价值,就是让那些“到处都是、随手可用”的 JS 全局变量,在 TypeScript 项目里也拥有姓名和类型。

declare x:T⟹x∈Globals,  x∉JS Output\text{declare } x : T \quad \Longrightarrow \quad x \in \text{Globals}, \; x \notin \text{JS Output}
含义是:一旦 x 被 declare,它在全局集合中存在,但不会被编译成任何 JavaScript 输出。这就是“只画图纸,不砌墙”。
  1. declare 关键字向 TypeScript 声明某物的存在,但不提供实现。
    declare\text{declare}
  2. xT:被声明的名字 x 以及它的类型 T,比如 apiVersion: string。
    x:Tx : T
  3. x ∈ Globalsx 不来自某个具体模块,而是直接存在于全局作用域。
    x∈Globalsx \in \text{Globals}
  4. x ∉ JS Output声明不会生成对应的运行时代码。
    x∉JS Outputx \notin \text{JS Output}

模块声明语法

老宅之外还有一片新区,每栋房子都有门牌和单元号。当一个 JS 库通过 import 被引用时,它就像一个模块化的“单元房”,需要一张标好门牌号的图纸,告诉 TypeScript 这个包导出什么、忽略什么。声明文件里用 export 描述模块的对外导出,用 import 在类型世界中进行引用。

模块声明最常见的形态是给第三方包写“外部模块声明”:在 .d.ts 中写 declare module "some-lib" { ... },把 some-lib 的导出项全部登记进去。如果项目里还有很多特殊后缀文件,比如 *.vue,也可以用通配符声明 declare module "*.vue",相当于给一整类文件提前贴上通用门牌。

一个模块的声明,本质上是模块名到导出集合的映射:

小陈给新区施工时,只要先看一眼单元房的“户型图”,就知道卧室在哪、厨房在哪。模块声明提供的正是这种确定性:import 进来的每个名字,都能在图上一眼找到对应位置,再也不会“明明文档里写了,编辑器却一直报错”。

Γ(m)={e:Te∣e∈exports(m)}\Gamma(m) = \{ e: T_e \mid e \in \text{exports}(m) \}
它表示:模块 m 的声明,就是“导出项 e 配类型 T_e”的集合。编译器遇到 import ... from 'm' 时,会去查这张表。
  1. Γ(m)读作“模块 m 的类型环境”,也就是这个模块的声明图纸。
    Γ(m)\Gamma(m)
  2. e ∈ exports(m)e 是模块 m 真正对外导出的某个成员。
    e∈exports(m)e \in \text{exports}(m)
  3. eT_e:每个导出成员都配上了自己的类型签名。
    e:Tee : T_e
  4. 整体映射从模块到导出集合的对应,构成了完整的模块类型图。
    Γ(m)={e:Te∣e∈exports(m)}\Gamma(m) = \{ e: T_e \mid e \in \text{exports}(m) \}

接口与类型声明

老宅装修时,插座面板是经常要换的部件:同一个面板形状,可以配两孔、三孔、USB 插口。interface 就像这样一个“面板模具”,它规定了一个对象必须有哪些字段、字段类型是什么;type 则像一个“缩略词”,把一段复杂的结构或联合类型起一个好记的名字。

接口还支持泛型,泛型就像给面板换上不同规格的插孔:Box<number> 装数字,Box<string> 装字符串。用泛型写一次,就能复用在多种数据类型上,减少了重复画图纸的时间。联合类型 A | B 表示“这个位置既可以接 A 也可以接 B”,交叉类型 A & B 表示“必须同时满足 A 和 B”。

从数学角度看,一个带泛型的接口就像一个参数化结构:

小陈在图纸上看到 Box<number>,就像看到“这个面板是两孔插座”;看到 Box<string>,则像是“这个面板是 Type-C 口”。接口和类型让复杂的图纸有了可复用的积木块,画起来省力,看起来也清晰。

interface Box⟨T⟩={value:T,  tag:string}\text{interface } \mathit{Box}\langle T\rangle = \{ \mathit{value}: T,\; \mathit{tag}: \text{string} \}
意思是:Box 这个结构有一个类型参数 T,value 的类型由 T 决定,tag 固定是字符串。传入不同的 T,就得到不同的具体类型。
  1. interface Box\<T\>声明一个名为 Box 的接口,尖括号里的 T 是类型参数。
    Box⟨T⟩\mathit{Box}\langle T\rangle
  2. valueT:字段 value 的类型是参数 T,所以它可以是 number、string 或任何类型。
    value:T\mathit{value}: T
  3. tagstring:字段 tag 被固定为字符串类型,不随泛型变化。
    tag:string\mathit{tag}: \text{string}
  4. 整体结构接口的实例必须同时包含 value 和 tag 两个字段。
    {value:T,  tag:string}\{ \mathit{value}: T,\; \mathit{tag}: \text{string} \}

DefinitelyTyped

老宅所在的社区有一个公共档案馆,叫 DefinitelyTyped。它收录了成千上万个流行 JS 库的声明文件,每个库对应一个 @types/包名 的 npm 包。比如 lodash 没有自带类型,社区就在 DefinitelyTyped 里维护了 @types/lodash;你只需要安装它,TS 就能立刻读懂 lodash。

安装方式很简单:npm install -D @types/lodash。因为 @types 只在开发阶段帮助编译器工作,运行时并不需要,所以通常放在 devDependencies(开发依赖)里,就像图纸只给施工队看,不塞进房子交付清单里。

公共档案馆的匹配规则可以形式化为:

小陈不用自己画所有图纸,公共档案馆已经保存了大部分成熟库的版本。他只需要按照 @types 命名规则去查、去装,就能让老宅项目立刻获得社区多年积累的类型经验。

getTypes(L)={@types/L,L 不带类型L.types,L 自带类型\text{getTypes}(L) =\begin{cases}@\text{types}/L, & L \text{ 不带类型} \\L.\text{types}, & L \text{ 自带类型}\end{cases}
意思是:遇到一个库 L,如果它自己声明文件入口,就用自己的;否则就去 DefinitelyTyped 找 @types/L。这是 TypeScript 查找类型的默认逻辑。
  1. getTypes(L)给定库 L 时,TypeScript 最终采用的类型来源。
    getTypes(L)\text{getTypes}(L)
  2. @types/LDefinitelyTyped 中为库 L 维护的声明包名。
    @types/L@\text{types}/L
  3. L 自带类型库自带类型时,优先读取它自己在 package.json 里指定的 types 字段。
    L.typesL.\text{types}
  4. 分支选择按“是否自带类型”分成两种情况,对应两种来源。
    {@types/LL 不带类型L.typesL 自带类型\begin{cases} @\text{types}/L & L \text{ 不带类型} \\ L.\text{types} & L \text{ 自带类型} \end{cases}

编写声明文件

当公共档案馆也查不到某个内部老库时,小陈就得自己动手画图纸。第一步不是埋头写代码,而是“勘察现场”:分析这个 JS 库对外导出了哪些函数、类、变量,支持哪些参数,返回值长什么样。把这些接口面梳理成清单,声明文件才不会漏画关键线路。

画图时要多设计公共类型。如果某个函数参数是一个对象,不要直接写一团长长的匿名类型,而是先 interface 或 type 给它命名;这样项目其他代码也能复用,后续维护只改一个地方。写完之后还要“模拟施工”:用一个真实的 TS 文件去调用这些 API,或者用类型测试工具验证,确保图纸和实际房子对得上。

一段类型声明可以看作对源码 API 面的签名建模:

自己画图纸时,最忌讳“想当然”。小陈每画一条线,都要回到老宅实际确认一下线路走向。好的声明文件不是越厚越好,而是每一个导出项都有对应、每一个类型都尽可能具体,不多不少刚刚好。

Surface(L)=⋃e∈exports(L)signature(e)\text{Surface}(L) = \bigcup_{e \in \text{exports}(L)} \text{signature}(e)
意思是:库 L 的“图纸总面积”,等于它所有导出成员签名合并起来的集合。每个导出成员都要有签名,缺一个就少一块。
  1. Surface(L)库 L 对外暴露的完整类型表面。
    Surface(L)\text{Surface}(L)
  2. union 符号把所有签名合并在一起,避免遗漏。
    ⋃\bigcup
  3. exports(L)库 L 的全部导出成员集合。
    exports(L)\text{exports}(L)
  4. signature(e)导出成员 e 的函数签名或属性类型。
    signature(e)\text{signature}(e)

发布与使用

图纸画完不能锁在抽屉里,得贴到房子门口,让每个需要的人都能看到。发布 npm 包时,声明文件要跟随包一起发布,并在 package.json 里告诉 TypeScript“入口图纸在哪里”——这就是 types 字段。它通常指向 index.d.ts 或构建产物里的类型入口文件。

同时,files 字段决定哪些文件会被打包进 npm 发布物。为了让 .d.ts 真正发布上去,必须把它列入 files 白名单,否则即使写了 types 字段,用户安装后也找不到图纸。发布后,项目里直接 import 这个库,TypeScript 就会按照 types 字段找到声明文件,完成类型检查。

发布配置的核心约束可以写作:

老宅装修完工后,小陈在门口钉了一个“管线示意图”信箱。路过的人不用敲开他家的门,也不用拆墙,就能知道线路布局。发布声明文件做的正是这件事:让你的库在 TypeScript 世界里成为一位“有图纸、可放心施工”的邻居。

pkgFields⊇{main,types,files},types∈files\text{pkgFields} \supseteq \{\text{main}, \text{types}, \text{files}\}, \quad \text{types} \in \text{files}
它强调:包的配置里既要有 types 入口,还要保证入口文件确实在 files 发布列表中。两条同时满足,用户才能拿到图纸。
  1. pkgFieldspackage.json 中与包发布相关的字段集合。
    pkgFields\text{pkgFields}
  2. main 字段JS 实现入口,决定运行时加载哪个文件。
    main\text{main}
  3. types 字段类型声明入口,决定编译时读取哪个 d.ts。
    types\text{types}
  4. types ∈ files类型文件必须出现在发布白名单中。
    types∈files\text{types} \in \text{files}

AI 应用场景

老项目渐进迁移

阶段:数据工程与前端基础设施改造

解决的问题:传统方式是一次性把老 JS 项目全部改写成 TS,周期长、风险高。用声明文件可以先给老模块补一张张“电路图”,让 TS 一边检查新代码,一边继续使用旧模块;每个模块验收后再逐步迁移,风险被拆小。

具体案例:一个维护了三年的可视化报表系统,内部有几十个全局工具函数。团队为这些函数编写 global.d.ts,让新开发的图表组件能安全调用,旧的 .js 代码则继续照常运行,最终将迁移周期从两个月缩短到两周。

接入社区 AI 工具库

阶段:模型服务调用与 SDK 集成

解决的问题:很多 AI/ML 相关 JavaScript SDK 早期没有类型,接入时经常拼错参数名或者把字符串传给数字字段,直到运行才报错。通过 @types 包或自定义声明文件,让所有参数在编辑器里自动补全和校验。

具体案例:某团队在项目中调用一个 LLM 对话 SDK,安装 @types/xxx 后,messages、maxTokens、temperature 等字段全部有了类型提示,联调时的参数错误率下降了约 70%。

发布开源组件库时补全类型

阶段:模型应用产品封装与对外发布

解决的问题:开源库如果只有 JS 没有声明文件,用户接入门槛极高,社区贡献者也难以维护。编写并发布声明文件,让组件库同时具备“可运行”和“可类型检查”两种能力。

具体案例:某团队开源了一个图表组件库,在发布前补充 index.d.ts,并在 package.json 中配置 types 字段和 files 白名单。发布后一周内,GitHub Issue 里关于“类型报错”的问题数量几乎清零。

Python 演示

python76 行
# -*- coding: utf-8 -*-"""Generate a minimal .d.ts for a JSON-described API."""from typing import Any, Dict, List  def to_ts_type(t: str, aliases: Dict[str, str]) -> str:    aliases_map = aliases or {}    return aliases_map.get(t, t)  def render_params(params: List[Dict[str, str]], aliases: Dict[str, str]) -> str:    parts = []    for p in params:        name = p["name"]        optional = "?" if p.get("optional") else ""        ts_type = to_ts_type(p["type"], aliases)        parts.append(f"{name}{optional}: {ts_type}")    return ", ".join(parts)  def render_function(name: str, decl: Dict[str, Any], aliases: Dict[str, str]) -> str:    params = render_params(decl.get("params", []), aliases)    ret = to_ts_type(decl.get("returns", "void"), aliases)    return f"export function {name}({params}): {ret};"  def render_class(name: str, decl: Dict[str, Any], aliases: Dict[str, str]) -> str:    lines = [f"export class {name} {{"]    for method in decl.get("methods", []):        lines.append("  " + render_function(method["name"], method, aliases)[len("export "):])    for prop in decl.get("props", []):        pname = prop["name"]        ptype = to_ts_type(prop["type"], aliases)        lines.append(f"  {pname}: {ptype};")    lines.append("}")    return "\n".join(lines)  def build_declaration(schema: Dict[str, Any]) -> str:    aliases = schema.get("aliases", {})    out = ["// Generated by typemarker demo", "// 为无类型 JS 库生成声明文件骨架", ""]    for name, decl in schema.get("functions", {}).items():        out.append(render_function(name, decl, aliases))    for name, decl in schema.get("classes", {}).items():        out.append(render_class(name, decl, aliases))    for name, type_expr in schema.get("types", {}).items():        out.append(f"export type {name} = {type_expr};")    return "\n\n".join(out) + "\n"  if __name__ == "__main__":    schema = {        "aliases": {"Response": "{ status: number, data: unknown }"},        "functions": {            "get": {                "params": [                    {"name": "url", "type": "string"},                    {"name": "timeout", "type": "number", "optional": True}                ],                "returns": "Promise<Response>"            }        },        "classes": {            "EventBus": {                "methods": [                    {"name": "on", "params": [{"name": "event", "type": "string"}], "returns": "this"},                    {"name": "emit", "params": [{"name": "event", "type": "string"}], "returns": "void"}                ],                "props": [{"name": "version", "type": "string"}]            }        },        "types": {            "User": "{ id: number, name: string }"        }    }    print(build_declaration(schema))
目的:用 Python 把一个 JSON 格式的 API 清单转换成 TypeScript 声明文件骨架,模拟“为无类型 JS 库自动生成 d.ts 初稿”的过程。
  1. def build_declaration(schema)把 JSON 格式的 API 描述翻译成 d.ts 文本。
  2. aliases_map.get(t, t)支持把简写类型名替换成更完整的 TS 类型表达式。
  3. parts.append(f"{name}{optional}: {ts_type}")拼出“参数名加问号加冒号加类型”的 TS 参数片段。
  4. render_function(name, decl, aliases)为函数生成 export function ...; 签名。
  5. render_class(name, decl, aliases)为类生成包含方法和属性的声明骨架。
  6. out.append(f"export type {name} = {type_expr};")抽出公共类型,生成 type 别名。
  7. schema = {...}模拟一个没有类型的 JS 库的 API 清单。
  8. print(build_declaration(schema))在终端输出完整的声明文件初稿。

常见误区

误区我以为 declaration 文件就像一个附加模块,会被编译进 JavaScript 包里,导致项目越来越大、运行变慢。

正解.d.ts 只存在于编译期。它像施工图纸,不会砌进墙里,也不会成为新装修的一部分。TypeScript 编译器使用它完成类型检查后,不会为它生成任何运行时代码,产物体积和运行时性能都不受影响。

为何常见:很多人看到 .d.ts 文件也会被编辑器加载、被 tsconfig 引用,就误以为它是运行时文件。实际上“类型代码只在编译期存在”是 TypeScript 最基本的心理模型。

误区我装了 `@types/xxx` 就万事大吉,即使库升到新版本,类型也自动跟着变。

正解@types 包由社区维护,有自己独立的版本号,通常和主库的版本号对应,但可能滞后。升级主库后要检查 @types 版本是否匹配;如果主库新增了 API,而过时的 @types 不知道,TS 仍可能报“类型不存在”。

为何常见:npm 安装规则让 @types 带上了主库的版本命名,人们容易以为 npm 会自动帮它们做双向同步。其实它是另一份独立的“图纸副本”。

误区我在声明文件中写了一个 `export function load()`,又想再写一个 `declare var version` 让 window 上也有这个变量,结果一直不生效。

正解只要 .d.ts 文件里有顶层 import 或 export,它就会被当作一个模块,而不是全局脚本。此时顶层 declare var 只属于该模块作用域,并不会挂到全局;要想影响 window,必须在模块内部写 declare global { ... } 块。

为何常见:大家默认“d.ts 都是全局的”,忘了模块语法会改变文件的“身份”。一个文件是模块还是脚本,取决于它有没有顶层 import/export。

误区我写 `declare function doThing(args: any): any;`,所有调用都不报错了,这就是合格的声明文件。

正解全面使用 any 等于把类型检查关掉,相当于图纸上所有线路都标着“随便接”,接错也不会有任何警告。真正有用的声明文件要尽量给出精确的结构:对象要有字段、函数要有参数类型和返回值类型。

为何常见:遇到复杂的第三方 API 时,写精确类型很费劲,写 any 最省事。但这种“省事”会让声明文件失去核心价值,项目里很快会到处是未知类型。

面试题

Q1. d.ts 文件里的类型信息在什么时候真正参与检查?

单选题 · easy

  • A.编译期类型检查← 正确答案
  • B.运行期函数调用
  • C.浏览器解析页面
  • D.打包压缩阶段

答案:A。声明文件像施工图纸,只在设计施工阶段(编译期)被检查使用;房子建成后图纸不会参与居住体验。运行期和浏览器加载时都不会读取 d.ts。

Q2. 库的声明文件入口通常配置在 package.json 的哪个字段?

单选题 · easy

  • A.types 字段← 正确答案
  • B.main 字段
  • C.scripts 字段
  • D.version 字段

答案:A。types 字段告诉 TypeScript 去哪里找声明文件;main 是运行时入口,与类型无关。

Q3. 一个 d.ts 文件以 import/export 开头,它会被 TypeScript 当作什么?

单选题 · mid

  • A.全局声明文件
  • B.模块声明文件← 正确答案
  • C.配置文件
  • D.运行时脚本文件

答案:B。有顶层 import/export 就说明它想做模块化交互,TS 会把它当成模块声明文件;只有没有任何顶层 import/export 时才会当作全局脚本。

Q4. 某个库 my-lib 没有自带类型,最常见的做法是?

单选题 · mid

  • A.安装对应 @types 包← 正确答案
  • B.删除所有 import 语句
  • C.去掉 tsconfig.json
  • D.把库改成 TS 源码

答案:A。没有自带类型时,先去 DefinitelyTyped 找 @types/my-lib;安装后编译器就能读到社区维护的类型图纸。删 import、关检查都是回避问题。

Q5. declare namespace App 在 d.ts 中最主要的作用是?

单选题 · mid

  • A.声明全局命名空间← 正确答案
  • B.导入外部 npm 包
  • C.生成运行时对象
  • D.定义默认导出

答案:A。declare namespace 把一组全局相关的类型和函数收进一个命名空间容器里,方便组织和引用,但不会生成任何运行时对象。

Q6. 模块 d.ts 想给 window 挂属性,必须写在?

单选题 · hard

  • A.declare global 内← 正确答案
  • B.export default 内
  • C.顶层 export 外
  • D.单独的 JS 文件

答案:A。模块中的顶层 declare 作用域只限当前模块;想去影响全局 window,就需要 declare global { ... } 块,像从单元房走到小区总配电房改造线路。

Q7. 发布 npm 包时,files 字段包含 .d.ts 的核心原因是?

单选题 · hard

  • A.让 npm 发布类型文件← 正确答案
  • B.减少包的运行体积
  • C.防止源码被看到
  • D.让 main 字段生效

答案:A。files 白名单决定哪些文件进入 npm 发布物。只有把 .d.ts 放进去,用户安装后才能真正拿到声明文件,否则 types 字段会指向一个不存在的文件。

Q8. 以下哪些是合法的 declare 声明形式?

多选题 · mid

  • A.declare var← 正确答案
  • B.declare module← 正确答案
  • C.declare function← 正确答案
  • D.declare import

答案:ABC。declare var、declare module、declare function 都是合法写法;declare import 不存在。import 本身就是引入类型,不需要再用 declare 修饰。

Q9. 关于 DefinitelyTyped 的 @types 包,正确说法有?

多选题 · hard

  • A.由社区维护类型← 正确答案
  • B.只包含类型声明← 正确答案
  • C.包含库的实现
  • D.版本通常跟随主库← 正确答案

答案:ABD。@types 包是 DefinitelyTyped 社区维护的“类型图纸包”,只含声明不含实现;版本通常与主库大版本对应,所以主库升级后也要检查 @types 版本。

Q10. d.ts 文件能带来哪些好处?

多选题 · easy

  • A.编译期捕获错误← 正确答案
  • B.获得编辑器提示← 正确答案
  • C.让 JS 运行更快
  • D.替代所有单元测试

答案:AB。声明文件让 TS 在编译期发现类型错误,也让编辑器提供自动补全和悬停提示;但它不会改变 JS 运行时性能,也不能替代单元测试,图纸再清晰也不能替工人验收。