Ensuring Generics are Required in your Code

Can Generics be marked as mandatory in typescript?

function validateGenerics<Result, Variables>({
    foo,
    bar
  }: {
    foo: Result
    bar: Variables
  }) {
    console.log(foo, bar)
}

// Attempting to call the function without passing Generics should not work
validateGenerics({ foo: 'foo', bar: 'bar' })

// Calling the function with specified Generics should work as expected
validateGenerics<number, number>({ foo: 'foo', bar: 'bar' })

Playground

Edit

I have found a solution to my question and it works perfectly. I can illustrate why this feature is important through an example.

validateGenerics<UpdateUserMutation, UpdateUserMutationVariables>({
  mutation: UpdateUserDocument,
  variables: { id: user.id, user: formData },
  refetchQueries: ['GetUserList'],
  onSuccess: ({ data }) => history.push(`/users/${data.updateUser.id}`),
})

If I omit

<UpdateUserMutation, UpdateUserMutationVariables>
, I will not know what data represents in the onSuccess function. Additionally, I may forget to include specific variables in the variables object and the IDE integration might suffer :-)

Although I could manually type { data }, using Generics ensures that I never miss specifying the necessary variables for the function calls.

For those curious about my updated validateGenerics function:

function validateGenerics<
  Result = [Error, 'Please specify the Result type parameter'],
  Variables = [Error, 'Please specify the Variables type parameter'],
  R extends Result = Result,
  V extends Variables = Variables
>({
  mutation,
  variables,
  refetchQueries,
  onSuccess,
}: {
  mutation: DocumentNode
  variables: V
  refetchQueries: ((string | PureQueryOptions)[]) | (string | PureQueryOptions)[]
  onSuccess: (response: FetchResult<R, Record<string, any>, Record<string, any>>) => void
}) {
  client
    .mutate<R>({
      mutation,
      variables,
      refetchQueries,
    })
    .then(response => {
      setErrors({})
      onSuccess(response)
    })
    .catch(handleErrors)
}

Answer №1

Deliberately limiting type inference may not be ideal, but if one were inclined to do so, the following approach could be taken:

function onSubmit<
  Result = [Error, "Please specify the Result type parameter"],
  Variables = [Error, "Please specify the Variables type parameter"],
  R extends Result = Result,
  V extends Variables = Variables
>({
  foo,
  bar
}: {
  foo: R
  bar: V
}) {
  console.log(foo, bar)
}

The concept is to leverage default generic type parameters for more control over non-inferred type parameters and introduce additional type parameters to circumvent type inference in the compiler. The types Result and Variables cannot be deduced from the supplied values of the foo and bar properties since they are not explicitly mentioned in the type signature. Therefore, without manual specification, these types default to a peculiar tuple structure like [Error, "error message"]. This unconventional tuple serves as a workaround for TypeScript's lack of an "invalid" type. Alternatively, using never instead of the tuple might result in more obscure error messages.

In essence, foo will automatically infer R and bar will infer V, which must align with Result and

Variables</code correspondingly. Without specifying <code>Result
and
Variables</code, they default to an error type, causing <code>foo
and bar to also adhere to that error type, leading to errors:

onSubmit({ foo: 'foo', bar: 'bar' }) // error
//         ~~~         ~~~        
// 'string' not assignable to 'Error, "Please specify the Result type parameter"'
// 'string' not assignable to 'Error, "Please specify the Variables type parameter"'

If explicitly defining Result and Variables</code, then <code>R and V will conform to them by default, allowing the function to operate similarly to its previous version:

onSubmit<string>({ foo: 'foo', bar: 'bar' }) // error
//                             ~~~
// 'string' not assignable to 'Error, "Please specify the Variables type parameter"'

onSubmit<number, number>({ foo: 3, bar: 4 }) // okay

onSubmit<number, number>({ foo: 'foo', bar: 'bar' })  // error
//                         ~~~         ~~~
// 'string' not assignable to 'number' x2

This method may seem convoluted as the language typically promotes type inference. Nevertheless, it fulfills your specifications. Best of luck utilizing this solution!

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

The error message "Property 'DecalGeometry' is not found in the type 'typeof "..node_modules/@types/three/index"'."

Within my Angular6 application, I am utilizing 'three.js' and 'three-decal-geometry'. Here is a snippet of the necessary imports: import * as THREE from 'three'; import * as OBJLoader from 'three-obj-loader'; import ...

How can I target the initial last element within an *ngFor loop?

In my current project using Ionic, I am developing a personal application where I aim to implement an alphabetical header/divider for a list of games. The idea is to check the first letter of each game, and whenever it differs from the last game's ini ...

