Typescript: почему он пытается согласовать тип аргумента с перегрузками всех функций? - PullRequest
1 голос
/ 10 июня 2019

Я ищу гуру машинописи, чтобы помочь мне с этим.Я пытаюсь реализовать некоторое монадическое поведение в Typescript.У меня есть тип данных, который может принимать 4 различных формы.Я хочу, чтобы у меня была базовая монадическая функция, такая как map и flatMap.

. Я изо всех сил старался, чтобы компилятор машинописного текста радовался моим типам, и я пробовал несколько способов (с классами, абстрактнымиклассы, простые типы и т.д ...) но это лучшее, что я придумал.

export namespace Data {
  export type Initial<T,E> = { kind: 'initial' }
  export type Loading<T,E> = { kind: 'loading' }
  export type Loaded<T,E> = { kind: 'loaded'; value: T }
  export type Failed<T, E> = { kind: 'failed'; error?: E }
  export type Data<T,E = any> = Failed<T,E> | Loaded<T,E> | Loading<T,E> | Initial<T,E>
  type Kind = Data<any,any>['kind']

  // Instantiations
  export function loadingOf<T=any,E=any>():Loading<T,E> { return {kind: 'loading'}}
  export function initialOf<T=any,E=any>():Initial<T,E> { return {kind: 'initial'}}
  export function loadedOf<T=any,E=any>(value: T):Loaded<T,E> { return {kind: 'loaded', value}}
  export function failedOf<T=any,E=any>(error?: E):Failed<T,E> { return {kind: 'failed', error}}

  // Type guards
  export function isFailed<T,E>( data: Data<T,E>): data is Failed<T,E> { return data.kind === 'failed'}
  export function isLoaded<T,E>( data: Data<T,E>): data is Loaded<T,E> { return data.kind === 'loaded'}
  export function isInitial<T,E>( data: Data<T,E>): data is Initial<T,E> { return data.kind === 'initial'}
  export function isLoading<T,E>( data: Data<T,E>): data is Loading<T,E> { return data.kind === 'loading'}

  // Map
  export function map<T,U,E>(fn: (t: T) => U, data: Loaded<T,E>): Loaded<U,E>;
  export function map<T,U,E>(fn: (t: T) => U, data: Failed<T,E>): Failed<U,E>;
  export function map<T,U,E>(fn: (t: T) => U, data: Loading<T,E>): Loading<U,E>;
  export function map<T,U,E>(fn: (t: T) => U, data: Initial<T,E>): Initial<U,E>;
  export function map<T,U,E>(fn: (t: T) => U, data: Data<T,E>) {
    if(isLoaded(data)) {
      return loadedOf(fn(data.value))
    }
    return data
  }
}

const a = Data.map(x => x, Data.loadedOf('hi'))
const b = Data.map(x => x, Data.loadingOf<string>())
const c = Data.map(x => x, Data.initialOf<string>())
const d = Data.map(x => x, Data.failedOf<string>())
const datas:Data.Data<string>[] = [a,b,c,d]
datas.map(data => Data.map(x=>x, data))

Это, конечно, неполно, потому что машинопись не в восторге от функции карты.

При проверке типа a, b, c, d это нормально.Но при попытке сделать это над массивом выдает такую ​​ошибку:

Argument of type 'Data<string, any>' is not assignable to parameter of type 'Initial<string, any>'.
  Type 'Loaded<string, any>' is not assignable to type 'Initial<string, any>'.
    Types of property 'kind' are incompatible.
      Type '"loaded"' is not assignable to type '"initial"'.ts(2345)

Если я поменяю местами порядок перегрузок, он просто изменит последнюю строку кода ошибки с initial на что-то другое.

Я не понимаю, почему он недоволен типами.Точно не подходит Data<T,E>?Почему он пытается подобрать тип аргумента, чтобы он соответствовал всем типам аргументов возможных перегрузок?

IF Я удаляю подписи с подтипами и сохраняю только самый общий, после чего перестает жаловаться НО это сделает типы свободными.Любой подтип будет приведен к более свободному типу Data<T,E>, что мне не нужно.

1 Ответ

0 голосов
/ 10 июня 2019

Подпись реализации не участвует в разрешении перегрузки, как упоминалось в последнем параграфе на этой странице .

Если вам нужна универсальная версия map, если реализации с общей сигнатурой недостаточно, вы должны добавить ее явно, как отдельную перегрузку только для объявления:

export function map<T,U,E>(fn: (t: T) => U, data: Data<T,E>): Data<U, E>;
export function map<T,U,E>(fn: (t: T) => U, data: Data<T,E>) {
    // implementation here....
}
...