Моделирование типов с использованием TypeScript и fp-ts и ошибок при использовании Either - PullRequest
0 голосов
/ 05 февраля 2019

Я пытаюсь использовать TypeScript и fp-ts, чтобы помочиться в моделировании предметной логики с типами, и я столкнулся с этой проблемой:

import { left, right, Either } from "fp-ts/lib/Either";

type MyType = {
  id: string,
  isValid: boolean,
}

type MyValidType = {
  id: string,
  isValid: true,
}

type CreateMyValidType = (t: MyType) => Either<Error, MyValidType>

// Compile error!
const createMyValidType: CreateMyValidType = t => {
  switch (t.isValid) {
    case true:
      return right({
        id: "test",
        isValid: true
      })
    default:
      return left(new Error())
  }
}

Компилятор кричит на меня, потому что:Type '(t: MyType) => Either<Error, { id: string; isValid: boolean; }>' is not assignable to type 'Either<Error, CreateMyValidType>'.

Если я удаляю Either и просто возвращаю тип суммы Error | MyValidType, тогда все нормально.

type CreateMyValidType = (t: MyType) => Error | MyValidType

// This compiles
const createMyValidType: CreateMyValidType = t => {
  switch (t.isValid) {
    case true:
      return {
        id: "test",
        isValid: true
      }
    default:
      return new Error()
  }
}

Кажется, что невозможно определить правильный тип, когда он внутриEither!

Я нашел способ избежать проблемы, указав типы при вызове right, но я не до конца понимаю последствия, поэтому не знаю, является ли это плохой идеей:

return right<Error, MyType2>({
  id: "test",
  isValid: true,
});

Как правильно решить эту проблему и собрать ее?Спасибо!

1 Ответ

0 голосов
/ 23 апреля 2019

Короткий ответ

Работает, как и ожидалось, с TS> = 3.4

Чуть более длинный ответ

Asвы, возможно, уже заметили, TypeScript не очень хорош в выводе в целом.В своем примере кода вы предоставляете аннотацию для возвращаемого типа функции Either<Error, MyValidType>, чтобы TS мог попытаться объединить все ветви в ожидаемый возвращаемый тип: без этой явной аннотации результаты были бы еще хуже.

Даже с ручной аннотацией типа TS до 3.4 будет «ленивым» и попытается разрешить все параметры универсального типа, объявленные функциями left и right (оба имеют L и R каквведите параметры) на месте, без «ожидания», чтобы получить лучшие знания, прежде чем принять этот выбор.Таким образом, было бы выведено Error как L для случая default и { id: string, isValid: boolean } как R для случая true.Проблема в том, что MyValidType требует, чтобы isValid было буквальным true (более конкретным, чем boolean), и поэтому в конечном итоге происходит сбой с

Type '{ id: string; isValid: boolean; }' is not assignable to type 'MyValidType'.
  Types of property 'isValid' are incompatible.
    Type 'boolean' is not assignable to type 'true'.

С TS> = 3.4R остается "нерешенным" до тех пор, пока в процессе, когда TS на самом деле знает ожидаемый (аннотированный) тип возвращаемого значения createMyValidType и правильно видит литеральный объект как назначаемый объявленному типу возвращаемого значения.

Вы можете прочитать больше об этом улучшении в официальном журнале изменений на https://github.com/Microsoft/TypeScript/wiki/What%27s-new-in-TypeScript#higher-order-type-inference-from-generic-functions

Примечание 1

Эта проблема на самом деле не связана с fp-ts, как с любымОбобщенная функция: аналогичная проблема возникает до 3.4:

declare function identity<T>(t: T): T;

function f(): { foo: 'foo' } {
  return identity({ foo: 'foo' });
}
// Type '{ foo: string; }' is not assignable to type '{ foo: "foo"; }'.
//   Types of property 'foo' are incompatible.
//     Type 'string' is not assignable to type '"foo"'.

Примечание 2

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

const foo = 'foo' // Type: "foo"

const fooObj = { foo: 'foo' } // Type: { foo: string }

Это "безопасное" значение по умолчанию с учетом изменчивости JS.Это поведение можно изменить с помощью «const подтверждений»:

const fooObj = { foo: 'foo' } as const // Type: { readonly foo: "foo" }

Это еще одно дополнение в 3.4 (см. https://github.com/Microsoft/TypeScript/wiki/What%27s-new-in-TypeScript#const-assertions),, которое не является строго необходимым в вашем примере из-за возвратавведите аннотацию в createMyValidType.

...