Typescript:对鸭子打字的误解

gev0vcfq  于 2023-03-13  发布在  TypeScript
关注(0)|答案(3)|浏览(121)

我正在学习打字阅读手册和文档。我只是好奇打字鸭子打字是如何工作的。
例如,此代码提供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:点应工作

m0rkklqb

m0rkklqb1#

Typescript根据你的操作执行不同的严格程度。当你创建一个显式类型的变量时,typescript对必须存在的内容非常严格,不允许任何额外的属性。额外的属性可能是你代码中的一个bug,因为你创建了一个值,然后立即分配一个类型,这使得该值不可访问。所以额外的严格程度有助于你捕捉这样的bug。
当你试图将一个变量赋给另一个变量,包括将一个变量传递给一个函数时,typescript会做一个宽松的检查,两个变量只需要彼此兼容,这大概意味着它们至少需要具有所列的属性和正确的类型。但是如果它有额外的属性也是可以的,这种宽松的检查级别是typescript支持子类型和子类的一部分。您应该能够在调用基类的地方传递子类(Liskov substitution principle),但是子类通常会有额外的属性。
有关类型兼容性的详细信息,请参见此链接:https://www.typescriptlang.org/docs/handbook/type-compatibility.html

pqwbnv8z

pqwbnv8z2#

第一个代码显示TypeError,因为Point接口没有成员z。第二个代码没有类型错误,因为在函数中,引用的所有属性(xy)存在于类型Point中。这是由于Duck类型化以及它是否被接受为Point,因为未引用z成员。在函数内部,如果您尝试使用p.z,将得到类型错误。

2lpgd968

2lpgd9683#

只要在tsconfig中启用了strictFunctionTypes,就会检查TS中的函数参数和返回类型值,以确保它们是子类型兼容的。这就是为什么您会在第二个示例中看到typeof p作为Point的子类型时的行为。
当你键入变量或常量时,它是严格类型化的,子类型或扩展类型只有在你显式允许的情况下才被允许。

interface Point {
    x: number;
    y: number;
}

// There are many ways to do this but you could for example allow
// an extended type to have a subtype of Point and any number 
// of string properties that have number values.
type ExtendedPoint = Point & Record<string, number>;

// Allow subtypes of Point
type UnrestrictedPoint = Partial<Point>;

TSPlayground:原始示例,但允许子类型

相关问题