hit*_*han 7 typescript typescript-generics typescript-typings
我有一个以下函数,它采用扩展 Foo (它是一个对象)的 T 类型参数。在该函数中,它迭代给定对象的每个键,以创建一个具有完全相同键但相应值均为 1 的新对象(该函数执行的操作并不重要)。
但它无法通过Type '1' is not assignable to type 'T[Extract<keyof T, string>]'.. 我认为分配T[Extract<keyof T, string>]哪个 应该有效。number1number
我的代码有什么问题?
type Foo = {
[key: string]: number
}
const func = <T extends Foo>(obj: T): T => {
for (const name in obj) {
obj[name] = 1
}
return obj
}
Run Code Online (Sandbox Code Playgroud)
编译器通常不会对泛型类型的操作进行非常复杂的分析(即依赖于未解析的类型参数的类型,例如T在 的实现中func())...它往往更擅长处理具体类型(例如Foo)更简单。
因此编译器非常满意并将允许您的函数使用以下具体版本:
\n\nconst concreteFunc = (obj: Foo): Foo => {\n for (const name in obj) {\n obj[name] = 1; // okay\n }\n return obj; // okay\n};\nRun Code Online (Sandbox Code Playgroud)\n\n由于尚不知道未解析的泛型类型,因此编译器将不太确定您正在做的事情是否安全,并且可能会发出警告。此警告并不一定意味着您肯定犯了错误。
\n\n这种情况经常发生在泛型函数的实现中。如果您仔细分析您正在做的事情并确定它确实是类型安全的,则可以使用类型断言来删除警告。
\n\n例如,您可以这样做:
\n\nconst func = <T extends Foo>(obj: T): T => {\n for (const name in obj) {\n obj[name] = 1 as T[typeof name]; // assert BUT BEWARE \xe2\x98\xa0\n }\n return obj;\n};\nRun Code Online (Sandbox Code Playgroud)\n\n但请注意,类型断言意味着类型安全的责任已从编译器转移到您身上......并且(回答您的问题)这是不安全的。
\n\n这就是为什么......考虑以下代码:
\n\ninterface Bar extends Foo {\n two: 2;\n four: 4;\n six: 6;\n eight: 8;\n}\n\nconst bar: Bar = {\n two: 2,\n four: 4,\n six: 6,\n eight: 8\n};\n\nconst b = func(bar);\n\nconsole.log(b.two); // 2 at compile time, but prints 1!\nconsole.log(b.four); // 4 at compile time, but prints 1!\nconsole.log(b.six); // 4 at compile time, but prints 1!\nconsole.log(b.eight); // 4 at compile time, but prints 1!\nRun Code Online (Sandbox Code Playgroud)\n\n在这里,我们看到一个接口,它通过添加已知属性Bar来扩展,这些属性的值为数字文字,其中没有一个等于。当我们调用 时,被推断为,因此 的输出也应该是。Foo1func(bar)TBarfunc(bar)Bar
糟糕的事情也会发生。我们有一个对象,其已知属性在编译时应该是偶数,但实际上是运行时的数字1。
这就是为什么您可能不应该在像func(). 可能有一种实际上安全的写法func()......比如,可能是这样的:
const funcSafer = <\n T extends { [K in keyof T]: 1 extends T[K] ? unknown : never }\n>(\n obj: T\n): T => {\n for (const name in obj) {\n obj[name] = 1 // error! still need "as T[typeof name]"\n }\n return obj;\n};\nRun Code Online (Sandbox Code Playgroud)\n\n这里,具体的约束T是1应该可分配给它的所有属性。这具有以下理想效果:
funcSafer(bar); // error! property "two" is incompatible\nconst foo: Foo = {two: 2, four: 4}; // just Foo, not Bar\nfuncSafer(foo); // okay\nfuncSafer({a: 1 as 1}); // okay\nfuncSafer({a: 4}); // okay, interpreted as {a: number}\nfuncSafer({a: 4 as 4}); // error, "a" is incompatible\nRun Code Online (Sandbox Code Playgroud)\n\n但当然,编译器仍然无法判断obj[name] = 1实现内部是否安全。它太复杂了......所以我们需要断言。
好的,希望有帮助。祝你好运!
\n\n\n| 归档时间: |
|
| 查看次数: |
4538 次 |
| 最近记录: |