Focusing in on a particular category of items based on a specific characteristic

I can't seem to understand why typescript has an issue with the code below. The errors from the compiler are detailed in the comments within the code.

const FOO = Symbol();

function bar<T>(param: T) {
  if (param !== null && typeof param === "object" && FOO in param) {
    // Element implicitly has an 'any' type because expression of type 'unique symbol' can't be used to index type 'unknown'.
    // Property '[FOO]' does not exist on type 'unknown'.ts(7053)
    const foo = param[FOO];
    console.log(foo);
  }
}

function bar2<T>(param: T) {
  if (param !== null && typeof param === "object" && "FOO" in param) {
    // Element implicitly has an 'any' type because expression of type '"FOO"' can't be used to index type 'unknown'.
    // Property 'FOO' does not exist on type 'unknown'.ts(7053)
    const foo = param["FOO"];
    console.log(foo);
  }

Don't you think the if statement should narrow down the type sufficiently?

Answer №1

How do you feel about this approach? It may not be the most optimal solution, but it gets the job done... Try out in Playground

const BAR = Symbol();
function foo<T>(that: T) {
  if (that !== null && typeof that === "object" && hasProperty(that, BAR)) {
    const bar = that[BAR];
    console.log(bar);
  }
}

function foo2<T>(that: T) {
  if (that !== null && typeof that === "object" && hasProperty(that, "BAR")) {
    const bar = that["BAR"];
    console.log(bar);
  }
}

function hasProperty<P extends keyof any>(obj: any, prop: P): obj is { [_ in P]: unknown } {
    return prop in obj;
}

Answer №2

When your generic function lacks constraints, TypeScript is unable to determine the type T. As a result, it raises an error. TypeScript performs static type analysis rather than runtime analysis, so your if statement does not fit into the former category.

The strength of TypeScript lies in its explicitness. Therefore, if you already know that the attribute you will pass to the function (that) has a specific key (BAR), you should declare it in advance.

If you prefer to continue using generics, you can achieve this by:

const BAR = 'BAR';

interface Barable {
  [BAR]: any,
}

function foo<T extends Barable>(that: T) {
  if (that !== null && typeof that === "object" && BAR in that) {
    const bar = that[BAR];
    console.log(bar);
  }
}

/*
string alternative:
 
interface Barable {
    BAR: any,
}

.. and then your string function from above

*/

Note that TypeScript does not support interfaces with symbols as keys.


Additional Considerations

I'm unsure why you are using a generic in this scenario. If you know that you will be passing an object to this function and have some understanding of its structure, you could simply opt for using an interface instead.

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

Mapped types: Specify mandatory properties depending on whether an array of identical objects includes a specific string value

Can an object property be set to required or optional based on the presence of a specific string in an array within the same object? type Operator = "A" | "B" type SomeStruct = { operators: Operator[]; someProp: string; // this should be ...

Is it possible to utilize the inline/hardcoded type declared in the component.d.ts file for reuse

Is there a way to pass props selectively to the library component? The library has hardcoded multiple values in an inline type. If my code needs to automatically update with any new additions to the library-defined type, can I reuse those inline values r ...

What is the process for creating a Deep Copy of an object in Angular?

I have a specific entity class defined as follows: export class Contact { id: number; firstName: string; lastName: string; constructor(id?, firstName?, lastName?) { this.id = id; this.firstName = firstName; this.lastName = lastName; ...

What are the conditions for Jasmine's .toHaveBeenCalledWith to match the parameters?

I'm working on an Angular service along with its Jasmine test. The test is calling f1() and spying on f2(). The function f2 takes a variable v2 and updates it by setting field 'a' to 3. I expected the function f2 to be called with v2 (as def ...

An HTML table featuring rows of input boxes that collapse when the default value is not filled in

My table is populated with dynamic rows of input boxes, some of which may have a default value while others return an empty string ''. This causes the table to collapse on those inputs. <tr *ngFor="let d of displayData"> < ...

The application is unable to load due to a console error showing that the endpoint is unreachable

Recently, I made an upgrade from Angular 5 to 7 while still keeping rxjs-compat in place. Initially, the application was running smoothly with no issues. However, we eventually decided to remove rxjs-compat and make the necessary changes. This is when we e ...

Angular is putting the page on ice - all clicks are officially off limits

When making an HTTP request to the backend in my project, I need the ability to sort of "freeze" the screen until the request is complete. Specifically, I want to prevent users from being able to interact with certain elements on the page, such as a butt ...

When using ngx-slider in Angular, it unexpectedly fires off when scrolling on mobile devices

I am currently developing a survey application that utilizes ngx-sliders. However, I have encountered an issue on mobile devices where users unintentionally trigger the slider while scrolling through rows of slider questions, resulting in unintended change ...

The process of setting up a dynamic cursor in ReactFlow with Liveblocks for precise zooming, panning, and compatibility with various screen resolutions

The challenge lies in accurately representing the live cursor's position on other users' screens, which is affected by differences in screen resolutions, zoom levels, and ReactFlow element positioning as a result of individual user interactions. ...

Is there a way to get interpolation working outside of onInit?

In one component, I have set up a functionality to subscribe to an HTTP GET request from a service and store the response in a variable. The service contains a Subject as an observable so that it can be subscribed to in another component. However, while I ...

Using useRef as a prop in React with TypeScript

I am currently experimenting with TypeScript and encountering an issue when trying to use useRef in a custom element specifically when passing it as a prop I have attempted the following: import React from "react"; export interface InputProps extends ...

What is the best way to determine the type of a static property in a TypeScript class?

I have a utility class containing various static methods: export default class MyHelper { private constructor() {} private static privateMethod() {} public static publicHelperMethod() {} } In my React component, I am using the publicHelperMet ...

Achieving a similar functionality to Spring Security ACL in a Node.js AWS Lambda serverless environment

I am tackling a javascript challenge that has me stumped. Specifically, I am trying to figure out how to implement fine-grained authorization using an AWS serverless approach. In Spring security ACL, users can be banned from specific tasks at the instanc ...

Can you explain the variance between the (Record<string, unknown>) and object type?

Can you explain the distinction between type Record<string, unkown> and type object? Create a generic DeepReadonly<T> which ensures that every parameter of an object - and its nested objects recursively - is readonly. Here's the type I c ...

Utilizing Class Types in Generics with TypeScript

Struggling to implement a factory method based on the example from the documentation on Using Class Types in Generics, but hitting roadblocks. Here's a simplified version of what I'm attempting: class Animal { legCount = 4; constructor( ...

Exciting New Feature in WebStorm 2016.3: TypeScript Tooltips Inspired by VS Code!

One of the great features of Visual Studio Code is its excellent support for TypeScript, such as type inference displayed in tooltips. However, by default in WebStorm, only Console/Errors are visible in the tool window when hovering over a function without ...

What could have caused these errors, since they never made an appearance?

'Link' component cannot be utilized within JSX. The type 'ForwardRefExoticComponent<LinkProps & RefAttributes<HTMLAnchorElement>>' is not a valid element for JSX. The type 'ForwardRefExoticComponent<LinkPro ...

Can we guarantee the uniqueness of a function parameter value during compilation?

I have a set of static identifiers that I want to use to tag function calls. Instead of simply passing the identifiers as arguments, I would like to ensure that each identifier is unique and throws an error if the same identifier is passed more than once: ...

Encountering a sign-in issue with credentials in next-auth. The credential authorization process is resulting in a

I am currently facing an issue with deploying my Next.js project on Vercel. While the login functionality works perfectly in a development environment, I encounter difficulties when trying to sign in with credentials in Production, receiving a 401 error st ...

Troubleshooting Missing Exports from Local Modules in React Vite (Transitioning from Create React App)

I have encountered an issue while trying to import exported members from local nodejs packages in my project. Everything was working fine with the standard CRA webpack setup, but after switching to Vite, I started getting the following error: Unca ...