You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在TypeScript中创建可自动窄化类型的通用JSON对象接口?

解决JSON对象嵌套属性访问的TypeScript类型问题

问题根源

你原来定义的JsonLikeObject接口中,每个属性的类型是string | number | boolean | JsonLikeObject | JsonLikeObject[]的联合类型。当访问嵌套属性(如myStuff.x.y)时,TypeScript无法确定myStuff.x到底是对象还是基本类型(string/number/boolean),因此会报错——因为基本类型没有y属性。

解决方案

我们需要定义完整的合法JSON类型体系,并结合类型守卫实现自动类型窄化,同时完全禁止Symbol等非JSON合法类型。

1. 定义合法的JSON类型

首先定义覆盖所有JSON规范的类型,包含null(JSON原生支持null),并通过递归结构支持嵌套对象和数组:

// 所有合法的JSON值类型
type JsonValue = string | number | boolean | null | JsonObject | JsonArray;

// JSON对象:键为字符串,值为任意JSON值
interface JsonObject {
  [key: string]: JsonValue;
}

// JSON数组:元素为任意JSON值
interface JsonArray extends Array<JsonValue> {}

这个类型体系自动排除了Symbol、Function等非JSON类型,完全符合JSON规范。

2. 使用类型守卫实现自动窄化

由于通用JSON对象的结构是动态的,TypeScript无法静态推断嵌套属性的类型,因此需要类型守卫来明确当前值的类型,从而允许合法的嵌套访问:

// 判断值是否为JSON对象的类型守卫
function isJsonObject(value: JsonValue): value is JsonObject {
  return typeof value === 'object' && value !== null && !Array.isArray(value);
}

// 判断值是否为JSON数组的类型守卫
function isJsonArray(value: JsonValue): value is JsonArray {
  return Array.isArray(value);
}

// 示例用法
const apiData: JsonObject = {
  profile: {
    name: "Bob",
    score: 95,
    tags: ["frontend", { category: "framework", name: "React" }]
  }
};

// 安全访问嵌套对象属性
if (isJsonObject(apiData.profile)) {
  console.log(apiData.profile.name); // 类型:string
  console.log(apiData.profile.score); // 类型:number

  // 安全访问数组元素
  if (isJsonArray(apiData.profile.tags)) {
    const secondTag = apiData.profile.tags[1];
    if (isJsonObject(secondTag)) {
      console.log(secondTag.name); // 类型:string
    }
  }
}

在类型守卫的代码块内部,TypeScript会自动将值的类型窄化为对应的JsonObject或JsonArray,此时嵌套属性访问完全合法。

3. 已知结构的JSON场景

如果你的JSON结构是提前已知的,可以使用satisfies关键字(TypeScript 4.9+)结合as const来保留精确的字面量类型,无需类型守卫即可直接访问嵌套属性:

const fixedData = {
  user: { id: 101, active: true },
  items: [{ title: "Book", price: 29.9 }]
} as const satisfies JsonObject;

// 直接访问嵌套属性,TypeScript自动推断类型
console.log(fixedData.user.id); // 类型:101
console.log(fixedData.items[0].price); // 类型:29.9

内容的提问来源于stack exchange,提问作者NoNameProvided

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 06:54:33