这个 Typescript 示例是否违反了 Liskov 替换原则?
Does this Typescript example violate the Liskov Subtitution Principle?
我有以下代码:
type T = { foo: string }
var t: T = { foo: 'foo' }
interface S { foo: string }
var s: S = t
所以我们知道 T < S
.
这个怎么样?
t = s
好的所以S < T
也是如此。
我们可以暗示 S == T
.
现在介绍U
:
type U = { [key: string]: string }
var u: U = t
所以T < U
。到目前为止一切顺利。
等等!
u = s // Error!
这似乎违反了里氏替换原则 (LSP):
if S is a subtype of T, then objects of type T may be replaced with objects of type S
这是否违反了LSP?有没有关系?
抛开原则,这看起来很傻:
u = s // Error!
u = <T>s // Ok!
这会被视为错误吗?编译器当然可以自己完成,不是吗?
TypeScript 的类型系统有些地方不健全;您找到了 this issue in which type aliases but not interfaces are given implicit index signatures。为类型提供隐式索引签名很有用,但通常不安全。考虑:
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
,因为它有 foo
属性 类型 string
。所以你可以把它赋值给tFooBar
。然后由于 TypeScript 允许您将 T
类型的值分配给 U
类型的变量,您可以将 tFooBar
分配给 uFooBar
。现在,如果您阅读 uFooBar
的 bar
属性,就会暴露出不合理之处。根据 U
,它应该是 string
,但它是 number
。哎呀
隐式索引签名很有用,因为通常函数需要具有索引签名的值,这有助于接受已知属性符合索引签名的值。所以,我们有这个有用的东西可以导致类型不安全的行为。应该怎么办?
显然,TypeScript 的当前规则是:
- 对象文字/匿名类型被赋予隐式索引签名
- 类型别名被赋予隐式索引签名
- 接口没有给出隐式索引签名
根据 this comment by @RyanCavanaugh:
显然这最后一个是故意的而不是错误
Just to fill people in, this behavior is currently by design. Because interfaces can be augmented by additional declarations but type aliases can't, it's "safer" (heavy quotes on that one) to infer an implicit index signature for type aliases than for interfaces. But we'll consider doing it for interfaces as well if that seems to make sense.
所以认为 declaration merging 可能会破坏接口到索引签名的兼容性,但类型别名不会。他们愿意改变它,也许,如果你有一个引人注目的用例,你可能想转到 Github 问题并提及它。
好的,希望对您有所帮助;祝你好运!
我有以下代码:
type T = { foo: string }
var t: T = { foo: 'foo' }
interface S { foo: string }
var s: S = t
所以我们知道 T < S
.
这个怎么样?
t = s
好的所以S < T
也是如此。
我们可以暗示 S == T
.
现在介绍U
:
type U = { [key: string]: string }
var u: U = t
所以T < U
。到目前为止一切顺利。
等等!
u = s // Error!
这似乎违反了里氏替换原则 (LSP):
if S is a subtype of T, then objects of type T may be replaced with objects of type S
这是否违反了LSP?有没有关系?
抛开原则,这看起来很傻:
u = s // Error!
u = <T>s // Ok!
这会被视为错误吗?编译器当然可以自己完成,不是吗?
TypeScript 的类型系统有些地方不健全;您找到了 this issue in which type aliases but not interfaces are given implicit index signatures。为类型提供隐式索引签名很有用,但通常不安全。考虑:
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
,因为它有 foo
属性 类型 string
。所以你可以把它赋值给tFooBar
。然后由于 TypeScript 允许您将 T
类型的值分配给 U
类型的变量,您可以将 tFooBar
分配给 uFooBar
。现在,如果您阅读 uFooBar
的 bar
属性,就会暴露出不合理之处。根据 U
,它应该是 string
,但它是 number
。哎呀
隐式索引签名很有用,因为通常函数需要具有索引签名的值,这有助于接受已知属性符合索引签名的值。所以,我们有这个有用的东西可以导致类型不安全的行为。应该怎么办?
显然,TypeScript 的当前规则是:
- 对象文字/匿名类型被赋予隐式索引签名
- 类型别名被赋予隐式索引签名
- 接口没有给出隐式索引签名
根据 this comment by @RyanCavanaugh:
显然这最后一个是故意的而不是错误Just to fill people in, this behavior is currently by design. Because interfaces can be augmented by additional declarations but type aliases can't, it's "safer" (heavy quotes on that one) to infer an implicit index signature for type aliases than for interfaces. But we'll consider doing it for interfaces as well if that seems to make sense.
所以认为 declaration merging 可能会破坏接口到索引签名的兼容性,但类型别名不会。他们愿意改变它,也许,如果你有一个引人注目的用例,你可能想转到 Github 问题并提及它。
好的,希望对您有所帮助;祝你好运!