sindresorhus / type-fest

A collection of essential TypeScript types
Creative Commons Zero v1.0 Universal
13.87k stars 527 forks source link

`NonEmptyObject` fails for objects with dynamic properties #821

Open DenizUgur opened 5 months ago

DenizUgur commented 5 months ago

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

Fund with Polar

sindresorhus commented 5 months ago

// @kkmuffme

kkmuffme commented 5 months ago

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