Пытаясь понять пределы «Т расширяет, а вы U» - PullRequest
8 голосов
/ 06 апреля 2020

Я понял, что что-то вроде:

type GenericExample<T> = T extends (infer U) ? U : 'bar';

равно:

type GenericExample<T> = T extends T ? T : 'bar';

Но когда вещи становятся более сложными, TypeScript жалуется:

type Types = 'text' | 'date' | 'articles' | 'params';

type MyExperiment<Type extends Types> =  { t : Type };

type MyExperimentsUnion = Types extends (infer U) ? MyExperiment<U> : never;
// Type 'U' does not satisfy the constraint 'Types'.
// Type 'U' is not assignable to type '"params"'.

Итак, я хотел бы спросить, почему это неправильно: в данном конкретном случае должно происходить распределение по объединению, поэтому выводимый тип U должен быть text, затем date и так далее. Итак, что на самом деле означает T extends (infer U) и когда было бы целесообразно использовать его?

Ответы [ 2 ]

0 голосов
/ 14 апреля 2020

Это, вероятно, не стоит больше двух центов, но:

type Types = 'text' | 'date' | 'articles' | 'params';
type MyExperiment<Type extends Types> =  { t : Type };

type MyExperimentsGenericUnion<T extends Types> = T extends (infer U) ? MyExperiment<U> : never;
// same error

type MyExperimentsUnionConstraint = any extends MyExperiment<infer U> ? MyExperiment<U> : never;
// no error

Я могу придумать несколько причин, по которым MyExperimentsUnionConstraint работает, но MyExperimentsUnion нет, но у меня нет углубить знание языка до express их, чтобы имело смысл.

Чтобы дать объяснение на примере:

  • в MyExperimentsUnion, из Types extends U, U может выводится как Types | 'bla', а затем MyExperiment<Type | 'bla'> не работает.
  • в MyExperimentsUnionConstraint, U может быть выведен только как то, что работает с MyExperiment
0 голосов
/ 11 апреля 2020

Я не думаю, что он должен был использоваться так, как вы его используете - в основном infer следует использовать для того, чтобы "вывести" (или решить, может быть, лучшее именование?) Тип, чаще всего из обобщенного c.

То, как вы его используете, вы создаете тип, который не имеет никакой части «dynamici c» (в основном это не generi c), то есть он всегда один и тот же, и, следовательно, выводит от чего-то, что всегда одинаково, не имеет смысла. Потому что во время компиляции вы уже знаете, что Types распространяется только на Types & '...anything else', и поскольку вы не можете определить эту другую часть в вашем MyExperimentsUnion типе, infer не имеет особого смысла.

Пример использования

interface Action<T> {
    payload: T
}

type ExtractGeneric<T> = T extends Action<infer X> ? X : never

function getPayload<T extends Action<any>>(action: T): ExtractGeneric<T> {
    return action.payload;
}

const myAction = { payload: 'Test' };
const myPayloadWithResolvedType = getPayload(myAction);

В приведенном выше примере myPayloadWithResolvedType будет иметь string в качестве разрешенного типа, потому что, если вы не используете infer, вам придется передать этот возвращаемый тип как Второй параметр, вероятно, так:

function getPayloadNonExtract<T extends Action<U>, U>(action: T): U {
    return action.payload;
}

Здесь - ссылка на игровую площадку.

Приветствия.

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