TypeScript: как использовать тип возвращаемого значения в качестве T из аргумента обратного вызова? - PullRequest
0 голосов
/ 26 февраля 2020

Цель:

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

Проблема:

кажется, что он указывает на неизвестный тип, как я могу исправить это?

function Test1<T>( msg, fn:(arg:T)=>void, fnTake:(any)=>T )
function Test1( msg, fn, fnTake ) {}

Test1( 'test', (arg) =>{ arg.ZZ }, m => { ZZ:123 } );
//             the 'arg' is unknown

код

Ответы [ 3 ]

2 голосов
/ 26 февраля 2020

Это ограничение дизайна TypeScript. Есть ряд проблем GitHub, поданных по этому поводу; microsoft / TypeScript # 25826 может быть хорошим, чтобы сосредоточиться на нем.

Когда вы вызываете Test1, компилятор должен оба вывести параметр типа * generic c T, а также контекстный тип для аргумента обратного вызова arg. Он делает это за несколько «проходов», и, к сожалению, делает это в плохом порядке для вашего случая.

У меня нет полной картины того, что происходит конкретно, но набросок выглядит следующим образом. Когда вы вызываете,

Test1('test', (arg) => { arg.ZZ }, m => ({ ZZ: 123 }));

, компилятор хочет вывести тип arg из T и тип T из arg, и он не может этого сделать, поэтому это откладывает вывод на потом. Он переходит ко второму обратному вызову. Этот аргумент также имеет аннотированный аргумент, поэтому здесь он также откладывает вывод. При следующем прохождении он должен попытаться вывести параметр типа T, но для него пока нет информации, которую можно использовать при проверке первого обратного вызова. Он должен что-то выбрать сейчас, поэтому он терпит неудачу и выбирает unknown. И вы получите свою ошибку. 101


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

После довольно продолжительного обсуждения вариантов у нас нет идей, как это исправить, не нарушая другие сценарии ios. Полное объединение - это, конечно, «решение», но это, по сути, полное переписывание, поэтому на данный момент не совсем в картах.

Упомянутое «полное объединение» - это топи c of microsoft / TypeScript # 30134 , но, похоже, нет никаких непосредственных планов что-либо здесь делать.


Итак, до тех пор, пока это не будет исправлено, есть обходные пути. Самый простой обходной путь - аннотировать параметр для второго обратного вызова:

Test1('test', arg => { arg.ZZ }, (m: any) => ({ ZZ: 123 })) // T is {ZZ: number}

Это откладывает вывод для arg => ..., а затем не откладывает для (m: any) => .... Он знает типы и видит, что тип возвращаемого значения {ZZ: number}. Теперь из этого можно правильно вывести T, и на втором проходе, как известно, arg имеет тип {ZZ: number} и все хорошо.

Аналогично вы можете указывать типы чтобы помочь логическому выводу, либо аннотируя arg, либо задав T при вызове Test1:

Test1('test', (arg: { ZZ: number }) => { arg.ZZ }, m => ({ ZZ: 123 })) // T is {ZZ: number}
Test1<{ ZZ: number }>('test', arg => { arg.ZZ }, m => ({ ZZ: 123 })) // obvs

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

declare function Test2<T>(msg: string, fnTake: (arg: any) => T, fn: (arg: T) => void): void;

Test2('test', m => ({ ZZ: 123 }), arg => { arg.ZZ }); // T is {ZZ: number}

Я думаю, что такая же отсрочка вывода происходит на первом проходе, но теперь на втором проходе компилятор может проверить m => ... и использовать контекстный тип any для m, а затем выводим тип возврата {ZZ: number}, и теперь у него есть кандидат на вывод для T, и все работает, когда доходит до обратного вызова arg => ....


Хорошо Надеюсь, что это имеет смысл и дает некоторые направления. Удачи!

Детская площадка ссылка на код

0 голосов
/ 26 февраля 2020

Вы должны передать тип аргумента в Test1 при вызове функции:

type Arg = {
  ZZ: number
}

Test1<Arg>( 'test', (arg) =>{ arg.ZZ }, m => { ZZ:123 } );

Test1<T> при инициализации функции означает, что тип, который вы передадите в Test1 при вызове это будет тип arg

0 голосов
/ 26 февраля 2020

Вы можете добавить любой тип для arg в вашем тесте.

Test1( 'test', (arg: any) =>{ arg.ZZ }, m => { ZZ:123 } );
...