Jym好😘,我是珑墨,今天给大家分享 全面详细对比 .d.ts、.ts、.tsx文件 ,嘎嘎的😍,看下面。
一、对比三个文件
1. .d.ts 文件(TypeScript 声明文件)
核心定义
- 纯类型声明:仅包含类型定义(无运行时逻辑)。
- 不参与编译:不会被编译为 .js 文件,仅在开发阶段提供类型支持。
核心用途
-
为 JS 库提供类型支持
例如:为旧版 jQuery 添加类型声明:ts // jquery.d.ts declare interface JQuery { hide(): void; show(): void; } declare var $: (selector: string) => JQuery; -
全局类型扩展
定义项目中全局可用的类型或模块:ts // global.d.ts declare type AnyObject = Record<string, any>; declare module "*.svg" { const content: string; export default content; } -
模块类型描述
描述第三方模块的类型结构:ts // my-module.d.ts declare module "my-module" { export function greet(name: string): string; }
语法规则
- 仅限类型语法:不能包含
const、function等运行时代码。 - 声明关键字:必须使用
declare声明全局可见的类型或变量。
配置要点
- 自动生成:在 tsconfig.json 中设置
"declaration": true,编译 .ts 文件时会生成对应的 .d.ts。 - 类型包含:通过
/// <reference path="..." />或include字段引入声明文件。
2. .ts 文件(TypeScript 源代码文件)
核心定义
- 可执行代码:包含 TypeScript 代码(类型注解 + 逻辑实现)。
- 参与编译:会被编译为 .js 文件。
核心用途
-
工具函数
ts // utils.ts export const deepClone = <T>(obj: T): T => JSON.parse(JSON.stringify(obj)); -
业务逻辑
ts // userService.ts interface User { id: number; name: string } export const fetchUser = async (id: number): Promise<User> => { /* ... */ }; -
非 JSX 组件
ts // logger.ts export class Logger { static log(message: string): void { console.log(`[INFO] ${message}`); } }
语法规则
- 完整 TypeScript 语法:支持所有 TS 特性(泛型、装饰器等)。
- 禁止 JSX:若包含 JSX 语法会触发编译错误。
配置要点
- 模块化:通过
import/export组织代码。 - 编译目标:在 tsconfig.json 中设置
"target": "ES6"等选项。
3. .tsx 文件(支持 JSX 的 TypeScript 文件)
核心定义
- JSX + TypeScript:允许在 TypeScript 中编写 JSX 语法。
- 参与编译:编译为 .js 文件,JSX 转换为目标框架的渲染函数。
核心用途
-
React 组件
tsx // Button.tsx interface Props { text: string } const Button = ({ text }: Props) => <button className="btn">{text}</button>; -
Vue 渲染函数
tsx // ListItem.tsx (Vue 3) import { defineComponent } from 'vue'; export default defineComponent({ props: { item: { type: Object, required: true } }, render() { return <div class="item">{this.item.name}</div>; } }); -
复杂 UI 逻辑
tsx // Table.tsx const Table = ({ data }: { data: AnyObject[] }) => ( <table> {data.map(row => ( <tr key={row.id}>{Object.values(row).map(cell => <td>{cell}</td>)}</tr> ))} </table> );
语法规则
- JSX 语法:支持 HTML-like 标签和表达式
{...}。 - 类型标注:组件 Props 需明确类型定义。
配置要点
-
JSX 模式:在 tsconfig.json 中设置:
json { "compilerOptions": { "jsx": "react", // React 项目 "jsx": "preserve", // 保留 JSX 结构(如 Vue JSX) "jsxFactory": "h" // 指定渲染函数名(Vue 使用 `h`) } }
三者的关键差异总结
| 特性 | .d.ts | .ts | .tsx |
|---|---|---|---|
| 文件内容 | 仅类型声明 | TypeScript 代码 | TypeScript + JSX 代码 |
| 编译输出 | 无 | .js | .js(含 JSX 转换) |
| JSX 支持 | ❌ | ❌ | ✅ |
| 运行时影响 | 无 | 直接影响运行时逻辑 | 直接影响运行时逻辑 |
| 典型场景 | 类型扩展、第三方库类型补全 | 工具函数、服务端逻辑 | React/Vue 组件、UI 逻辑 |
是否需 declare | 必须 | 禁止 | 禁止 |
实际项目中的选择策略
-
何时用 .d.ts?
- 为无类型的 JS 库添加类型支持。
- 集中管理全局类型(如
window扩展)。 - 分离类型定义与实现(大型项目推荐)。
-
何时用 .ts?
- 工具函数、API 服务层、非 UI 逻辑。
- 类型复杂的业务逻辑(如数据校验)。
-
何时用 .tsx?
- React/Vue 的组件开发。
- 需要内联 HTML 结构的渲染逻辑。
- 动态生成的 UI 元素(如表单生成器)。
高级技巧与注意事项
-
.d.ts 的模块合并
扩展第三方库类型时,使用declare module合并类型:ts // 扩展 Express 的 Request 类型 declare module 'express' { interface Request { user: { id: string }; } } -
.tsx 中的类型断言
在 JSX 中强制类型转换:tsx const element = <div>{someValue as unknown as string}</div>; -
避免 .d.ts 与 .ts 重复定义
若同时存在utils.ts和 utils.d.ts,TypeScript 会优先使用 .ts 中的类型。 -
Vue 项目的特殊配置
在 Vue 3 + TypeScript 项目中:
.d.ts、.ts 和 .tsx 文件的底层原理
这三种文件类型的核心差异源于 TypeScript 编译器(tsc)对它们的处理方式不同,最终影响代码的编译、类型检查和运行时行为。以下是它们的底层原理分析:
1. .d.ts 文件(声明文件)
核心原理
- 类型声明容器:
.d.ts 文件仅包含 类型信息(接口、类型别名、模块声明等),本质是为 TypeScript 的类型检查器提供静态类型元数据。 - 不参与编译:
TypeScript 编译器(tsc)会完全忽略 .d.ts 文件中的内容,不会生成对应的 JavaScript 代码。它的作用仅限于开发阶段的 类型推导和检查。
工作流程
- 类型解析:
当 TypeScript 编译器遇到一个模块(如import { Button } from 'my-lib')时,会优先查找对应的 .d.ts 文件(如my-lib.d.ts),从中获取Button的类型定义。 - 类型合并:
如果存在多个 .d.ts 文件对同一模块声明类型(例如扩展全局类型),TypeScript 会合并这些声明(Declaration Merging)。
示例
typescript
// jquery.d.ts(声明文件)
declare interface JQuery {
hide(): void;
show(): void;
}
declare const $: (selector: string) => JQuery;
- 运行时行为:实际调用的是外部的 jQuery 库(通过
<script>标签引入的jquery.js)。 - 开发阶段:TypeScript 通过 .d.ts 知道
$的类型,提供代码提示和类型检查。
2. .ts 文件(TypeScript 源代码文件)
核心原理
- 可编译的源代码:
.ts 文件包含 TypeScript 代码(类型注解 + 逻辑实现),会被 tsc 编译为 JavaScript 代码。 - 类型擦除:
在编译过程中,TypeScript 会移除所有类型注解(如: string),最终生成纯净的 JavaScript 代码。
工作流程
- 词法分析 & 语法分析:
tsc 将 .ts 文件解析为抽象语法树(AST),识别类型注解和代码结构。 - 类型检查:
编译器基于类型注解验证代码的类型安全性(如变量类型是否匹配)。 - 代码生成:
删除类型注解后,将 AST 转换为目标版本的 JavaScript(如 ES5、ES6)。
示例
typescript
// utils.ts(源代码)
export const add = (a: number, b: number): number => a + b;
-
编译后:
javascript // utils.js(生成的代码) export const add = (a, b) => a + b;
3. .tsx 文件(支持 JSX 的 TypeScript 文件)
核心原理
- JSX 语法支持:
.tsx 文件允许在 TypeScript 中嵌入 JSX(类似 HTML 的语法),编译器需特殊处理这些结构。 - JSX 转换规则:
JSX 元素会被转换为 JavaScript 函数调用(如React.createElement()或 Vue 的h())。
工作流程
-
JSX 解析:
编译器将 JSX 标签解析为 AST 节点,并验证其类型(如组件是否存在、属性类型是否匹配)。 -
JSX 转换:
根据 tsconfig.json 中的 jsx 配置,将 JSX 转换为目标代码:jsx: "react"→ 转换为React.createElement()。jsx: "preserve"→ 保留 JSX 结构(由其他工具处理,如 Babel)。jsx: "react-native"→ 保留 JSX 但生成 .js 文件。
示例
tsx
// Button.tsx(React 组件)
interface Props { text: string }
const Button = ({ text }: Props) => <button className="btn">{text}</button>;
-
编译后(
jsx: "react"):javascript const Button = ({ text }) => React.createElement("button", { className: "btn" }, text);
二、关键底层机制对比
| 机制 | .d.ts | .ts | .tsx |
|---|---|---|---|
| 类型检查 | 提供类型元数据 | 基于注解检查类型 | 检查 JSX 类型和组件 Props |
| 编译输出 | 无 | 生成 .js 文件 | 生成 .js(含 JSX 转换) |
| 语法扩展 | 无 | 标准 TypeScript 语法 | TypeScript + JSX 语法 |
| 运行时依赖 | 无 | 依赖生成的 .js 文件 | 依赖生成的 .js 和框架运行时(如 React) |
高级原理扩展
1. 类型声明的作用域
-
全局声明:
在 .d.ts 中使用declare global可扩展全局类型:typescript // global.d.ts declare global { interface Window { myApp: { version: string }; } } -
模块声明:
使用declare module为外部模块添加类型:typescript // my-module.d.ts declare module "my-module" { export function doSomething(): void; }
2. JSX 的类型检查
-
JSX 元素类型:
TypeScript 要求 JSX 组件的返回值必须符合JSX.Element类型。例如,在 React 中,函数组件需返回ReactElement。 -
属性类型推导:
编译器会根据组件 Props 接口验证 JSX 属性的类型:tsx // 错误:`size` 属性未定义 <Button size="large" /> // ❌ 若 Props 接口无 `size` 会报错
3. 声明文件与模块解析
-
模块查找策略:
TypeScript 根据 tsconfig.json 的moduleResolution配置(如node、classic)查找模块对应的 .d.ts 文件。 -
三斜线指令:
通过/// <reference path="..." />显式引入声明文件:typescript /// <reference path="./my-types.d.ts" />