Here's a method to extract dates from today to the next 15 days and exclude weekends -Saturday and Sunday

Is there a way to generate an array of dates starting from today and spanning the next 15 days, excluding Saturdays and Sundays? For example, if today is 4/5/22, the desired array would look like ['4/5/22', '5/5/22', '6/5/22' ...

Is it possible to utilize Typescript and Browserify in tandem?

As I explore the compatibility of TypeScript and Browserify, one perplexing aspect stands out - they both utilize 'require' but for different purposes. TypeScript uses 'require' to import other TS modules, while Browserify employs it to ...

I anticipated receiving an array of objects from the API response, but instead, I am dealing with an array of Promises in my React app using TypeScript

Apologies if this question seems basic to you. I am still new to TypeScript and working with Promises. Despite searching on various platforms, I couldn't find a relevant solution. Can you assist me in receiving an array of objects instead of promises? ...

The parent class has not been specified

I am facing an issue with my parent class, HTTPConnection, which I intend to use as a network utility class in order to avoid redundant code. However, when attempting to utilize it, the file core.umd.js throws an error stating Uncaught ReferenceError: HTTP ...

Passing a Typescript object as a parameter in a function call

modifications: { babelSetup?: TransformationModifier<babel.Configuration>, } = {} While examining some code in a React project, I came across the above snippet that is passed as an argument to a function. As far as I can tell, the modifications p ...

An easy method to define argument types for every function type in TypeScript

How can I assign argument types to each function type in TypeScript? Each function has a parameter associated with it. [Example] type F1 = (arg: number) => void; type F2 = (arg: string) => void; const caller = (f: F1 | F2) => (n: number | strin ...

TypeScript class featuring a unique method that is not utilized in every instance

I have a TypeScript class that is managing ZMQ bus communication. Initially, I created a general class that can be instantiated multiple times. However, now I need to create instances with slight variations that do not perfectly fit the generic class I o ...

Unable to employ the .some() method with an array consisting of objects

I am currently attempting to determine whether my array of objects contains an attribute with a specific value. I wanted to use the array.some() method instead of a foreach loop, but I keep encountering an error: Error TS2345: Argument of type '(ele ...

Exploring how process.argv in NodeJS can be utilized within JavaScript code compiled by

I'm having trouble compiling a basic TypeScript file using webpack (with 'awesome-typescript-loader') that needs to access command line arguments. It seems like the compiled JavaScript is causing a problem by overriding the Node 'proce ...

Obtaining Prisma arguments by providing the table name as a string

Is there a way to retrieve the query arguments for a Prisma function by only passing the table name? Currently, I know I can obtain the table by providing the table name as a string in the following manner: function (tablename: string) { await prisma.[tab ...

Create a TypeScript type that represents an empty collection

I recently acquired some knowledge about TypeScript through a university course I took this month. Is it possible for this to represent an empty set? type emptySet=0&1; Whenever I attempt to assign this to any value (for example: boolean, number, st ...

Implement ExpressTS on vercel platform

I have recently developed an Express TypeScript project on GitHub and now I am attempting to deploy it to Vercel. The folder structure of the project is as follows, with 'dist' containing app.js and 'src' containing app.ts. dist dto mi ...

Combine iron-page and bind them together

Recently, I've started learning about Polymer and I want to bind together paper-tabs and iron-pages so that when a tab is clicked, the content loads dynamically. After going through the documentation, this is what I have tried: <app-toolbar> ...

Creating a higher order component (HOC) that utilizes both withRouter and connect functions in React

I am currently working with the following stack: <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="87f5e2e6e4f3c7b6b1a9b6b4a9b6">[email protected]</a> <a href="/cdn-cgi/l/email-protection" class="__cf_email__" dat ...

Appending an item to an array in TypeScript

I'm feeling lost. I'm attempting to insert new objects into an array in TypeScript, but I encountered an error. My interface includes a function, and I'm puzzled. Can anyone offer guidance? interface Videos{ title: string; descriptio ...

The variable is accessed prior to being assigned with the use of the hasOwnProperty method

Continuing my journey from JavaScript to TypeScript, I find myself faced with a code that used to function perfectly but is now causing issues. Despite searching for alternative solutions or different approaches, I am unable to resolve the problem. Snippe ...

Angular 2 interprets my JSON object as a function

My webapp retrieves data via Json and places it in objects for display. I have successfully implemented this process twice using services and classes. However, when I recently copied and pasted the old code with some modifications to redirect to the correc ...

Is the return type determined by the parameter type?

I need to create an interface that can handle different types of parameters from a third-party library, which will determine the return type. The return types could also be complex types or basic types like void or null. Here is a simple example demonstra ...