GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Allow calls on unions of dissimilar signatures by weswigham · Pull Request #29011 · microsoft/TypeScript

Conversation

@weswigham

@weswigham Wesley Wigham (weswigham) commented Dec 13, 2018

Copy link
Copy Markdown
Member

Fixes #7294 with caveats.

function test1() {
    type stringType1 = "foo" | "bar";
    type stringType2 = "baz" | "bar";

    interface Temp1 {
        getValue(name: stringType1): number;
    }

    interface Temp2 {
        getValue(name: stringType2): string;
    }

    function test(t: Temp1 | Temp2) {
        const z = t.getValue("bar"); // Should be fine
    }
}

The above now has no errors, and z is of type number | string.

The caveats are that only one type in the list of union members is allowed to have overloads, and only one type within the list of union members is allowed to have type parameters. The first is because creating the power set of overloads results in a (long) list of overloads whose ordering is unclear. This is maybe surmountable in the future, but for now this seems like a sane place to stop. The second is because introducing multiple sets of type parameters requires unifying them, as multiple type parameters in the same position likely do not infer well (eg, (arg: A & B & {x: string}) => A | B | {x: string}). Again, with more infrastructure, this is potentially fixable, but for now, this seems like a good stopping point. In the case of a union where either of those things are true, we simply do not create a union signature and fall back to the old behavior of not making a signature at all (and so you'll get the "type has no call or construct signatures" error).

There's one open question here which is contextual typing: In the new test I added, I included one of the motivating examples: using forEach on a union of array types:

function test3(items: string[] | number[]) {
    items.forEach(item => console.log(item));
}

With this change, that is now callable, but the argument to the callback you pass will be implicitly any if defined inline. This is because the argument types are intersected, and the intersection of two signatures currently creates an overload list of signatures. As-is, we have logic that explicitly removes contextual typing from overload lists, so no contextual type is provided. This seems incorrect - I believe fixing this could go in one of two ways:

  1. Generate a contextual type from an overload list with similar requirements/caveats that union signature creation has.
  2. Generate a combined intersection signature just like we now do for union signatures, rather than an overload set.

Even as-is, this seems like a huge improvement over what we do today; we could also choose to do nothing and require an annotation and still be better off than now. I'd rather we make the usecase work fully without user code changes, but it's still better than what we do in master.

I also have one small TODO I'm still working on (and then writing tests for) which is deriving the names for the amalgamated signature's parameters from the input signatures' parameter names. It's more complex than it was the last time I tried this since we added tuple types - I don't really know how to look up symbol names through the effective argument types which may have been fulfilled by a tuple.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

jack-williams, trotyl, ulrichb, bradenhs, pelotom, brunolemos, thorn0, bolasblack, sergey-shandar, SlurpTheo, and 3 more reacted with thumbs up emoji bolasblack and ethanresnick reacted with hooray emoji bolasblack, Ranguna, and HumanRupert reacted with heart emoji
All reactions
  • 👍 13 reactions
  • 🎉 2 reactions
  • ❤️ 3 reactions
Wesley Wigham (weswigham) added 3 commits December 13, 2018 13:28
@weswigham
Add core of new union signature logic and test - needs intersection s…
8184037
…ignature logic to fully work
@weswigham
Add inversion of variance for class props lookup from union sig returns
6c2e1e1
@weswigham
Fix lints
8ea94cd
@weswigham Wesley Wigham (weswigham) mentioned this pull request Dec 13, 2018
Errors on non-callable unions should be more specific #29013
Open
@DanielRosenwasser Daniel Rosenwasser (DanielRosenwasser) mentioned this pull request Dec 14, 2018
Design Meeting Notes, 12/14/2018 #29033
Closed
@weswigham
Combine parameter names for nicer quick info
407054a
@weswigham

Wesley Wigham (weswigham) commented Dec 15, 2018

Copy link
Copy Markdown
Member Author

Post-design meeting update. In the meeting, we said we wanted to look at changing contextual typing based on overloading. Looked at it, and recognized that when applied, things like

declare var c: {
  (x: number): number;
  (x: string): string;
}
declare function f(a: typeof c): void;

f(x => x);

which was not an error outside noImplicitAny mode (as x had no contextual type and was implicitly any) becomes an error (as x is assigned number | string which then flows to the return type and is incompatible with both overload return types).

In lieu of fixing this now, we think we'll wait on being able to properly run a lambda in multiple contexts to fix that and leave this PR as is (so the .forEach case is still an implicit any error).

Also: I've finished the TODO in the OP - parameter names are now copied into the combined signature (and there are tests verifying this).

Anders Hejlsberg (@ahejlsberg) this is done and could use a review now.

cpplearner reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

ahejlsberg
Anders Hejlsberg (ahejlsberg) reviewed Dec 19, 2018
View reviewed changes
Comment thread src/compiler/checker.ts Resolved

Uh oh!

There was an error while loading. Please reload this page.

ahejlsberg
Anders Hejlsberg (ahejlsberg) reviewed Dec 19, 2018
View reviewed changes
Comment thread src/compiler/checker.ts Resolved

Uh oh!

There was an error while loading. Please reload this page.

ahejlsberg
Anders Hejlsberg (ahejlsberg) reviewed Dec 19, 2018
View reviewed changes
Comment thread src/compiler/checker.ts Resolved

Uh oh!

There was an error while loading. Please reload this page.

Wesley Wigham (weswigham) added 3 commits December 19, 2018 13:10
@weswigham
PR feedback 1
d7c668a
@weswigham
Merge branch 'master' into dissimilar-union-signatures
5df3c69
@weswigham
Fix miscopy
f24c7f5
ahejlsberg
Anders Hejlsberg (ahejlsberg) reviewed Dec 19, 2018
View reviewed changes
Comment thread src/compiler/checker.ts Resolved

Uh oh!

There was an error while loading. Please reload this page.

ahejlsberg
Anders Hejlsberg (ahejlsberg) reviewed Dec 19, 2018
View reviewed changes
Comment thread src/compiler/checker.ts Resolved

Uh oh!

There was an error while loading. Please reload this page.

ahejlsberg
Anders Hejlsberg (ahejlsberg) reviewed Dec 19, 2018
View reviewed changes
Comment thread src/compiler/checker.ts Resolved

Uh oh!

There was an error while loading. Please reload this page.

ahejlsberg
Anders Hejlsberg (ahejlsberg) reviewed Dec 19, 2018
View reviewed changes
Comment thread src/compiler/checker.ts Resolved

Uh oh!

There was an error while loading. Please reload this page.

ahejlsberg
Anders Hejlsberg (ahejlsberg) reviewed Dec 19, 2018
View reviewed changes
Comment thread src/compiler/checker.ts Resolved

Uh oh!

There was an error while loading. Please reload this page.

ahejlsberg
Anders Hejlsberg (ahejlsberg) reviewed Dec 19, 2018
View reviewed changes
Comment thread src/compiler/checker.ts Resolved

Uh oh!

There was an error while loading. Please reload this page.

ahejlsberg
Anders Hejlsberg (ahejlsberg) reviewed Dec 19, 2018
View reviewed changes
Comment thread src/compiler/checker.ts Resolved

Uh oh!

There was an error while loading. Please reload this page.

ahejlsberg
Anders Hejlsberg (ahejlsberg) reviewed Dec 19, 2018
View reviewed changes
Comment thread src/compiler/checker.ts Resolved

Uh oh!

There was an error while loading. Please reload this page.

ahejlsberg
Anders Hejlsberg (ahejlsberg) reviewed Dec 19, 2018
View reviewed changes
Comment thread src/compiler/checker.ts Resolved

Uh oh!

There was an error while loading. Please reload this page.

ahejlsberg
Anders Hejlsberg (ahejlsberg) reviewed Dec 19, 2018
View reviewed changes
Comment thread src/compiler/checker.ts Resolved

Uh oh!

There was an error while loading. Please reload this page.

ahejlsberg
Anders Hejlsberg (ahejlsberg) reviewed Dec 19, 2018
View reviewed changes
Comment thread src/compiler/checker.ts Resolved

Uh oh!

There was an error while loading. Please reload this page.

ahejlsberg
Anders Hejlsberg (ahejlsberg) reviewed Dec 19, 2018
View reviewed changes
Comment thread src/compiler/checker.ts Resolved

Uh oh!

There was an error while loading. Please reload this page.

ahejlsberg
Anders Hejlsberg (ahejlsberg) reviewed Dec 19, 2018
View reviewed changes
Comment thread tests/baselines/reference/functionCallOnConstrainedTypeVariable.errors.txt Resolved

Uh oh!

There was an error while loading. Please reload this page.

ahejlsberg
Anders Hejlsberg (ahejlsberg) reviewed Dec 19, 2018
View reviewed changes
Comment thread tests/baselines/reference/tsxUnionElementType1.errors.txt Resolved

Uh oh!

There was an error while loading. Please reload this page.

Wesley Wigham (weswigham) added 2 commits December 19, 2018 14:32
@weswigham
PR feedback round 2
d28d87f
@weswigham
Remove argument name combining because loc :(
f1d78e9
@weswigham

Wesley Wigham (weswigham) commented Dec 19, 2018

Copy link
Copy Markdown
Member Author

As discussed, here's a note for future reference:

This PR allows unions of signatures to (almost always) be callable, however take the union var c: (x: number) => string | (x: string) => number. I can call c, but there is no value I can use for the first parameter that actually satisfies both signatures (except something of type any). Our error message in this case will be that your argument type is not assignable to string & number, which is pretty easy to understand. However if that vacuous intersection is simplified to never by the presence of a union (eg, one parameter type is boolean | number and the other is string), it can be harder to understand. So a signature always exists and is always callable, however individual parameters may be unsatisfiable, and that may be represented with the never type for that parameter. Keeping these signatures around is important for correctly checking any other satisfiable arguments (this way you can continue in the presence of errors), allowing just the parameter to be cast to allow the unsatisfiable call (rather than casting the variable you're calling and losing checking on the other arguments), and for allowing the type to be satisfied in contravariant positions (eg, the parameter of a parameter) where the parameter can simply be ignored or unused.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@weswigham
No step on sneky off-by-one error
7b2ad7f
ahejlsberg
Anders Hejlsberg (ahejlsberg) approved these changes Dec 20, 2018
View reviewed changes
@weswigham
Wesley Wigham (weswigham) merged commit 08022d5 into microsoft:master Dec 20, 2018
@weswigham
Wesley Wigham (weswigham) deleted the dissimilar-union-signatures branch December 20, 2018 00:35
@jack-williams Jack Williams (jack-williams) mentioned this pull request Jan 3, 2019
Conditional type does not narrow union type #29188
Closed
@lmcarreiro

Leonardo Carreiro (lmcarreiro) commented Jan 3, 2019

Copy link
Copy Markdown

Does this solution resolve this case too?

type A = { x: string };
type B = { x: string, y: number };

const arr1: (A | B)[] = [];
arr1.find(e => e.x === ""); // OK
arr1.map(e => e.x); // OK

const arr2: A[] | B[] = [];
arr2.find(e => e.x === ""); // OK
arr2.map(e => e.x); // Error: Cannot invoke an expression whose type lacks a call signature.
SlurpTheo and jung-han reacted with thumbs up emoji
All reactions
  • 👍 2 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@weswigham

Wesley Wigham (weswigham) commented Jan 3, 2019

Copy link
Copy Markdown
Member Author

find yes, map, no - map has overloads if I recall correctly, which are still quite troublesome.

chbdetta reacted with thumbs up emoji cglong reacted with thumbs down emoji
All reactions
  • 👍 1 reaction
  • 👎 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

This was referenced Feb 7, 2019
Suggestion: Opt-in distributive control flow analysis #25051
Closed
Call signatures of union types #7294
Closed
@weswigham Wesley Wigham (weswigham) mentioned this pull request Mar 7, 2019
Broken jsx element with not static type with ts 3.2.1 #28768
Closed
@jack-williams Jack Williams (jack-williams) mentioned this pull request Mar 8, 2019
Union types not correctly handled in array.map #30271
Closed
@jcalz Joe Calzaretta (jcalz) mentioned this pull request Mar 25, 2019
Wishlist: support for correlated union types #30581
Closed
@kring Kevin Ring (kring) mentioned this pull request May 4, 2019
Merge overloads that only differ by return type when intersecting #30520
Closed
@dgieselaar Dario Gieselaar (dgieselaar) mentioned this pull request May 29, 2019
[APM]: Inferred types for aggregations elastic/kibana#37360
Merged
@jcalz Joe Calzaretta (jcalz) mentioned this pull request Aug 24, 2019
Intersect interfaces instead of union #33070
Closed
@nomcopter

Kevin Verdieck (nomcopter) commented Aug 29, 2019

Copy link
Copy Markdown

Does an issue exist for the follow-up of this for overloads? The lack of .map() and .filter() on unioned arrays is pretty painful. Thanks!

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@weswigham

Wesley Wigham (weswigham) commented Aug 29, 2019

Copy link
Copy Markdown
Member Author

I don't think so, but reference #31023 as maybe related if you open one~

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@jack-williams Jack Williams (jack-williams) mentioned this pull request Aug 30, 2019
How to capture FAQ's, Gotcha's, and other useful TypeScript details. microsoft/TypeScript-Website#58
Closed
@connorjclark Connor Clark (connorjclark) mentioned this pull request Oct 17, 2019
Type discrimination in function calls for callable types doesn't work if discriminating property is optional #34529
Open
@jcalz Joe Calzaretta (jcalz) mentioned this pull request Nov 12, 2019
Unable to call map on array of variable depth #35045
Closed
@dragomirtitian Titian Cernicova-Dragomir (dragomirtitian) mentioned this pull request Dec 16, 2019
Unexpected intersection inferred from indexed type #35695
Closed
@jcalz Joe Calzaretta (jcalz) mentioned this pull request Dec 17, 2019
Function signature confusion after ternary operator #35707
Closed
This was referenced Jan 24, 2020
.map() .reduce() .filter() methods fail with "This expression is not callable." error on valid array with union type #36390
Closed
extends converts type of the form A | B to A & B #36445
Closed
@Ranguna Luis Pais (Ranguna) mentioned this pull request May 9, 2020
Each member of the union type has signatures, but none of those signatures are compatible with each other #33591
Closed
@jcalz Joe Calzaretta (jcalz) mentioned this pull request May 11, 2024
Event type (name string) doesn't get properly checked or suggestions when using addEventListener() on an Union of multiple HTMLElement types #58497
Closed
@Renegade334 René (Renegade334) mentioned this pull request Dec 1, 2024
[node] Cleanup generics of EventEmitter and correct types DefinitelyTyped/DefinitelyTyped#71060
Closed
@jcalz Joe Calzaretta (jcalz) mentioned this pull request Dec 12, 2024
Incorrect constructor overload: takes only first overload, but not others #60745
Closed
@microsoft Microsoft (microsoft) locked as resolved and limited conversation to collaborators Oct 21, 2025
@typescript-bot TypeScript Bot (typescript-bot) assigned Wesley Wigham (weswigham) Oct 21, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Reviewers

@ahejlsberg Anders Hejlsberg (ahejlsberg) ahejlsberg approved these changes

Uh oh!

There was an error while loading. Please reload this page.

Assignees

@weswigham Wesley Wigham (weswigham)

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Call signatures of union types

Uh oh!

There was an error while loading. Please reload this page.

4 participants

@weswigham @lmcarreiro @nomcopter @ahejlsberg
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.