What exactly are Discriminating Unions in the realm of Function Types?

I am struggling to differentiate between members of a discriminated union made up of function types. Take a look at the following example:

type _NumFunc = (n: number) => string;
type _StrFunc = (s: string) => string;
interface NumFunc extends _NumFunc { __type: 'NumFunc'; }
interface StrFunc extends _StrFunc { __type: 'StrFunc'; }
type Func = NumFunc | StrFunc;

let myNumFunc = ((n: number) => `Hello x${n}!`) as NumFunc;
let myStrFunc = ((s: string) => `Hello, ${s}!`) as StrFunc;

let funcGen = (n: number): Func => n % 2 == 0 ? myNumFunc : myStrFunc;

for (let i = 0; i < 2; i++)
{
    let func = funcGen(i);
    switch (func.__type)
    {
    case 'NumFunc':
        console.log(func(3));
        break;
    case 'StrFunc':
        console.log(func('World!'));
        break;
    default:
        console.error(func);
        console.error('How did this happen?');
        break;
    }
}

I anticipate that the program should output:

Hello x3!

Hello, World!

However, when you execute this code, you will notice that the default case is triggered for each iteration. Simply logging func will display the function object, but trying to access __type on the object results in an error indicating that the type of func is never. Why does this method not work, and is there an alternative approach to utilize discriminated unions of function types?

Answer â„–1

Interesting point to consider. TypeScript operates without a runtime environment, meaning that type definitions do not exist during the execution of code. Instead, types in TypeScript are simply aliases for real data structures within JavaScript itself. This implies that when defining a type in TypeScript, you must specify to the compiler the exact structure it represents at runtime.

If we define a type B as {y: number}, then creating a value of type B requires an object with a property 'y' of type number. TypeScript does not automatically generate these structures—it relies on receiving or creating them elsewhere, such as from a server response during runtime.

Regarding your question, the issue arises from specifying discriminant properties like __type in your functions without actually setting those properties anywhere. By doing so, you are essentially deceiving the type system by claiming that function f is of type NumFunc and function g is of type StrFunc, even though this information is not reflected in the runtime. The switch statement operates at runtime, where the __type property you're utilizing doesn't exist.

To resolve this dilemma, manually assign the discriminant property to your functions. For instance:

let myNumFunc = ((n: number) => `Hello x${n}!`) as NumFunc;
myNumFunc.__type = 'NumFunc';

let myStrFunc = ((s: string) => `Hello, ${s}!`) as StrFunc;
myStrFunc.__type = 'StrFunc';

By adding these properties to both functions, they will now possess the desired discriminant and should function correctly. I trust this explanation clarifies things for you.

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

Is there a way to trigger validation with a disabled property?

