Can we specify a more specific type for an argument in Typescript based on another argument in a function?

I'm in the process of developing a compact DSL for filter operations and crafting helper methods to support it.

Suppose I have a function

const equals = (left, right) => {}

This function needs to be typed so that the left value is a field within an object, while the right is a value of the same type as that object.

If I write

const equals = <T> (left: keyof T, right: T[keyof T]) => {}
, it almost works but the type of right is limited to the types available on T, rather than just the type of left.

The desired functionality can be achieved with the following:

const equals = <T, F extends keyof T>(left: F, right: T[F])

However, this setup requires two generic parameters, disrupting the type inference flow for the surrounding code. Ideally, I would like to infer the type of the second parameter based on the first parameter. Is there a way to accomplish this?

Thank you

Answer №1

Here is a potential solution to your query. Consider transferring some of the type validation logic to smart constructors for better organization.

// model
type PickPropertiesOfType<T, A> = Pick<
  A,
  {
    [K in keyof A]: A[K] extends T ? K : never;
  }[keyof A]
>;

export type Filter<A> =
  | { kind: "Equals"; field: string & keyof A; value: A[string & keyof A] }
  | { kind: "EqualsField"; field: string & keyof A; value: string & keyof A };

export const equalsOtherField = <A, K extends string & keyof PickPropertiesOfType<A[K], A>>(
  field: string & K,
  value: string & keyof PickPropertiesOfType<A[K], A>
): Filter<A> => ({
  kind: "EqualsField",
  field,
  value: value,
});

export const equals = <A, K extends string & keyof A>(field: string & K, value: A[K]): Filter<A> => ({
  kind: "Equals",
  field,
  value: value,
});

// example
type ExampleEntity = {
    author: string;
    publisher: string;
    year: number;
}

const exampleEntity: ExampleEntity = {
    author: "Foo",
    publisher: "Foobar",
    year: 2000
}

const filterArr: Filter<ExampleEntity>[] = [
  equals("author", "Bar"),
  equalsOtherField("author", "publisher"),
];

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

Master Angular Autocompletion

Looking to add a filter autocomplete feature but unsure of how to implement it. Any assistance would be greatly appreciated! I've come across some examples, but they don't quite fit my needs. Here's the snippet of code: <mat-form-field c ...

Can dynamic string types be declared in Typescript?

Let's consider the following scenario: export enum EEnv { devint, qa1 }; export type TEnv = keyof typeof EEnv; export const env:Record<TEnv, {something:number}> = { devint: { something: 1, }, qa1: { something: 1, }, } Now, I ai ...

Difficulties in Networking Requests Following Event Emitter Notification in an Angular Application

Within my Angular application, a network request is sent to retrieve filtered data based on user-selected filters. The function responsible for handling the filter values and executing the request is outlined as follows: public onFilterReceived(values) { ...

What is the reason for the inability of `Array<Value>, Value` to properly retrieve the type of the array value?

I am encountering an issue with the type declaration below: function eachr<Subject extends Array<Value>, Value>( subject: Subject, callback: ( this: Subject, value: Value, key: number, subject: Subject ...

Allowing the use of a string as a parameter in a Typescript constructor

Currently, I am utilizing TypeScript to create a constructor for a model within Angular. One of the attributes in the model is configured as an enum with specific string values. Everything functions well if an enum value is passed to the constructor. The i ...

Unable to impose a restriction on the number input field limit

My input field has a type of "number" with the min and max attributes applied to limit user input. However, I am facing an issue where users can still enter values beyond the set limit. How can I prevent users from entering values above the specified lim ...

Uh-oh! A circular dependency has been detected in the Dependency Injection for UserService. Let's untangle this web and fix the issue!

Encountering the following error: "ERROR Error: Uncaught (in promise): Error: NG0200: Circular dependency in DI detected for UserService." The auth.component.ts utilizes the UserService and User classes, while the user.service.ts only uses the User class. ...

Uncovering the perfect body proportions using Webpack and SystemJS

In the process of developing an Angular2 library that needs to work with both SystemJS and Webpack, I encountered a situation where I had to detect the height and width in pixels of the body tag to set dimensions for child tags. However, the behavior of An ...

The Console.log function first displays an Object, and then changes to display an Array containing a single object when clicked

When I utilize MobX to call an observable within my React component, something peculiar happens. Upon console logging the observable, initially it appears as an Object. However, upon clicking on it, the object transforms into an Array for that one particul ...

The Vue $refs Object is classified as 'unidentified' in nature

I'm encountering an issue while attempting to utilize $refs in my Vue 3 application. Each time I try, I receive the Typescript error stating that "Object is of type 'unknown'". I am uncertain about how to resolve this problem. Here's ...

Problems with the zoom functionality for images on canvas within Angular

Encountering a challenge with zooming in and out of an image displayed on canvas. The goal is to enable users to draw rectangles on the image, which is currently functioning well. However, implementing zoom functionality has presented the following issue: ...

Steps for utilizing a function from the parent component to initialize TinyMCE

I have implemented an uploading function in my parent component. As I set up tinymce, I connected the [init] property of my component to the loadConfig() function. <editor [(ngModel)]="data" [init]="loadConfig()"></editor> The loadConfig func ...

Display Module within Component using Angular 5

In the application I'm working on, I want to incorporate a variety of progress-loader-animations such as spinners or bars. To achieve this, I've developed a module with a component. Now, I'm trying to figure out how to display the module&ap ...

Angular Delight: Jaw-Dropping Animation

After setting up my first Angular project, I wanted to incorporate Angular Animations to bring life to my content as the user scrolls through the page. I aimed to not only have the content appear on scroll but also implement a staggering animation effect. ...

Is there a way to specify this component without it being nested within the parent element?

So I have this component nested within another one const selectColumn = useMemo<ColumnDef<Person>[]>( () => [ { id: "select", header: ({ table }) => ( <IndeterminateCheckbox {.. ...

Prisma DB is a versatile database that excels in handling m-n

In my database, I have implemented a structure with 3 tables: Member, Characters, and MemberCharacters. Each member can have multiple Characters, and each Character can be used by multiple Members. To handle this many-to-many relationship, I have utilized ...

Retrieving data from notifications without having to interact with them (using Firebase Cloud Messaging and React Native)

Currently, I am able to handle messages whether the app is open, minimized, or closed. However, how can I process a message if a user logs into the application without receiving a notification? useEffect(() => { messaging().setBackgroundMessageHa ...

Tips for validating and narrowing a type in TypeScript using isNull instead of isNonNullable

I want to implement a universal type guard for entities returned from an API. The goal is to handle any null | undefined values by throwing an HttpStatus.NOT_FOUND error. Initially, I tried the following approach: export const entityOr404 = <T>(entit ...

Using TypeScript to define callback functions within the Cordova.exec method

I'm encountering an issue with the TypeScript definition for Cordova. The codrova.d.ts file doesn't allow for any function arguments in the success-callback and error-callback. To better illustrate my problem, here's a small example: Here ...

Expanding a class in Typescript by adding a function with the same name but varying properties and types

class A { play(a: string): string{ return a; } } class B extends A { play(c: string, b: number): string{ return c + ' ' + b.toString(); } } let x = new A(); console.log(x.play('John')); let y = new B(); console.lo ...