如何在TypeScript中限制函数仅接受含访问器(getter/setter)的对象?
如何用TypeScript类型系统限制对象必须拥有访问器(getter/setter)
要实现只接受带访问器属性的对象,我们可以通过TypeScript的条件类型和泛型约束来区分普通属性与访问器属性,具体有两种常用方案:
方案一:约束指定属性为访问器
这种方案会检查你传入的属性是否为访问器,若传入普通属性则直接报错:
// 定义访问器属性的结构类型 type AccessorProperty<V> = { get(): V; set(v: V): void; }; // 辅助类型:验证对象T的K属性是否为访问器,符合则返回原类型,否则返回never type HasAccessor<T, K extends keyof T> = T extends Record<K, AccessorProperty<T[K]>> ? T : never; // 修改后的函数,仅接受带有指定访问器属性的对象 function replaceUsingAccessor< T, TKey extends keyof T, R extends T[TKey] = T[TKey] >(obj: HasAccessor<T, TKey>, prop: TKey, replacement: R): R { (obj as any)[prop] = replacement; return replacement; }
测试用例
// 合法:带访问器的对象 const accessorObj = { get count() { return 0; }, set count(v: number) { console.log(`设置count为${v}`); } }; replaceUsingAccessor(accessorObj, 'count', 5); // 正常执行 // 非法:普通属性对象,会触发类型错误 const plainObj = { count: 0 }; replaceUsingAccessor(plainObj, 'count', 5); // 报错:Argument of type '{ count: number; }' is not assignable to parameter of type 'never'.
方案二:仅允许选择访问器属性作为参数
这种方案更严格,直接限制prop参数只能是对象中所有访问器属性的键,连普通属性的键都无法传入:
// 提取对象中所有属于访问器的键 type AllAccessorKeys<T> = { [K in keyof T]: T extends { get [K](): T[K]; set [K](v: T[K]): void } ? K : never }[keyof T]; // 修改后的函数,prop只能是访问器属性的键 function replaceUsingAccessor< T, TKey extends AllAccessorKeys<T>, R extends T[TKey] = T[TKey] >(obj: T, prop: TKey, replacement: R): R { (obj as any)[prop] = replacement; return replacement; }
测试用例
const mixedObj = { id: 123, // 普通属性 get name() { return 'Alice'; }, set name(v: string) {} // 访问器属性 }; // 合法:选择访问器属性键 replaceUsingAccessor(mixedObj, 'name', 'Bob'); // 正常执行 // 非法:选择普通属性键,直接触发类型错误 replaceUsingAccessor(mixedObj, 'id', 456); // 报错:Type '"id"' is not assignable to type '"name"'.
原理说明
TypeScript中,普通属性的对象与带有访问器的对象在类型兼容性上是单向的:带访问器的对象可以赋值给普通属性类型,但普通属性对象无法赋值给要求访问器的类型。我们正是利用这一点,通过条件类型构造约束,过滤掉不符合要求的普通属性对象。
内容的提问来源于stack exchange,提问作者oligofren
相关产品推荐
相关产品推荐

