прочитать часть 1 здесь
теперь мы знаем, как создавать тесты для нашего типа, но это еще не сделано, есть крайние случаи, о которых нам нужно позаботиться, и нам нужно понять некоторые теории, прежде чем мы сможем их решить.
теперь взгляните еще раз на наш тип Expect
type Expect<T, U>= T extends U ? U extends T ? true : false : false
обратите внимание на T
и U
?
они известны как параметры открытого типа.
Вы можете думать о них как о некоторой переменной.
так что если есть параметры голого типа, то есть и параметры не голого типа и выглядят они так, примеры:
- ключ T
- U[]
- [U]
- 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