1: Почему это происходит?
PlainObject[]
является подтипом из PlainObject
. В теории множеств, когда у вас есть объединение Subtype | Supertype
, тогда результирующий тип будет Supertype
- он поглощает Subtype
. Итак, b
получает здесь тип PlainObject
.
Почему PlainObject[]
является подтипом? Это становится понятнее, когда мы смотрим на типы Array
и PlainObject
:
interface Array<T> { [n: number]: T} // T is PlainObject for PlainObject[]
type PlainObject = { [key: string]: any }
В JS, ключ свойства number
станет string
. И независимо от того, что T
, его можно присвоить any
.
type PlainObjArr_extends_PlainObj = PlainObject[] extends PlainObject ? true : false // true
2: Почему полезная нагрузка не работает, как Promise>, и как я могу вывести это иначе?
Для защитников пользовательских типов предикат типа (is xxx
) должен быть подтипом проверяемого аргумента. TS не может проверить здесь, что Promise<Unpacked<T>>
является подтипом T
. Пренебрегая этим фактом, isPromise
выполняет свою работу, отфильтровывая все несовместимые значения a
:
let b = isPromise(a) ? await a /*Promise<PObj | PObj[]>*/ : a /*PObj | PObj[]*/
any
- как часто - это зло: o). Решение - сделать PlainObject[]
несовместимым с PlainObject
:
export type PlainObject = { [key: string]: unknown } // e.g. replace `any` by `unknown`
let b = isPromise(a) ? await a : a // b: PlainObject | PlainObject[]