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)
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
};
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 | {};
}
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>
}
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,
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With