type-fest icon indicating copy to clipboard operation
type-fest copied to clipboard

`NonEmptyObject` fails for objects with dynamic properties

Open DenizUgur opened this issue 5 months ago • 2 comments

The following code is considered as valid by Typescript, although I was expecting it to complain that the filter foo in commonArguments was empty.

import type { NonEmptyObject } from "type-fest"

interface CommonArguments {
  [filter: string]: NonEmptyObject<{ [argument: string]: string | number | undefined }>
}

export const commonArguments: CommonArguments = {
  foo: {}
}

Upvote & Fund

  • We're using Polar.sh so you can upvote and help fund this issue.
  • The funding will be given to active contributors.
  • Thank you in advance for helping prioritize & fund our backlog.
Fund with Polar

DenizUgur avatar Feb 27 '24 22:02 DenizUgur

// @kkmuffme

sindresorhus avatar Feb 28 '24 04:02 sindresorhus

This issue isn't specific to NonEmptyObject but the same happens also with RequireAtLeastOne:

import type { RequireAtLeastOne } from "type-fest";

type Util = { [argument: string]: string | number | undefined };

interface CommonArguments {
  [filter: string]: RequireAtLeastOne<Util, keyof Util>
  
}

export const commonArguments: CommonArguments = {
  foo: {}
}

The fix for NonEmptyObject is relatively simple:

export type NonEmptyObject<T extends object> = HasRequiredKeys<T> extends true ? T : RequireAtLeastOne<T, keyof T>;

to

export type NonEmptyObject<T extends object> = HasRequiredKeys<OmitIndexSignature<T>> extends true ? T : RequireAtLeastOne<T, keyof T>;

However this issue will persist until someone fixes RequireAtLeastOne

kkmuffme avatar Feb 28 '24 14:02 kkmuffme