I have the following types:
interface AutosuggestState<Item> {
highlightedIndex: number | null;
inputValue: string | null;
isOpen: boolean;
selectedItem: Item | null;
}
interface ItemToString<Item> {
(item: Item): string;
}
interface AutosuggestProps<Item> {
itemToString?: ItemToString<Item>;
highlightedIndex?: number | null;
inputValue?: string | null;
isOpen?: boolean;
selectedItem?: Item;
initial?: {
highlightedIndex?: number | null;
inputValue?: string | null;
isOpen?: boolean;
selectedItem?: Item;
}
default?: {
highlightedIndex?: number | null;
inputValue?: string | null;
isOpen?: boolean;
selectedItem?: Item;
}
}
This is the code:
const defaultStateValues: AutosuggestState<null> = {
highlightedIndex: -1,
isOpen: false,
selectedItem: null,
inputValue: ''
}
function getDefaultValue<
I,
P extends AutosuggestProps<I>,
K extends keyof AutosuggestState<I>
>(
props: P,
statePropKey: K
) {
if (props.default && typeof props.default[statePropKey] !== "undefined") {
const ret = props.default[statePropKey];
return ret as Exclude<typeof ret, undefined>;
}
return defaultStateValues[statePropKey]
}
function getInitialValue<
I,
P extends AutosuggestProps<I>,
K extends keyof AutosuggestState<I>
>(
props: P,
statePropKey: K
) {
if (statePropKey in props && typeof props[statePropKey] !== 'undefined') {
const ret = props[statePropKey];
return ret as Exclude<typeof ret, undefined>
}
if (props.initial && typeof props.initial[statePropKey] !== 'undefined') {
const ret = props.initial[statePropKey];
return ret as Exclude<typeof ret, undefined>
}
return getDefaultValue(props, statePropKey);
}
function getInitialState<
I
>(
props: AutosuggestProps<I>
): AutosuggestState<I> {
const selectedItem = getInitialValue(props, 'selectedItem');
const highlightedIndex = getInitialValue(props, 'highlightedIndex');
const isOpen = getInitialValue(props, 'isOpen');
const inputValue = getInitialValue(props, 'inputValue');
return {
highlightedIndex,
isOpen,
selectedItem,
inputValue,
};
}
function useAutosuggest<
I
>(
userProps: AutosuggestProps<I>
){
const initialState = getInitialState(userProps);
}
Problems:
- If you open this code on TS Play you'll find that
The getInitialValue
calls inside the getInitialState
function are complaining, for example for:
const highlightedIndex = getInitialValue(props, 'highlightedIndex')
props
complain –
Argument of type 'AutosuggestProps<I>' is not assignable to parameter of type 'AutosuggestProps<unknown>'.
Similarly, the getDefaultValue
call inside getInitialValue
also causes complaints:
return getDefaultValue(props, statePropKey)
props
complain about the same thing.
I'm unable to figure out proper types for functions getInitialValue
and getDefaultValue
that will incorporate the generic type I
correctly, as passed from useAutosuggest
. The control flow based types are challenging to write properly.
- The types come incorrect (in the line below) for
initialState
, except forselectedItem
const initialState = getInitialState( userProps );
/**
initialState is of type
highlightedIndex: number | null; ✅
isOpen: boolean; // ✅
selectedItem: unknown; // should be of type I | null
inputValue: string | null; ✅
*/
How do I limit the
I
generic in theuseAutosuggest
function and hence everywhere in the flow of the function call, to a particular constraint (it could either be an object or a string, nothing else)Given the code, am I wrongly typing something, or not doing something properly? Could something be typed better to make this code more efficient?