学习 TypeScript - 类型转换

17

我刚开始学习 TypeScript 并且在尝试使用不同的语言特性。下面是我在其中一个在线课程中一直在努力的代码示例。

我正在尝试让继承和重载正常工作,但是我遇到了问题。在整个代码中,我使用了一个基类 Auto 下的车辆,以及一个子类 Truck 下的卡车。

我想要看看是否可以将车辆强制转换为卡车并访问专门的函数 HonkHorn。 此外,我还试图将卡车转换回 Auto 并访问基础函数 WriteDetails。

在这两种情况下,似乎对象都保持了其原始类型。因此,typecast4.HonkHorn(); 会生成运行时错误:

Uncaught TypeError: typecast4.HonkHorn is not a function.

尝试将 Truck 强制转换为 Auto 将始终导致调用专门的覆盖 WriteDetails。转换代码在示例底部。

可以有人帮帮我理解为什么会出现这种情况吗?

提前感谢您的帮助!

// 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();
1个回答

30
在Typescript中没有类型转换,只有类型断言。这是为了进行类型检查,不会影响运行时行为。
例如,类型断言:
car as Truck  // older syntax syntax: <Truck> car 

告诉编译器car的类型是Truck,但不会影响生成的JS代码。
TypeScript允许您以任何方式覆盖其推断和分析的类型。这是通过一种称为“类型断言”的机制来实现的。 TypeScript的类型断言纯粹是告诉编译器你比它更了解类型,并且它不应该怀疑你。
类型断言与强制转换的区别在于,强制转换通常意味着某种运行时支持。然而,类型断言纯粹是一个编译时构造,是一种向编译器提供提示的方式,告诉它如何分析您的代码。

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

更新:

现在另一个好的参考资料是Typescript官网本身

类型断言是一种告诉编译器“相信我,我知道自己在做什么”的方法。 类型断言类似于其他语言中的类型转换,但不执行任何特殊的检查或数据重组。 它没有运行时影响,仅由编译器使用。 TypeScript 假定您作为程序员已经执行了您需要的任何特殊检查。

对于更改类型,这里的答案可能有用:如何将 JSON 对象转换为 TypeScript 类?


@Školstvo 创建一个新实例或更改原型。 - Julian
这能在不改变基类的情况下完成吗?我不能改变我正在使用的源代码。这就是为什么我选择使用扩展类的原因。 - Školstvo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接