Mapping TypeScript function return types based on parameter conditions using dictionaries

I'm trying to create a function in Typescript that will return different objects depending on the function parameter. Can someone help me find a solution?

This is what I have so far:

type outputMap = {
    'x': number,
    'y': string[]
}

function fetchValue<T extends outputMap, K extends keyof T>(input: K): T[K] {
    if (input === 'x') {return 10}
    if (input === 'y') {return ['hello', 'world']}
    return 42
}
console.log(fetchValue('x')) // Outputs 10

However, I am getting an error message saying "TS2322: Type '10' is not assignable to type 'T[K]'" for the return values.

Any help would be greatly appreciated. Thank you!

Answer №1

Here's a solution:

interface ResultMap {
  'a': number;
  'b': string[];
}

function getValue<K extends keyof ResultMap>(key: K): ResultMap[K];
function getValue(key: keyof ResultMap): ResultMap[keyof ResultMap] {
  if (key === 'a') return 5;
  if (key === 'b') return ['a', 'b'];
  throw new Error();
}

const result1 = getValue('a'); // const result1: number
const result2 = getValue('b'); // const result2: string[]

// Argument of type '"c"' is not assignable to parameter of type '"a" | "b"'.ts(2345)
const result3 = getValue('c'); // const result3: number | string[]

Take note of the function overloading syntax:

