jca*_*alz 7
这是 TypeScript 中的预期行为。
TypeScript 的类型系统在很大程度上是结构性的,而不是名义上的。所以重要的是类型的形状或结构,而不是它的名称或声明。因此,TypeScript 编译器可以确定 typeA
是(或“可分配给”或“扩展”)的子类型,无论您是否在. 仅当 的表观性质在 中具有兼容性质时才重要:B
A
B
A
B
A
B
B
A
interface A {
x: string;
y: number;
}
interface B {
x: string;
}
const a: A = { x: "", y: 0 };
const b: B = a; // okay, A extends B
最后一行不像名义上键入的语言那样是错误的。
请注意,“表观属性”意味着即使是像,和等一些原语也可以被认为在结构上与对象类型兼容,因为当您对它们进行索引时,JavaScript 会自动将一些原语包装在包装器对象中。所以类型是 的子类型:number
string
boolean
{length: number}
string
interface L { length: number };
const l: L = "hello"; // okay
因为string
值具有 type 的明显length
属性number
。
在 TypeScript 中,class
声明也通常在结构上进行处理(尽管在某些情况下会发生类似名义类型的类型,例如usinginstanceof
来区分两个类或添加private
orprotected
成员时)。因此,如果您有一个空类:
class GroupLeader { }
此类在结构上与空对象类型相同,空对象类型{}
是没有成员的对象类型。因此,任何可以像对象一样被索引的值都将被视为可分配给该类:
function foo(leader: GroupLeader): void { /* snip: do stuff */ }
foo(true); // okay
foo(123); // okay
foo({}); // okay
foo(() => 3); // okay
foo(new Date()); // okay
foo(Symbol("oops")); // okay
foo(null); // error
foo(undefined); // error
只有null
并且undefined
不可分配给GroupLeader
, 因为如果您像对象一样对它们进行索引,则会引发运行时错误null
。undefined
这就是它发生的原因。通常,您希望防止此类行为,因此最好避免使用空类和空对象类型,即使在示例代码中也是如此。(有些过时的)TypeScript FAQ有很多关于这个的条目,比如为什么所有类型都可以分配给空接口?为什么这些空类表现得很奇怪?. 如果您希望编译器将两种类型视为不同的类型,则应确保它们具有不兼容的形状。
添加任何boolean
不共享的属性GroupLeader
都会改变事情:
class GroupLeader { unsnip = 0 };
function foo(leader: GroupLeader): void { /* snip: do stuff */ }
foo(true); // error
foo(123); // error
foo({}); // error
foo(() => 3); // error
foo(new Date()); // error
foo(Symbol("oops")); // error
foo(new GroupLeader()); // okay
当然,仍然有可能传入一些结构上兼容的东西:
foo({ unsnip: 123 }); // okay
如果需要,您可以尝试阻止这种情况(private
属性会这样做),但阻力最小的路径是只编写只关心结构兼容性的代码。无论foo()
实现是什么,它都应该只关心的结构而leader
不是声明。
Playground 代码链接
更多推荐
这是,错误,TypeScript
发布评论