Home > OS >  Is it possible to persist type assertion of a variable, or have a "type assertion block"?
Is it possible to persist type assertion of a variable, or have a "type assertion block"?

Time:07-07

Consider something like this:

type BodyTypeA = {
    width: number;
    height: number;
    label: string;
    id: string;
    importantNumber: number;
    // Many other BodyTypeA specific fields
}

type BodyTypeB = {
    width: number;
    height: number;
}

type BodyTypeC = {
    width: number;
    height: number;
}

class PhysicsObject {
    body: BodyTypeA | BodyTypeB | BodyTypeC

    createFromSprite(...) {
        this.body = // This is what I use and it will create a BodyTypeA body
    }

    createFromJson() {
        this.body = // This will set body to BodyTypeB body
    }

    createFromWhatever() {
        this.body = // This will set body to BodyTypeC body
    }
}

I wrote that on a notepad, so there could be some issues.

My problem is that I have a situation where I have objects which are instances of PhysicsObject. This PhysicsObject class and its typings are owned by a third-party library and I cannot / don't want to edit them. Every PhysicsObject has a body-field with three possible BodyTypes - I don't know why, but that's just how it is.

I am basically only creating bodies for PhysicsObjects with the createFromSprite-method, which creates a BodyTypeA body for the PhysicsObject.

The issue here is that when I create a body and then want to set id, label and many other fields for the body, I have to do it by constantly asserting that the physicsObject.body is of type BodyTypeA:

let physicsObject = new PhysicsObject();
physicsObject.createFromSprite(...);

(physicsObject.body as BodyTypeA).label = "abc";
(physicsObject.body as BodyTypeA).id = 124;
let saveImportantNumber = (physicsObject.body as BodyTypeA).importantNumber;
...

So I am wondering if I could somehow persist the type assertion here, without touching any of the third-party library code or types?

I am looking for something like this:

let physicsObject = new PhysicsObject();
physicsObject.createFromSprite(...);

// Assertion block?
(physicsObject.body as BodyTypeA) {
    // No need to spam type assertion
    physicsObject.body.label = "abc";
    physicsObject.body.id = 124;
    let saveImportantNumber = physicsObject.body.importantNumber;
    ...
}

// Just assert it and persist somehow?
physicsObject.body as BodyTypeA;

// No need to spam type assertion
physicsObject.body.label = "abc";
physicsObject.body.id = 124;
let saveImportantNumber = physicsObject.body.importantNumber;
...

Or is there some other way to do something like this that I'm not aware of?

For whatever reason when creating the body with createFromSprite, the type of the body doesn't like "collapse" to BodyTypeA. I am not sure if this is something that TypeScript should do, but if so, it doesn't happen here. I believe that createFromSprite doesn't exactly create a BodyTypeA body, but instead wants to keep it general enough. However, fields like id and label definitely don't exist on PhysicsObject.body created with other methods than CreateFromSprite.

CodePudding user response:

You can persist the type assertion of a variable, but not of an object property (since that might get mutated in the meantime).

const body = physicsObject.body as BodyTypeA;
body.label = "abc";
body.id = 124;
let saveImportantNumber = body.importantNumber;

CodePudding user response:

One solution would be to just assign the body to its own variable and type that like this:

const body = physicsObject.body as BodyTypeA;

body.label = "abc";
body.id = 124;

playground


Another way would be to use a type predicate, this however does require your createFromSprite to set the id, but it is a safer way to do things:

if (isBodyTypeA(physicsObject.body)) {
    physicsObject.body.label = "abc";
    physicsObject.body.id = 124;
}

function isBodyTypeA(body: BodyTypeA | BodyTypeB | BodyTypeC): body is BodyTypeA {
    return body.hasOwnProperty('id');
}

playground

  • Related