TypeScript-DOM-lib-generator icon indicating copy to clipboard operation
TypeScript-DOM-lib-generator copied to clipboard

TimerHandler should not be an alias for `string | Function`

Open sandersn opened this issue 7 years ago • 10 comments

Because of global type alias interning, the global type TimerHandler = string | Function means any string | Function union gets called TimerHandler. This makes (for example) lodash errors and quick info even more confusing.

Edit: here is an example of the confusing error:

function f(code: string | Function) {
  code = typeof code === 'string' ? new Function(code) : code;
  // etc...
}
f(null)

Argument of type "null" is not assignable to parameter of type 'TimerHandler'.

sandersn avatar Aug 02 '18 20:08 sandersn

This is actually coming from the webidl, so there is not a simple way to remove these. do you want Function to be sometime different?

mhegazy avatar Aug 02 '18 21:08 mhegazy

//CC @saschanaz

So the proposal is to flatten unions of primitives when we are emitting them.. so string | Function would be inlined in all usage sites instead of using the alias.

mhegazy avatar Aug 02 '18 21:08 mhegazy

So TS now automatically converts unions into typedefs? Interesting.

saschanaz avatar Aug 02 '18 22:08 saschanaz

@saschanaz only for display purposes

We might be able to do a sneaky workaround like string | Function | never ?

RyanCavanaugh avatar Aug 03 '18 21:08 RyanCavanaugh

IMO one of the core issues is the automatic union-typedef conversion for display purposes:

  1. A user cannot easily use "Go to definition" to see what's going on.
  2. Even after we remove TimerHandler, a third party non-module type definition may insert a similar union of primitives, and then the same confusing error.

Edit:

Anyway I'm not a fan for those mini-typedefs as I think they are for spec authors rather than devs. If we remove them, will we also remove non-union typedefs e.g. typedef boolean GLboolean from WebGL?

saschanaz avatar Aug 04 '18 05:08 saschanaz

@saschanaz A few replies:

  1. What goto-def scenario are you thinking of? I don't think that string | Function needs much explanation beyond what would be on the @param tag. Same for many of the other typedefs.

  2. 3rd parties may indeed insert similar unions, but at least the damage is contained to users of those packages. Nearly everybody uses the DOM.

    It does mean that we should perhaps make a sweep of definitely typed to make sure that commonly used packages like jquery, react, lodash do not define small, common type aliases.

  3. I think only unions have this behaviour because it's a combination of union interning and type alias display. Union interning means that string | Function always refers to the same type, no matter how many different places it's used. And types maintain a pointer to their type alias name -- but only one name per type. So if string | Function is seen in the alias type TimerHandler = string | Function, and all uses of string | Function are exactly the same type, that type always prints as "TimerHandler".

    Also note that boolean actually is a union (true | false), so GLboolean should also be removed.

sandersn avatar Aug 06 '18 17:08 sandersn

@sandersn Oops, sorry for replying too late.

What goto-def scenario are you thinking of?

Users may confused because the compiler is using the names the user didn't write, so they may want to find where the name is from, by goto-def, Control+F, or anything.

I think the message itself is confusing here. Can we do something like:

Error: Argument of type "null" is not assignable to parameter of union type "string | Function" (aliased as "TimerHandler")

This way unions won't confuse users too much.

Edit: I just posted a comment and it unassigned a member 😮

saschanaz avatar Aug 17 '18 03:08 saschanaz

@sandersn So this is again a potential problem in #590.

I'm still not sure it will be intuitive to have GLint but not GLboolean, ScrollLogicalPosition but not ScrollBehavior, etc. Is revising the error message impossible?

saschanaz avatar Oct 30 '18 01:10 saschanaz

Is this still the case? I can't reproduce this in TS3.3.

saschanaz avatar Feb 13 '19 07:02 saschanaz

Possibly off-topic: any plans to only allow Function? TSESL has a rule to enforce this. I'm aware this would be a breaking-change, so it has to be deferred to TS v6

Rudxain avatar Dec 20 '24 01:12 Rudxain