Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve TypeScript compatibility #183

Open
felixh10r opened this issue Nov 21, 2023 · 2 comments
Open

Improve TypeScript compatibility #183

felixh10r opened this issue Nov 21, 2023 · 2 comments

Comments

@felixh10r
Copy link

Description

When using TypeScript > 5.1, there are numerous type check errors, most of which are related to Element implicitly has an 'any' type because expression of type 'string' can't be used to index type (…), but also Parameter (…) implicitly has an 'any' type.. My tsconfig.json already includes "skipLibCheck": true so there is not a lot I can do to work around besides pinning the TS version.

Steps to reproduce

Include typesense in a JS project and e.g. import type { SearchResponse } from "typesense/lib/Typesense/Documents";. Run tsc >= 5.2.

Expected Behavior

The project compiles without any issues.

Actual Behavior

There are a lot of typescript issues, probably depending on how much typesense interfaces are imported.

Metadata

Typesense Version:
1.7.2

OS:
MacOS 14.1.1
Node 21.2.0

@berenddeboer
Copy link

Yeah, just started using this, and it's unusable in typescript because of the errors. At least for me, Trying to figure out how I can ignore typechecks on this library only.

@CavalcanteLeo
Copy link

CavalcanteLeo commented Oct 11, 2024

I'm working a lot with multi-search, and I created a util class to generate the type and return the include_fields as well, I mainly created it because I wanted a model that also gets the include_fields, I didn't want to get all data for each request, neither to manually add the include_fields.

// modeGenerator.ts
type ConfigObject = {
  type: 'string' | 'number' | 'boolean' | 'array' | 'object';
  required: boolean;
  items?: ConfigObject;
  properties?: Record<string, ConfigObject>;
};

// Utility type to generate TypeScript types from the configuration
export type GenerateType<T> = {
  [K in keyof T]: T[K] extends { type: 'object'; properties: infer P }
    ? GenerateType<P> &
        (K extends keyof T
          ? T[K] extends { required: true }
            ? {}
            : undefined
          : undefined)
    : T[K] extends { type: 'string' }
      ? K extends keyof T
        ? T[K] extends { required: true }
          ? string
          : string | undefined
        : never
      : T[K] extends { type: 'number' }
        ? K extends keyof T
          ? T[K] extends { required: true }
            ? number
            : number | undefined
          : never
        : T[K] extends { type: 'boolean' }
          ? K extends keyof T
            ? T[K] extends { required: true }
              ? boolean
              : boolean | undefined
            : never
          : T[K] extends { type: 'array'; items: infer I }
            ? (I extends { type: 'number' }
                ? number[]
                : I extends { type: 'string' }
                  ? string[]
                  : I extends { type: 'boolean' }
                    ? boolean[]
                    : I extends { type: 'object'; properties: infer P }
                      ? GenerateType<P>[]
                      : never) &
                (K extends keyof T
                  ? T[K] extends { required: true }
                    ? {}
                    : undefined
                  : undefined)
            : never;
};

// Function to generate INCLUDE_FIELDS string
export function generateIncludeFields(
  config: Record<string, ConfigObject>,
  prefix: string = ''
): string {
  const fields: string[] = [];

  for (const key in config) {
    if (config.hasOwnProperty(key)) {
      const path = prefix ? `${prefix}.${key}` : key;
      if (config[key].type === 'object' && config[key].properties) {
        const nestedFields = generateIncludeFields(config[key].properties!, path);
        fields.push(...nestedFields.split(',')); 
      } else {
        fields.push(path);
      }
    }
  }
  const joinedFields = fields.join(',');
  return joinedFields;
}

// Function to create a model with type and INCLUDE_FIELDS
export function createModel<T extends Record<string, ConfigObject>>(config: T) {
  const includeFields = generateIncludeFields(config);
  return { object: config, includeFields };
}

and to use it:

import { createModel, GenerateType } from 'YOUR_DIR';

const { includeFields, object } = createModel({
  name: { type: 'string', required: true },
  logo: { type: 'string', required: false },
  id: { type: 'string', required: true },
  location: {
    type: 'array',
    required: true,
    items: { type: 'number', required: true },
  },
  someObj: {
    type: 'object',
    required: true,
    properties: {
      id: { type: 'string', required: true },
      name: { type: 'string', required: true },
    },
  }
});

type MyBeautifulType = GenerateType<typeof object>;

// check the include_fields:
console.log(includeFields)

It's not perfect, but it does the job for what I need. Improvements are welcome, as they would benefit me

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants