【问题标题】:Empty interface allow any object?空接口允许任何对象?
【发布时间】:2021-01-30 00:20:49
【问题描述】:

为什么空接口不需要对象为空?

interface A {};
const a: A = {a: 1};
console.log(a);

是有效代码,将输出{ a: 1 }

我认为添加可选属性应该可以正常工作,但是

interface A {};
interface B extends A {
    b?: any;
}
const a: B = {a: 1};
console.log(a);

以错误Type '{ a: number; }' is not assignable to type 'B'结束。

  • 如果接口定义了对象必须具有的属性,B case 应该可以正常工作,所有必需的属性都存在。
  • 如果接口定义了对象可以具有的属性,则应导致错误,a 未在接口中定义。

非空接口定义了对象可以和必须拥有的内容。 空界面的行为类似于any

有没有解释为什么空接口会这样?这是故意的还是只是一个错误?

【问题讨论】:

  • 空接口不定义任何约束,适用于任何对象。不过我不确定它的实用性。

标签: typescript


【解决方案1】:

这种行为是故意的。

当目标是空对象类型时,不会执行多余的属性检查,因为它很少只允许空对象。


其实你可以{a: 1}分配给B,这里的其他答案大多是错误的。

您偶然发现了 TypeScript 中另一个稍微令人困惑的怪癖,即您不能直接将对象字面量分配到对象字面量包含除类型中指定的属性之外的其他属性的类型。

但是,您可以将对象的任何现有实例分配给类型,只要它满足该类型即可。

例如:

interface Animal {
    LegCount: number;
}

let dog: Animal = { LegCount: 4, Fur: "Brown" }; // Nope

var myCat = { LegCount: 4, Fur: "Black" };
let theCat: Animal = myCat; // OK

当你有一个空类型时,这个约束就会被忽略。

阅读更多 herehere

Typescript 团队稍后会在GitHub 上提供答案。

【讨论】:

  • 感谢您的链接。但这种解释并不完整。为什么我直接使用接口中未指定的属性分配文字的情况 A 有效?
  • @mleko 因为 A 是空的。所以 TS 并不关心你只能在直接赋值时在对象字面量中声明已知属性的规则。
  • 但是为什么空接口这么特别呢?是有意的(如果是,目的是什么)还是只是碰巧以这种方式工作?
  • @mleko 据我所知,这是故意的。而且我相信这背后的想法是:空类型基本上是无用的,你将无法以任何有意义的方式使用它们来打字。因此,确保您不能为对象字面量分配类型中不存在的属性、阻止您声明将按类型消失的属性的全部意义是无关紧要的,因为您不能真正使用目标类型一点也不。所以编译器只是不打扰。我可能错了,但这是我最有根据的猜测。
  • “当目标是空对象类型时,不会执行多余的属性检查,因为在这种情况下,很少会只允许空对象。” - 是这样吗?为什么我会不遗余力地制作一个空界面?除非我的意思是“空对象,请执行多余的检查”,否则这将如何/何时有用。无论如何,如果空接口意味着“任何形状;跳过文字的多余属性检查”,那么指定一个空对象我会做type EmptyObject = {}?
【解决方案2】:

好的,有趣的问题。

测试用例 1:

interface A {};
interface B extends A {
    b?: any;
}
const a: A = {a: 1};
console.log(a);

它通过了,因为 A 是一个空接口,无论你在里面装什么笨蛋都会返回。像class Object 一样工作

测试用例 2:

interface A {};
interface B extends A {
    b?: any;
}
const a: A = {b: 1};
console.log(a);

只是将值 a 更改为 b 只是为了证明第一个测试用例。它通过了。

测试用例 3:

interface A {};
interface B extends A {
    b?: any;
}
const a: B = {a: 1};
console.log(a);

失败,因为接口AB 内没有名为a 的道具

测试用例 4:

interface A {};
interface B extends A {
    b?: any;
}
const a: B = {b: 1};
console.log(a);

它通过了,因为接口 B 有一个名为 b 的道具

希望这能帮助你理解。

PS:prop 指的是属性。

以此类比:Interface A 是一所空房子,随便你怎么做。它不会说一句话。 Interface B 是殖民地的房子,这意味着它需要根据大小、形状进行特定的行为,并且需要坚持Interface A。这意味着接口 B 不再是空的,并且受到规则的限制。

【讨论】:

    【解决方案3】:

    这是 Typescript 中结构类型的工作方式。它基于结构子类型。

    总之

    如果B 实现了A 所需的所有成员,则B 的实例与A 兼容。

    由于A不需要任何成员,所有对象都与A兼容。

    this documentation中的详细信息

    【讨论】:

    • B 也不需要任何成员,但是 B 情况导致错误。接口不仅有要求,而且有限制。您只能使用接口中定义的属性。恕我直言,因为 A 没有定义任何你不应该使用的属性。
    • 不幸的是,这是您的意见,而不是 typescript 类型系统的工作方式:)。没有所谓的“限制”
    • 如果没有限制为什么B case会出错?如果接口只需要对象应该有哪些属性 B case 应该可以正常工作,我定义了所有必需的属性。
    • 引用 Ryan Cavanaugh 的话:“问题是额外的属性检查不会作为上下文类型的结果发生,它会在对象类型为“新鲜”时的常规可分配性期间发生。因此,只有当您分配一个“新鲜”对象时,例如 B,才会发生此检查。结构类型以上面链接中描述的方式发生。我意识到这很令人困惑,并且是 typescript GitHub 项目上多个问题的根源
    • 结构类型与OP描述的问题无关。仅就 TS 中的类型系统而言,问题中的代码绝对没问题。
    【解决方案4】:

    是的,官方文档中有解释。不,不是错误,而是令人惊讶的行为。这篇文章microsoft/TypeScript - Why are all types assignable to empty interfaces? 解释了这种行为。

    该文档还补充说:“一般来说,您永远不应该发现自己声明一个没有属性的接口。”

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-05-13
      • 2021-07-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-20
      • 1970-01-01
      • 2017-09-08
      相关资源
      最近更新 更多