Distinguish private member unions in Typescript

Struggling with discriminating between private class member types? Attempting to access variables v1 and v2 using string literals resulting in type union issues. With a bit of tweaking, I found a workaround for public members only. Check out this example:

class Test {
    private v1: string;
    private v2: number;
    v3: string;
    v4: number;
    private v5: string;
    private v6: number;

    // Works for all members but does not discriminate union
    get12(what: 'v1' | 'v2') {
        return this[what];
    }

    // Only works for public members
    get34<T extends 'v3' | 'v4'>(what: T) {
        return this[what];
    }

    // Explicit overload method works but is not the lazy/generic solution desired
    get56(what: 'v5'): string;
    get56(what: 'v6'): number;
    get56(what: 'v5' | 'v6') {
        return this[what];
    }
}

let myTest = new Test();
myTest.get12('v1'); // returns "string | number" type
myTest.get34('v3'); // returns "string" type
myTest.get56('v5'); // returns "string" type

Looking for a workaround that doesn't require explicit overloading or manual maintenance due to variable type changes. Any ideas?

Edit: To clarify, seeking a lazy/generic solution as opposed to other methods requiring ongoing upkeep if variable types change.

Answer №1

Seems like there is a design limitation in TypeScript that restricts the use of indexed access with private properties in certain situations. Navigating around this limitation can be tricky.

One approach that "works", but comes with a disclaimer:

//@ts-ignore
type UnsafeIndexed<T, K extends string> = T[K]

The above type alias forces the compiler to perform indexed access even when its success cannot be guaranteed. Here's an example:

type Foo = { bar: string, baz: number };
type FooBar = UnsafeIndexed<Foo, "bar">; // string
type FooBaz = UnsafeIndexed<Foo, "baz">; // number
type FooBad = UnsafeIndexed<Foo, "nope">; // any

With this setup, you can now implement something like this:

class Test {
    private v1: string;
    private v2: number;

    get<K extends 'v1' | 'v2'>(what: K): UnsafeIndexed<this,K> {
        return (this as any)[what];
    }
}

Due to limitations, using this[what] directly will cause a compiler error, hence the need for (this as any). Give it a try:

let myTest = new Test();
const v1 = myTest.get('v1'); // string
const v2 = myTest.get('v2'); // number

This method technically "works", though not recommended for production code. There might be better workarounds available, such as using public properties or overloads. Ultimately, choose the solution that best suits your needs. Good luck!

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

Verifying the Presence of an Image in the Public Directory of Next JS

My TypeScript Next.js project contains a large number of images in the public folder. I am wondering if there is a way to verify the existence of an image before utilizing the <Image /> component from next/image. I have managed to achieve this using ...

The call to react.cloneElement does not match any overloads

I'm encountering a typescript error in my React code when using React.cloneElement with the first parameter "children", and I am unsure of how to resolve it. As a newcomer to typescript, I believe that the type definitions in index.d.ts for cloneElem ...

Struggling with implementing click events on Ionic-Calendar 2?

Attempting to implement a (click) event for ionic-calendar 2 where it deactivates a button if the user clicks on a date that has already passed. The issue I am facing is that when I initially click on a past date, the button remains enabled. However, upon ...

Tips on utilizing the identical template in ngIf

I need to display different templates based on certain conditions. For example: <template [ngIf]="item.url.indexOf('http') == -1"> <a class="ripple-effect" [routerLink]="[item.url]" *ngIf="isUserLoggedIn == true" > ...

Guide to executing various datasets as parameters for test cases in Cypress using Typescript

I am encountering an issue while trying to run a testcase with multiple data fixtures constructed using an object array in Cypress. The error message states: TS2345: Argument of type '(fixture: { name: string; sValue: string; eValue: string}) => vo ...

Unable to retrieve the state in a functional component using the useLocation hook from react-router-dom

