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

如何为函数参数与嵌套对象属性强制设置相同类型?

没问题,我来帮你搞定这个TypeScript类型约束的需求——要让append函数里的value严格匹配目标实体对应路径上的列表元素类型,我们可以用TypeScript的高级泛型和条件类型来实现,同时先把Collection接口补全,让类型推导更准确。

解决方案

首先,我们需要完善Collection接口的定义,通常这类集合状态会包含一个实体字典,方便通过entityId快速查找实体:

export type EntityId = number;

/**
 * 实体接口
 */
export interface Entity extends Object {
  readonly id: EntityId;
}

/**
 * 基于集合的状态接口
 */
export interface Collection<T extends Entity> {
  entities: Record<EntityId, T>;
  // 可根据需求添加其他字段,比如存储实体id列表的`ids: EntityId[]`
}

接下来,我们需要两个工具类型来解析路径并获取对应属性的类型:

// 将路径字符串拆分为数组(例如 "a.b.c" → ["a", "b", "c"])
type PathStringToArray<S extends string> = S extends `${infer Head}.${infer Tail}` 
  ? [Head, ...PathStringToArray<Tail>] 
  : [S];

// 递归获取对象指定路径的属性类型
type PathValue<T, P extends string | string[]> = P extends string[]
  ? P extends [infer First, ...infer Rest]
    ? First extends keyof T
      ? Rest extends string[]
        ? PathValue<T[First], Rest>
        : never
      : never
    : T
  : PathValue<T, PathStringToArray<P>>;

现在我们可以重新定义append函数,通过泛型约束让value的类型严格匹配目标路径的列表元素类型:

export function append<
  T extends Entity,
  S extends Collection<T>,
  P extends string | string[]
>(
  state: S,
  entityId: EntityId,
  path: P,
  // 强制value为目标路径数组的元素类型,如果路径不是数组则报错
  value: PathValue<T, P> extends (infer U)[] ? U : never
): S {
  // 实现逻辑示例(根据你的状态管理方式调整,比如用immer优化不可变更新)
  const targetEntity = state.entities[entityId];
  if (!targetEntity) return structuredClone(state);

  // 将路径统一转为数组格式
  const pathSegments = Array.isArray(path) ? path : path.split('.');
  
  // 递归更新嵌套属性
  const updateNested = (obj: any, segments: string[], val: any) => {
    if (segments.length === 1) {
      obj[segments[0]].push(val);
      return;
    }
    updateNested(obj[segments[0]], segments.slice(1), val);
  };

  // 深拷贝状态保证不可变性
  const newState = structuredClone(state);
  updateNested(newState.entities[entityId], pathSegments, value);

  return newState;
}

验证示例

我们定义一个具体的实体类型来测试类型约束是否生效:

// 具体实体
interface User extends Entity {
  id: number;
  name: string;
  posts: {
    titles: string[];
    likes: number[];
  };
}

// 集合状态实例
const userState: Collection<User> = {
  entities: {
    1: { id: 1, name: "Alice", posts: { titles: ["My First Post"], likes: [15] } }
  }
};

// ✅ 合法:value是string,匹配posts.titles的元素类型
append(userState, 1, "posts.titles", "New Blog Post");

// ✅ 合法:value是number,匹配posts.likes的元素类型
append(userState, 1, ["posts", "likes"], 25);

// ❌ 类型报错:value是number,但posts.titles需要string类型
append(userState, 1, "posts.titles", 123);

关键说明

  • PathValue递归遍历对象路径,精准定位目标属性的类型
  • 通过PathValue<T, P> extends (infer U)[] ? U : never提取数组元素类型,确保value只能传入符合要求的类型
  • 如果传入的路径对应的属性不是数组,TypeScript会直接报错,避免非法调用

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 11:15:49