【发布时间】:2019-09-29 23:30:42
【问题描述】:
我有一个通用函数,可以读取或写入给定对象的调用者选择的属性。我使用类型约束来确保传递的键是用于可分配给相关类型或从相关类型分配的属性。调用代码似乎可以正确地进行类型检查。实现中对象属性的使用未按预期进行类型检查。
在本例中,我使用 boolean 作为预期类型。我已经评论了没有按预期进行类型检查的行。 You can also see this example in the typescript playground here.
如何表达booleanAssignmentTest 的签名,以便类型检查器理解obj[key] 的类型为boolean?是否可以以保持boolean 本身通用的方式完成,以允许统一定义多个与其他类型一起使用的类似函数?
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 版以防万一。
更新:
我在类似问题上找到了以下答案:https://stackoverflow.com/a/52047487/740958
我尝试应用他们的更简单且效果更好的方法,但是obj[key] = true; 语句仍然存在同样的问题。
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!
【问题讨论】:
-
也可能相关:stackoverflow.com/questions/52188399/… 特别是用户@jcalz 提到的地方:“编译器不够聪明,无法意识到 T[K] 在一般情况下可分配给数字”。尽管我的问题似乎相反,但该布尔值不能分配给 T[K]。
-
我可能对这个级别的 Typescript 有点陌生,但不是说 'boolean' 不是对象类型,所以虽然它有值 'true' 和 'false' ,它实际上并没有代表“真”和“假”的属性,因此您无法真正确定 T[K] 是否扩展了 U 或反之亦然 - 因为它不能。实际上,对于 boolean/string/number,您必须指定其中一种类型的 instanceof。还是我错过了什么?
标签: javascript typescript typescript-generics