5

TypeScript novice here, and i'm currently learning the language on TutorialsPoint here. I'm currently having a hard time understanding the differences between these two pieces of code.

interface Person{
    age: number;
}

interface Musician extends Person{
    instrument: string;
}

var drummer:Musician = {
    instrument: "drum",
    age: 28
}

and

interface Person{
    age: number;
}

interface Musician extends Person{
    instrument: string;
}

var drummer = <Musician>{}
drummer.instrument = "drum"
drummer.age = 28

What is the difference between the two? and is there a specific situation where it is better to use the first/second implementation?

Thank you.

1

2 Answers 2

9

They amount to the same thing in the end, but the former is preferred when possible.

In this case:

var drummer: Musician = {
    instrument: "drum",
    age: 28
}

you are declaring that drummer is a Musician by using a type annotation, and assigning to it an object literal. The compiler is happy about this because it can verify that yes, the object literal you're assigning is compatible with the Musician interface. It has an string-valued instrument property and a numeric-valued age property.

Now what if we try this:

var drummer: Musician = {};
//  ~~~~~~~ <-- error!
// Type '{}' is not assignable to type 'Musician'.
//  Property 'instrument' is missing in type '{}'.
drummer.instrument = "drum"
drummer.age = 28

The assignment of an empty object literal to a value declared as a Musician causes a compiler error. After all, an empty object literal does not have an string-valued instrument property or a numeric-valued age property. And you're being warned about that. Now, you know that the next two lines will fix that problem, but the compiler doesn't.

So you can change it to use a type assertion instead of a type annotation:

var drummer = <Musician>{}; // okay
drummer.instrument = "drum"
drummer.age = 28

The assertion is where you tell the compiler "this object is really a Musician, even though right now it doesn't look like it." You're taking on the responsibility of ensuring that drummer is a Musician and relieving the compiler of the responsibility of verifying that for you.

And since the next two lines add the required properties, everything is fine.


The former is preferable because you usually want the compiler to verify your types if it can. A type assertion gives up some safety, which is fine until it isn't, like driving an automobile without a seat belt:

var drummer = <Musician>{}; // okay
drummer.age = 28;
// whoops, forgot the instrument, but TypeScript isn't complaining

// ... later ...
console.log(drummer.instrument.toUpperCase()); 
// no error at compile time
// but blows up at runtime

There are times when you have to use type assertions. For example, when you have some kind of circular reference where you need to build the object in pieces:

interface MarriedPerson extends Person {
  spouse: MarriedPerson
}

var adam: MarriedPerson = {
  age: 0,
  // spouse: eve <-- can't do this before eve is defined
} as MarriedPerson;

var eve: MarriedPerson = {
  age: 0,
  spouse: adam
}

adam.spouse = eve;  // okay now

In the above, each MarriedPerson needs a reference to a MarriedPerson... but there won't be one until you've created one. So you are forced to resort to having a short period of time where one of the MarriedPerson objects does not have the required spouse. And thus assertions are required.

Does that make sense? Hope it helps; good luck!

Sign up to request clarification or add additional context in comments.

Comments

0

The examples are subtly different:

  • In the first, you're defining a variable with type Musician and then assigning an object to that variable which fits that type.
  • In the second, you're defining a variable with no explicit type (which means that TypeScript will infer it), creating an empty object and then casting that object to type Musicican.
    • As an aside, the preferred syntax for casting in TypeScript is {} as Musician - the angle bracket syntax you're using isn't compatible with the JSX syntax extension commonly used with React applications, so it was replaced with something less ambiguous.

I would recommend the first example over the second in the vast majority of cases - an empty object doesn't really implement Musician, so you're effectively sidestepping the type checker there!

The one scenario in which you could make a case for using the second method would be if you have a Musician that starts empty and gets populated later on - in that case though, I think you'd be better off modelling that through the type definition itself by making the fields optional:

interface Person {
    // '?' makes a field optional 
    age?: number;
}

interface Musician extends Person {
    instrument?: string;
}

// The cast can now be replaced with a proper type
var drummer: Musician = {};
drummer.instrument = "drum";
drummer.age = 28;

This makes it clearer (both to you, other developers and the compiler) that there's cases where those fields will be undefined.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.