
问题有点长,不过主要是有例子
比如说我有这样的一个 interface
interface MyInterface { propertyA: string | null; propertyB: number | null; properyC: 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 ,这不是我想要的。那么这边的这个泛型约束应该如何定义?
1 ifdef 2022-08-10 14:38:48 +08:00 Partial<MyInterface> |
2 exonuclease 2022-08-10 16:26:30 +08:00 const updateProperties = ( originalObject: MyInterface, newProperties: Partial<MyInterface> ): MyInterface => { return { ...originalObject,...newProperties }; }; |
3 YuJianrong 2022-08-12 07:51:10 +08:00 第一个你需要类似 |
5 YuJianrong 2022-08-12 07:54:56 +08:00 范型函数里面的报错一般都不重要,就不要管了,any 吧…… |