问题历史:
TypeScript 旨在为现有的 JS 应用程序添加静态类型。问题是——如果我们导入一个用纯 JavaScript 编写的外部或自定义模块,而没有类型,应该如何进行类型定义?在这种情况下,TypeScript 会使用所谓的声明文件(.d.ts),或者自动推断类型(有时会错误)。
问题:
如果 TypeScript 在导入时找不到合适的类型描述,变量将获得类型 any,这意味着类型安全的完全丧失。这可能导致编译器无法察觉的错误,以及运行时的 bug。开发人员经常忘记明确声明导入的函数/对象的类型。
解决方案:
代码示例:
// 1. JS 模块导入的显式类型 import myFunc from './myLib'; declare function myFunc(x: number): boolean; // 2. 从 JS 模块导入,已创建 myLib.d.ts 文件,内容为 export function myFunc(x: number): boolean; import { myFunc } from './myLib'; // 3. 导入未类型化的模块并明确描述类型 import * as legacy from './legacy'; const typedLegacy: { runTask: (name: string) => void } = legacy;
关键特性:
TypeScript 可以在没有声明的情况下自动推断导入的 JS 模块的类型吗?
不能,如果文件是用 JavaScript 编写的,并且没有类型声明,TypeScript 被迫假定其为 any,且除简单情况外,失去类型信息(export const x = 1;)。
如果在 JS 模块中出现新字段,是否可以“扩展”导入的类型?
只能在更新声明文件(.d.ts)后。如果类型在 .d.ts 中被固定,TypeScript 将把它们视为“真实”,任何新字段将保持未类型化,或者导致错误。
在 TypeScript 项目中导入没有 @types/ 和声明的外部 JS 模块安全吗?
不,这会大幅降低安全性——整个导入工作变得未类型化(any),编译器不会报错,即使模块不可用或者 API 已更改。处理这些模块只应作为临时解决方案,伴随明确的数据类型或代码隔离。
开发人员在没有声明的情况下导入外部 JS 库,自信地使用 API,得到类型 any。库更新后,方法签名改变,但没有发生错误,仅在运行时出现 bug。
优点:
缺点:
创建 .d.ts 声明文件或添加 @types/ 包,API 描述严格对应于源 JS 模块。所有导入的方法都有类型,IDE 提供自动补全,任何不匹配都会显示编译错误。
优点:
缺点: