TypeScript Learning - Casting Types

I am new to TypeScript and am playing around with various language features. Below is a sample code I worked on during one of the many online courses.

I'm having trouble getting inheritance and overloading to work properly. In the code, I use a base class Auto car and a truck belonging to the Truck class.

I'm trying to figure out if it is possible to cast a car as a Truck and access the specialized HonkHorn feature. Also I am trying to get the truck back to Auto and access the base function for WriteDetails.

In both cases, the objects seem to remain in their original type. So, it typecast4.HonkHorn();

generates an error at runtime:

Uncaught TypeError: typecast4.HonkHorn is not a function.

Attempting to set Truck back to Auto will always result in a custom override for WriteDetails being called. The casting code is located completely at the bottom of the sample.

Can someone help me understand why this is happening?

Thank you in advance!

// defines the structure of auto options
interface IAutoOptions{
    engine: Engine,
    color: string,
    price: number,
    year: number
}

// extends autooptions with truck specific options
interface ITruckOptions extends IAutoOptions{
    fourbyfour: boolean,
    bedlength: string
}

// defines the structure of engines
interface IEngine {
    enginetype: string;
    horsepower: number;
    hydraulicpump?: string
    start(warmuptime: number, callback: () => void): void;
    stop(): void;
}

// the engine class must implement the members as specified in the IEngine interface
class Engine implements IEngine{
    enginetype: string;
    horsepower: number;
    hydraulicpump?: string; //optional hydraulic parameter
    constructor(enginetype: string, horsepower: number, hydraulicpump? : string ) {
        this.enginetype = enginetype;
        this.horsepower = horsepower;
        if (!(hydraulicpump)){
            hydraulicpump = "Not Available"; //if no hydraulic parameter is provided we set it to "Not Available"
        }
        this.hydraulicpump = hydraulicpump;
    }
    // start requires a callback parameter which accepts a specialized callback object/function that accepts and returns nothing
    // by accepting a callback object/function that code can be seperated off, which makes this class much cleaner and organized
    start(warmuptime: number, callback: () => void) { 
        window.setTimeout(() => {
            callback();
            document.write(this.enginetype + " has started!" + "</br>");
        }, warmuptime);
    };
    stop() { 
        document.write(this.enginetype + " has stopped!" + "</br>");
    };
}

// base class for autos
class Auto {
    engine: Engine;
    color: string;
    price: number;
    year: number;

    constructor(options: IAutoOptions) {
        this.engine = options.engine;
        this.color = options.color;
        this.price = options.price;
        this.year = options.year;
    }

    //WriteDetails contains the base details for each Auto which can be overriden in specialized classes
    WriteDetails() {
        document.write("Color: " + this.color + "</br>");
        document.write("Year: " + this.year + "</br>");
        document.write("Price: $"  + this.price + "</br>");
        document.write("Engine Type: " + this.engine.enginetype + "</br>");
        document.write("Horse Power: " + this.engine.horsepower + "</br>");
        document.write("Hydraulic Pump: " + this.engine.hydraulicpump + "</br>");    
    };
}

// Truck extends Auto to add Truck specific fields and function overloads
// Note that it does not contains all the base fields from Auto thus making it much smaller and cleaner
// Only truck specific code is added.
class Truck extends Auto{
    fourbyfour: boolean;
    bedlength: string;
    constructor(options: ITruckOptions) {
        // to overload the constructor super() must to be called, which calls the base class constructor in Auto
        super(options);
        this.bedlength = options.bedlength;
        this.fourbyfour = options.fourbyfour;
    }
    // WriteDetails overrides the Auto WriteDetails, but first calls the base WriteDetails function
    WriteDetails() {
        super.WriteDetails();
        document.write("Bed Length: " + this.bedlength + "</br>");
        document.write("4x4 : " + this.fourbyfour + "</br>");
    };

    HonkHorn() {
        document.write("Honk Honk!</br>");
    }
}

// below is one of the notations to define a callback object that can be used to call
// the start function on the Engine class
// this callback function has encapsulated car specific logic for starting the engine
// much cleaner than putting the specialized code in the Auto class

var CarEngineStart = () => {
    document.write("<h1>Starting Car</h1>");
    document.write("Check Tires!" + "</br>");
    document.write("Fasten Seatbelts!" + "</br>");
    document.write("Check Mirrors!" + "</br>");
    document.write("Starting Engine!" + "</br>");
};

// yet another way to define a callback object (function)
// this callback function has encapsulated truck specific logic for starting the engine
// much cleaner than putting the specialized code in the Auto or Truck classes

function TruckEngineStart() {
    document.write("<h1>Starting Truck</h1>");
    document.write("Check Tires!" + "</br>");
    document.write("Check if load is properly fastened!" + "</br>");
    document.write("Check timesheet!" + "</br>");
    document.write("Fasten Seatbelts!" + "</br>");
    document.write("Check Mirrors!" + "</br>");
    document.write("Starting Engine!" + "</br>");
}

// ###################### Start logic

// creating an engine
var carengine = new Engine("V8", 300);
// creating another engine, but now providing the optional hydraulicpump parameter
var truckengine = new Engine("V12", 1000, "Flexpump 3000");

var car = new Auto({
    engine: carengine,
    color: 'Blue',
    price: 20000,
    year: 2017
});

var truck = new Truck({
    engine: truckengine,
    color: 'Red',
    price: 80000,
    year: 2015,
    bedlength: 'Long Bed',
    fourbyfour: true
});
document.write("<h1>Car Details</h1>");
car.WriteDetails();

document.write("<h1>Truck Details</h1>");
truck.WriteDetails();

truck.engine.start(10000, TruckEngineStart);
car.engine.start(5000, CarEngineStart);

window.setTimeout(() => {
    document.write("<h1>Stopping Car</h1>");
    car.engine.stop();
    document.write("<h1>Stopping Truck</h1>");
    truck.engine.stop();
}, 15000);

document.write("<h1>Casting Autos</h1>");
document.write("<h2>Auto WriteDetails for Car</h2>");
var typecast: Auto;
typecast = car;
typecast.WriteDetails();
document.write("<h2>Truck WriteDetails for Car with type cast</h2>");
var typecast4: Truck;
typecast4 = <Truck>car;
typecast4.HonkHorn();
typecast4.WriteDetails();
document.write("<h2>Auto WriteDetails for Truck without type cast</h2>");
var typecast2: Auto;
typecast2 = truck;
typecast2.WriteDetails();
document.write("<h2>Auto WriteDetails for Truck with type cast</h2>");
var typecast3: Auto;
typecast3 = <Auto>truck;
typecast3.WriteDetails();

      

+10


source to share


1 answer


There is no typecasting in Typescript, only type assertions. This is for type checking and does not affect runtime behavior.

For example, a type statement:

car as Truck  // older syntax syntax: <Truck> car 

      



tells the compiler what car

the type is Truck

, but it won't affect the generated JS code.

TypeScript allows you to override the inferred and parsed representation of types in any way you like. This is done through a mechanism called type assertion. TypeScript type assertion is purely what you tell the compiler that you know better about types than it does and that it shouldn't guess you.

Type Approval versus Casting

The reason it is not called "typecasting" is that casting usually implies some runtime support. However, type assertions are just a compile-time construct and a way to give you hints to the compiler about how you want your code to be parsed.

https://basarat.gitbooks.io/typescript/content/docs/types/type-assertion.html

+18


source







All Articles