Распределительные условные типы Typescript

#typescript

#typescript #typescript-условные-типы

Вопрос:

Итак, я просматривал документацию по typescript и не могу разобраться в этой концепции.

Итак, в документации говорится :-

При создании экземпляров распределительного условного типа T extends U ? X : Y ссылки на T внутри условного типа преобразуются в отдельные составляющие объединенного типа (т.е. T ссылаются на отдельные составляющие после того, как условный тип распределен по объединенному типу). Кроме того, ссылки на T внутри X имеют дополнительное ограничение параметра типа U (т.е. T считается, что оно может быть присвоено U внутри X ).

Я не могу понять часть T refers to the individual constituents after the conditional type is distributed over the union type .

Кто-нибудь, пожалуйста, может мне это объяснить. Я был бы весьма признателен за аналогичный пример, тот, что в документации, мне не очень понятен.

Ответ №1:

Хм, я только что прочитал документацию, и для меня это имеет смысл… Я не знаю, смогу ли я объяснить это лучше, чем это, но давайте пройдемся по этому. В дальнейшем, и ...x... , означает «некоторое выражение, в котором x может появиться».

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

В таком случае тип параметра означает, что параметр универсального типа, и голый тип параметра является выражение типа, где тип параметра появляется в одиночку и не является частью какой более сложным типом выражения. А отмеченный тип — это тип, указанный перед extends . Давайте посмотрим на несколько примеров:

  • type A<T> = string extends T ? "yes" : "no" Это не дистрибутивный условный тип. Проверяемый тип является string , который не является параметром универсального типа.
  • type B<T> = {x: T} extends {x: number} ? "yes" : "no" Это не дистрибутивный условный тип. Проверяемым типом является {x: T} , который содержит в себе параметр type T , но не является голым параметром type.
  • type C<T> = T extends string ? "yes" : "no" Это распределительный условный тип; отмеченный тип T , который является параметром общего типа.

Распределительные условные типы автоматически распределяются по типам объединения во время создания экземпляра. Например, создание экземпляра T extends U ? X : Y с аргументом типа A | B | C for T разрешается как (A extends U ? X : Y) | (B extends U ? X : Y) | (C extends U ? X : Y) .

В этом суть того, что делает дистрибутивное свойство. Если у вас есть псевдоним типа, F<T> определенный как дистрибутивный условный тип, как в:

 type F<T> = T extends ...T... ? ...T... : ...T...
  

Затем F<T> будет распространяться по объединениям, что означает, что для любых типов A и B тип F<A | B> будет эквивалентен типу F<A> | F<B>

При создании экземпляров распределительного условного типа T extends U ? X : Y ссылки на T внутри условного типа преобразуются в отдельные составляющие объединенного типа (т.е. T ссылаются на отдельные составляющие после того, как условный тип распределен по объединенному типу).

Это та часть, которая вас смутила, но она просто объясняет, как работает дистрибутив. Здесь говорится, что для оценки F<A | B> вы должны оценить F<A> | F<B> . Итак, для F<A> вы берете F<T> = T extends ...T... ? ...T... : ...T... и подключаете A for T (чтобы получить A extends ...A... ? ...A... : ...A... ), а затем подключаете B for T (чтобы получить B extends ...B... ? ...B... : ...B... ), а затем объединяете их.

Давайте рассмотрим конкретный пример:

 type D<T> = T extends string ? T : "nope"
  

Что это:

 type E = D<"a" | "b" | 0 | true> 
  

Ну, вот как не этого делать:

 type E = ("a" | "b" | 0 | true) extends string ? ("a" | "b" | 0 | true) : "nope" //👎

type E = "nope" //👎
  

Я просто подключился "a" | "b" | 0 | true к T без распространения, и это неправильно. Вот как это сделать правильно:

 type E = D<"a"> | D<"b"> | D<0> | D<true> //👍

type E = ("a" extends string ? "a" : "nope") |
         ("b" extends string ? "b" : "nope") |
         (0 extends string ? 0 : "nope") |
         (true extends string ? true : "nope") //👍

type E = ("a") | ("b") | ("nope") | ("nope") //👍

type E = "a" | "b" | "nope" //👍
  

Видите, мы взяли «отдельные составляющие объединения» и заменили T на каждую из них по очереди.

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

Комментарии:

1. Очень подробное объяснение, всегда приятно читать ваши материалы 🙂

Ответ №2:

Внутри распределительного условного типа (скажем, type BoxIfObject<T> = T extends object ? Array<T> : T; ), когда тип применяется к объединению (скажем, number | { a : string } ), это как если бы условный тип применялся к каждой составляющей объединения и, таким образом, внутри условного типа T , в свою очередь, будет ссылаться на каждую составляющую объединения (так T сначала будет number , а затем T будет { a : string } )

Поэтому, когда мы применяем, BoxIfObject<number | { a : string }> это T будет относиться не ко всему объединению number | { a : string } , а к каждому из его составляющих по очереди. В основном BoxIfObject<number | { a : string }> = BoxIfObject<number> | BoxIfObject<{ a : string }> = number | Array<{ a : string }

Комментарии:

1. Это кажется прекрасным, но, поскольку я просматривал документацию, слово after во фразе T refers to the individual constituents after the conditional type is distributed over the union type выделено курсивом, имеет ли это какое-либо значение?

2. @AmolGupta Я не могу говорить о выборе курсива …. но, похоже, это согласуется с тем, что я написал, когда (т. Е. после) произойдет создание экземпляра T , будет по очереди каждая составляющая объединения.