gh9
gh9

Reputation: 10703

typescript Object types are compared structurally

Per Structural Subtyping

Object types are compared structurally. For example, in the code fragment below, class 'CPoint' matches interface 'Point' because 'CPoint' has all of the required members of 'Point'. A class may optionally declare that it implements an interface, so that the compiler will check the declaration for structural compatibility. The example also illustrates that an object type can match the type inferred from an object literal, as long as the object literal supplies all of the required members.

 interface Point {  
     x: number;  
     y: number;   }
 
 function getX(p: Point) {  
     return p.x;   }
 
 class CPoint {  
     x: number;  
     y: number;  
     constructor(x: number,  y: number) {  
         this.x = x;  
         this.y = y;  
     }   }
 
 getX(new CPoint(0, 0));  // Ok, fields match
 
 getX({ x: 0, y: 0, color: "red" });  // Extra fields Ok
 
 getX({ x: 0 });  // Error: supplied parameter does not match

In their example CPoint is considered of type Point, since it is of type Point I can pass it anywhere I can a Point. If Point stated that all implementors had method Foo(x:string), CPoint wouldn't have that method. Thus anyone accepting a Point and expecting to use Foo would blow up if CPoint was passed into it.

My question is, am I interpreting this wrong, if not why is this considered a good enough to put into the language specs?

Upvotes: 0

Views: 571

Answers (2)

Andreas Jägle
Andreas Jägle

Reputation: 12250

The type definitions/interfaces one creates are just a compile-time thing and in my opinion, the structural subtyping is a great feature.

If you extend the Point interface with an additional method Foo(x: string), you tell the compiler that every object that wants to be a Point now also needs this method. In my opinion, this is exactly what you want to have because you can spot all places where you are passing objects that are lacking these additional properties.

Before adding such a method to an interface, it's probably better to ask yourself, if every object that has the characteristics of a Point really needs this method. In my experience it is often better to introduce some more interfaces, e.g. Fooable and using this where you need an object that needs to have the Foo method.

If you have a method that really needs objects having both characteristics, (x,y) and Foo, you can do something like this:

function myMethodRequiringASpecialObject(x: Point & Fooable) {}

This is called "intersection types". See more here: https://basarat.gitbooks.io/typescript/content/docs/types/type-system.html

Upvotes: 1

Ryan Cavanaugh
Ryan Cavanaugh

Reputation: 220964

If Point stated that all implementors had method Foo(x:string), CPoint wouldn't have that method. Thus anyone accepting a Point and expecting to use Foo would blow up if CPoint was passed into it.

If you did this, you would get a compile-time error that CPoint lacked the Foo method. I recommend trying it on the TypeScript Playground.

Upvotes: 2

Related Questions