Я пытаюсь определить вспомогательные типы для определения типа значений вложенных объектов, в то же время рассматривая любые необязательные родительские ключи, например, в структурах, подобных этим (или более глубоким):
type Foo = { a: { b?: number; } };
type Foo2 = { a?: { b: number } };
Для моих целей тип b
в Foo
и Foo2
должен быть выведен как number | undefined
. В Foo2
b
сам по себе не является обязательным, а потому что a
для моих целей поиска b
теперь тоже должен быть необязательным ... так много для контекста.
Использование этих вспомогательных типов (извлечено из большего набора ) в качестве строительных блоков:
type Keys<T> = keyof Required<T>;
type IsOpt<T> = T extends undefined ? true : never;
type HasOptKey1<T, A> = A extends Keys<T> ? IsOpt<T[A]> : never;
type HasOptKey2<T, A, B> = A extends Keys<T>
? IsOpt<T[A]> extends never
? HasOptKey1<T[A], B>
: true
: never;
type Val1<T, A> = A extends Keys<T> ? T[A] : never;
type Val2<T, A, B> = A extends Keys<T> ? Val1<Required<T>[A], B> : never;
Используя их для хорошего использования, мы получаем:
type F1 = HasOptKey1<Foo, "a">; // never - CORRECT!
type F2 = HasOptKey1<Foo2, "a">; // true - CORRECT!
type F3 = HasOptKey2<Foo, "a", "b">; // true - CORRECT!
type F4 = HasOptKey2<Foo2, "a", "b">; // true - CORRECT!
// infer type of `a` in Foo
type A1 = HasOptKey1<Foo, "a"> extends never
? Val1<Foo, "a">
: Val1<Foo, "a"> | undefined;
// { b: number | undefined; } - CORRECT!
// infer type of `a` in Foo2
type A2 = HasOptKey1<Foo2, "a"> extends never
? Val1<Foo2, "a">
: Val1<Foo2, "a"> | undefined;
// { b: number } | undefined - CORRECT!
// infer type of `b` in Foo
type B1 = HasOptKey2<Foo, "a", "b"> extends never
? Val2<Foo, "a", "b">
: Val2<Foo, "a", "b"> | undefined;
// number | undefined - CORRECT!
// infer type of `b` in Foo2
type B2 = HasOptKey2<Foo2, "a", "b"> extends never
? Val2<Foo2, "a", "b">
: Val2<Foo2, "a", "b"> | undefined;
// number | undefined - CORRECT!
Чтобы избежать этих повторяющихся условий, Я хотел использовать другой тип помощника:
// helper type w/ same logic as used for A1/A2/B1/B2 conditionals
type OptVal<PRED, RES> = PRED extends never ? RES : RES | undefined;
// applied
type OptVal1<T, A> = OptVal<HasOptKey1<T, A>, Val1<T, A>>;
type OptVal2<T, A, B> = OptVal<HasOptKey2<T, A, B>, Val2<T, A, B>>;
Однако, несмотря на то, что он работает в 3 из 4 случаев, A3
неправильно выводится как never
, и я не понимаю, почему :
type A3 = OptVal1<Foo, "a">;
// never - WHHHYYY??? (should be same as A1!) <-----
type A4 = OptVal1<Foo2, "a">;
// { b: number } | undefined - CORRECT! (same as A2)
type B3 = OptVal2<Foo, "a", "b">; // number | undefined - CORRECT!
type B4 = OptVal2<Foo2, "a","b">; // number | undefined - CORRECT!
ссылка на игровую площадку