Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How would you define a type in typescript that's both an object and a function at the same time?

After reading this answer to the question How is jQuery's $ a function and an object?, it got me thinking. How would you define this kind of type in typescript?

In regular JS this is completely valid:

var f = function() { alert('yo'); }
f.foo = "bar";

alert(f.foo); // alerts "bar"
f();          // alerts "yo"

However in typescript, f.foo would throw an error, Property 'foo' does not exist on type '() => void'.

While you could achieve a similar result using bracket notation:

var f = function() { alert('yo'); }
f['foo'] = "bar";

alert(f['foo']); // alerts "bar"
f();          // alerts "yo"

This would entirely bypass the type system, and there for the type safety, of typescript.

Is there a way of implementing this type of functionality without violating the type safety of typescript?

like image 609
Olian04 Avatar asked Dec 09 '25 03:12

Olian04


1 Answers

You can define this kind of type by specifying an invocation signature on an interface:

interface MyFoo {
    (): void;
    foo: string;
}

And you can initialize the object by casting the function to any before assigning it to the variable:

let f: MyFoo = function() { alert('yo'); } as any;
f.foo = 'bar';
x();

Playground link


As an aside, the JQueryStatic type (which is the type of the $ variable) is defined similarly in the JQuery defintions:

interface JQueryStatic<TElement extends Node = HTMLElement> {
    ...
    Deferred: JQuery.DeferredStatic;
    ...
    (html: JQuery.htmlString, ownerDocument_attributes: Document | JQuery.PlainObject): JQuery<TElement>;
    ...
    (selector: JQuery.Selector, context: Element | Document | JQuery | undefined): JQuery<TElement>;

(Can't put the links inline, so here they are)

  • interface start
  • Deferred property
  • First invokable signature

However, this doesn't really help, as your question is also how to initialize a variable with this type. The JQuery library doesn't have that problem, because it isn't written in Typescript.

like image 199
Zev Spitz Avatar answered Dec 11 '25 17:12

Zev Spitz



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!