Reputation: 1492
Update: TSPlayground link
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
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
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!');
};
Upvotes: 2