прочитать часть 1 здесь

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

теперь взгляните еще раз на наш тип Expect

type Expect<T, U>= T extends U ? U extends T ? true : false : false

обратите внимание на T и U?

они известны как параметры открытого типа.

Вы можете думать о них как о некоторой переменной.

так что если есть параметры голого типа, то есть и параметры не голого типа и выглядят они так, примеры:

  1. ключ T
  2. U[]
  3. [U]
  4. SomeGeneric‹ T ›

Необнаженный параметр распределяется по объединению, в то время как необнаженные и типы не распределяются по объединению (так же, как и обычные типы), чтобы понять это, давайте взглянем на код ниже.

нормальный:

type A = "a" | "b" extends "a" ? true : false // false 
type B = "a" extends "a" | "b" ? true : false // true

довольно просто

"a" | "b" нельзя сузить от "a", потому что "a" | "b" шире, поэтому "a" | "b" не может расширить "a" и наоборот

голый параметр:

type C<T,U> = T extends U ? true : false
type r1 = C<"a" | "b", "a"> // boolean
type r2 = C<"a", "a" | "b"> // true

в случае r1 "a" | "b" попытаться распределить по "a": ("a" extends "a"? true : false) | ("b" extends "a"? true : false)

"a" расширяет "a" это true, "b" расширяет "a" это false, в итоге мы получаем true | false что эквивалентно boolean

в случае r2 "a" не является объединением, поэтому распределения нет, а поскольку "a" можно сузить из "a" | "b", то получается true

"детская площадка"

К этому моменту мы знаем, что может пойти не так с Expect, на этот раз давайте возьмем Odd Number Type в качестве объекта тестирования.

type OddNumber<
    X extends number,
    Y extends unknown[] = [1],
    Z extends number = never
> = Y['length'] extends X
    ? Z | Y['length']
    : OddNumber<X, [1, 1, ...Y], Z | Y['length']>
type Expect<T, U> = T extends U ? (U extends T ? true : false) : false
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const assert = <T extends true>() => {
    //
}
assert<Expect<OddNumber<5>,1 | 3 | 5>>() // true, pass test
// @ts-expect-error
assert<Expect<OddNumber<5>,1>>() // false, fail test

"детская площадка"

как и ожидалось, это не работает.

так как мы можем это исправить?

На самом деле подсказка уже есть: не голые параметры типа.

решение состоит в том, чтобы преобразовать параметр голого типа в параметр неголого типа, и самый безопасный способ — превратить их в массивы.

type Expect<T, U>= T[] extends U[] ? U[] extends T[] ? true : false : false

Давай попробуем еще:

"детская площадка"

проверить еще раз на примере из части 1

"детская площадка"

отлично оба работают!

это конец части 2, в части 3 (еще не готовой) мы рассмотрим больше пограничных случаев и дополнительно уточним наш тип Expect