【ts】全面详细对比 .d.ts、.ts、.tsx文件

1,493 阅读6分钟

Jym好😘,我是珑墨,今天给大家分享  全面详细对比 .d.ts、.ts、.tsx文件  ,嘎嘎的😍,看下面。


一、对比三个文件

1. .d.ts 文件(TypeScript 声明文件)

核心定义
  • 纯类型声明:仅包含类型定义(无运行时逻辑)。
  • 不参与编译:不会被编译为 .js 文件,仅在开发阶段提供类型支持。
核心用途
  1. 为 JS 库提供类型支持
    例如:为旧版 jQuery 添加类型声明:

    ts
    // jquery.d.ts
    declare interface JQuery {
      hide(): void;
      show(): void;
    }
    declare var $: (selector: string) => JQuery;
    
  2. 全局类型扩展
    定义项目中全局可用的类型或模块:

    ts
    // global.d.ts
    declare type AnyObject = Record<string, any>;
    declare module "*.svg" { 
      const content: string;
      export default content;
    }
    
  3. 模块类型描述
    描述第三方模块的类型结构:

    ts
    // my-module.d.ts
    declare module "my-module" {
      export function greet(name: string): string;
    }
    
语法规则
  • 仅限类型语法:不能包含 constfunction 等运行时代码。
  • 声明关键字:必须使用 declare 声明全局可见的类型或变量。
配置要点
  • 自动生成:在 tsconfig.json 中设置 "declaration": true,编译 .ts 文件时会生成对应的 .d.ts
  • 类型包含:通过 /// <reference path="..." /> 或 include 字段引入声明文件。

2. .ts 文件(TypeScript 源代码文件)

核心定义
  • 可执行代码:包含 TypeScript 代码(类型注解 + 逻辑实现)。
  • 参与编译:会被编译为 .js 文件。
核心用途
  1. 工具函数

    ts
    // utils.ts
    export const deepClone = <T>(obj: T): T => JSON.parse(JSON.stringify(obj));
    
  2. 业务逻辑

    ts
    // userService.ts
    interface User { id: number; name: string }
    export const fetchUser = async (id: number): Promise<User> => { /* ... */ };
    
  3. 非 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 转换为目标框架的渲染函数。
核心用途
  1. React 组件

    tsx
    // Button.tsx
    interface Props { text: string }
    const Button = ({ text }: Props) => <button className="btn">{text}</button>;
    
  2. 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>;
      }
    });
    
  3. 复杂 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必须禁止禁止

实际项目中的选择策略

  1. 何时用 .d.ts

    • 为无类型的 JS 库添加类型支持。
    • 集中管理全局类型(如 window 扩展)。
    • 分离类型定义与实现(大型项目推荐)。
  2. 何时用 .ts

    • 工具函数、API 服务层、非 UI 逻辑。
    • 类型复杂的业务逻辑(如数据校验)。
  3. 何时用 .tsx

    • React/Vue 的组件开发。
    • 需要内联 HTML 结构的渲染逻辑。
    • 动态生成的 UI 元素(如表单生成器)。

高级技巧与注意事项

  1. .d.ts 的模块合并
    扩展第三方库类型时,使用 declare module 合并类型:

    ts
    // 扩展 Express 的 Request 类型
    declare module 'express' {
      interface Request {
        user: { id: string };
      }
    }
    
  2. .tsx 中的类型断言
    在 JSX 中强制类型转换:

    tsx
    const element = <div>{someValue as unknown as string}</div>;
    
  3. 避免 .d.ts 与 .ts 重复定义
    若同时存在 utils.ts 和 utils.d.ts,TypeScript 会优先使用 .ts 中的类型。

  4. Vue 项目的特殊配置
    在 Vue 3 + TypeScript 项目中:

    • 单文件组件使用 <script lang="ts">
    • 渲染函数或高阶组件使用 .tsx 文件。
    • 配置 vue-tsc 确保类型检查。

.d.ts.ts 和 .tsx 文件的底层原理

这三种文件类型的核心差异源于 TypeScript 编译器(tsc)对它们的处理方式不同,最终影响代码的编译、类型检查和运行时行为。以下是它们的底层原理分析:


1. .d.ts 文件(声明文件)

核心原理
  • 类型声明容器
    .d.ts 文件仅包含 类型信息(接口、类型别名、模块声明等),本质是为 TypeScript 的类型检查器提供静态类型元数据。
  • 不参与编译
    TypeScript 编译器(tsc)会完全忽略 .d.ts 文件中的内容,不会生成对应的 JavaScript 代码。它的作用仅限于开发阶段的 类型推导和检查
工作流程
  1. 类型解析
    当 TypeScript 编译器遇到一个模块(如 import { Button } from 'my-lib')时,会优先查找对应的 .d.ts 文件(如 my-lib.d.ts),从中获取 Button 的类型定义。
  2. 类型合并
    如果存在多个 .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 代码。
工作流程
  1. 词法分析 & 语法分析
    tsc 将 .ts 文件解析为抽象语法树(AST),识别类型注解和代码结构。
  2. 类型检查
    编译器基于类型注解验证代码的类型安全性(如变量类型是否匹配)。
  3. 代码生成
    删除类型注解后,将 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())。
工作流程
  1. JSX 解析
    编译器将 JSX 标签解析为 AST 节点,并验证其类型(如组件是否存在、属性类型是否匹配)。

  2. 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 配置(如 nodeclassic)查找模块对应的 .d.ts 文件。

  • 三斜线指令
    通过 /// <reference path="..." /> 显式引入声明文件:

    typescript
    /// <reference path="./my-types.d.ts" />
    

三、总结

  • .d.ts:是 TypeScript 类型系统的基石,提供静态类型元数据,不参与运行时。
  • .ts:是 TypeScript 的核心编译单元,通过类型擦除生成可执行的 JavaScript。
  • .tsx:是 TypeScript 对 JSX 的扩展,编译时需特殊处理 JSX 语法,与 UI 框架深度集成。