Skip to content
This repository was archived by the owner on May 22, 2025. It is now read-only.

Handle callable interfaces / call signatures #413

Open
mprobst opened this issue Feb 28, 2017 · 8 comments
Open

Handle callable interfaces / call signatures #413

mprobst opened this issue Feb 28, 2017 · 8 comments

Comments

@mprobst
Copy link
Contributor

mprobst commented Feb 28, 2017

interface ToStringer {
  (a: number): string;
  prop: string;
}

Currently generates an interface including prop, but dropping the call signature.

Options:

  1. emit {?} for the time being
  2. special case interfaces that only have callable members. This seems to be frequent
  3. special Closure tricksery

For the last item, it's possible to re-open a type through a value in Closure:

/**
 * @typedef {function(number): string}
 */
ToStringer;

/** @type {ToStringer} */
var ToStringer_;
/** @type {string} */
ToStringer_.prototype.prop;

The last idea might be overkill.

@mprobst mprobst added this to the Warning free typed mode in g3 milestone Feb 28, 2017
@evmar
Copy link
Contributor

evmar commented Feb 28, 2017

We have a precedent for #2 here:

// Try to special-case plain key-value objects and functions.

That handles indexable interfaces as long as they don't have other fields.

@evmar
Copy link
Contributor

evmar commented Feb 28, 2017

Oh actually maybe that code already implements #2 ?

@mprobst
Copy link
Contributor Author

mprobst commented Feb 28, 2017

Looks like it, but seems like this has issues for at least the callable that I ran into. Which I of course don't remember :-(

@trevorade
Copy link

Any traction on this?

I would assume your example would be written as an externs file like so:

/**
 * @param {number} a
 * @return {string}
 */
function ToStringer(a);

/** @type {string} */
ToStringer.prop;

If, as an author, you have control over the .d.ts file, you can work around this issue using declare namespace with the same name as the underlying function like so:

/**
 * Loads a single module.
 * @param module Module to load
 * @return The loaded module
 */
declare function require(module: string): any;

/**
 * Asynchronously loads the specified modules.
 * @param modules Required modules to load.
 * @param ready Called when required modules are ready.
 * @param errback Called when required modules are ready.
 */
declare function require(
    modules: string[], ready?: () => void,
    errback?: (error: any) => void): void;

declare namespace require {
  function config(
      params: AMDLoader.IConfigurationOptions, shouldOverwrite?: boolean): void;
}

Which generates the following externs:

/**
 * Loads a single module. / Asynchronously loads the specified modules.
 * @param {string|!Array<string>} module_or_modules Module to load / Required modules to load.
 * @param {(undefined|function(): void)=} ready Called when required modules are ready.
 * @param {(undefined|function(?): void)=} errback Called when required modules are ready.
 * @return {?|void} The loaded module
 */
function require(module_or_modules, ready, errback) {}

/**
 * @param {!AMDLoader.IConfigurationOptions} params
 * @param {(undefined|boolean)=} shouldOverwrite
 * @return {void}
 */
require.config = function(params, shouldOverwrite) {};

@mprobst
Copy link
Contributor Author

mprobst commented Nov 9, 2018

I would assume your example would be written as an externs file like so:

Did you check if that works in Closure Compiler? I.e. can you use ToStringer as a type?

@trevorade
Copy link

No, you're right. That doesn't work.

Given the following externs:

/** @record */
class _ToStringerProps {
  constructor() {
    /** @type {string} */
    this.prop;
  }
}

/** @typedef {function(string): !ToStringer} */
let _ToStringerFunc;

/** @typedef {_ToStringerProps|_ToStringerFunc} */
let ToStringer;

/** @type {!ToStringer} */
let toStringer;

And the following source code:

const /** string */ str1 = toStringer('a').prop;
const /** string */ str2 = toStringer.prop;

We get:

input0:1: WARNING - (_ToStringerProps|function(string): (Function|_ToStringerProps|null)) expressions are not callable
const /** string */ str1 = toStringer('a').prop;

Again, if you don't care about using the interface as a type you can make this work. But yeah, if you need it to be treated as a type, looks like you're out of luck.

Is there a sister Issue for the Closure Compiler to support this?

@trevorade
Copy link

To be clear, I first tried to just use my first externs but that didn't work so I was trying some other things.

You can't have a function which is not a constructor be used as a type in Closure.

@mprobst
Copy link
Contributor Author

mprobst commented Nov 9, 2018 via email

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants