为什么下面的代码可以编译成功?由于 bar
不是 MyState
的一部分,我预计它会生成编译器错误。
type MyState = { foo: number; };
type Reducer<T> = (state: T) => T;
const wtf: Reducer<MyState> = (state) => {
return { foo: 123, bar: 123 }; // `bar` isn't part of MyState
};
最佳答案
crashmstr 的答案是正确的,但值得解释为什么这种情况与您确实遇到错误的情况不同。
对象字面量只会在特定情况下导致额外的属性错误。
在这种情况下:
var x: MyState = { foo: 10, bar: 20 };
类型系统执行以下步骤:
- 检查 MyState 是否是有效类型(它是)
- 检查初始化程序是否有效:
- 初始化器的类型是什么?
- 它是新鲜对象类型
{foo: 10, bar: 20}
- 它是新鲜对象类型
- 是否可以分配给
MyState
?- 是否有
foo
属性?- 是
- 它的类型匹配吗?
- 是(
10
->number
)
- 是(
- 是否有来自新类型的任何额外属性?
- 是的
- 错误
- 是的
- 是否有
- 初始化器的类型是什么?
这里的关键是新鲜度。如果来自对象字面量的类型直接来自对象字面量本身,则它是新鲜的。这意味着两者之间存在差异
// Error
var x: MyState = { foo: 10, bar: 20 };
和
// OK
var x1 = { foo: 10, bar: 20 };
var x2: MyState = x1;
因为一旦对象字面量被分配到 x1
中,它的新鲜度就消失了。
您的示例遭遇同样的命运 - 一旦对象文字成为函数返回类型的一部分,它的新鲜度就消失了。这也解释了如果函数表达式上有返回类型注释,为什么会再次出现错误。
关于typescript - 通用函数允许任意键,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45868086/