Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add EmptyObject (the type you'd expect for {}) #395

Closed
fregante opened this issue May 7, 2022 · 9 comments
Closed

Add EmptyObject (the type you'd expect for {}) #395

fregante opened this issue May 7, 2022 · 9 comments
Labels
enhancement New feature or request type addition

Comments

@fregante
Copy link

fregante commented May 7, 2022

https://stackoverflow.com/a/60112239/288906

type EmptyObject = Record<string, never>;

const one: EmptyObject = {}; // yes ok
const two: EmptyObject = {a: 1}; // error

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
@sindresorhus
Copy link
Owner

Makes sense for discoverability 👍

@sindresorhus sindresorhus added enhancement New feature or request type addition labels May 8, 2022
@HunterKohler
Copy link
Contributor

I like this idea. However there may some issues due to unexpected behavior in niche instances. I have just one of those and an adjustment.

First, this would be more complete:

type EmptyObject = Record<keyof any, never>;

Second, I saw this example from someone on stack overflow, which seems to violate the type safety of this.

type EmptyObject = Record<keyof any, never>;

type Union = EmptyObject | { id: string }; 

const a: Union = {}; 
const b: string = a.id // no error
const c = a.id; // inferred type: `never`;

A Typescript playground for the above.

@bbrk24
Copy link
Contributor

bbrk24 commented May 24, 2022

@fregante Record<never, never> doesn't solve the original problem:

type EmptyObject = Record<never, never>;

const foo: EmptyObject = {a: 1}; // should error, but is allowed

In fact, the tooltip suggests Record<never, never> is just {}.

Playground link

@bbrk24
Copy link
Contributor

bbrk24 commented May 24, 2022

Record<any, InvariantOf<unknown>> seems to work:

import type { InvariantOf } from 'type-fest';

type EmptyObject = Record<any, InvariantOf<unknown>>;

// test cases
const foo: EmptyObject = {a: 1}; // errors
const bar: EmptyObject = {}; // does not error
const baz: string = bar.a; // errors
// for comparison, both `Record<keyof any, never>` and `Record<never, never>` fail one of these

Playground link

@bbrk24
Copy link
Contributor

bbrk24 commented Jun 21, 2022

Coming back to this a while later, { [tag]?: never } (using the const tag: unique symbol from Opaque) seems even better than that. Compare:

import type { InvariantOf } from 'type-fest';
type EmptyObject = Record<any, InvariantOf<unknown>>;

const foo: EmptyObject = {};
const bar = foo.a; // doesn't error, but should
declare const tag: unique symbol;
type EmptyObject = { [tag]?: never };

const foo: EmptyObject = {};
const bar = foo.a; // errors as expected

Playground link demonstrating the difference between these two suggestions. The errors generated by the latter are also easier to understand.

@sindresorhus
Copy link
Owner

@bbrk24 Interested in submitting a pull request for it? No worries if not.

@bbrk24
Copy link
Contributor

bbrk24 commented Jul 24, 2022

I don't want to right now, if only because I'm not sure how much to put in the doc comment.

@sindresorhus
Copy link
Owner

#447

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request type addition
Projects
None yet
Development

No branches or pull requests

4 participants