TypeScript: Despite declaring specific types, generic functions still treat parameters as "any"

When using TypeScript 4.4.3, I am looking to specify the types of function parameters for a function that returns a generic. However, TypeScript seems to be treating the parameters as any when working with functions that involve generics.

Here's a simplified example that demonstrates this issue: if this code is pasted into the TS Playground, a warning will show up regarding the input parameter being of type "any"...

type GenericResult = <T>(input: number) => T
export const returnWhatever: GenericResult = <T>(input) => <T> input

Should the parameter's type declaration in the first line be disregarded?

My real-world scenario is as follows...

import type { QueryResult } from 'pg'
import pg from 'pg'

const pgNativePool = new pg.native.Pool({
  max: 10,
  connectionString: import.meta.env.VITE_DATABASE_URL,
  ssl: {
    rejectUnauthorized: false
  }
})

type AcceptableParams = number | string | boolean

type PostgresQueryResult = (sql: string, params?: AcceptableParams[]) => Promise<QueryResult<any>>
const query: PostgresQueryResult = (sql, params?) => pgNativePool.query(sql, params)

type GetOneResult = <T>(sql: string, id: number | string) => Promise<T>
const getOne: GetOneResult = async <T>(sql, id) => {
  const { rows } = await query(sql, [id])
  return <T> rows[0]
}

const user = await getOne<User>('SELECT * FROM users WHERE id = $1;', 33)
// returns a User

In the above example, the sql parameter should always be a string, while id can be either a number or a string.

Even though the return value of the function is determined when the function is called, it would be beneficial to be able to type the parameters of the function since that information is known.

Is this achievable?

Answer №1

Here's an updated example that reflects our discussion:

type GenericResult = <T = number>(input: T) => T;
export const returnWhatever: GenericResult = (input) => input;

returnWhatever<string>("1");
returnWhatever(1);
returnWhatever<number>("1"); // this will generate an error

Typescript can become confused because it might think you are explicitly overriding the types for the function arguments. It seems like you may have added these type specifications because you wanted to redefine the generic, but this is not necessary since you have already defined it in the arrow function type. The following should suffice:

type GetManyResults = <T>(sql: string) => Promise<{ body: T[]; }>
export const getMany: GetManyResults = async (sql) => {
  const { rows } = await query(sql)
  return {
    body: rows
  }
}

https://i.sstatic.net/MCu5n.png

https://i.sstatic.net/Uc9Gx.png

Answer №2

According to the guidance from Microsoft, it is recommended to explicitly specify the parameter types in the generic function's definition, as demonstrated in the getOne function below. Although it may seem redundant to define the parameter types twice, it is advised to follow this approach.

import type { QueryResult } from 'pg'
import pg from 'pg'

const pgNativePool = new pg.native.Pool({
  max: 10,
  connectionString: import.meta.env.VITE_DATABASE_URL,
  ssl: {
    rejectUnauthorized: false
  }
})

type AcceptableParams = number | string | boolean

type PostgresQueryResult = (sql: string, params?: AcceptableParams[]) => Promise<QueryResult<any>>
const query: PostgresQueryResult = (sql, params?) => pgNativePool.query(sql, params)

type GetOneResult = <T>(sql: string, id: number | string) => Promise<T>
const getOne: GetOneResult = async <T>(sql: string, id: number | string) => {
  const { rows } = await query(sql, [id])
  return <T> rows[0]
}

const user = await getOne<User>('SELECT * FROM users WHERE id = $1;', 33)
// returns a User

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

Utilizing Angular's ngx-bootstrap date range picker for a customized date range filtering system

Currently, I am incorporating ngx-bootstrap's datepicker functionality and utilizing the date range picker. This feature allows users to choose a start and end date. After selecting these dates, my goal is to filter the array(content) based on whethe ...

When comparing TypeScript index signatures to Record<Keys, Type> return type, the focus is on handling objects with unspecified properties

