Create a Typescript generic function that can return a variety of data types including strings, numbers, and

I have a function written in Typescript and I am looking to determine the return type based on the value retrieved from process.env.

For instance, the variables in my Node process.env can be strings, numbers, or booleans. I want to fetch them with their specific types without encountering any warnings or errors during compilation.

The compile time errors I encounter generally look like the one below, each corresponding to string, number, or boolean variations. The errors occur within the function where I'm seeing these issues:

Type 'string' is not assignable to type 'T'.
  'T' could be instantiated with an arbitrary type which might not correspond to 'string'.ts(2322)

In the simple function provided below, I face compile time errors. Is there a way to generically return these values?

function get<T>(envVar: string): T {
  // Not fetching the actual value from process.env for this demonstration.
  
  // If the process env value is a string, then return a string.
  if (envVar === 's') {
      return 'string value';
      // compile error here: 2322
      // Type 'string' is not assignable to type 'T'.
  }
  
  if (envVar === 'n') {
      return 52;
      // compile error here: 2322
      // Type 'number' is not assignable to type 'T'.
  }
  
  if (envVar === 'b') {
      return true;
      // compile error here: 2322
      // Type 'boolean' is not assignable to type 'T'.
  }

  return null;
}

Sample calling code:

// No compile errors. This is how I expect to use this function.
const s1: string = this.get<string>("s");
const n1: number = this.get<number>("n");
const b1: boolean = this.get<boolean>("b");

// Another approach without compile errors. Here, no explicit type is defined, hence it works.
const s2: string = this.get("s");
const n2: number = this.get("n");
const b2: boolean = this.get("b");

// Expected compile errors below.

// Type 'number' is not assignable to type 'string'.ts (2322)
const sN: string = this.get<number>("s");

// Type 'string' is not assignable to type 'number'.ts (2322)
const nS: number = this.get<string>("n");

Below is a pseudo-code example of what I ideally want to achieve, although uncertain if feasible in Typescript.

function get<T>(envVar: string): T {
  // Obtain variable's value from process.env, always as a string

  const val; // imagine val exists as a string

  // Depending on T, return the appropriate type 
  if (T instanceof Number) {
    return Number.parseInt(val) as T; // return as number
  }
  else if (T instancef Boolean) {
    return Boolean.parse(val) as T; // return as boolean
  }
  else {
    return val as T; // return as string
  }
}

// Sample calling code:
const s1 = get<string>("key");
const n1 = get<number>("key");
const b1 = get<boolean>("key");

Upon further consideration, my main issue pertains to inferring the type "T" passed in and utilizing that information to handle returning an object of type "T".

Considering this, perhaps a more straightforward solution would be:

const s1 = get("key");
const n1 = getAsInt("key");
const b1 = getAsBoolean("key");

Answer №1

It's important to understand that in TypeScript, all type information available at compile time is erased.
This means that expressions like T instanceof Number cannot function as intended. T represents a type, not a value, and after compilation, it no longer exists, which is why the TypeScript compiler will reject it.

If you want your function to behave differently based on typing information, you need to pass that information as something more than just a type - it should also be a value.

One approach mentioned in the comments is using overloads. This offers a solution to the issue you're encountering.
For example, you could introduce a second argument for an optional type identifier.
This can then be utilized within the function to alter the output type.
By defining multiple type variations for the function, TypeScript can determine the output types based on the input types supplied to the function.
In the case of your get function, here's how you could implement it:

function get(envVar: string): string;
function get(envVar: string, toType: "number"): number;
function get(envVar: string, toType: "boolean"): boolean;
function get(envVar: string, toType?: "number" | "boolean") {
  const val = "Example value" as string;
  if (toType === "number") {
    return Number.parseInt(val); 
  }
  else if (toType === "boolean") {
    return Boolean(val); // return as boolean
  }
  else {
    return val; // return as string
  }
}

This method relies on multiple function signatures instead of generics. It ensures that the function returns a suitable type by prompting TypeScript to flag any discrepancies between the overload signature and its implementation.

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

Achieving checkbox values in Typescript: A guide

I need help with capturing the values of checked checkboxes and storing them in a string to use in my API. I want to retrieve the value if a checkbox is unchecked. <div *ngFor="let x of groupesTable"> <input type="checkbox" [(ngModel)] ...

Tips for declaring a non-reactive instance property in Vue.js with TypeScript

I am currently in the process of transitioning my Vue.js components to TypeScript. In accordance with the recommendations provided in the documentation, I attempted to utilize Vue.extend() for my component implementation. The code snippet for my component ...

Secure your NetSuite API calls with OAuth 1.0 signature authorization

After extensively reviewing the documentation and various posts on Stack Overflow about creating a signature for NetSuite OAuth1.0 with TBA, I believe that I have followed all of the necessary steps and correctly generated the key. However, upon making the ...

