Вы можете сделать это относительно просто с помощью оператора 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
Ссылка на площадку