Support for utility types

This issue has been tracked since 2022-10-26.

Hi @Newbie012 , hope you are well! 👋

Is your feature request related to a problem? Please describe.

It would be great to be able to use generic utility types such as Pick and Omit with existing types to be able to transform existing types instead of copying them throughout the codebase:

type User = {
  id: number;
  username: string;
}

export async function getUserByUsername(username: string) {
  const [user] = await sql<Pick<User, 'id'>[]>`
    SELECT
      id
    FROM
      users
    WHERE
      username = ${username}
  `;
  return user;
}

Right now, this leads to a confusing error message:

Query has incorrect type annotation.
	Expected: null[]`
	Actual: { id: number; }[]
eslint @ts-safeql/check-sql

Describe the solution you'd like

It would be great if the utility types worked

Describe alternatives you've considered

Workaround:

Use the literal object type instead of the utility type (decreasing maintainability):

export async function getUserByUsername(username: string) {
  const [user] = await sql<{ id: number }[]>`
    SELECT
      id
    FROM
      users
    WHERE
      username = ${username}
  `;
  return user;
}

Additional context

--

Newbie012 wrote this answer on 2022-10-26

It seems like TypeScript doesn't expose its API if the type is identical to another one, so I created a question in Stack Overflow

Until then, I'll write my own isIdentical function, it should initially support:

  1. Type Literal
  2. Type Reference
  3. Pick
  4. Omit
  5. Type Intersection

Once it's complete, I'll be able to close #93 as well.

karlhorky wrote this answer on 2022-10-27

@orta mentioned that there are some prior art for testing types here: effectivetypescript.com/2022/05/28/eslint-plugin-expect-type

Newbie012 wrote this answer on 2022-10-28

I did a little digging at the source code of eslint-plugin-expect-type (the one that is in the article), and the "magic-souce" is the following lines:

cosnt qi = languageService.getQuickInfoAtPosition(sourceFile.fileName, tad.getStart());
const actual = qi.displayParts.map((dp) => dp.text).join("");

The thing is, It's very accurate when you need to get the type definition of a reference.
It's so accurate that when you want to know what fn<Pick<Person, "name">>() is, it returns type Pick<T, K extends keyof T> = { [P in K]: T[P]; }.

But, if I assign that type to a new type (type PickPersonName = Pick<Person, "name">) it will work.

image

karlhorky wrote this answer on 2022-10-28

Interesting. Usually I assign to an alias already, but I guess at some point, some user will want it inline... if you can't find a quick way to do it now, maybe could be a documented "known issue" + open GitHub issue and then maybe implementation considered for later in the future?

Newbie012 wrote this answer on 2022-10-28

I think I'll go with the following approach:

  • If it's a type literal, keep the current behavior
  • If it's a type reference (that is not a utility type), use the language service.
  • If it's a utility type, go with the poor implementation I wrote above. It should cover most of the cases.
Newbie012 wrote this answer on 2022-10-29

Another problematic approach with getQuickInfoAtPosition:

type Starship = { id: number; name: string; captain_id: number | null; };
type Type = Pick<Starship, "captain_id" | "id"> & { x: number; }

function fn<T>() {};
fn<Type>();
   // ?^ type Type = Pick<Starship, "captain_id" | "id"> & { x: number; }
karlhorky wrote this answer on 2022-10-29
  1. Does it matter here that ? comes before ^ in this comment? In the other example, the ^ was before the ?
  2. Does the _ "type resolver" type trick used in Yup (or anything similar) help here?
Newbie012 wrote this answer on 2022-10-29
  1. Typo, should be ^?
  2. It's impossible to look for a type that doesn't exist in the file (physically).
Newbie012 wrote this answer on 2022-10-30

I eventually found a better solution that solves all of the scenarios

karlhorky wrote this answer on 2022-10-30

Great! I'll try out the new changes in the release @ts-safeql/[email protected]!

What's the quick summary? (just for anyone else who is trying to do something similar)

I imagine it has to do with these getTypeProperties and toInlineLiteralTypeString functions exported in packages/eslint-plugin/src/utils/get-type-properties.ts?

Newbie012 wrote this answer on 2022-10-30

Correct. There's no quick way of doing this. It depends on the ts.TypeNode kind

karlhorky wrote this answer on 2022-10-31

@ts-safeql/[email protected] works great, thanks! 🙌

More Details About Repo
Owner Name ts-safeql
Repo Name safeql
Full Name ts-safeql/safeql
Language TypeScript
Created Date 2022-09-08
Updated Date 2023-03-16
Star Count 795
Watcher Count 5
Fork Count 14
Issue Count 7

YOU MAY BE INTERESTED

Issue Title Created Date Updated Date