I am exploring how to define a conditional type in TypeScript that can differentiate between "record" types (with dynamic/unbounded keys) and plain object types (with predefined keys).
type R = { [x: string]: any }
type O = { a: string; b: number }
One approach is to extract the keys of the types and then verify using a nonexistent property:
type R = { [x: string]: any }
type O = { a: string; b: number }
type IsRecord<T> = '__NEVER__' extends keyof T ? true : false
type RR = IsRecord<R> // true
type OO = IsRecord<O> // false
However, this method feels somewhat hacky. Is there a cleaner solution for achieving this distinction?