TypeScript не выводит правильный тип

Для этого произведения:

Promise.resolve('kromid')
  .then(all(identity))
  .then(([a]) => a.splita);

TypeScript ничего не говорит о a.splita. Я ожидаю, что это не удастся:

Свойство splita не существует для типа string. Вы имели в виду "раскол"?

Вот остальной код:

function all<T1, Param>(a1: Res<Param, T1>): (p: Param) => Promise<[T1]>;
function all<T1, T2, Param>(a1: Res<Param, T1>, a2: Res<Param, T2>): (p: Param) => Promise<[T1, T2]>;
function all<T1, T2, T3, Param>(a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>): (p: Param) => Promise<[T1, T2, T3]>;
function all<T1, T2, T3, T4, Param>(a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>): (p: Param) => Promise<[T1, T2, T3, T4]>;
function all<T1, T2, T3, T4, T5, Param>(a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>, a5: Res<Param, T5>): (p: Param) => Promise<[T1, T2, T3, T4, T5]>;
function all<T1, T2, T3, T4, T5, T6, Param>(a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>, a5: Res<Param, T5>, a6: Res<Param, T6>): (p: Param) => Promise<[T1, T2, T3, T4, T5, T6]>;
function all<T1, T2, T3, T4, T5, T6, T7, Param>(a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>, a5: Res<Param, T5>, a6: Res<Param, T6>, a7: Res<Param, T7>): (p: Param) => Promise<[T1, T2, T3, T4, T5, T6, T7]>;
function all<T1, T2, T3, T4, T5, T6, T7, T8, Param>(a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>, a5: Res<Param, T5>, a6: Res<Param, T6>, a7: Res<Param, T7>, a8: Res<Param, T8>): (p: Param) => Promise<[T1, T2, T3, T4, T5, T6, T7, T8]>;
function all<T1, T2, T3, T4, T5, T6, T7, T8, T9, Param>(a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>, a5: Res<Param, T5>, a6: Res<Param, T6>, a7: Res<Param, T7>, a8: Res<Param, T8>, a9: Res<Param, T9>): (p: Param) => Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9]>;
function all<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, Param>(a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>, a5: Res<Param, T5>, a6: Res<Param, T6>, a7: Res<Param, T7>, a8: Res<Param, T8>, a9: Res<Param, T9>, a10: Res<Param, T10>): (p: Param) => Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]>;
function all<Param>(...values: Res<Param, any>[]): (p: Param) => Promise<any[]>;
function all<Param>(...values: Res<Param, any>[]): (p: Param) => Promise<any[]> {
  return param => Promise.all(values.map(obj => obj.apply ? obj(param) : obj));
}
type Res<I, O> = ((i: I) => O | Promise<O>) | O | Promise<O>


function identity<T>(a: T): T {
  return a;
}

Вы можете определить, в чем проблема?


person Daniel Birowsky Popeski    schedule 22.12.2017    source источник
comment
Я не могу воспроизвести это с помощью TypeScript 2.6.2, так какую версию вы используете?   -  person cartant    schedule 22.12.2017
comment
@cartant Я тоже не заметил отрицания в предложении. OP ожидает ошибки компиляции, которую он НЕ замечает. Я тоже не вижу ошибки.   -  person Igor Soloydenko    schedule 22.12.2017


Ответы (1)


Спасибо, что указали мне на проблему, которую я не совсем понял. Я немного поигрался с вашим кодом, и вот мои выводы. Вывод типа не работает с композицией функций в TypeScript:

function identity<T>(a: T): T { return a; }
function toString<T>(a: T): string { return JSON.stringify(a); }

Promise.resolve('kromid')
  .then(promiseValue => {
    const composedFn = all(identity, toString);
//        ^^^^^^^^^^
//        T type of `promiseValue` was not inferred properly,
//        and was replaces with `any`
    return composedFn(promiseValue);
  })
  .then(([identityResult, toStringResult]) => {

  })

Обратите внимание, что вывод типа не работает для функции (T) => T, но работает для неуниверсальной функции возвращаемого типа (T) => string. Дальше по дороге identityResult относится к типу any, а toStringResult - к правильному типу string.

введите здесь описание изображения

Я думаю, настоящая основная причина кроется в этой проблеме GitHub. Извините, мой ответ не очень полезен. Вывод типа - самая сложная для меня тема, поэтому я копаюсь в вашем примере.

Почесывая мой старый ответ

<удар>

Почему это происходит?

Думаю, ключ кроется в этой строчке:

  .then(all(identity))

Это эквивалентно

  .then(x => all(identity)(x))

а не

  .then(x => all(identity(x)))

Типы будут выводиться по-разному в двух случаях, и я думаю, вы ожидаете последней интерпретации вашего кода, в то время как получаете первую.

Как добиться ошибки, которую вы ожидали увидеть

