Typescript中的对象类型

对象类型


1. 对象类型定义

基本语法
  • 匿名对象类型:直接在函数参数中声明。

    ts 复制代码
    function greet(person: { name: string; age: number }) {
      return "Hello " + person.name;
    }
  • 接口(Interface):命名复用性强的对象类型。

    ts 复制代码
    interface Person {
      name: string;
      age: number;
    }
    
    function greet(person: Person) {
      return "Hello " + person.name;
    }
  • 类型别名(Type Alias):适用于更复杂的类型组合。

    ts 复制代码
    type Person = {
      name: string;
      age: number;
    };
    
    function greet(person: Person) {
      return "Hello " + person.name;
    }
差异比较
  • 接口 vs 类型别名
    • 接口:支持合并(相同名称的接口会自动合并),适合定义契约。
    • 类型别名 :一次性定义,不能重复声明,适合联合类型(type ID = string | number)。

2. 属性修饰符

可选属性
  • 使用 ? 标记属性为可选,调用时可省略。

    ts 复制代码
    interface PaintOptions {
      shape: Shape;
      xPos?: number;
      yPos?: number;
    }
    
    paintShape({ shape }); // 合法
    paintShape({ shape, xPos: 100 }); // 合法
  • 默认值处理

    • 使用解构赋值设置默认值,简化代码。

      ts 复制代码
      function paintShape({ shape, xPos = 0, yPos = 0 }: PaintOptions) {
        console.log("x coordinate at", xPos);
      }
只读属性
  • 使用 readonly 防止属性被修改。

    ts 复制代码
    interface SomeType {
      readonly prop: string;
    }
    
    const obj: SomeType = { prop: "value" };
    obj.prop = "new value"; // ❌ 错误:不可写
  • 嵌套对象的只读性

    • readonly 只限制顶层属性,不影响内部对象的可变性。

      ts 复制代码
      interface Home {
        readonly resident: { name: string; age: number };
      }
      
      let home: Home = { resident: { name: "Alice", age: 30 } };
      home.resident.age++; // ✅ 允许修改内部属性
      home.resident = { ... }; // ❌ 不允许重新赋值

3. 索引签名

动态键值对
  • 描述未知属性名的对象。

    ts 复制代码
    interface StringArray {
      [index: number]: string;
    }
    
    const arr: StringArray = ["a", "b"]; // ✅
    console.log(arr[1]); // 输出 "b"
  • 字符串索引签名

    • 限制所有属性值类型一致,适用于字典模式。

      ts 复制代码
      interface Dictionary {
        [key: string]: number;
      }
      
      const dict: Dictionary = { a: 1, b: 2 }; // ✅
  • 混合固定属性与索引签名

    • 固定属性类型必须是索引签名类型的子集。

      ts 复制代码
      interface NumberOrStringDictionary {
        [key: string]: number | string;
        length: number; // ✅ 允许
        name: string; // ✅ 允许
      }
只读索引签名
  • 防止通过索引修改值。

    ts 复制代码
    interface ReadonlyStringArray {
      readonly [index: number]: string;
    }
    
    let arr: ReadonlyStringArray = ["a", "b"];
    arr[0] = "c"; // ❌ 错误:不可修改

4. 超额属性检查

严格检查机制
  • 创建对象字面量时,若包含目标类型未声明的属性,TypeScript 报错。

    ts 复制代码
    interface SquareConfig {
      color?: string;
      width?: number;
    }
    
    createSquare({ colour: "red", width: 100 }); // ❌ 'colour' 不存在于 SquareConfig 中
绕过检查的方法
  1. 类型断言(需谨慎使用):

    ts 复制代码
    createSquare({ colour: "red", width: 100 } as SquareConfig);
  2. 添加字符串索引签名

    ts 复制代码
    interface SquareConfig {
      [propName: string]: unknown; // 允许任意属性
      color?: string;
      width?: number;
    }
  3. 中间变量赋值

    ts 复制代码
    const options = { colour: "red", width: 100 };
    createSquare(options); // ✅ 无错误

5. 类型扩展与交叉

接口扩展
  • 使用 extends 继承并扩展其他接口。

    ts 复制代码
    interface BasicAddress {
      street: string;
      city: string;
    }
    
    interface FullAddress extends BasicAddress {
      postalCode: string;
    }
