Вывод типа функции в качестве аргумента в Typescript - PullRequest
0 голосов
/ 18 октября 2019

У меня есть регистр, как показано на изображениях ниже - моя функция принимает параметры в форме объекта с одним из аргументов, являющимся transform функцией. Эта функция принимает аргумент response, тип которого правильно вычисляется для всей функции (первое и второе изображение), но мой компилятор машинописного текста неявно обрабатывает аргумент response как тип any (третье изображение). Я не могу понять, почему он не может правильно принять правильный response тип, который должен быть ApiResponse<NewsListApiResponseData> в этом случае?

First picture Second picture Third picture

Error:(27, 20) TS7006: Parameter 'response' implicitly has an 'any' type.

Вот мое перегруженное useAxios определение крючка для этого случая:

export function useAxios<ResponseData, TransformedData = false | null | ResponseData>(
    endpoint: string,
    options: {
        autoStart?: boolean;
        transform: (response: ApiResponse<ResponseData>) => TransformedData;
        onResolve?: (data: TransformedData) => void;
        onReject?: (error: Error) => void;
    } & AxiosHookRequestConfig
): {
    loading: boolean;
    canceled: boolean;
    error?: Error;
    response?: AxiosResponse<ApiResponse<ResponseData>>;
    data?: TransformedData;
    request: (config?: AxiosHookRequestConfig) => Promise<TransformedData>;
    cancel: (reason?: string) => void;
};

Редактировать:Добавлено AxiosHookRequestConfig определение.

export interface AxiosHookRequestConfig extends Omit<AxiosRequestConfig, 'url' | 'cancelToken'> {
    page?: number;
    lang?: string;
    cache?: boolean | string;
}

export interface AxiosRequestConfig {
  url?: string;
  method?: Method;
  baseURL?: string;
  transformRequest?: AxiosTransformer | AxiosTransformer[];
  transformResponse?: AxiosTransformer | AxiosTransformer[];
  headers?: any;
  params?: any;
  paramsSerializer?: (params: any) => string;
  data?: any;
  timeout?: number;
  withCredentials?: boolean;
  adapter?: AxiosAdapter;
  auth?: AxiosBasicCredentials;
  responseType?: ResponseType;
  xsrfCookieName?: string;
  xsrfHeaderName?: string;
  onUploadProgress?: (progressEvent: any) => void;
  onDownloadProgress?: (progressEvent: any) => void;
  maxContentLength?: number;
  validateStatus?: (status: number) => boolean;
  maxRedirects?: number;
  socketPath?: string | null;
  httpAgent?: any;
  httpsAgent?: any;
  proxy?: AxiosProxyConfig | false;
  cancelToken?: CancelToken;
}

Edit2: Пример

1 Ответ

1 голос
/ 18 октября 2019

Давайте посмотрим, что я бы рассмотрел суть этой проблемы:

declare function foo<T>(x: { prop: T }): void;
declare function foo<T>(x: { prop: T, func: (x: T) => void }): void;
foo({ prop: "hey", func: x => x.length }); // error!
//   ┌─────────────────> ~
// Parameter 'x' implicitly has an 'any' type.

Ситуация такая же, как и в этой сообщенной проблеме , помеченной как "работающая по назначению",Проблема в том, что вызов соответствует первой сигнатуре перегрузки, и поэтому компилятор не может понять, как определить тип x.

Как совпадают обе подписи? Ответ в том, что типы объектов в TypeScript не точны . Если у вас есть интерфейс, такой как interface Foo {a: string}, и интерфейс interface Bar extends Foo {b: string}, то это факт, что каждый экземпляр Bar также является Foo. Это означает, что в общем случае тип, подобный {a: string}, совместим с любым объектом с дополнительными свойствами, если он имеет строковое свойство с именем a. Часто компилятор пытается помочь людям не совершать ошибок, предупреждая о лишних свойствах , но эти проверки здесь не выполняются, поскольку func относится к одному из типов, которые он проверяет. Не знаю почему, и, возможно, это ограничение дизайна или ошибка.

В любом случае, компилятор видит func как некоторый тип функции, но, поскольку он соответствует первой перегрузке, контекстная типизация для x не делает 'Это сработает, и вы получите эту «неявную любую» ошибку.


Есть несколько способов, чтобы продолжить здесь. Одним из них является переключение порядка перегрузок таким образом, чтобы первая перегрузка была более строгой. Сигнатурная перегрузочная подпись сверху предотвращает прохождение разрешения перегрузки. В общем, это хорошее правило: более конкретные перегрузки вверху, более общие перегрузки внизу:

declare function foo<T>(x: { prop: T, func: (x: T) => void }): void;
declare function foo<T>(x: { prop: T }): void;
foo({ prop: "hey", func: x => x.length }); // okay

, который выбирает первую перегрузку, и тип func известен, а xвыводится как string.


Другой способ продолжить - взять более общую перегрузку и изменить ее так, чтобы она фактически запрещала рассматриваемый вызов, делая func свойством, которое оно можетЭто не так:

declare function foo<T>(x: { prop: T, func?: never }): void;
declare function foo<T>(x: { prop: T, func: (x: T) => void }): void;
foo({ prop: "hey", func: x => x.length }); // okay

Это работает сейчас, потому что при первой перегрузке func является необязательным свойством, значение которого имеет тип never. Нет другого пути, кроме undefined, чтобы удовлетворить это, а такая функция, как x => x.length, конечно, нет. Таким образом, вызов пропускает первую перегрузку, выбирает вторую и выводит string для x.


Наконец, в случае, когда две рассматриваемые перегрузки так похожи, за исключениемВ данном случае я был бы склонен свести их в одну подпись и полностью забыть о перегрузках. Это может не соответствовать вашему варианту использования, но об этом следует помнить:

declare function foo<T>(x: { prop: T, func?: (x: T) => void }): void;
foo({ prop: "hey", func: x => x.length }); // okay

Теперь существует только одна подпись вызова, и func может присутствовать или отсутствовать.


Один из них, надеюсь, будет работать на вас. Я протестировал второй на вашем примере , для чего он стоит.

Хорошо, удачи!

Ссылка на код

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...