typescript通过兼容的属性类型约束泛型

时间:2017-09-14 13:21:39

标签: typescript

我正在编写一个函数,用一个对象中的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>

来修复错误

targetsource属于不同类型。 TS可能有也可能没有相同名称的礼节,但T可以拥有与S

中其他属性相同的属性

是否有办法保证typeof target[targrtKey] === typeof source[sourceKey]以便在传递不兼容的属性键时编译器捕获错误。

修改:提交上游功能请求here

2 个答案:

答案 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]>时,我们要求SS[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");