交叉类型(Intersection Types)
  • 使用 & 合并多个类型。

    ts 复制代码
    interface Colorful {
      color: string;
    }
    
    interface Circle {
      radius: number;
    }
    
    type ColorfulCircle = Colorful & Circle;
    
    const cc: ColorfulCircle = {
      color: "red",
      radius: 42,
    };

6. 工具类型

常用工具类型
  • Partial:将所有属性变为可选。

    ts 复制代码
    type PartialPerson = Partial<Person>; // name?: string; age?: number
  • Required:将所有属性变为必填。

    ts 复制代码
    type RequiredPerson = Required<Person>; // name: string; age: number
  • Readonly:将所有属性设为只读。

    ts 复制代码
    type ReadOnlyPerson = Readonly<Person>;

7. 实际应用场景

表单数据处理
  • 动态表单字段类型化:

    ts 复制代码
    interface FormData {
      [fieldName: string]: string | number | boolean | undefined;
    }
    
    function processForm(data: FormData) {
      Object.entries(data).forEach(([key, value]) => {
        if (typeof value === "string") {
          console.log(`${key}: ${value}`);
        }
      });
    }
缓存实现
  • 使用索引签名管理缓存数据:

    ts 复制代码
    interface Cache<T> {
      [key: string]: { value: T; timestamp: number };
    }
    
    class DataCache<T> {
      private cache: Cache<T> = {};
      
      set(key: string, value: T): void {
        this.cache[key] = { value, timestamp: Date.now() };
      }
      
      get(key: string): T | undefined {
        return this.cache[key]?.value;
      }
    }

8. 最佳实践

  1. 优先使用接口:定义明确的契约,便于扩展和维护。
  2. 合理使用可选属性:避免强制要求不必要的字段。
  3. 避免过度使用索引签名 :可能导致类型不安全,优先考虑 MapRecord
  4. 谨慎使用类型断言:确保类型正确性,避免隐藏错误。
  5. 利用工具类型:减少重复代码,提升类型安全性。

9. 高级技巧

泛型对象类型
  • 结合泛型实现灵活的类型约束:

    ts 复制代码
    interface ApiResponse<T> {
      data: T;
      meta: { [key: string]: unknown };
    }
    
    async function fetchData<T>(url: string): Promise<ApiResponse<T>> {
      const response = await fetch(url);
      return response.json();
    }
映射类型
  • 自定义映射类型转换属性:

    ts 复制代码
    type Optional<T> = {
      [K in keyof T]?: T[K];
    };
    
    type OptionalPerson = Optional<Person>; // name?: string; age?: number

10. 常见误区

  • 索引签名与固定属性冲突

    • 固定属性类型必须符合索引签名类型,否则报错。

      ts 复制代码
      interface User {
        [key: string]: string | number; // ❌ 下方的布尔值不符合
        isAdmin: boolean;
      }
  • 只读属性与不可变性的区别

    • readonly 仅限制属性本身不可重写,不影响内部对象的修改。
相关推荐
前端码虫14 分钟前
JS分支和循环
开发语言·前端·javascript
GISer_Jing16 分钟前
MonitorSDK_性能监控(从Web Vital性能指标、PerformanceObserver API和具体代码实现)
开发语言·前端·javascript
余厌厌厌16 分钟前
墨香阁小说阅读前端项目
前端
fanged18 分钟前
Angularjs-Hello
前端·javascript·angular.js
lichuangcsdn19 分钟前
springboot集成websocket给前端推送消息
前端·websocket·网络协议
程序员阿龙19 分钟前
基于Web的濒危野生动物保护信息管理系统设计(源码+定制+开发)濒危野生动物监测与保护平台开发 面向公众参与的野生动物保护与预警信息系统
前端·数据可视化·野生动物保护·濒危物种·态环境监测·web系统开发
岸边的风23 分钟前
JavaScript篇:JS事件冒泡:别让点击事件‘传染’!
开发语言·前端·javascript
前端大卫2 小时前
Vue3 里的 h 函数的运用场景!
前端·vue.js
bubiyoushang8882 小时前
matlab雷达定位仿真
开发语言·matlab
ladymorgana2 小时前
【OSS】 前端如何直接上传到OSS 上返回https链接,如果做到OSS图片资源加密访问
前端·网络协议·https