确保键和值类型之间的对应

时间:2019-10-25 12:24:44

标签: typescript

如果我有类型:

interface ITest {
    a: number;
    b: string;
}

我可能想定义一个函数,该函数通过该接口动态设置对象的属性。

我可以找到一些办法,但是以下代码似乎无法正确比较键和值类型:

interface ITest {
    a: number;
    b: string;
}

const item: ITest = {
    a: 1,
    b: 'A Test',
}

const setValue = <K extends keyof ITest>(x: ITest) =>
    (key: K) =>
        (value: ITest[K]) => x[key] = value; 

参与其中。但是,通过将其放置在操场上,我们得到的结果并不明确:

interface ITest {
    a: number;
    b: string;
}

const item: ITest = {
    a: 1,
    b: 'A Test',
}

const setValue = <K extends keyof ITest>(x: ITest) =>
    (key: K) =>
        (value: ITest[K]) => x[key] = value; 

setValue(item)('a')('test');
setValue(item)('b')(false);    // only this ones shows an error
setValue(item)('b')(1);
setValue(item)('b')('b');      // if properly working, only this will work

这似乎意味着只要valueITest上任何值的类型,编译器都会感到满意。

1 个答案:

答案 0 :(得分:1)

定义可能应该是:

const setValue = (x: ITest) =>
    <K extends keyof ITest>(key: K) =>
        (value: ITest[K]) => x[key] = value;

您可以测试的

setValue(item)("a")(3); // okay
setValue(item)("b")("hello"); // okay
setValue(item)("c")(123); // error!
//  ---------> ~~~
// "c" is not assignable to "a" | "b".
setValue(item)("a")("whoops"); // error!
//  --------------> ~~~~~~~~
// "whoops" is not assignable to number

想法是setValue本身是一个具体的非泛型函数,但是setValue(item)返回的函数应该是K的{​​{1}}类型的泛型,然后应将函数 返回的key参数限制为value


请注意,您不希望ITest[K]本身在setValue中是通用的,否则当您调用K时,将已经指定setValue(item)的类型,然后返回的函数将是类型为K的具体类型,它允许诸如(key: keyof ITest)=>(value: ITest[keyof ITest])=>ITest[keyof ITest]这样的坏事情:

setValue(item)("a")("whoops")

此外,由于const badSetValue = <K extends keyof ITest>(x: ITest) => (key: K) => (value: ITest[K]) => x[key] = value; badSetValue(item)("a")(3); // okay badSetValue(item)("b")("hello"); // okay badSetValue(item)("c")(123); // error! // ------------> ~~~ // "c" is not assignable to "a" | "b". badSetValue(item)("a")("whoops"); // okay?! uh oh 的实现是如此普遍,因此您可能决定您希望它变得更加普遍:

setValue()

const generalSetValue = <T>(x: T) => <K extends keyof T>(key: K) => (value: T[K]) => x[key] = value; 对象上的setValue()类似,

ITest

但在其他对象类型上也同样适用:

generalSetValue(item)("a")(3); // okay
generalSetValue(item)("b")("hello"); // okay
generalSetValue(item)("c")(123); // error!
//  ----------------> ~~~
// "c" is not assignable to "a" | "b".
generalSetValue(item)("a")("whoops"); // error!
//  ---------------------> ~~~~~~~~
// "whoops" is not assignable to number

好的,希望能有所帮助;祝你好运!

Link to code