Accessing specific elements in Typescript 3.5 using indexes

I am looking to create a type-safe getter function that has the ability to return modified values while still being sound.

class Foo {
  a: number;
  b: boolean;
}

function getProp<K extends keyof Foo>(o: Foo, p: K): Foo[K] {
  switch (p) {
    case 'a':
      return 1;
    case 'b':
      return o[p];
  }
  return undefined;
}

In the provided code snippet, I intended for getProp to return Foo[K] in this scenario: const v = getProp(f, 'a') where v is of type number. However, an error is thrown when attempting to return 1 because it cannot be assigned to never.

It should be noted that this behavior worked in Typescript 3.4 thanks to "Fixes to unsound writes to indexed access types" ().

What would be the best approach to address this issue without resorting to using return 1 as any?

Answer №1

Indeed, there have been numerous reports regarding this issue ever since TypeScript 3.5 was released. The enhanced soundness of indexed accesses, as highlighted in the breaking change documented here, has resulted in catching real bugs but also triggering warnings on relatively safe code practices.

The root problem lies in the fact that generic type parameters extending unions do not undergo narrowing via control flow analysis (refer to microsoft/TypeScript#24085). This limitation prevents the compiler from inferring that if p is narrowed down to "a", then the type parameter K should be similarly narrowed to "a". The current language lacks certain functionalities that would facilitate this kind of inference securely.


As a temporary solution, workarounds and refactoring techniques can be employed. One potential workaround, suggested by @TitianCernicova-Dragomir in this answer, involves using a single-call-signature overload function to revert back to the behavior seen in TS3.4 or earlier. While this approach may introduce some unsafety, it counteracts the changes implemented in TS3.5. Other possible workarounds, such as type assertions or alternative unsound methods, may also prove effective.

In light of the limitations with generics and control flow narrowing, a refactoring strategy devoid of such expectations could prove beneficial depending on the scenario. Consider returning something recognizable to the compiler as a legitimate Foo[K] by accessing an object of type Foo with a key of type K.

function getProp<K extends keyof Foo>(o: Foo, p: K): Foo[K] {
  return { a: 1, b: o.b }[p];
}

Access Code Here

Answer №2

There seems to be no straightforward way to achieve type-safety in this scenario (but I am open to brilliant suggestions that could make me reconsider my answer). The issue lies in the fact that due to `p` being of type K, which is a union type, narrowing it down becomes challenging even if it extends a union. Additionally, assigning a concrete value to Foo[K] proves to be problematic as long as the generic parameter K remains unresolved.

A potential workaround, slightly more elegant than resorting to `as any`, involves utilizing a distinct implementation signature:


class Foo {
    a!: number;
    b!: boolean;
}

function getProp<K extends keyof Foo>(o: Foo, p: K): Foo[K] | undefined
function getProp(o: Foo, p: keyof Foo): Foo[keyof Foo] | undefined {
    switch (p) {
        case 'a':
            return 1;
        case 'b':
            return o[p];
    }
    return undefined;
}

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

Combining Multiple .ts Files into a Single File: A Simplified Application Structure with TypeScript 1.8

Currently, I am in the process of developing an Electron application and I have decided to implement TypeScript for this project. While TypeScript essentially boils down to JavaScript in the end, my familiarity with it makes the transition seamless. As of ...

What's the best way to process a string array using iteration in Typescript?

I have an array of colors: colors = ['red','blue','purple']; I would like to display the following message: colors in ('red','blue','purple') When I tried to achieve this using forEach metho ...

Issue with displaying data using a custom pure pipe and boolean in ngIf condition

For my current web project, I require a friendship/follow feature. There are two roles involved: admins and regular users. Regular users have the ability to follow other users, while admins do not possess this capability. When a user wishes to follow anot ...

What is the process for obtaining the feedback from a new StepFunctionsStartExecution operation using AWS CDK?

Task Explanation: Iterate through the children in Step Function 1 Forward each JSON object to Step Function 2 Upon completion of Step Function 2, a response is obtained from an external API Utilize this API response within Step Function 1 Visual Represen ...

Transforming the elements within an array of objects into individual key-value pairs and then adding them to a fresh array

Hello, I am diving into the world of Typescript/Javascript and encountering some challenges when it comes to manipulating arrays of data. Please bear with me as I navigate through my learning curve. Imagine having an array of objects structured like this: ...

What is the best way to bring in a service as a singleton class using System.js?

I have a unique Singleton-Class FooService that is loaded through a special import-map. My goal is to efficiently await its loading and then utilize it in different asynchronous functions as shown below: declare global { interface Window { System: Sy ...

The outcome of using Jest with seedrandom becomes uncertain if the source code undergoes changes, leading to test failures

Here is a small reproducible test case that I've put together: https://github.com/opyate/jest-seedrandom-testcase After experimenting with seedrandom, I noticed that it provides consistent randomness, which was validated by the test (running it multi ...

Issue with Type-Error when accessing theme using StyledComponents and TypeScript in Material-UI(React)

I attempted to access the theme within one of my styled components like this: const ToolbarPlaceholder = styled('div')((theme: any) => ({ minHeight: theme.mixins.toolbar.minHeight, })); This information was found in the documentation: htt ...

Transform JSON reply in JavaScript/Typescript/Angular

Looking for assistance with restructuring JSON data received from a server API for easier processing. You can find the input JSON file at assets/input-json.json within the stackblitz project: https://stackblitz.com/edit/angular-ivy-87qser?file=src/assets/ ...

The React-Typescript error message is stating that the module "react-router-dom" does not have the exported member "RouteComponentProps"

I encountered an issue with my project involving a login page and the usage of "RouteComponentProps". Unfortunately, I received the following error: Module '"react-router-dom"' has no exported member 'RouteComponentProps'. Upon attempt ...

Utilizing the useSearchParams() function to retrieve live data in Next.js

Is there anyone who has successfully migrated from the pages router to the app router in Next.js? I am facing an issue with dynamic data migration. In the pages router, dynamic data is retrieved on a page using useRouter().query, but in the app router, it ...

What is the most effective way to perform unit testing on the "present" function of an Ionic alert controller?

I decided to write a unit test for the alert present function that gets triggered after creating an alert. This is what my code looks like. it('should call attempt To call alert present', async () => { const alert = { header: 'P ...

The Angular2 view is failing to display updated data from a shared service

I've been struggling to show data from my shared service, but it's not displaying. Can someone please help me out? I've been stuck on this for the past few days. I've tried NgZone and ChangeDetectorRef, but they haven't worked for ...

What is the reason behind installing both Typescript and Javascript in Next.js?

After executing the command npx create-next-app --typescript --example with-tailwindcss my_project, my project ends up having this appearance: https://i.stack.imgur.com/yXEFK.png Is there a way to set up Next.js with Typescript and Tailwind CSS without i ...

Is there a way in TypeScript to prevent the modification of a class property and still trigger a runtime error if attempted?

I currently have the following code snippet: class Computed<Value> { _value: Value; get value(){ return this._value; } } When attempting to set the value property, TypeScript returns an error message: Cannot assign to value because it is ...

Utilizing a Custom Validator to Compare Two Values in a Dynamic FormArray in Angular 7

Within the "additionalForm" group, there is a formArray named "validations" that dynamically binds values to the validtionsField array. The validtionsField array contains three objects with two values that need to be compared: Min-length and Max-Length. F ...

Issue with accessing class property in events.subscribe in Ionic3

I am currently working on a class that listens for events. When the event is triggered, I need to add the data that accompanies it to an array and then display it. Here's what my class looks like: export class customClass { dataArray:Array<stri ...

Authentication checkpoint within an Angular application using a JWT cookie

After searching extensively, I was unable to find a question that directly addresses my specific concern. Currently, I am working on a project involving an angular application that utilizes jwt and http-only cookies for authentication. In order to authenti ...

Transferring data from the server to the client side in Next JS with the help of getInitialProps

I am currently developing an application using nextJS. Within server/index.ts, I have the following code: expressApp.get('/', (req: express.Request, res: express.Response) => { const parsedUrl = parse(req.url, true); const { query } = ...

Exploring the mechanics behind optional chaining, known as the Elvis operator, in TypeScript. How does this feature operate within the

Can someone explain the concept of optional chaining (Elvis operator) in TypeScript and how it can be used effectively? public static getName(user: IUser){ if(user.firstName != null && user.firstName != ""){ return user.firstName; } ...