Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extending the Function interface in the presence of an import statement #5944

Closed
bgrieder opened this issue Dec 5, 2015 · 5 comments
Closed

Comments

@bgrieder
Copy link

bgrieder commented Dec 5, 2015

I would like to extend Function and this compiles fine

interface Function {
    hello(): any
}
Function.prototype.hello = function() {
    console.log('hello world')
}

function noop() {}

noop.hello() //==> 'hello world' in the console

However, as soon as I try to add an import statement, say

import { sayHello } from './Anything'

where Anything only contains

export function sayHello() { console.log('hello')}

The compilation fails on Function.protoype.hello with the message error TS2339: Property 'hello' does not exist on type 'Function'

Why ?

View in Playground

Note1: the compilation fails with tsc options set to either --target es5 --module commonjs or --target es6
Note2: my final goal is to export utilities in a library to be able to write code like myfunc.liftM(myMonad1, MyMonad2) or myfunc1.compose(myfunc2), etc...

@yortus
Copy link
Contributor

yortus commented Dec 5, 2015

In the TypeScript spec section 11.3 it states

Source files that contain at least one module import or export declaration are considered separate modules.

In your original working version, there are no import or export declarations, so the things you've declared there are in the global namespace. So you've told tsc that the global Function type has a 'hello' member (your declaration of Function merges with the built-in declaration).

When you introduce the export line, the quote above kicks in and now the whole file is considered to be in its own separate namespace. So now you have a local type called Function that has a hello member, and this is completely distinct from the global Function type.

The final line noop.hello() should still work if you run the emitted code, but the compiler error is because the global Function type does not have a hello member.

@yortus
Copy link
Contributor

yortus commented Dec 5, 2015

As per your 'Note2' you want to monkey-patch built-in objects, which is frowned on if it can be avoided.

Having said that, TypeScript can model monkey-patching just fine if you must do it. Just declare the additions to the built-ins ambiently in a .d.ts file. Any TypeScript project using your library would need to include that .d.ts file in its own build to pick up the patched types. E.g.:

// file: monkey.d.ts
interface Function {
    hello(): any
}

// file: monkey.ts
///<reference path="monkey.d.ts" />
export function patch() {
    Function.prototype.hello = function() {
        console.log('hello world')
    }
}


// file: example.ts
///<reference path="monkey.d.ts" />
import monkey = require('./monkey');
monkey.patch();

function noop() {}

noop.hello() // OK: should pick up 'hello' member declared in monkey.d.ts

Hopefully you can see the pitfalls of doing this (eg what if you forget to call monkey.patch()?). But that's how you can do it.

@yortus
Copy link
Contributor

yortus commented Dec 5, 2015

See also #4166

@bgrieder
Copy link
Author

bgrieder commented Dec 5, 2015

@yortus Great answer. Thanks.
#4166 is spot on (and this can be considered as a duplicate)

So, unfortunately there is no way to "export" augmentation to the existing interfaces from a library.

I understand the dangers of monkey patching but still see this as unfortunate: I come from the Scala world where there is a way to "pimp" existing types without having to resort to monkey patching.
Such a powerful mechanism is badly missed in the javascript/typescript world....

@bgrieder bgrieder closed this as completed Dec 5, 2015
@yortus
Copy link
Contributor

yortus commented Dec 5, 2015

Agreed. It's trivial to do in JavaScript, so it would be good to be able to model it just as easily in TypeScript.

@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
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

2 participants