涉及泛型对象泛型属性的赋值无法在泛型函数中正确类型检查 [英] Assignment involving generic property of generic object fails to typecheck correctly within generic function

查看:33
本文介绍了涉及泛型对象泛型属性的赋值无法在泛型函数中正确类型检查的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个通用函数,可以读取或写入给定对象的调用者选择的属性.我正在使用类型约束来确保传递的键用于可分配给相关类型或从相关类型分配的属性.调用代码似乎可以正确进行类型检查.实现中对象属性的使用未按预期进行类型检查.

I have a generic function that reads or writes the caller-chosen property of a given object. I'm using type constraints to ensure that the key passed is for a property that is assignable to or from the relevant type. Calling code appears to typecheck correctly. The usage of the object's property within the implementation does not typecheck as expected.

在本例中,我使用 boolean 作为预期类型.我已经评论了未按预期进行类型检查的行.您也可以在此处的打字稿游乐场中看到此示例.

In this example I use boolean as the expected type. I've commented the lines that are not typechecking as expected. You can also see this example in the typescript playground here.

如何表达booleanAssignmentTest 的签名,以便类型检查器理解obj[key] 的类型为boolean?能否以一种保持 boolean 本身通用的方式来完成,以允许统一定义多个与其他类型一起使用的类似函数?

How can I express the signature of booleanAssignmentTest so that the typechecker understands obj[key] has type boolean? Can it be done in a fashion that keeps the boolean itself generic to allow multiple such similar functions, that work with other types, to be defined uniformly?

type KeysOfPropertiesWithType<T, U> = {
  // We check extends in both directions to ensure assignment could be in either direction.
  [K in keyof T]: T[K] extends U ? (U extends T[K] ? K : never) : never;
}[keyof T];

type PickPropertiesWithType<T, U> = Pick<T, KeysOfPropertiesWithType<T, U>>;

function booleanAssignmentTest<T extends PickPropertiesWithType<T, boolean>, K extends KeysOfPropertiesWithType<T, boolean>>(obj: T, key: K): void {
    let foo: boolean = obj[key]; // Fine!
    let foo2: string = obj[key]; // No error, but there should be!
    obj[key] = true; // Error: "Type 'true' is not assignable to type 'T[K]'."
}

let foo = { aBool: false, aNumber: 33, anotherBool: false };
booleanAssignmentTest(foo, "aBool"); // Fine!
booleanAssignmentTest(foo, "anotherBool"); // Fine!
booleanAssignmentTest(foo, "aNumber"); // Error: working as intended!

我使用的是 tsc 3.4.5 版,以防万一.

I'm using tsc Version 3.4.5 in case it's relevant.

更新:

我在类似问题上找到了以下答案:https://stackoverflow.com/a/52047487/740958

I found the following answer on a similar issue: https://stackoverflow.com/a/52047487/740958

我尝试应用他们的方法,它更简单,效果更好,但是 obj[key] = true; 语句仍然存在相同的问题.

I tried to apply their approach which is simpler and works a little better, however the obj[key] = true; statement still has the same issue.

function booleanAssignmentTest2<T extends Record<K, boolean>, K extends keyof T>(obj: T, key: K): void {
    let foo: boolean = obj[key]; // Fine!
    let foo2: string = obj[key]; // Error: working as intended!
    obj[key] = true; // Error: "Type 'true' is not assignable to type 'T[K]'."
}

let foo = { aBool: false, aNumber: 33, anotherBool: false };

booleanAssignmentTest2(foo, "aBool"); // Fine!
booleanAssignmentTest2(foo, "anotherBool"); // Fine!
booleanAssignmentTest2(foo, "aNumber"); // Error: working as intended!

TS Playground 上的这个 ^^ 示例.

推荐答案

第一个选项(使用 KeysOfPropertiesWithType)不起作用,因为 typescript 无法推理仍然包含未解析类型参数的条件类型(如本例中的 TK)

The first option (using KeysOfPropertiesWithType) does not work because typescript can't reason about conditional types that still contains unresolved type parameters (such as T and K in this example)

第二个选项不起作用,因为 T extends Record 意味着 T 例如可以是 { a: false }这意味着赋值 obj[key] = true 将无效.通常,T[K] 必须扩展类型这一事实并不意味着我们可以在泛型函数内部为其分配任何值,约束只是告诉我们该值的最低要求是什么,我们还不知道 T[K] 需要的完整合同.

The second option does not work because T extends Record<K, boolean> means T can for example be { a: false } which would mean the assignment obj[key] = true would not be valid. Generally, the fact that T[K] must extends a type does not mean that inside the generic function we can assign any value to it, the constraint just tells us what the minimum requirement is for the value, we don't yet know the full contract T[K] requires.

至少对您的示例代码有效的解决方案是根本不使用 T.在这种情况下似乎没有必要:

A solution that does work, at least for your sample code, is to not use T at all. It does not seem necessary in this context:

function booleanAssignmentTest2<K extends PropertyKey>(obj: Record<K, boolean>, key: K): void {
    let foo: boolean = obj[key]; // Fine!
    let foo2: string = obj[key]; // Error: working as intended!
    obj[key] = true; // Ok now we know T[K] is boolean
}

let foo = { aBool: false, aNumber: 33, anotherBool: false };

booleanAssignmentTest2(foo, "aBool"); // Fine!
booleanAssignmentTest2(foo, "anotherBool"); // Fine!
booleanAssignmentTest2(foo, "aNumber"); // Error: working as intended!

如果你的例子更复杂,请提供一个完整的例子,尽管如果你确定值可以分配给T[K],通常解决方案将使用类型断言,所以这是一个可能的解决方案:

If your example is more complicated, please provide a full example, although generally the solution will be use a type assertion if you are sure the value is assignable to T[K], so this is a possible solution:

function booleanAssignmentTest2<T extends Record<K, boolean>, K extends keyof T>(obj: T, key: K): void {
    let foo: boolean = obj[key]; // Fine!
    let foo2: string = obj[key]; // Error: working as intended!
    obj[key] = true as T[K]; // ok now
}

这篇关于涉及泛型对象泛型属性的赋值无法在泛型函数中正确类型检查的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