1

I am trying to create a type for a specific object that can be empty, let's start at the bottom and work our way up to the React class.

Note: I am not using hooks, but even f your answer was in hooks, it can't be that hard to translate.

We first need to define the state for our class:

export default interface CharacterInventoryTabsState {
    table: string;

    dark_tables: boolean;

    loading: boolean;

    inventory: Inventory | {};
}

The issue is the inventory property. So lets look at the Inventory type:

export default interface Inventory {

    equipped: InventoryDetails[] | [];

    inventory: InventoryDetails[] | [];

    quest_items: InventoryDetails[] | [];

    usable_items: InventoryDetails[] | [];

    savable_sets: SetDetails[] | [];

    usable_sets: SetDetails[] | [];

    sets: {[key: string]: InventoryDetails[] | []}

    set_equipped: boolean;
}

The inventory object is made up of other objects and their associated types. Fantastic. So lets look at the react class:

The below is just a snippit of the class.

export default class CharacterInventoryTabs extends React.Component<any, CharacterInventoryTabsState> {

constructor(props: any) {
    super(props);

     // ...
 
    this.state = {
        table: 'Inventory',
        dark_tables: false,
        loading: true,
        inventory: {} as Inventory, // I saw this trick to initialize "empty objects"
    }
}

componentDidMount() {
    watchForDarkModeInventoryChange(this);

    (new Ajax()).setRoute('character/'+this.props.character_id+'/inventory').doAjaxCall('get', (result: AxiosResponse) => {
        this.setState({
            loading: false,
            inventory: result.data, // => Here we initialize inventory but ...
        });
    }, (error: AxiosError) => {
        console.log(error);
    })
}

// ... some where in the render method we do:

render() {
    // ... Loader logic so we would never access the state until loading was set to false.
    return(
        <InventoryTable dark_table={this.state.dark_tables} character_id=. {this.props.character_id} inventory={this.state.inventory.inventory} />
    );
}

}

The issue is simple: Property 'inventory' does not exist on type '{} | Inventory'.   Property 'inventory' does not exist on type '{}'.

Well duh, its not initialized till after the ajax call and I have a "if loading show the loader jazz" so it would "technically be initialized after the ajax call" but how do I make typescript be quiet about this, aside from the dreaded: any.

I saw the trick of const var = {} as Type in another stack question related to this, but alas it does not seem to work for me.

What is the correct way to handle a situation like this?

2
  • I saw the trick of const var = {} as Type in another stack question related to this, but alas it does not seem to work for me. -- some explanation about why it didn't work for you might help. Commented Mar 23, 2022 at 19:36
  • I've used <Partial<Inventory>> in some situations before. Not sure if it's suitable for you here, but something to maybe look into. Commented Mar 23, 2022 at 19:36

1 Answer 1

2
  1. Well, first of all get rid of any ases - they're you pinky-swearing that "I know perfectly well what I'm doing with my types".
  2. Make the type for the inventory state field something like inventory: Inventory | null, and initialize it with null.
  3. In your render function, destructure inventory into a local: const {inventory} = this.state;. (This helps TS with inference.)
  4. if(inventory === null) return <Loading />;
  5. The if/return will help TypeScript narrow the type of inventory for the rest of the render function; after all, if it's not null, it can only be a real Inventory.

Unless you need loading for anything else, you could also drop it from state, since you can tell whether you're loading based on just whether inventory !== null.

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

2 Comments

You sir have simplified my confusion. Hahahaha. Thank you - I dont know why I didn't just say null to begin with, probably because I expect an object ... hahaha. Thanks again :D
@TheWebs You're welcome - glad I could help! In general, though... there are very, very few places where you need any ases, if the rest of your program is type-sound, so I'd do my best to avoid them :)

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.