You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Sometimes there is more than one way to write a type definition for a function. In particular, the same types often can be expressed using either union types or overloads. Compare:
When writing type definitions for libraries, I used to think union types are preferable to overloads. I was sure union types enable for more flexibility. For example, I can't call foo with a parameter of type number[] | number if the class A2 is used:
vara1: A1,a2: A2;varx: number|number[];a1.foo(x);// Union types: OKa2.foo(x);// Overloads: ERROR
For this reasons, in my PRs sent to DefinitelyTyped I always tried to prefer union types unless it was clear that overloads is a better choice (different parameter names for different overloads, different semantics, etc.). However, I've started discovering counter-examples recently:
declareclassB{foo(arg: number[]): string;}vara1b: A1|B,a2b: A2|B;a1b.foo([1]);// Union types: ERRORa2b.foo([1]);// Overloads: OK
This example is of course artificial, so let me describe a more realistic situation. Imagine we have two implementations of Promises — Q and the promises from Selenium WebDriverJs — and need to use one or another depending on some logic. So we have a promiseImplementation variable:
Both implementations have the all method whose argument can be an array (the type of parameter is specified as (T|Promise<T>)[]). As for the Q library, this argument can also be a promise ((T|Promise<T>)[] | Promise< (T|Promise<T>)[] >). But should it matter if we want to pass an array, which anyway is supported by both libraries? I believe it shouldn't. So let's try:
declareletx: any[];letresult=promiseImplementation.all(x);// ERROR:// "Cannot invoke an expression whose type lacks a call signature."
No luck! However, if the all method of Q was defined using overloads, it'd work.
So now, I'm completely unsure about what I should prefer when writing type definitions. Both union types and overloads are able to cause unexpected counter-intuitive errors, which make you think 'I wish it was defined the other way around' when you find the reason of those errors. However, a developer who will use the type definitions shouldn't need to care at all about these subtleties.
So, back to my initial example. The type of a1b.foo is:
I'd like to propose that the compiler should be smart enough to understand that this type is callable if it's called with an argument of type number[]. That is the type of a1b.foo would be narrowed down to (arr: number[]) => string when a1b.foo is called as a function. If there has been a previous discussion on this, I apologize. I wasn't able to find it.
TypeScript Version: 2.0.6
The text was updated successfully, but these errors were encountered:
Sometimes there is more than one way to write a type definition for a function. In particular, the same types often can be expressed using either union types or overloads. Compare:
and
When writing type definitions for libraries, I used to think union types are preferable to overloads. I was sure union types enable for more flexibility. For example, I can't call
foo
with a parameter of typenumber[] | number
if the classA2
is used:For this reasons, in my PRs sent to DefinitelyTyped I always tried to prefer union types unless it was clear that overloads is a better choice (different parameter names for different overloads, different semantics, etc.). However, I've started discovering counter-examples recently:
This example is of course artificial, so let me describe a more realistic situation. Imagine we have two implementations of Promises — Q and the promises from Selenium WebDriverJs — and need to use one or another depending on some logic. So we have a
promiseImplementation
variable:Both implementations have the
all
method whose argument can be an array (the type of parameter is specified as(T|Promise<T>)[]
). As for the Q library, this argument can also be a promise ((T|Promise<T>)[] | Promise< (T|Promise<T>)[] >
). But should it matter if we want to pass an array, which anyway is supported by both libraries? I believe it shouldn't. So let's try:No luck! However, if the
all
method of Q was defined using overloads, it'd work.So now, I'm completely unsure about what I should prefer when writing type definitions. Both union types and overloads are able to cause unexpected counter-intuitive errors, which make you think 'I wish it was defined the other way around' when you find the reason of those errors. However, a developer who will use the type definitions shouldn't need to care at all about these subtleties.
So, back to my initial example. The type of
a1b.foo
is:I'd like to propose that the compiler should be smart enough to understand that this type is callable if it's called with an argument of type
number[]
. That is the type ofa1b.foo
would be narrowed down to(arr: number[]) => string
whena1b.foo
is called as a function. If there has been a previous discussion on this, I apologize. I wasn't able to find it.TypeScript Version: 2.0.6
The text was updated successfully, but these errors were encountered: