20

i extended function prototype but typescript doesn't recognize it.

Function.prototype.proc = function() {
  var args, target, v;
  var __slice = [].slice;
  args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
  target = this;
  while (v = args.shift()) {
    target = target(v);
  }
  return target;
};
// generated by coffee-script

var foo: (number) => (string) => number
  = (a) => (b) => a * b.length;
console.log(foo.proc("first", "second"))

result: tsc -e

The property 'proc' does not exist on value of type 'Function'

how do i extend this object?

0

5 Answers 5

32

There is a Function interface in the standard typescript lib which declares the members of Function objects. You will need to declare proc as a member of that interface with your own add on like the following:

interface Function {
    proc(...args: any[]): any;
}

This interface will need to be referenced from anywhere you intend to use 'proc'.

Sign up to request clarification or add additional context in comments.

1 Comment

How to do this with your own classes?
15

Like this:

declare global {
    interface Function {
        proc() : any;
    }
}

Without 'declare global' it doesn't work.

That's how module augmentation works in recent TypeScript versions. Check out the documentation and scroll down to the Module augmentation section.

2 Comments

I wish you could explain why, but I confirm, same here. Thanks!
That's how module augmentation works in recent TypeScript versions. Scroll it down: typescriptlang.org/docs/handbook/declaration-merging.html
1

Static method

declare global { 
  interface NumberConstructor {
    formatCurrency(num: number): string;
  }
}

export const formatCurrency = (num: number) => {
  if (!num) return '$0';
  return '$' + num.toFixed(0).replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');
};

Number.formatCurrency = formatCurrency;

non-static method

declare global {
  interface Number {
    formatCurrency: () => string;
  }
}

Number.prototype.formatCurrency = function() : string {
  return '$' + this.toFixed(0).replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');
}

Comments

0

Just adding that if you're trying to add define something that's already declared, then this is the typesafe way of doing so, that also guards against buggy for in implementations.

export const augment = <U extends (string|symbol), T extends {[key :string] :any}>(
    type  :new (...args :any[]) => T,
    name  :U,
    value :U extends string ? T[U] : any
) => {
    Object.defineProperty(type.prototype, name, {writable:true, enumerable:false, value});
};

Which can be used to safely polyfill. Example

//IE doesn't have NodeList.forEach()
if (!NodeList.prototype.forEach) {
    //this errors, we forgot about index & thisArg!
    const broken = function(this :NodeList, func :(node :Node, list :NodeList) => void) {
        for (const node of this) {
            func(node, this);
        }
    };
    augment(NodeList, 'forEach', broken);

    //better!
    const fixed = function(this :NodeList, func :(node :Node, index :number, list :NodeList) => void, thisArg :any) {
        let index = 0;
        for (const node of this) {
            func.call(thisArg, node, index++, this);
        }
    };
    augment(NodeList, 'forEach', fixed);
}

Unfortunately it can't typecheck your Symbols due to a limitation in current TS, and it won't yell at you if the string doesn't match any definition for some reason, I'll report the bug after seeing if they're already aware.

Comments

-1

I am adding this to advise against adding prototypes like the example shown in question since many people view this question. Add it as follows:

interface Function {
    proc(...args: any[]): any;
}

Object.defineProperty(Function.prototype, 'proc', { value: function(arg: any[]) {
    // Your function body
}});

The reason is if you add it to the prototype directly, it could get enumerated if an instance of that function get's enumerated over. for i in ... Now this block could be in a code you do not control (recently happened to me), so it is best to keep your code as safe as possible.

4 Comments

you're not typechecking anything (defineProperty() doesn't do any for you), you need to use a helper like this
@Hashbrown did you read the question? there is no typechecking here, just how you extend an existing prototype
this is typescript, literally the whole point of using the language. Everything you add you should type properly. I just thought if you're going to warn people about enumeration and stuff, why not keep up the good practice and use the interface that we just wrote (not to mention that these types of questions are seen by maaany people and not just the question-asker, so putting in best practice and not just answering the question, like you did here adding your own advice too, is nothing but a good thing)
I agree with you, everything should be typed, but some of the typing syntax could be a little daunting and confuse people and make them miss the point.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.