I have a function called getQueryParams that takes a string as input and returns an object with unknown properties: function getQueryParams(s) { if (!s || typeof s !== 'string' || s.length < 2) { return {} } return s .substr(1) ...

Comparing the functions of useMemo and the combination of useEffect with useState

Is there a benefit in utilizing the useMemo hook instead of using a combination of useEffect and useState for a complex function call? Here are two custom hooks that seem to function similarly, with the only difference being that useMemo initially returns ...

Just starting out with TypeScript and running into the error: "Cannot assign type 'null' to type 'User[]'"

Why is TypeScript giving me an error message here? const dispatch = useAppDispatch(); useEffect(() => { onAuthStateChanged(auth, (user) => { dispatch(getUser(user)); }); }, [dispatch]); Error: Argument of type 'User | nul ...

Incorporate a typescript library into your Angular application

Recently, I added a text editor called Jodit to my angular application and faced some challenges in integrating it smoothly. The steps I followed were: npm install --save jodit Inserted "node_modules/jodit/build/jodit.min.js" in angular.json's bui ...

I encountered an error with Firebase when attempting to run functions on my local machine

Encountering a Firebase error when running the function locally using emulator in CLI $ firebase emulators:start --only functions Initiating emulators: ["functions"] functions: Using node@8 from host. functions: Emulator started at http://localhost:50 ...

Typescript: Implementing the 'types' property in a function returned from React.forwardRef

I'm exploring the option of adding extra properties to the return type of React's forwardRef function. Specifically, I want to include the types property. Although my current implementation is functional, given my limited experience with TypeScri ...

Troubleshooting a Missing Call Display Issue in Angular2 API

Greetings, I am a new web developer and I have been tasked with creating a prototype Inventory Page using Angular2. Please bear with me as my code may not be perfect. In the snippet below, you'll notice that we are calling our base back-end API (&apo ...

Is there a method in typescript to guarantee that a function's return type covers all possibilities?

In the case of having a constant enum like: enum Color { RED, GREEN, BLUE, } A common approach is to create a helper function accompanied by a switch statement, as shown below: function assertNever(x: never): never { throw new Error(`Unexpecte ...

What is the best way to combine the attributes of multiple objects within a union type?

I have a clearly defined schema type Schema = { a: { a: 1 } b: { b: 2 } } I am in need of a function that can generate objects that adhere to multiple schemas. function createObject<K extends keyof Schema>(schema: Array<K>, obj: Sche ...

Solving Typing Problems in React TypeScript with "mui-icons" Props

I have a small compost project with only an App.JSX file that is functioning perfectly, but now I need to convert it to App.TSX. After changing the extension to .TSX, I encountered two errors that I'm unsure how to resolve. function MyComponentWithI ...

Setting a default check on a checkbox within an ngFor loop in Angular 2

I'm attempting to initialize a default value as checked for a checkbox within my ngFor loop. Here is an array of checkbox items I am working with: tags = [{ name: 'Empathetic', checked: false }, { name: 'Smart money', che ...

Encountering Duplicate Identifier Error while working on Angular 2 Typescript in Visual Studio Code

Currently attempting to configure a component in Angular 2 with Typescript using Visual Studio Code on Mac. Encounter the following errors when trying the code below: duplicate identifier 'Component'. and Duplicate identifier' DashboardCompo ...

Building an Angular 4 app featuring a customized default landing page and URL parameters functionality

I am in the process of developing a web portal that will be embedded within an iFrame. Below is the code snippet I am using to set up the portal: Routes Configuration const routes: Routes = [ { path: '', redirectTo: '/dash ...

The parameter type `SetStateAction<EventDetails[] | undefined>` does not allow for assigning arguments

I am currently facing an issue in a ReactJS project where I am trying to update state that was initially set up like this: const [eventsData, setEventsData] = useState<EventDetails[]>(); Every time I try to update the state using setEventsData(obj), ...

Locating items within an array of objects using Angular 6 with TypeScript or JavaScript

I have the following code in my HTML template for my Angular 6 application. <div *ngIf ="conversation.participants[conversation.updatedBy.toLowerCase()].firstName">somedata </div> My goal is to search within the participants array object base ...

Set up a remapping for Istanbul to encompass every source file

Currently, I am setting up my Ionic 2 application with Angular 2 and TypeScript to produce code coverage reports for my test files. For unit testing and coverage report generation, I am utilizing Jasmine, Karma, and remap-istanbul. I came across an inform ...

Importing multiple features in Angular

[UPDATE]: Oops, my mind is a bit muddled from fatigue and I've mixed up two ideas which resulted in a rather meaningless question... Let's just blame it on the coffee! :P This may not be a pressing issue but more of a quest for knowledge... ...

What is the best way to pass a variable from a class and function to another component in an Angular application?

One of the components in my project is called flow.component.ts and here is a snippet of the code: var rsi_result: number[]; @Component({ selector: 'flow-home', templateUrl: './flow.component.html', styleUrls: ['./flow.comp ...

Is it possible for me to create an If statement that can verify the current index within a Map?

I have the following TypeScript code snippet: export default class SingleNews extends React.Component<INews, {}> { public render(): React.ReactElement<INews> { return ( <> {this.props.featured ...