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

如何创建兼具Pick与Readonly功能的泛型以简化TypeScript代码?

优化组合泛型类型的可读性

嘿,这个场景太熟悉了——把多个工具类型用交叉类型&拼在一起确实显得乱糟糟的,可读性大打折扣。咱们可以通过两种方式来简化这段代码,让它更清爽直观:

方案1:创建复用的组合类型别名

最直接的办法是把你那个冗长的交叉类型封装成一个可复用的类型别名,以后用的时候直接传参数就行,不用每次都写一遍交叉逻辑:

interface MyInterface { a?: string; b?: string; c?: string; }
type PropsToPick = "a" | "b"
type PropsToMakeReadonly = "a"
type MyPick<T, K extends keyof T> = { [P in K]: T[P]; };
type MyReadonly<T, K extends keyof T> = { readonly [P in K]: T[P]; }

// 新增:封装组合类型
type MyPickWithReadonly<T, PickKeys extends keyof T, ReadonlyKeys extends keyof T> = 
  MyPick<T, PickKeys> & MyReadonly<T, ReadonlyKeys>;

const myObject: MyInterface = { a: '1', b: '2' }
// 现在用法简洁多了!
const myNewObject: MyPickWithReadonly<MyInterface, PropsToPick, PropsToMakeReadonly> = myObject

myNewObject.a = '11' // 依然会报错,因为a是只读的
myNewObject.b = '22' // 正常可修改

方案2:自定义一站式工具类型

如果想更进一步,咱们可以直接写一个同时支持挑选属性和设置只读的工具类型,不用依赖两个泛型的交叉,这样类型结构更清晰,IDE的类型提示也会更友好:

interface MyInterface { a?: string; b?: string; c?: string; }
type PropsToPick = "a" | "b"
type PropsToMakeReadonly = "a"

// 自定义一站式工具类型:挑选指定属性,并将其中部分设为只读
type PickWithReadonly<T, PickKeys extends keyof T, ReadonlyKeys extends PickKeys> = {
  // 处理可写属性:属于要挑选的集合,但不在只读集合里的属性
  [P in Exclude<PickKeys, ReadonlyKeys>]: T[P];
} & {
  // 处理只读属性:同时在挑选集合和只读集合里的属性
  readonly [P in ReadonlyKeys]: T[P];
};

const myObject: MyInterface = { a: '1', b: '2' }
const myNewObject: PickWithReadonly<MyInterface, PropsToPick, PropsToMakeReadonly> = myObject

myNewObject.a = '11' // 依然报错,符合预期
myNewObject.b = '22' // 正常工作

这里我给ReadonlyKeys加了extends PickKeys的约束,确保你要设为只读的属性一定是你挑选出来的属性,避免出现无效的属性名,让类型更严谨。

两种方案都能达到你想要的效果,方案1更简单,适合快速复用现有类型;方案2的类型结构更直观,类型提示也更清晰,适合长期使用的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 23:57:40