问题有点长,不过主要是有例子
比如说我有这样的一个 interface
interface MyInterface {
propertyA: string | null;
propertyB: number | null;
propertyC: SomeOtherInterface | null;
propertyD: AnotherOtherInterface | null;
}
interface SomeOtherInterface {
propertyX: string | null;
propertyY: number | null;
}
interface AnotherOtherInterface {
propertyM: string | null;
propertyN: number | null;
}
然后我需要一个方法去修改这个 MyInterface 的一个对象上的属性(假如说返回一个新对象而不是原地修改),那么我可以写出这样的一个泛型方法:
const updateProperty = <
TKey extends keyof MyInterface,
TValue extends MyInterface[TKey]
>(
originalObject: MyInterface,
propertyName: TKey,
propertyValue: TValue
): MyInterface => {
// ... some internal logic
return { ...originalObject, [propertyName]: propertyValue };
};
到这里也没有问题,调用起来也很简单
const new1 = updateProperty(original, "propertyA", "some string");
第一个问题是,如果再写一个方法,一次性需要修改不确定数量的多个属性,这个泛型定义怎么写? 我想到的方法是,写个数组传
const updateProperties = <
TKey extends keyof MyInterface,
TValue extends MyInterface[TKey]
>(
originalObject: MyInterface,
propertiesToUpdate: { propertyName: TKey; propertyValue: TValue }[]
): MyInterface => {
// ... some internal logic
const updatedObject = { ...originalObject };
propertiesToUpdate.forEach(
(p) => (updatedObject[p.propertyName] = p.propertyValue)
);
return updatedObject;
};
设想的调用方法是
const new2 = updateProperties(original, [
{
propertyName: "propertyA",
propertyValue: "some string",
},
{
propertyName: "propertyB",
propertyValue: 10,
},
]);
但是实际上是不可以的,因为在修改多个不同类型的属性的时候,TKey 就是联合类型了,TValue 也会变成相对于 TKey 的联合类型,导致无法赋值回去 我想的是在 propertyName 为 A 的时候,propertyValue 只能为 string | null ,同时对于 B ,只能接收 number | null ,数组中各个元素是相互独立的 那我这个方法应该如何修改?
第二个问题是,如果我想写一个方法去修改 propertyC -> propertyX / Y, propertyD -> propertyM/N ,这个泛型方法怎么写 期望的调用方法是
updateSubProperty("propertyC", "propertyX", "some string");
updateSubProperty("propertyD", "propertyY", 20);
其中第一个参数限定为像 propertyC/D 这样的“对象”而不是 string number 这样的基础类型(描述的可能不准确),第二个第三个参数是限定为第一个参数确定下来的类型的属性和值 简单的想法如下
const updateSubProperty = <
TTop extends keyof MyInterface, // Should be restricted to some "object" only properties, but how?
TSub extends keyof MyInterface[TTop],
TValue extends keyof MyInterface[TTop][TSub]
>(
originalObject: MyInterface,
topProperty: TTop,
subProperty: TSub,
value: TValue
) => {
// ... some internal logic
const originalSubObject = originalObject[topProperty] ?? ({} as TTop); // It's acceptable to leave some properties missing, no worry
const newSubObject = { ...originalSubObject, [subProperty]: value }; // ERROR: Spread types may only be created from object types.ts(2698)
return { ...originalObject, [topProperty]: newSubObject };
};
然后理所当然是报了错,Spread types may only be created from object types.ts(2698)
原因我也知道,TTop 那边应该限制为 object-only ,像上面那样写的话依然可以给第一个参数传 propertyA ,这不是我想要的。那么这边的这个泛型约束应该如何定义?
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.