The function column.getHeaderGroupProps does not seem to be available

Struggling with the initial setup of react-table with typescript. I keep encountering an error related to the data passed into my table function: column.getHeaderGroupProps is not a function TypeError: column.getHeaderGroupProps is not a function at ht ...

What causes the error message "Expected ':' when utilizing null conditional in TypeScript?"

UPDATE: After receiving CodeCaster's comment, I realized the issue was due to me using TypeScript version 3.5 instead of 3.7+. It was surprising because these checks seemed to be working fine with other Angular elements, such as <div *ngIf="pa ...

Angular Error: Cannot call function panDelta on this.panZoomAPI

Check out my small demonstration using a stackblitz, I'm having an issue. In the setup, there's a master component with pan-zoom functionality containing a parent component with children content. The library in use is ngx-panzoom. The default c ...

The database did not respond, causing the API to resolve without sending a response for the specified endpoint (/api/dates). This could potentially lead to requests becoming stalled in Next

I have been attempting to retrieve a list of dates from my database in a straightforward manner, but unfortunately, I am not receiving any response (even after trying to log the response data to the console). The only feedback I seem to be getting when sel ...

What is the correct way to input the 'name' HTML attribute in an Ant Design select element?

I am facing an issue with the 'name' attribute in my Ant Design Select component. When trying to set the 'name' attribute, I encountered an error message that is causing issues. https://i.stack.imgur.com/Lzb4t.png Ant Design Select Co ...

How to trigger a click event in React using TypeScript and material-ui library

Currently, I am facing an issue when trying to update the value of material-ui TextFields from the store. When manually typing inside the field, everything works fine as expected with the handleChange() and handleBlur() functions handling the events. Howev ...

Concealing Dropdown Box Information Based on a Customized List in Angular

Is there a way to remove an item from a dropdown list once it has been selected? For example, if I have a dropdown box with options 'Dog', 'Lion', and 'Cat', how can I make it so that once I select 'Dog', it will no ...

How to leverage tsconfig paths in Angular libraries?

While developing an Angular library, I made configurations in the tsconfig.lib.json file by adding the following setup for paths: "compilerOptions": { "outDir": "../../out-tsc/lib", "target": "es2015", "declaration": true, "inlineSources ...

Exploring for JSON keys to find corresponding objects in an array and adding them to the table

I'm currently working on a project where I need to extract specific objects from a JSON based on an array and then display this data in a table. Here's how my situation looks: playerIDs: number[] = [ 1000, 1002, 1004 ] The JSON data that I am t ...

Retrieving information from a JSON API using Angular with Typescript

Currently, I am dealing with an API JSON to fetch a list of countries, followed by a list of states, and then cities within that state and country. The challenge lies in the second API call that I make. In the beginning, I load a list of continents and the ...

Searching for client using mqtt.js in Angular2 with Typescript yields no results

I am facing a unique issue while trying to incorporate the mqtt.js library into Angular 2 using TypeScript. Below is my app.component.ts file: import { Component } from '@angular/core'; import * as mqtt from 'mqtt'; @Component({ sel ...

What is the best way to implement a switch case with multiple payload types as parameters?

I am faced with the following scenario: public async handle( handler: WorkflowHandlerOption, payload: <how_to_type_it?>, ): Promise<StepResponseInterface> { switch (handler) { case WorkflowHandlerOption.JOB_APPLICATION_ACT ...

Using TypeScript in the current class, transform a class member into a string

When converting a class member name to a string, I rely on the following function. However, in the example provided, we consistently need to specify the name of the Current Component. Is there a way to adjust the export function so that it always refers ...

Encountering a compilation error due to a Typescript assignment

While working with Typescript, I encountered a compilation error in the code shown below: console.log('YHISTORY:login: data = '+data); let theData = JSON.parse(data); console.log('YHISTORY:login: theData = '+JSON.stringify(theData)); ...

Typescript interface created specifically for React Higher Order Component Props

Consider the React HOC provided below which adds sorting state to a component: import React, {Component, ComponentClass, ComponentType} from 'react' interface WithSortState { sortOrder: string } interface WithSortInjectedProps { sortO ...

Dynamic Text Labels in Treemap Visualizations with Echarts

Is it possible to adjust the text size dynamically based on the size of a box in a treemap label? I haven't been able to find a way to do this in the documentation without hardcoding it. Click here for more information label: { fontSize: 16 ...

Utilize JQuery to choose the angular element

Can Angular tags be selected using JQuery? I am currently utilizing the ui-select Angular component, which is integrated into the HTML page as shown below: <ui-select ng-model="rec.currencyCode" on-select="ctrl.selectCurrencyCode(rec, $item)"> & ...