/
Write
Start a team blog
NEW ✨
Start a team blog, invite your team, and start publishing.

How To Use Generics Properly To Infer Return Type of Function Correctly?

Arihant's photo
Arihant
·Oct 31, 2019

I've the following types

type ItemDefaultType = object | null | string

interface ItemToString<Item = ItemDefaultType> {
  (item: ItemDefaultType): string;
}

interface AutosuggestState<Item = ItemDefaultType> {
  highlightedIndex: number | null
  inputValue: string | null
  isOpen: boolean
  selectedItem: Item
}

interface AutosuggestProps<Item = ItemDefaultType> extends AutosuggestState<Item> {
  itemToString?: ItemToString<Item>;

  initialSelectedItem?: Item;
  initialInputValue?: string;
  initialHighlightedIndex?: number | null;
  initialIsOpen?: boolean;

  defaultHighlightedIndex?: number | null;
  defaultIsOpen?: boolean;

  id?: string;
  inputId?: string;
  labelId?: string;
  menuId?: string;

  itemCount?: number
}

I want to make a getInitialValue function with the intended signature –

// this has to be done correctly using generics, not been able to do it, read on…
(
  props: Props extends AutosuggestProps,
  stateKey: StateKey extends keyof AutosuggestState
) => AutosuggestState[StateKey] // return the correct type of AutosuggestState property based on which stateKey was passed

The behaviour of getInitialValue looks like this

const defaultStateValues = {
  highlightedIndex: -1,
  isOpen: false,
  selectedItem: null,
  inputValue: ''
}

function getDefaultValue(props, statePropKey) {
  const defaultPropKey = `default${capitalizeString(statePropKey)}`
  if (defaultPropKey in props) {
    return props[defaultPropKey]
  }
  return defaultStateValues[statePropKey]
}

function getInitialValue(props, statePropKey) {
  if (statePropKey in props) {
    return props[statePropKey]
  }
  const initialPropKey = `initial${capitalizeString(statePropKey)}`
  if (initialPropKey in props) {
    return props[initialPropKey]
  }
  return getDefaultValue(props, statePropKey)
}

I'm finding it hard to write types of both getInitialValue and getDefaultValue such that getInitialValue correctly infers the right type as below –

const selectedItem = getInitialValue(props, 'selectedItem') // selectedItem variable should correctly be inferred as **object | null | string** since that's what its type is in **AutosuggestState** interface

Can someone help me write the types?