Условный тип для пустых объектов - PullRequest
0 голосов
/ 16 июня 2020

Возможно ли иметь условный тип, который может проверять объекты, которые могут быть пустыми?

Например:

   function test<T>(a: T): T extends {} ? string : never {
        return null
      }
   let o1: {}
   let o2: { fox? }
   let o3: { fox }
   test(o1)
   test(o2)
   test(o3) // this one should be 'never'

Поскольку проверка условного типа также предназначена для наследования, все 3 случаи дают 'строку', но я хочу выдать 'никогда', если требуется хотя бы одно свойство типа (непустой тип объекта, например o3)

UPDATE

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

В основном я пытался создать функцию, в которой первый аргумент является объектом, а второй является необязательным, если первый может быть полностью частичным (инициализирован с помощью {})

 function test<T extends {}>(a: T, ...x: T extends {} ? [never?] : [any])
 function test(a, b) {
   return null
 }

let o1: {}
let o2: { fox? }
let o3: { fox }

test(o1) // ok
test(o2) // ok
test(o3) // should fail and require second argument

Ответы [ 2 ]

0 голосов
/ 16 июня 2020

Вы можете сделать это относительно просто с помощью оператора keyof.

Помните, что never по сути эквивалент пустого набора

type Foo = keyof {} // never
type Bar = keyof { a: 1, b: 2 } // "a" | "b"

Применяется к вашему преобразуется в

declare function f<T>(t: T): keyof T extends never ? string : never

const a = f({}) //string
const b = f({ a: 1 }) //never

edit

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

Давайте позаимствуем определение OptionalPropertyOf из этого вопроса.

Для объекта типа T, где T расширяет object, мы можем определить следующее

type OptionalPropertyOf<T extends object> = Exclude<{
  [K in keyof T]: T extends Record<K, T[K]>
    ? never
    : K
}[keyof T], undefined>

type ExcludeOptionalPropertyOf<T extends object> = Omit<T, OptionalPropertyOf<T>>

type Foo = { a: number, b: string }
type OptFoo = OptionalPropertyOf<Foo> // never
type NonOptFoo = ExcludeOptionalPropertyOf<Foo> // { a: number, b: string }

type Bar = { a: number, b?: string }
type OptBar = OptionalPropertyOf<Bar> // "b"
type NonOptBar = ExcludeOptionalPropertyOf<Bar> // { a: number }

type Baz = { a?: number, b?: string }
type OptBaz = OptionalPropertyOf<Baz> // "a" | "b"
type NonOptBaz = ExcludeOptionalPropertyOf<Baz> // {}

Затем давайте немного изменим определение f на

declare function f<T extends object>(t: T): keyof ExcludeOptionalPropertyOf<T> extends never ? string : never

И теперь вы получаете то, что искали

declare const a: Foo
declare const b: Bar
declare const c: Baz
declare const d: {}

const fa = f(a) // never
const fb = f(b) // never
const fc = f(c) // string
const fd = f(d) // string

Ссылка на площадку

0 голосов
/ 16 июня 2020

Здесь не используется условный тип, и он не использует тот же код, что и ваш вопрос; поэтому я понимаю, что это может быть совершенно бесполезно. Но Я подумал, что добавлю что-нибудь, чтобы посмотреть, работает ли это в качестве отправной точки для работы над полезным ответом.

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

function test(a: {}): string;
function test(a: Record<string | number | symbol, any>): never;
function test(a: {} | Record<string | number | symbol, any>): string | never {
    if (Object.keys(a).length === 0) {
        return 'test';
    } else {
        throw new Error();
    }
}

const a = test({});       // type string
const b = test({ a: 1 }); // type never

Ссылка на игровую площадку

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