Вариант 1. Если вы перепишете код так, чтобы all() получал string значение обещания и identity() в правильном порядке, вы увидите ошибку.

Promise.resolve('kromid')
  .then(text => all(text)(identity))
  .then(([a]) => a.splita);

В результате чего:

[ts] Свойство splita не существует для типа string. Вы имели в виду "раскол"?

введите здесь описание изображения  введите описание изображения здесь

Вариант 2. Измените порядок параметров перегруженной функции all(), чтобы она позволяла использовать ее так, как вы хотите:

function all<T1, Param>(p: Param): (a1: Res<Param, T1>) => Promise<[T1]>;
function all<T1, T2, Param>(p: Param): (a1: Res<Param, T1>, a2: Res<Param, T2>) => Promise<[T1, T2]>;
function all<T1, T2, T3, Param>(p: Param): (a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>) => Promise<[T1, T2, T3]>;
function all<T1, T2, T3, T4, Param>(p: Param): (a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>) => Promise<[T1, T2, T3, T4]>;
function all<T1, T2, T3, T4, T5, Param>(p: Param): (a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>, a5: Res<Param, T5>) => Promise<[T1, T2, T3, T4, T5]>;
function all<T1, T2, T3, T4, T5, T6, Param>(p: Param): (a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>, a5: Res<Param, T5>, a6: Res<Param, T6>) => Promise<[T1, T2, T3, T4, T5, T6]>;
function all<T1, T2, T3, T4, T5, T6, T7, Param>(p: Param): (a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>, a5: Res<Param, T5>, a6: Res<Param, T6>, a7: Res<Param, T7>) => Promise<[T1, T2, T3, T4, T5, T6, T7]>;
function all<T1, T2, T3, T4, T5, T6, T7, T8, Param>(p: Param): (a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>, a5: Res<Param, T5>, a6: Res<Param, T6>, a7: Res<Param, T7>, a8: Res<Param, T8>) => Promise<[T1, T2, T3, T4, T5, T6, T7, T8]>;
function all<T1, T2, T3, T4, T5, T6, T7, T8, T9, Param>(p: Param): (a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>, a5: Res<Param, T5>, a6: Res<Param, T6>, a7: Res<Param, T7>, a8: Res<Param, T8>, a9: Res<Param, T9>) => Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9]>;
function all<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, Param>(p: Param): (a1: Res<Param, T1>, a2: Res<Param, T2>, a3: Res<Param, T3>, a4: Res<Param, T4>, a5: Res<Param, T5>, a6: Res<Param, T6>, a7: Res<Param, T7>, a8: Res<Param, T8>, a9: Res<Param, T9>, a10: Res<Param, T10>) => Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]>;
function all<Param>(p: Param): (...values: Res<Param, any>[]) => Promise<any[]>;

function all<Param>(p: Param): (...values: Res<Param, any>[]) => Promise<any[]> {
  return values => Promise.all(values.map((obj: any) => obj.apply ? obj(p) : obj));
}

type Res<I, O> = ((i: I) => O | Promise<O>) | O | Promise<O>;

function identity<T>(a: T): T {
  return a;
}

Promise.resolve('kromid')
  .then(all(identity))
  .then(([a]) => a.splita);

Это даст тот же результат:

введите здесь описание изображения

Надеюсь, теперь мой ответ полный и правильный. :)

person Igor Soloydenko    schedule 22.12.2017
comment
О, ты даже не представляешь, как я на это надеялся. Но вы могли упустить из типов, которые я хочу, чтобы функция all могла получать несколько аргументов: all(identity, identity..), отсюда и перегрузка. К сожалению, при изменении порядка типов возникают две проблемы: тип сверху и количество аргументов в all. - person Daniel Birowsky Popeski; 22.12.2017
comment
Оцените ваши усилия, Игорь! - person Daniel Birowsky Popeski; 22.12.2017
comment
@Birowsky, теперь я думаю, у нас есть ответ на why, что это происходит, но не как с этим справиться. : / Я помечу этот вопрос звездочкой, если вы найдете способ заставить TS определять тип. - person Igor Soloydenko; 22.12.2017
comment
И я буду поддерживать усилия:} Жаль, что у нас до сих пор нет этой функции. Это должно было читаться как стихи, теперь это будет похоже на повседневную политику. - person Daniel Birowsky Popeski; 23.12.2017
comment
Что ж, хорошая новость в том, что они считают это ошибкой: github.com / Microsoft / TypeScript / issues / - person Daniel Birowsky Popeski; 23.12.2017
comment
@Birowsky Да, я откопал тот же пост, который вы комментировали вчера. Но я не видел твоего отчета об ошибке. Но я думаю, что мой ответ объясняет, что вы хотели выяснить (Can you spot where the problem is?) Теперь дело разработчиков TypeScript исправить это поведение. - person Igor Soloydenko; 23.12.2017