我正在学习打字阅读手册和文档。我只是好奇打字鸭子打字是如何工作的。
例如,此代码提供Type错误(使用Typescript官方Playground)
interface Point {
x: number;
y: number;
}
const p: Point = {
x: 1,
y: 2,
z: 3
}
function printPoint(p: Point) {
console.log(p.x + p.y);
}
printPoint(p);
但此代码不提供类型错误
interface Point {
x: number;
y: number;
}
const p = {
x: 1,
y: 2,
z: 3
}
function printPoint(p: Point) {
console.log(p.x + p.y);
}
printPoint(p);
我认为这个问题与Typescript的鸭子类型有关,但我不明白为什么参数的类型系统和常量的类型系统工作方式不同。
我是不是误解了鸭子打字法?const p包含x,y,所以我认为const p:点应工作
3条答案
按热度按时间m0rkklqb1#
Typescript根据你的操作执行不同的严格程度。当你创建一个显式类型的变量时,typescript对必须存在的内容非常严格,不允许任何额外的属性。额外的属性可能是你代码中的一个bug,因为你创建了一个值,然后立即分配一个类型,这使得该值不可访问。所以额外的严格程度有助于你捕捉这样的bug。
当你试图将一个变量赋给另一个变量,包括将一个变量传递给一个函数时,typescript会做一个宽松的检查,两个变量只需要彼此兼容,这大概意味着它们至少需要具有所列的属性和正确的类型。但是如果它有额外的属性也是可以的,这种宽松的检查级别是typescript支持子类型和子类的一部分。您应该能够在调用基类的地方传递子类(Liskov substitution principle),但是子类通常会有额外的属性。
有关类型兼容性的详细信息,请参见此链接:https://www.typescriptlang.org/docs/handbook/type-compatibility.html
pqwbnv8z2#
第一个代码显示TypeError,因为
Point
接口没有成员z
。第二个代码没有类型错误,因为在函数中,引用的所有属性(x
和y
)存在于类型Point
中。这是由于Duck类型化以及它是否被接受为Point
,因为未引用z
成员。在函数内部,如果您尝试使用p.z
,将得到类型错误。2lpgd9683#
只要在tsconfig中启用了
strictFunctionTypes
,就会检查TS中的函数参数和返回类型值,以确保它们是子类型兼容的。这就是为什么您会在第二个示例中看到typeof p
作为Point
的子类型时的行为。当你键入变量或常量时,它是严格类型化的,子类型或扩展类型只有在你显式允许的情况下才被允许。
TSPlayground:原始示例,但允许子类型