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 仅限制属性本身不可重写,不影响内部对象的修改。
相关推荐
鸡鸭扣7 分钟前
DRF/Django+Vue项目线上部署:腾讯云+Centos7.6(github的SSH认证)
前端·vue.js·python·django·腾讯云·drf
龙井茶Sky9 分钟前
验证码与登录过程逻辑学习总结
前端·登录·验证码
重生之后端学习15 分钟前
day23-集合(泛型&Set&数据结构)
java·开发语言·数据结构·算法
老胖闲聊34 分钟前
Python httpx库终极指南
开发语言·python·httpx
Edward Nygma1 小时前
springboot3+vue3融合项目实战-大事件文章管理系统-更新用户密码
android·开发语言·javascript
sunbyte1 小时前
Three.js + React 实战系列 - 职业经历区实现解析 Experience 组件✨(互动动作 + 3D 角色 + 点击切换动画)
javascript·react.js·3d
2401_831943321 小时前
Element Plus对话框(ElDialog)全面指南:打造灵活弹窗交互
前端·vue.js·交互
菲兹园长1 小时前
MyBatis-Plus
java·开发语言·mybatis
计算机学姐2 小时前
基于SpringBoot的在线教育管理系统
java·vue.js·spring boot·后端·mysql·spring·mybatis
strongwyy2 小时前
DA14585墨水屏学习(2)
前端·javascript·学习