25

In TypeScript, what exactly is type '{}' and how does it relate to other built-in types?

For example, the last line of the following example gives Type '{}' is not assignable to type 'number', and I am not completely clear on what type {} is in this context, or indeed how it comes about:

// A function taking no arguments and returning T
type NoArgsFn<T> = () => T;

// An instance of NoArgsFn<number>
function get_the_answer(): number {
  return 42;
}

// Call the supplied function and return its value
function call<T, Fn extends NoArgsFn<T>>(fn: Fn): T {
  return fn();
}

// Expect this to be equivalent to `let the_answer: number = 42', but
// instead get "Type '{}' is not assignable to type 'number'"
let the_answer: number = call(get_the_answer);
NPE
  • 486,780
  • 108
  • 951
  • 1,012
  • 1
    A good question, as it isn't clear how `{}` comes about in the example. However, I don't believe you need the `Fn` generic type, and this might relate to the lack of a resolution of `T` to `number`. – E_net4 May 01 '16 at 13:39
  • 2
    That's right, thanks. If I get rid of `Fn`, the error goes away. That said, I would still like to understand what `type {}` is and why it shows up here. – NPE May 01 '16 at 13:44
  • Does this answer your question? [What does {} mean in TypeScript?](https://stackoverflow.com/questions/60381643/what-does-mean-in-typescript) – Bart Louwers Aug 11 '22 at 20:05

3 Answers3

13

type {}

Consider the object type { id: number, name: string }, which represents a 2-field object. Legal values of this type include { id: 1, name: "Foo" } and { id: 2, name: "Bar" }.

The type object {} represents a 0-field object. The only legal value of this type is an empty object: {}.

So the value { id: 1, name: "Foo" } is of type { id: number, name: string }, and the value {} (i.e. an empty object) is of type {}.

The error

The error seems to be a bug in the TypeScript compiler (I submitted an issue here). It fails to infer the type arguments in the call to call. You can work around this by explicitly specifying the type arguments:

let the_answer: number = call<number, NoArgsFn<number>>(get_the_answer);

But it's simpler and more straightforward to use a single type argument instead, as @NitzanTomer suggested:

function call<T>(fn: NoArgsFn<T>): T {
  return fn();
}

EDIT: I issue I submitted was closed as a duplicate of #7234 which is to be fixed before the release of TypeScript 2.0.

Spike
  • 725
  • 5
  • 12
5

You need to have your call function like this:

function call<T>(fn: NoArgsFn<T>): T {
    return fn();
}

In your original call function you had 2 generic types which you did not pass when calling the function and the compiler failed to infer what types they are.


Edit

Type {} is an object literal (as far as I'm aware), and so you can face the same error like this:

var o = {};
var n: number = o; // Error: Type '{}' is not assignable to type 'number'

I'm not exactly sure why the compiler inferred that the function returns {} in your example.

Community
  • 1
  • 1
Nitzan Tomer
  • 155,636
  • 47
  • 315
  • 299
  • Thanks, but what *exactly* is `type '{}'`? (I'd also like to understand *why* the inference is failing, but we can tackle that separately.) – NPE May 01 '16 at 13:45
  • @NPE check my revised answer – Nitzan Tomer May 01 '16 at 13:47
  • 2
    I've been trying to read the specs in a way to justify this behaviour, but [the section on Type inference](https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#3117-type-inference) is a bit dense. – E_net4 May 01 '16 at 13:57
5

As far as I know, {} casts directly to a hash map like usage. You can only use keyed properties, versus the dot notation.

var o:{} = {}

should be equivalent to

var o:{[key:string]:any} = {}

So

o.x = 5; //fails

But

o['x'] = 5; //succeeds
ArcSine
  • 648
  • 6
  • 14