我有以下代码:
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!
这似乎违反了Liskov替代原则(LSP):
如果S是T的子类型,则T类型的对象可以替换为S类型的对象
这是否违反LSP?是否重要?
撇开原理,这看起来很愚蠢:
u = s // Error!
u = <T>s // Ok!
这会被视为错误吗?编译器肯定可以单独执行此操作吗?
答案 0 :(得分:1)
TypeScript的类型系统在某些地方不健全;您发现this issue的类型别名为,但没有提供接口 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
的{{1}}属性。因此,您可以将其分配给string
。然后,由于TypeScript允许您将类型tFooBar
的值分配给类型T
的变量,因此可以将U
分配给tFooBar
。现在,如果您阅读了uFooBar
的{{1}}属性,就会暴露出声音的不健全。根据{{1}},它应该是bar
,但是它是uFooBar
。糟糕!
隐式索引签名很有用,因为函数通常需要带有索引签名的值,并且对于那些已知属性符合索引签名的值很有用。因此,我们有一个有用的东西,它可能导致类型不安全的行为。应该怎么办?
显然,TypeScript的当前规则是:
根据this comment by @RyanCavanaugh,显然这是故意的,而不是错误:
仅是为了填补人员缺席,这种行为目前是设计使然。由于可以通过其他声明来增强接口,但不能使用类型别名来进行扩展,因此推断类型别名的隐式索引签名比接口的隐式索引签名“更安全”(在该引用上使用大量引号)。但是,如果看起来合理,我们也将考虑对接口执行此操作。
因此,我们认为declaration merging可能会破坏接口到索引签名的兼容性,但类型别名不能。他们可能愿意改变它,如果您有一个引人注目的用例,则可能要转到Github问题并提及它。
好的,希望能有所帮助;祝你好运!