我正在编写一个函数,用一个对象中的property
更新另一个对象的property
像
function compareAndUpdate<T, S>(target: T, source: S, targrtKey: keyof T, sourceKey: keyof S): boolean {
// some processing
target[targrtKey] = source[sourceKey]; // this line gives error
// some more processing
}
我知道我可以使用<any>
target
和source
属于不同类型。 T
和S
可能有也可能没有相同名称的礼节,但T
可以拥有与S
是否有办法保证typeof target[targrtKey] === typeof source[sourceKey]
以便在传递不兼容的属性键时编译器捕获错误。
修改:提交上游功能请求here
答案 0 :(得分:6)
事实上,你可以这样做。以下是:
function compareAndUpdate<T, TK extends keyof T, SK extends string, S extends Record<SK,T[TK]>>(target: T, source: S, targetKey: TK, sourceKey: SK): boolean {
// some processing
target[targetKey] = source[sourceKey]; // okay
return true;
}
让我们解开声明:
T
是目标的类型TK
是目标键的类型(因此需要扩展keyof T
)。请注意,T[TK]
是该密钥的目标值的类型。SK
是源键的类型(因此需要扩展string
)S
是源的类型,需要SK
作为键,并且您从该键获得的任何值都需要分配给T[TK]
。 Record<SK,T[TK]>
是一种类型为SK
的键,类型为T[TK]
的值。因此,当S extends Record<SK,T[TK]>
时,我们要求S
为S[SK]
与T[TK]
相同的任何类型。让我们试一试:
interface Person {
name: string;
age: number;
}
const person: Person = { name: 'Stephen King', age: 69 };
interface Book {
title: string;
authorName: string;
}
const book: Book = { title: 'The Running Man', authorName: 'Richard Bachman' };
compareAndUpdate(book, person, 'authorName', 'name'); // okay
compareAndUpdate(book, person, 'authorName', 'age'); // error, number not assignable to string
compareAndUpdate(person, book, 'authorName', 'name'); // error, 'name' property not found
这对你有用吗?
答案 1 :(得分:2)
如果您希望它们相同,您可以使用相同的类型:
function compareAndUpdate<T>(target: T, source: T, targrtKey: keyof T, sourceKey: keyof T): boolean { return true; }
另一个版本是保证S
T
的任何属性
function compareAndUpdate<T extends S, S>(target: T, source: S, targrtKey: keyof T, sourceKey: keyof S): boolean { return true; }
当您使用时,如果有人试图放置目标,则会出现错误,属性不属于T
//Valid
compareAndUpdate({ test: "S"}, { test: "DD" }, "test", "test")
compareAndUpdate({ test: "S", bla : 0}, { test: "DD" }, "test", "test")
//Invalied, bla does not exist on T
compareAndUpdate({ test: "S"}, { test: "DD", bla : 0 }, "test", "test")
为简单起见,我使用对象文字,但如果S包含类中的字段,它也适用于类:
class Dog {
name: string;
bark(): void{}
}
// OK
compareAndUpdate(new Dog(), {name: "" }, "name", "name");
// NOK
compareAndUpdate(new Dog(), {isAGodBoy: "" }, "name", "name");