function getValue<K extends keyof ResultMap>(key: K): ResultMap[K];
function getValue(key: keyof ResultMap): ResultMap[keyof ResultMap] { // ...

If you only used the second signature, the returned type would always be number | string[].

If you only used the first signature, then ResultMap[K] would be evaluated as number & string[], which is contravariant and can lead to errors described in your question.

Alternatively, if you prefer not to use overloaded signatures, you can cast the result inside the function's body:

function getValue<K extends keyof ResultMap>(key: K): ResultMap[K] {
  if (key === 'a') return 5 as ResultMap[K];
  if (key === 'b') return ['a', 'b'] as ResultMap[K];
  throw new Error();
}

Answer №2

Overloading can be a useful technique:

type dataMap = {
    'a': number,
    'b': string[],
    'x': 42,
    'z': 42,
}

function customFunc<T extends dataMap, K extends Exclude<keyof dataMap,'a'|'b'>>(val: K): 42;
function customFunc<T extends dataMap, K extends 'b'>(val: K): string[];
function customFunc<T extends dataMap, K extends 'a'>(val: K): number;
function customFunc<T extends dataMap, K extends keyof T>(val: K): number | string[] {
    if (val == 'a') {
        return 5
    }
    if (val == 'b') {
        return ['a', 'b']
    }
    return 42
}

const output = customFunc('a') // number
const output1 = customFunc('b') // string[]
const output2 = customFunc('x') // 42
const output3 = customFunc('z') // 42

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Exploring the power of Typescript and Map in Node.js applications

I am feeling a little perplexed about implementing Map in my nodejs project. In order to utilize Map, I need to change the compile target to ES6. However, doing so results in outputted js files that contain ES6 imports which causes issues with node. Is t ...

This element is not suitable for use as a JSX component since its return type 'void' is not a valid JSX element. Please check the return type to ensure it is compatible with

I have been working on this code snippet: function searchData(searchWord: any) { if (originalData.length > 0) { if (searchWord !== "") { setDataList([...originalData.filter((svc: any) => ...

Tips for converting the iterator from a v-for to a string within a v-if

I am seeking to comprehend how to utilize v-for with v-if's in order to generate repeated teasers without resorting to more simplistic vue-logic. Currently, it appears that when using v-for with v-if nested within, it is not feasible to assign the ind ...

What is the best way to stay on track with internal anchor links when navigating with Aurelia?

I'm currently working on developing a style guide for a project and one of the features I would like to implement is a basic click behavior on anchor links, allowing them to smoothly scroll to the corresponding section on the page. For instance: < ...

What is the best way to assign an index signature to a plain object?

Currently, I have this object: const events = { i: 'insert', u: 'update', d: 'delete' }; I am struggling to assign an index signature to the object. When I try the following: export interface EventsSignature { [key: ...

Is there a way to limit the keys of T to only number fields, where T[keyof T] is a number

I'm looking to restrict the field parameter within this function: function calculate<T>(source: T[], field: keyof T) { for(const item of source) { } } The goal is to ensure that item[field] will always be a number. Is there a way to ac ...

A sophisticated method for converting a string to a number within a Typescript class without the need for a constructor?

Imagine needing to process JSON responses where all parameters are encoded as strings, regardless of their actual type. The goal is to strictly typecast these values into various Typescript classes. For example, consider the following JSON: {"id":"1","lab ...

The React TypeScript variable does not get updated promptly

I have a textfield that contains text and a button which will process the text. The Button should only be clickable if the textfield is not empty. Here's the code I used to achieve this functionality: const [text, setText] = useState(""); const [activ ...

Error encountered when attempting to locate the file declaration for module 'jwt-decode'

Currently, I am utilizing the Visual Studio 2017 template for my Angular project and encountering an issue when attempting to import the 'jwt-decode' module after adding it to package.json. The error (mentioned in the title of my post) arises aft ...

Exploring the usage of slots in WebComponents without relying on shadow DOM

I am working on developing a WebComponent without utilizing ShadowDOM. So far, everything has been going smoothly. However, I now aim to create a component that wraps other components similar to how it is done in Angular with @ViewChild / @ViewChildren. (I ...

Incorporating a Custom CKEditor5 Build into an Angular Application

I am currently in the process of developing an article editor, utilizing the Angular Integration for CKEditor5. By following the provided documentation, I have successfully implemented the ClassicEditor build with the ckeditor component. Below are the ess ...

Creating a new list by grouping elements from an existing list

I have successfully received data from my API in the following format: [ {grade: "Grade A", id: 1, ifsGrade: "A1XX", ifsType: "01XX", points: 22, type: "Type_1"}, {grade: "Grade B", id: 2, ifsGrade: &quo ...

Cannot access a Typescript method variable within an inline function

I've encountered an issue with my code involving loading values into the array usageCategory within an inline function. Despite successfully adding values to the array inside the function, I am unable to print them outside it. getAllUsageCategoryElem ...

Is Typescript familiar with the import -> require syntax, but unfamiliar with the require -> import syntax?

My code includes a simple file that utilizes the ES6 module loader: 1.ts import {foo} from "./2" foo('hello'); 2.ts function foo (a){console.log(a);}; export {foo} When the tsconfig is configured as follows: "module": "commonjs", We can o ...

Having trouble changing the value of a field within an object stored in an array in JavaScript/TypeScript?

I'm wrestling with what seems to be a straightforward issue, but for some reason I can't update the pos field value in the WorkSetTemplate object within an array. Here's the code snippet: export class WorkSetTemplate { static alignPosit ...

Different categories combined into a singular category

Trying to define a type that can be one of two options, currently attempting the following: type TestConfig = { file: string; name: string; } type CakeConfig = { run: string; } type MixConfig = { test: TestConfig | CakeConfig }; const typeCheck: M ...

Prevent receiving the "deprecated warning for current URL string parser" by enabling the useNewUrlParser option

I recently encountered an issue with my database wrapper class that connects to a MongoDB instance: async connect(connectionString: string): Promise<void> { this.client = await MongoClient.connect(connectionString) this.db = this.cli ...

Having trouble disabling an ESLint rule for ESLint, TypeScript, Vue, and WebPack Encore?

I've been delving into Webpack Encore and ESLint issues for quite some time now, but unfortunately, I haven't been able to find a solution to my problem. Specifically, I've been trying to change or disable certain TypeScript-ESLint rules, b ...

Tips for obtaining the iframe #document with cheeriojs?

I've been struggling to scrape the anime videos page [jkanime], specifically with extracting the mp4 video formats embedded in an iframe #document. Despite trying to use cheerio for querying, I've only managed to retrieve src links from Facebook ...

Using template strings in a Mongoose update query when working with TypeScript

When working with a mongoose update query in typescript, I am trying to incorporate a template string. The specific field I need to update is a Map named messages, which consists of string keys and array values of type Message. interface Message { conte ...