当前位置: 首页 > 知识库问答 >
问题:

这个打字示例是否违反了Liskov Subtitution原则?

刘凡
2023-03-14

我有以下代码:

type T = { foo: string }
var t: T = { foo: 'foo' }

interface S { foo: string }
var s: S = t

所以我们知道T

这个怎么样?

js prettyprint-override">t = s

好的所以S

我们可以暗示S==T

现在介绍U

type U = { [key: string]: string }
var u: U = t

所以T

但是等等!

u = s // Error!

这似乎违反了Liskov替代原则(LSP):

如果S是T的子类型,则T类型的对象可以替换为S类型的对象

这是否违反了LSP?是不是重要?

撇开原则不谈,这看起来相当愚蠢:

u = s    // Error!
u = <T>s // Ok!

这会被认为是一个bug吗?当然,编译器可以自己做到这一点吗?


共有1个答案

萧德庸
2023-03-14

TypeScript的打字系统有些地方不健全;您发现了这样一个问题:类型别名而不是接口被赋予了隐式索引签名。为类型提供隐式索引签名是有用的,但通常是不安全的。考虑:

const fooBar = { foo: "foo", bar: 123 };
const tFooBar: T = fooBar; // okay
const uFooBar: U = tFooBar; // okay?
const whoopsie = uFooBar.bar; // string at compile time, number at runtime?!
console.log(whoopsie);

fooBar是有效的T,因为它具有类型为stringfoo属性。因此,您可以将其分配给tFooBar。然后,由于TypeScript允许您将T类型的值分配给U类型的变量,因此您可以将tFooBar分配给uFooBar。现在,如果您阅读uFooBarbar属性,就会发现不健康。根据U,它应该是一个字符串,但它是一个数字。哎呀。

隐式索引签名非常有用,因为函数通常需要具有索引签名的值,并且对于已知属性符合索引签名的值来说,隐式索引签名非常有用。所以,我们有一个有用的东西,它可以导致类型不安全行为。应该怎么做?

显然,TypeScript的当前规则是:

  • 对象文本/匿名类型被赋予隐式索引签名

根据@RyanCavanaugh的评论,显然这最后一个是故意的,不是一个bug:

只是想让人们了解一下,这种行为目前是经过设计的。由于接口可以通过附加声明来扩充,但类型别名不能,因此推断类型别名的隐式索引签名比推断接口的隐式索引签名“更安全”(在该声明上加上大量引号)。但我们也会考虑为接口做这件事,如果这似乎有道理的话。

因此,我们的想法是,声明合并可能会破坏接口与索引签名的兼容性,但类型别名不能。它们可能会改变它,如果您有一个引人注目的用例,您可能希望转到Github问题并提及它。

好吧,希望这会有帮助;祝你好运

链接到代码

 类似资料:
  • 考虑以下程序: (编译器资源管理器) GCC和Clang的各种版本都可以接受它,但MSVC不能接受它,因为MSVC编译失败,出现错误消息 第一条错误消息向我暗示了ODR违规--但如果这个程序是格式不良的NDR,我需要帮助理解为什么会这样。我已经检查了标准草案中的temp.over.link,但我不相信我对它的解释是正确的。根据我的理解,程序是可以的,因为这些函数模板有不同的签名。 在不太可能的情况

  • 有人能告诉我下面的例子是否违反了LSP吗? 我有一个例子: 和子类: 和主类: 在此示例中,子类添加名为 的新属性,并通过对其自己的属性 进行附加检查来覆盖方法。 在main方法中,我创建了2个对象。第一个是类型的对象,第二个是类型的对象。 当验证人员时,因为所有前提条件都是正确的,所以它是正确的,但是对于员工,它将抛出< code > IllegalArgumentException ,因为它与

  • Liskov替换原则指出,您应该编写您的类继承,这样将子类型交换为它们的基类型就不会改变应用程序的行为。 然而,虚拟关键字字面上似乎存在,以允许子类型的行为不同于基类型。虚拟/覆盖关键字(不包括覆盖抽象成员)的大多数使用不可能违反Liskov吗?我觉得这可能比我理解的更微妙。也许这是一个“规则有时会被打破”的情况,或者原则中的“不要有不同的行为”部分有一个灰色地带。

  • 如果S是T的一个子类型,那么T类型的对象可以被S类型的对象替换。 子类有两种不同的行为(选中与未选中),在某些情况下,除非更改当前代码,否则无法用子类对象有效地替换基类用法,例如,如果编写如下代码: 这是违反吗?,为什么/为什么不?。 资料来源:http://www.oracle.com/technetwork/articles/entarch/effective-exceptions-09234

  • 根据方法<code>java.util.concurrent的约定。未来#取消: 此方法返回后,对 isDone 的后续调用将始终返回 true。 Netty的Future接口扩展了它: 所以Netty应该遵守合同。但事实上Netty没有。您可以运行以下示例代码: 控制台应打印: 真 但实际上它打印: 假 以下方法也违反了合同: 我已经在github上创建了一个问题:问题 但是我仍然想在stack

  • 我是OOP的新手。最近我读到了关于Liskov替换原理的文章。 在下面给出的代码中,Square类继承Give_区域。假设Square类与Square相关(比如有效性检查)。Give_Area给出正方形的面积(4个顶点位于圆的周长上)和圆的面积。所以,如果给我一个半径,我必须打印圆和正方形的面积(由放置在圆周长上的顶点组成)。为了得到圆的面积,我使用了一个参数。但在求平方面积时没有参数。因此,我在