3

I'm looking for a TypeScript type definition that describes an object with a single property (having any value).

I know that thare are index signatures, e.g.

type X = { [key: string]: any }

or alternatively

type X = Record<string, any>

However this would allow an object like

const obj: X = {
  "a": 12,
  "b": "c"
}

I'm looking for a type Y that restricts obj to have a single property, representing kind of a "RecordEntry", that is

const obj: Y = {
  "a": 12
}

should be fine but

const obj: Y = {
  "a": 12,
  "b": "c"
}

should be rejected by the compiler.

Is that even possible?

scythe
  • 31
  • 1

2 Answers2

2

I don't think this is possible in a nice way, here is the best I could come up with:

type NotUnion<T, U = T> =
  T extends any ?
    [U] extends [T] ? T
    : never
  : never;

type SingleProperty<T extends {}> =
  keyof T extends NotUnion<keyof T> ? T
  : never;

const oneProperty = {
  a: 'a'
};
const onePropertyAgain: SingleProperty<typeof oneProperty> = oneProperty; // works

const twoProperties = {
  a: 'a',
  b: 'b'
};
const twoPropertiesAgain: SingleProperty<typeof twoProperties> = twoProperties; // compiler error

You can make this a little nicer like so:

function asSinglePropertyObject<T extends {}>(obj: SingleProperty<T>): SingleProperty<T> {
    return obj;
}

const oneProperty = asSinglePropertyObject({
    a: 1 // works
});

const twoProperties = asSinglePropertyObject({
    a: 1, // Type 'number' is not assignable to type 'never'
    b: 'a'
});
Mike Jerred
  • 9,551
  • 5
  • 22
  • 42
-1

It sounds like what you want is simply

type RecordEntry<T> = Record<string, T>

I don't believe there's a way to restrict to only one unspecified type. The semantics of such a thing could also be confusing. What if, for example, you had

class A {}

class B extends A {}

const obj: RecordEntry = { a: new A(), b: new B() };

Would that be a compiler error?

Rubydesic
  • 3,386
  • 12
  • 27