I am facing an issue while trying to access a state property that I have passed through NavLink into a component. Despite using useLocation as recommended, I seem to be struggling with it due to some typescript error. Here is the code snippet: import Reac ...

What could be the reason for my function throwing a TypeError with the message "<function> is not a function"?

Every time I try to call a function that clearly appears to be defined as a function, I continuously receive the error message: TypeError: [function name] is not a function. To demonstrate the issue, here is a simple example: main.ts import someFunction ...

Encountering a 500 Internal Server Error while making a call to a RESTful (GET) Web

Currently, I have a setup where my web server is housed inside a virtual machine, with the client located outside of it. On the server side, I am utilizing .NET Framework 4.6.1 for development. Below is the architecture of my WEB API controller on the se ...

TypeScript throws an error when jQuery is imported unexpectedly

How does the compiler resolve the $ in the code snippet below, even without importing jQuery? function f () { $('#loadFiles').click() // ok $$('#loadFiles').click() // error, can't find name '$$' } The compile ...

Retrieve data from a table within an Angular component

Struggling with the ng2-smart-table library, I am facing challenges in passing values entered in the edit line to a custom component: Refer to the code snippet below for passing Maximum and Minimum Temperature values to the SmartTableEditorFunctionsCompon ...

Angular's HttpClient.get method seems to have trouble retrieving real-time data from a Firebase database

I have been debugging and I suspect that the error lies in this part of the code. The DataService class's cargarPersonas function returns an Observable object, but I am struggling to understand how to properly retrieve the database data and display it ...

Typescript input event

I need help implementing an on change event when a file is selected from an input(file) element. What I want is for the event to set a textbox to display the name of the selected file. Unfortunately, I haven't been able to find a clear example or figu ...

Learn how to easily set a radio button using Angular 4 and JavaScript

It seems like a simple task, but I am looking for a solution without using jQuery. I have the Id of a specific radio button control that I need to set. I tried the following code: let radiobutton = document.getElementById("Standard"); radiobutton.checke ...

I am currently working on an Angular 8 project and experiencing difficulties with displaying a specific value from a JSON object in my template using an ngFor loop

Apologies if I am not familiar with all the terms, as I am mostly self-taught. I started with Udemy and then turned to Stack Overflow to tackle the more challenging aspects. This platform has been incredibly valuable and I am truly grateful for it. Now, l ...

When trying to pull a component from Svelte, I receive an error message stating "Selection Range

I'm still relatively new to svelte, so I might be handling things incorrectly. Whenever I attempt to separate my button component, regardless of whether I name the component ./Button.svelte, ./Button, Button.svelte, or try variations with capitalizat ...

What are some effective methods for handling data from a backend API within an Angular service?

I'm currently facing challenges in effectively managing and manipulating data retrieved from a backend API within an Angular 2+ service. Take for instance the creation of a cart system. Upon sending an initial get request to fetch the current cart de ...

Elements that allow for asynchronous data submission without requiring a traditional submit button

Hey there, I could really use some help with this puzzle I'm trying to solve. Here's the situation: <ul> <li>Number: <span id="Number">123</span></li> </ul> I want to set up a connection between t ...

Building an Angular 4 app featuring a customized default landing page and URL parameters functionality

I am in the process of developing a web portal that will be embedded within an iFrame. Below is the code snippet I am using to set up the portal: Routes Configuration const routes: Routes = [ { path: '', redirectTo: '/dash ...

Typescript: Delay code execution until a function has completed running

I've encountered an issue with my code that involves calling a function. Here is the snippet of code in question: this.getAllOptions(questionID); console.log("++++++++++++++++++++++++++++++++"); console.log(this.result); The task of this function is ...

Struggling with getting render props to work in Next.js version 13

Looking to develop a custom component for Contentful's next 13 live preview feature in the app directory, I thought of creating a client component that can accept a data prop and ensure type safety by allowing a generic type to be passed down. Here is ...