Typescript 通用函数允许任意键

Typescript 通用函数允许任意键,typescript,Typescript,为什么下面的代码编译成功?由于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 }; typem

为什么下面的代码编译成功?由于
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
};
typemystate={foo:number;};
减速器类型=(状态:T)=>T;
常量wtf:Reducer=(状态)=>{
return{foo:123,bar:123};/`bar`不是MyState的一部分
};

TypeScript中的类型兼容性基于结构子类型。结构类型是一种仅基于其成员关联类型的方法。这与标称类型形成对比

为了检查y是否可以分配给x,编译器检查x的每个属性,以在y中找到相应的兼容属性。在这种情况下,y必须有一个名为name的字符串成员。确实如此,因此允许分配。请注意,y有一个额外的location属性,但这不会创建错误。检查兼容性时,只考虑目标类型(在本例中命名)的成员

因此,在您的示例中,
{foo:123,bar:123}
满足了一个
foo
这是一个数字的要求,并且为了类型兼容性,额外的
bar
被忽略


注意:另请参见

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 };

因为一旦将对象文本分配到
x1
,它的新鲜度就会消失


您的示例也面临同样的命运——一旦对象文本成为函数返回类型的一部分,它的新鲜度就消失了。这也解释了如果函数表达式上有返回类型注释,则错误会再次出现的原因。

为什么会出现此错误。MyState只需要foo,如果它有更多,它仍然满足MyState的定义。@ErikPhilips编译器会对此抱怨:
const MyState:MyState={foo:123,bar:123}并会说“Object literal可能只指定已知属性,而'MyState'类型中不存在'bar'。”那么编译器为什么会抱怨以下问题
const wtf2(state:MyState):MyState=>{return{foo:123,bar:123};
它说,“Object literal只能指定已知的属性,而'bar'在类型'MyState'中不存在。”另请参见@crashmstr I downvoted,因为编译器会在我们使用同一函数的非泛型形式时发出抱怨。答案没有说明为什么泛型形式允许未知属性,而非泛型形式不允许未知属性。具体来说,有一种情况是,对象文本保持在更高的标准,我不同意现在,在指定的文档中,这也不是问题的一部分,只是一个注释。问题的函数实现返回一个对象文本,因此对象文本是问题的一部分,因为它当前的状态。除了名义类型之外,是否有计划允许对像这样的情况吗?这不是一个健全的缺陷。没有观察到错误的类型;函数返回一个正确的子类型。对不起,我可能天真地使用了术语“声音”。我只是试图防止在我的缩减器中输入错误,但在当前的类型系统中,这是很困难的。
// OK
var x1 = { foo: 10, bar: 20 };
var x2: MyState = x1;