My form element is set up like this: <input type="text" id="country" formControlName="Country" /> The form group looks like this: this.myForm = this.formbuilder.group({ 'Country': [{ value: this.user.Country, disabled: this.SomeProperty= ...

The conversion of an array to Ljava/lang/Object is not possible

I'm currently working on a project using NativeScript app with TypeScript where I am trying to pass an array of android.net.Uri to a function. However, when attempting to do so, I encounter an error mentioning that the 'create' property does ...

`How can default query parameters be configured for Routes in Angular 7?`

Within our Angular-7-Application, we rely on @ngrx and @ngrx/router-store to incorporate query params into the state. Many parts of the application consist of paginated lists. Each list is represented as a component with the Pagination-Component integrate ...

Troubleshooting and setting breakpoints in TypeScript code for Angular Web applications using the Firefox browser

Is there a method to add breakpoints to .typescript source files in my Angular application with Firefox Developer Tools? While I am able to add breakpoints to the generated javascript files, is it possible to debug the .ts source files directly? This quer ...

Issue encountered while executing jest tests - unable to read runtime.json file

I've written multiple unit tests, and they all seem to pass except for one specific spec file that is causing the following error: Test suite failed to run The configuration file /Users/dfaizulaev/Documents/projectname/config/runtime.json cannot be r ...

Error: JSON parsing error caused by an unexpected character '<' at the start of the JSON data while executing an Angular post request to a

Using Angular 2, I have been attempting to send data from my Angular application through a POST request to a PHP file on the server. However, I keep encountering an error that reads "SyntaxError: Unexpected token < in JSON at position 0". Below is the c ...

Vue composable yields a string value

I am currently using a Vue composable method that looks like this: import { ref } from 'vue'; const useCalculator = (num1: number, num2: number, operation: string) => { const result = ref(0); switch (operation) { case 'add& ...

Issue with locating module in Visual Studio 2015 when using Angular5 and TypeScript version TS2307

I'm currently attempting to integrate Angular in Visual Studio 2015 update 3, but encountering the following error: TS2307 cannot find module '../node_modules/@angular/core'. The error is shown in the image linked here. Can anyone help me fi ...

Tips on utilizing MobX State Tree models as function arguments in TypeScript

I am working on creating a helper for my App to generate a list model, but I am struggling with defining the correct type using generics. Can you help me understand how to do it properly? const createListInterface = <T>(Model: T) => { const init ...

Combining the date from one source and the time from another source to create a unified date in Typescript

In my TypeScript code, I'm working with four fields of type Date: StartDate StartTime EndDate EndTime My goal is to combine StartDate and StartTime into a single field called Start, as well as EndDate and EndTime into a field called End. Desp ...

Disabling `no-dupe-keys` in ESLint does not seem to be effective

Currently, I am working on a project where I have incorporated Typescript and ESLint. However, I have encountered an issue with the error message stating: An object literal cannot have multiple properties with the same name. I am looking to disable this s ...

Techniques for returning errors to the calling function using async functions

I am currently encountering an error where if "dateofBirth" is not found, an empty object is sent back to the client. How can I change this so that an error object is sent back instead of an empty object? Essentially, I want to send back a catch process. ...

Determining data types through type guarding in Typescript

interface A = { name: string; ... }; interface B = { name: string; ... }; interface C = { key: string; ... }; type UnionOfTypes = A | B | C | ...; function hasName(item: UnionOfTypes) { if ("name" in item) { item; // typescript knows ...

You cannot assign void to a parameter expecting a function with no return value

I have been working on an angular application that was initially developed in Angular 2, then upgraded to versions 7 and 9, and now I'm attempting to migrate it to Angular 11. There is a function in my code that fetches the notification count for the ...

Transform array sequences into their own unique sequences

Reorder Array of List to Fit My Custom Order Current Output: [ { "key": "DG Power Output", "value": "6.00", "unit": "kWh", }, { "key": "DG Run Time", "value": "5999999952", "unit": "minutes", }, { "key": "Fuel Level (Before)", "value": "8.00" ...

Setting a dynamically addressed property within a TypeScript interface

I have a situation where I need to dynamically access an object property using a variable that represents a keyof the object type. Here's an example: interface FidelityCheckRow { P1: number; P2: string; P3: string; } const keys: (keyof F ...

What techniques can I use to adjust the size of an image through zooming in and out?

In my custom gallery component, the crucial code section looks like this: <Gallery> <Header> <img src={galleryIcon} alt='Galley icon' /> <h1>My Gallery</h1> </Header> ...

What is the process to enable mandatory validation after a change in input in Angular 4?

Currently, I am working on a project using Angular 4. One of the tasks I need to achieve is validation. <input [(ngModel)]="someModel" required placeholder="some placeholder"/> The validation triggers immediately, but I want it to only trigger aft ...

The union type generated by TS Expression is too intricate for proper representation using Material-UI and @react-three/fiber

I've been working on a Next.js application that incorporates both Material-UI and the @react-three/fiber library. Recently, after upgrading to Material-UI V5, I encountered an error. Here's the specific error message: https://i.sstatic.net/SNEw5 ...

Errors in Compiling Dependencies for d3.js Using Typescript

Currently, I am in the process of developing a web application utilizing Node.js alongside Angular, Typescript, and d3.js, among other technologies. The application is functioning properly with library features working as expected. However, I am encounteri ...