Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set key/value default state for TypeScript React app

I am having to convert a React app to Typescript and I can't figure out to property set the initial state of hash object.

original js

export default class Wizard extends PureComponent {
    constructor(props) {
        super(props);

        this.state = this.initialState();
    }



    /** Setup Steps */
    initialState = () => {
        const state = {
            activeStep: 0,
            hashKeys: {},
        };

        // Set initial classes
        // Get hash only in client side
        const hash = typeof window === 'object' ? this.getHash() : '';
        const children = React.Children.toArray(this.getSteps());
        children.forEach((child, i) => {
            // Create hashKey map
            state.hashKeys[i] = (child.props && child.props.hashKey) || `step${i + 1}`;
            state.hashKeys[state.hashKeys[i]] = i;
        });

        ...

        return state;
    }
...

My Failed Attempt

export interface TState {
  activeStep?: number
  hashKeys: {
    [key: number]: string
  }
}

export default class StepWizard extends PureComponent<{},TState> {
   constructor(props: IStepWizardProps) {
       super(props)
       this.state = this.initialState()
   }

   initialState = () => {
    const state = {
      activeStep: 0,
      hashKeys: {},  /* <---- This seems to be the problem */
    }

    // Set initial classes
    // Get hash only in client side
    const hash = typeof window === "object" ? this.getHash() : ""

    const children = React.Children.toArray(this.getSteps())
    children.forEach((child, i) => {
      // Create hashKey map
      // the following give a TS error
      // ERROR: (property) hashKeys: {}
      //           Element implicitly has an 'any' type because expression of type 'number' 
      //           can't be used to index type '{}'.
      //        No index signature with a parameter of type 'number' was found on type '{}'.ts(7053)
      state.hashKeys[i] = (child.props && child.props.hashKey) || `step${i + 1}`
      state.hashKeys[state.hashKeys[i]] = i
    })

   ...

I get for state.hasKeys[i] (property) hashKeys: {} Element implicitly has an 'any' type because expression of type 'number' can't be used to index type '{}'. No index signature with a parameter of type 'number' was found on type '{}'.ts(7053)

like image 670
John Hamman Avatar asked Apr 01 '26 00:04

John Hamman


2 Answers

The problem seems to come from the definition of hashKeys as {}.

Typescript doesn't know what the types of data will be for the key/value, but, you can tell it!

const hashKeys = {} as Record<number, string>;

const x = hashKeys[0];

For your example:

        const state = {
            activeStep: 0,
            hashKeys: {} as Record<string, string>, // or whatever your types are
        };
like image 172
OliverRadini Avatar answered Apr 03 '26 12:04

OliverRadini


Option 1: Type Union

export interface TState {
  activeStep?: number;
  hashKeys: {
    [key: number]: string
  } | {};
}

This syntax indicates that hashKeys could take the shape of either {[key: number]: string} or {}.

If you wanted to improve the readability of that union, you could do this:

type HashMap = {
  [key: number]: string;
}

export interface TState {
  activeStep?: number;
  hashKeys: HashMap | {};
}


Option 2: Partial (pssst probably not optimal - see option 3)

Partial constructs a type with all properties set to optional, which means that {} fits the definition by having both non-existent key and value properties.

type HashMap = {
  [key: number]: string;
}

export interface TState {
  activeStep?: number;
  hashKeys: Partial<HashMap>
}


Option 3: Type assertion

As pointed out in this answer, this approach does a better job of striking "a balance between correctness and productivity."

type HashMap = {
  [key: number]: string;
}

export interface TState {
  activeStep?: number;
  hashKeys: HashMap;
}

//

const state = {
  activeStep: 0,
  hashKeys: {} as HashMap,
};
like image 37
simmer Avatar answered Apr 03 '26 13:04

simmer