javascript - 涉及泛型对象的泛型属性的赋值无法在泛型函数中正确进行类型检查
问题描述
我有一个通用函数,可以读取或写入给定对象的调用者选择的属性。我使用类型约束来确保传递的键是用于可分配给相关类型或从相关类型分配的属性。调用代码似乎可以正确地进行类型检查。实现中对象属性的使用未按预期进行类型检查。
在此示例中,我使用 boolean 作为预期类型。我已经评论了没有按预期进行类型检查的行。您还可以在此处的打字稿游乐场中查看此示例。
我怎样才能表达的签名,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!
解决方案
第一个选项(使用KeysOfPropertiesWithType
)不起作用,因为打字稿无法推理仍然包含未解析类型参数的条件类型(例如T
和K
在这个例子中)
第二个选项不起作用,因为例如T extends Record<K, boolean>
手段T
可以是{ a: false }
这意味着分配obj[key] = true
无效。通常,T[K]
必须扩展类型这一事实并不意味着在泛型函数内部我们可以为其分配任何值,约束只是告诉我们该值的最低要求是什么,我们还不知道完整的合同T[K]
要求。
至少对您的示例代码有效的解决方案是根本不使用T
。在这种情况下似乎没有必要:
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]
,因此这是一个可能的解决方案:
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
}
推荐阅读
- python - 使用 Python/Pandas 将未知长度的表打印到 CSV 文件
- pandas - 在python中获取最近合并前后30分钟的记录
- terminal - 使用 systemd-run 限制多个进程的 CPU 和内存使用的语法?
- python - 在将列表输入谷歌表格时,我应该如何修复列表索引超出范围错误?
- sendgrid - 收件人服务器阻止电子邮件在 SendGrid 中传递
- notificationcenter - 在 macOS Big Sur 中使用代码切换 DoNotDisturb
- forms - R Web Scraping rvest 表单 submit_form
- java - 如何为转置矩阵编写方法?
- android - 如何在 Android 应用上获取 STUN 或 TURN 服务器?
- javascript - 自定义 Kendo 下拉列表消息以替换“未找到数据”