4

I want to create a promise which settles when a WebSocket connects.

import * as WebSocket from "ws"

let ws = WebSocket("ws://example.com")

function foo(echo: string) {
    return new Promise((resolve, reject) => ws.once("open", () => resolve(echo)));
}

In VS Code, the foo function infers as yielding Promise<{}>.

  1. What a Promise<{}>?
  2. More generally, how do I get this to refer a Promise<string>? Am I misunderstanding Trigger Promise when an event fires ?
Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
Grant Wu
  • 47
  • 5
  • Maybe `return new Promise(...);`? – Patrick Roberts Jul 13 '18 at 23:08
  • Ugh, just found https://stackoverflow.com/questions/36967176/what-is-type Is {} in inferred types always a compiler issue? – Grant Wu Jul 13 '18 at 23:10
  • Possibly. I haven't worked _that_ much with TypeScript yet, but I can check out the global `.d.ts` files for an explanation of this particular shortcoming if you'd like. – Patrick Roberts Jul 13 '18 at 23:13
  • @PatrickRoberts In that case, I would want to know why TS can't infer the type here, and why it's putting a weird {} in the inferred type... (sorry, this ought to have been in reply to the `Promise` suggestion) – Grant Wu Jul 13 '18 at 23:14
  • @PatrickRoberts Is there a way I can do that myself? – Grant Wu Jul 13 '18 at 23:16
  • 1
    FWIW [here's the issue](https://github.com/Microsoft/TypeScript/issues/5254) that discusses this limitation. – CRice Jul 13 '18 at 23:36

1 Answers1

4

Generics have {} (empty object) as default parameter value.

Promise is a generic. Since generic type cannot be inferred from resolve argument due to TypeScript limitations, it's {}, or an empty object, by default.

It should be:

function foo(echo: string) {
    return new Promise<string>((resolve, reject) => ws.once("open", () => resolve(echo)));
}
Derek Pollard
  • 6,953
  • 6
  • 39
  • 59
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • Hrm, interesting. Do you have any links/sources? I'd like to know more about these limitations so I know when I run into them in the future. EDIT: Oh, it seems to be in the default parameter value link. – Grant Wu Jul 13 '18 at 23:21
  • (I actually upvoted you, FYI, this comment is not a criticism of your answer) I was looking for these "TypeScript limitations" myself, but the `lib.es2015.promise.d.ts` defines `new` as `new (executor: (resolve: (value?: T | PromiseLike) => void, reject: (reason?: any) => void) => void): Promise;` so given that `echo` is declared as `string`, I can't seem to wrap my head around the fact that TS can't infer `T` from the value passed to `resolve`. – Patrick Roberts Jul 13 '18 at 23:25
  • Wait, I don't see anything in the link about having the empty object as the default parameter value. – Grant Wu Jul 13 '18 at 23:25
  • It's just how generics work. There are some quirks that aren't described anywhere because TS reference is basically a guide (see this generic question for example, https://stackoverflow.com/questions/50262700/inference-in-generic-function-parameters ) The connection between Promise generic type and resolve argument type is too complex. It works only in one direction. I.e., generic type cannot be inferred but if wrong type was provided, it will cause type error, e.g. `Promise` and `resolve(echo)`. – Estus Flask Jul 13 '18 at 23:29
  • @PatrickRoberts Thanks. I'm not aware of what the complexities exactly are, but I guess that T will be harder to infer if there are multiple points it could be inferred from. I'm sure there's open issue somewhere in TS repo with 'Discussion needed' label that addresses this issue but wasn't assigned to anybody. – Estus Flask Jul 13 '18 at 23:35