#Need help with typing in refine

31 messages · Page 1 of 1 (latest)

polar umbra
#

I don't understand why response.data doesn't fit to TData

import { BaseRecord, DataProvider, GetListParams, GetListResponse } from '@refinedev/core';

import axios from 'axios';

class SearchResultItem implements BaseRecord {
  constructor(
    public id: string,
    public title: string,
    public date: string,
    public highlights: string[],
  ) {}
}

class Search implements DataProvider {
  private httpClient = axios.create({ baseURL: globalThis.SETTINGS.API_URL });

  async getList<TData extends BaseRecord = SearchResultItem>(
    params: GetListParams,
  ): Promise<GetListResponse<TData>> {
    const response = await this.httpClient.get<SearchResultItem[]>('/items', { params });
    return { data: response.data, total: response.data.length }; // response.data has a wrong type
  }
}

export default Search;

Error:

Type 'SearchResultItem[]' is not assignable to type 'TData[]'.
  Type 'SearchResultItem' is not assignable to type 'TData'.
    'SearchResultItem' is assignable to the constraint of type 'TData', but 'TData' could be instantiated with a different subtype of constraint 'BaseRecord'.ts(2322)

What am I doing wrong?

PS neural net suggest weird solution:

return { data: response.data as unknown as TData[], total: response.data.length };
vague hawk
#

because you aren't saying httpClient.get returns a TData[], you're saying it returns SearchResultItem[]

#

use the generic you have instead

#

don't use that as unknown as TData[], that's a horrible solution for this. don't trust generative ai in general.

polar umbra
#

Ok, but what if I want to get some data from the response before? I want to get SearchResultItem in this place

vague hawk
#

also:

  • remove the = SearchResultItem default, that messes with inference.
  • be aware that this pattern is unsafe:
#

!:thats-no%

hardy auroraBOT
#
tjjfvi#0
`!tjjfvi:thats-no-generic-its-a-type-cast`:

When you use a type parameter in the function, it should generally be inferrable from the arguments.

For example, this is a misuse of generics:

function getMeA<T>(): T {
   /* magic */
}

because getMeA<string>() and getMeA<number>() compile to the same code at runtime, there's so way to implement this function safely (other than always throwing); this is just a type cast in disguise. Instead of using a generic here, you should return unknown, and cast at the call site if necessary, to be clear it's an unsafe operation:

-function getMeA<T>(): T {
+function getMeA(): unknown {
    /* magic */
 }

-getMeA<number>()
+getMeA() as number

One exception to this rule is if you're returning a possibly-empty container of T. For example, these are all perfectly safe, even though the generic can't be inferred from the parameters:

function emptyArray<T>(): Array<T> {
  return []
}

function useRef<T>(): { current?: T } {
  return {}
}
vague hawk
polar umbra
# vague hawk then make `TData` extend that, or don't use a generic to begin with

It can be a problem. This is original DataProvider type:

export type BaseKey = string | number;
export type BaseRecord = {
  id?: BaseKey;
  [key: string]: any;
};

export type DataProvider = {
  getList: <TData extends BaseRecord = BaseRecord>(
    params: GetListParams,
  ) => Promise<GetListResponse<TData>>;
...

I can't extend TData from my class

vague hawk
#

ooh, yikes. that's not a great type. that generic should be on the type, not the function

polar umbra
#

@vague hawk So, what can you suggest me? Which solution will be best?

vague hawk
#

mm, not sure tbh. this is kinda a bad situation to be in

polar umbra
#

Sorry, i'm a new dev in js/ts

vague hawk
#

it's basically saying any DataProvider can provide any BaseRecord, which is a horrible api honestly

polar umbra
#

Oh, I see...

#

So, what about the NN solution? 😅
data: response.data as unknown as TData[]

vague hawk
#

that's also a bad solution because it's just lying

polar umbra
#

Yeah, i know...

vague hawk
#

you're just losing data

#

you'll end up casting on the other end and that makes it more prone to bugs

vague hawk
#

that will be a nightmare for your future self

polar umbra
#

😢
Looks like all solutions are bad. Maybe we can go from another side? What's the solution will be not so bad?

vague hawk
#

do you need to use DataProvider? honestly seems to me like you'd be better off without it

polar umbra
#

Anyway, thanks for your time

#

!resolved

vague hawk
#

ok so i think refine isn't meant to be used with axios, at least not to define types

#

you're supposed to do dataProvider.getList<T>(), for example, so not supposed to implement it to provide types

#

axios uses the same pattern, so by using both, you have to use the types weirdly twice