Class field type based on generics

Consider the code snippet below in the playground:

type AvailableTypes = {
    'array': Array<any>;
    'string': string;
    'object': object;
}

class Wrapper<T extends keyof AvailableTypes> {

    // Can be of type array, string, or object
    private readonly type: T;

    // ERROR: Property 'value' has no initializer and is not definitely assigned in the constructor.
    private readonly value: AvailableTypes[T];

    constructor(type: T) {
        this.type = type;

        /**
         * ERROR:
         *  TS2322: Type 'never[]' is not assignable to type 'AvailableTypes[T]'.   
         *  Type 'never[]' is not assignable to type 'never'.
         */
        switch (type) {
            case 'array':
                this.value = [];
                break;
            case 'string':
                this.value = '';
                break;
            case 'object':
                this.value = {};
                break;
        }
    }
}

Two significant errors are present:

TS2322: Type 'never[]' is not assignable to type 'AvailableTypes[T]'.
Type 'never[]' is not assignable to type 'never'

Even though AvailableTypes[T] always corresponds to one of the types defined in AvailableTypes, with T being the key.

... and

Property 'value' has no initializer and is not definitely assigned in the constructor.

Despite the requirement for type to be either a string, array, or object.

What could I possibly be overlooking here?

Check out these related SO Threads:

  • Typescript Generic Union
  • Create union out of interface using tag generic
  • TypeScript: use of generic and union types

Update

(update to @jcalz answer) It should be possible to type-check the value based on the type property:

// Inside the Wrapper class, this method should work since `value` can only be an array if the type is 'array':
public pushValue(val: unknown) {
    if (this.type === 'array') {
        this.value.push(val);
    }
}

Playground

Answer №1

An ongoing issue in TypeScript is that generic type parameters are not narrowed through control flow analysis, a long-standing challenge as documented on microsoft/TypeScript#24085.

While the compiler can narrow the type of a variable within a switch/case statement, it does not extend this narrowing to the type parameter itself. This lack of analysis prevents verifying the safety of assigning values within certain conditions.

This same issue leads to errors like "value isn't definitely assigned." The compiler fails to recognize all possibilities for the type parameter due to the absence of thorough control flow analysis.


A workaround involves using type assertions to inform the compiler of your intent when it cannot verify the assignment's safety.

To address exhaustiveness problems, you can either add a default case that throws an error or broaden the type from T to keyof AvailableTypes to allow for better analysis.


Another alternative suggested involves leveraging how indexing works in TypeScript to achieve safer code without relying heavily on type assertions or switch statements.

This approach offers a more elegant and type-safe solution compared to alternatives but should be chosen based on specific use cases.


Hopefully, one of these methods proves useful in resolving your issue. Good luck!

Playground link to code


update: discriminated union instead of generic classes

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

Restrict the number of subscriptions allowed for an rxjs observable

Seeking a replacement for observable, subject, or event emitter that allows only one subscription at a time. The first subscriber should have priority to execute its subscribe function, with subsequent subscribers waiting their turn until the first unsubsc ...

Guide on showing the content of an uploaded file as an object in JavaScript using file reader

When using the file upload function to upload a json file and read its contents, I am encountering an issue where the result is in string format instead of object. How can I display it as an object? Here is my code: .html <div class="form-group"> ...

What is the best way to halt the parcel/babel/typescript process when encountering compilation errors or warnings?

index.jsx import React from 'react' import ReactDOM from 'react-dom' import Home from "./home"; const x:number = "aaa" const x:number = "aaa" const x:number = "aaa" ReactDOM.render(<Home/>, document.getElementById('root&ap ...

When utilizing "ng2-file-upload" in conjunction with Angular 2 and Typescript, encountering a limitation where files larger than 1MB cannot be uploaded

My attempt to upload a file with a size exceeding 1MB is triggering an error regarding its large size. Despite setting the limit to 50 MB, it doesn't seem to be working as expected. Can someone please assist me in figuring out what I am doing incorrec ...

ESLint prohibits the usage of React.StatelessComponent and React.FunctionalComponent within the codebase

Is there a way to restrict the use of React.StatelessComponent or React.FunctionalComponent and only allow React.FC in my code? For instance: export const ComponentOne: React.StatelessComponent<Props> = (props) => { return <....> }; export ...

Leveraging keyboard input for authentication in Angular

Would it be possible to modify a button so that instead of just clicking on it, users could also enter a secret passphrase on the keyboard to navigate to the next page in Angular? For example, typing "nextpage" would take them to the next page. If you&apo ...

The 'posts' binding element is assumed to have a type of 'any' by default

Currently, I'm working on a code project that involves graphql, react, and typescript. In the middle of the process, I encountered an error message stating "Binding element 'posts' implicitly has an 'any' type." I am unsure about w ...

Creating an Express server using Webpack, TypeScript, and ESM

Hello, I am currently working on a small project using node.js and TypeScript with webpack. Here is a snippet of my tsconfig.json file: tsconfig.json { "compilerOptions": { "lib": ["ESNext"], "target": "ES2020", "module": "NodeNext", "mod ...

What to do when the 'image' property in Next.js next/image has an implicit 'any' type and needs fixing?

I'm a newcomer to Next.js and TypeScript and I can't seem to find any helpful resources on resolving this particular issue: import Image from 'next/image'; export default function Item({ image }) { // <-- parameter image needs a &ap ...

Invoke a TypeScript function from the HTML code embedded within a TypeScript component

In my pop-up window, there are 2 buttons: Update and Delete. I need to implement functionality so that when the Update button is clicked, the current pop-up should disappear and a new editable pop-up with the same fields should appear, along with two addit ...

The TypeScript compiler generates a blank JavaScript file within the WebStorm IDE

My introduction to TypeScript was an interesting experience. I decided to convert a simple JavaScript application, consisting of two files, into TypeScript. The first file, accounts.ts, contains the main code, while the second one, fiat.ts, is a support f ...

Functionality for communicating components is only operational on a single platform

I am looking to create a service that can notify my components when there are any changes to the 'idCustomer' property. These changes should trigger certain actions in different components. Currently, I am using console.log to check if the change ...

Using ES modules with TypeScript, Webpack, and Jasmine: A comprehensive guide

My Package Workflow For my personal projects, I have a consistent structure for the packages I create and reuse. In production, I write these packages in TypeScript and compile them to JavaScript using `tsc` before publishing them to npm. This allows me t ...

Adding a fresh element to an object array in TypeScript

How can we add a specific value to an array of objects using TypeScript? I am looking to insert the value 1993 into each "annualRentCurrent" property in the sample object. Any suggestions on how to achieve this in TypeScript or Angular? Thank you! #Data ...

Can a Python script be executed asynchronously in a TypeScript fashion?

Currently, I have a Python script that sends an http request to a microservice, and the request typically takes 3 seconds to complete. Here is a summary of my Python script: def main(): response = request_to_MS(url) # This part of the process doesn& ...

Examining Axios HttpService piping through a NestJS middleware in a unit test

A middleware function retrieves a JSON document from a microservice endpoint and appends it to the request. The good path test is successful, but I'm struggling to make the bad path test throw a ForbiddenException and stop it from invoking next(). W ...

Trouble with expanding multiple rows in an Angular nested mat table functionality

I recently built a nested mat-table grid using Angular Material. However, I am facing an issue where only one row can be expanded at a time. I am looking for a solution to allow multiple rows to be expanded simultaneously without automatically collapsing t ...

Issue: Inadequate parameters have been supplied for the localized pathname (getPathname()). This problem arose after the inclusion of "next-intl/routing" module

After implementing the config file and replacing : Navigation File import { createLocalizedPathnamesNavigation, Pathnames } from 'next-intl/navigation'; With : Config File import {Pathnames, LocalePrefix} from 'next-intl/routing';} ...

Navigating to a different page in Ionic 2 upon app initialization

Is there a way to automatically redirect the page to the home page instead of displaying the login page if there is already a token stored in localStorage? I currently have the following code in the constructor() of app.component.ts, but it still display ...

What is the method for determining the type of search results returned by Algolia?

My connection between firestore and algoliasearch is working well. I am implementing it with the help of typescript in nextjs. I am attempting to fetch the results using the following code snippet products = index.search(name).then(({hits}) => { ret ...