google / google/closure-compiler

Determine semantics for function overloads

Open
#1,616 1 comment 0 reactions 0 assignees View on GitHub
enhancement Types
Dominant language
JavaScript
Stars
7.7k
Forks
1.2k
Avg merge
2d 12h
Merged PRs (30d)
6

Description

Function overloads would provide a simpler alternative to TTL that we could potentially support in NTI, and would allow correctly and precisely typing many functions that are currently underspecified.

I can think of several potential use cases, which we should consider while defining any semantics:

``` javascript
function then(this: Promise, T => Promise): Promise
function then(this: Promise, T => U): Promise
```

This is currently implemented via TTL.

``` javascript
function map(IArrayLike, T => U): Array
function map(string, string => U): Array
```

This could potentially be worked around with a string ~ IArrayLike extern, but that's kind of nasty.

``` javascript
function assertThrows(string, function())
function assertThrow(function())
```

It would be nice if `assertThrows(function(){}, function(){})` failed to typecheck.

``` javascript
function get(Object, string, R): (V|R)
function get(Object, string): (V|undefined)
```

This might already work in NTI, but the problem with the current implementation is that the `R` template type is effectively free if the optional argument is missing, which means that the function is effectively allowed to return anything without complaint, when we would like to say `R=undefined` always if the optional argument is missing.

I propose the following semantic. Given a set of overloads, figure out which function signatures are assignable to which other signatures, provided that `?` is mapped to `*` in all parameter types and to `⟂` in all return types. If a function type with signature `f` is assignable to a function type with signature `g` (e.g. `f(string|number, *): number` is assignable to `function(string): ⟂`) then `g` is said to be a _refinement_ of `f`. When selecting overloads, find all overloads that could possibly match the currently-inferred argument (and return?) types, using the same temporary `*`/`⟂` substitution scheme. Compare the matching overloads pairwise and if any match is a refinement of another match, then discard the less-refined match. Now take the union of each parameter across the remaining matches to use for the inferred type of the unknown parameters (and the intersection of the return types?). This scheme has the advantage that it is independent of declaration order. But I'm not at all convinced it's actually sane, or sound. In particular, I'm very unsure about the return types' variance, and could potentially be convinced that they should have the same variance as the parameter types. I haven't worked through these examples thoroughly yet, but that's probably the next step.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.