Thimma
Thimma

Reputation: 1492

How do I accept a generic return type for my function in TypeScript?

Update: TSPlayground link

Click Here


I currently got a function which extracts data from a .json file with section information, each section can be of one of three types.

/**
 * The types of sections which are supported
 */
type SupportedSectionTypes = BasicSection | CardSection | CheckedSection;

/**
 * A collection of sections
 */
type SectionCollection = {
  [key: string]: SupportedSectionTypes;
};

/**
 * All the sections which are available for the website
 */
export const SECTIONS: SectionCollection = sections; // <-- `sections` is the imported json

/**
 * Extracts data from the section given to the function
 * @param key The key of the section to get data from
 * @returns The data from the requested section
 */
export const getSectionData = <T = SupportedSectionTypes>(key: string): T => {
  const section: SupportedSectionTypes = SECTIONS[key];

  if (section) return section;

  throw new Error('This section has not been implemented!');
};

Currently I want to make a generic function where you can declare in the generic what the the type will be, for example:

const sectionData = getSectionData<CheckedSection>("my_checked_section_key");
// return type of function should be CheckedSection

However this code does not compile and I get the following error

  if (section) return section;
  //           ^^ Type 'SupportedSectionTypes' is not assignable to type 'T'.
  // 'T' could be instantiated with an arbitrary type which
  // could be unrelated to 'SupportedSectionTypes'.ts(2322)

How do I make it that the generic matches the return type


Update after @blackgreen's answer

/**
 * Extracts data from the section given to the function
 * @param key The key of the section to get data from
 * @returns The data from the requested section
 */
export const getSectionData = <T extends SupportedSectionTypes>(
  key: string,
): T => {
  const section: SupportedSectionTypes = SECTIONS[key];

  if (section) return section;

  throw new Error('This section has not been implemented!');
};

The error I am getting now is

  if (section) return section;
  //           ^^ Type 'SupportedSectionTypes' is not assignable to type 'T'.
  // 'SupportedSectionTypes' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'SupportedSectionTypes'.
    // Type 'BasicSection' is not assignable to type 'T'.
      'BasicSection' is assignable to the constraint of type 'T', but  'T' could be instantiated with a different subtype of constraint 'SupportedSectionTypes'.ts(2322)

Upvotes: 0

Views: 59

Answers (2)

gbalduzzi
gbalduzzi

Reputation: 10166

The problem is here:

const section: SupportedSectionTypes = SECTIONS[key];

you are returning section, that is of type SupportedSectionTypes, and may not be T. That's why Typescript gives you an error.

You can force it to be considered equivalent to T:

const section: T = SECTIONS[key] as T;

Just note that Typescript can not know if the returning type is really T.

Imagine your SECTIONS object has a key foo containing a CardSection. The following code will be considered valid:

const checkedSection = <CheckedSection>getSectionData('foo')

But, at runtime, checkedSection will not contain a CheckedSection object.


If you already know which key you will face, you can properly define your SECTIONS object and then your generic type can be inferred by typescript:

type SectionCollection = {
  section_info: BasicSection
  foo: CheckedSection
  bar: CardSection
};

export const getSectionData = <T extends keyof SectionCollection>(key: T): SectionCollection[T] => {
  const section: SectionCollection[T] = SECTIONS[key];

  if (section) return section;

  throw new Error('This section has not been implemented!');
};

// Here Typescript understands that the returning type is `CheckedSection`
const section = getSectionData('foo'); 

Upvotes: 2

Alireza Ahmadi
Alireza Ahmadi

Reputation: 9893

First you need to tell ts compiler key is key of a SECTIONS then in return type you have to cast as T.

export const getSectionData = <T extends SupportedSectionTypes>(key : string): T => {
  let f = key as keyof  typeof SECTIONS;
  const section: SupportedSectionTypes =  SECTIONS[f];

  if (section) return section as T;

  throw new Error('This section has not been implemented!');
};

PlaygroundLink

Upvotes: 2

Related Questions