I want to create a function object, which also has some properties held on it. For example in JavaScript I would do:
var f = function() { }
f.someValue = 3;
Now in TypeScript I can describe the type of this as:
var f: { (): any; someValue: number; };
However I can't actually build it, without requiring a cast. Such as:
var f: { (): any; someValue: number; } =
<{ (): any; someValue: number; }>(
function() { }
);
f.someValue = 3;
How would you build this without a cast?
This departs from strong typing, but you can do
if you are trying to get around oppressive strong typing like I was when I found this question. Sadly this is a case TypeScript fails on perfectly valid JavaScript so you have to you tell TypeScript to back off.
"You JavaScript is perfectly valid TypeScript" evaluates to false. (Note: using 0.95)
Update: This answer was the best solution in earlier versions of TypeScript, but there are better options available in newer versions (see other answers).
The accepted answer works and might be required in some situations, but have the downside of providing no type safety for building up the object. This technique will at least throw a type error if you attempt to add an undefined property.
TypeScript is designed to handle this case through declaration merging:
Declaration merging lets us say that something is both a function and a namespace (internal module):
This preserves typing and lets us write both
f()
andf.someValue
. When writing a.d.ts
file for existing JavaScript code, usedeclare
:Adding properties to functions is often a confusing or unexpected pattern in TypeScript, so try to avoid it, but it can be necessary when using or converting older JS code. This is one of the only times it would be appropriate to mix internal modules (namespaces) with external.
This is easily achievable now (typescript 2.x) with
Object.assign(target, source)
example:
The magic here is that
Object.assign<T, U>(t: T, u: U)
is typed to return the intersectionT & U
.Enforcing that this resolves to a known interface is also straight-forward. For example:
which errors due to incompatible typing:
caveat: you may need to polyfill Object.assign if targeting older browsers.
Old question, but for versions of TypeScript starting with 3.1, you can simply do the property assignment as you would in plain JS, as long as you use a function declaration or the
const
keyword for your variable:Reference and online example.
An updated answer: since the addition of intersection types via
&
, it is possible to "merge" two inferred types on the fly.Here's a general helper that reads the properties of some object
from
and copies them over an objectonto
. It returns the same objectonto
but with a new type that includes both sets of properties, so correctly describing the runtime behaviour:This low-level helper does still perform a type-assertion, but it is type-safe by design. With this helper in place, we have an operator that we can use to solve the OP's problem with full type safety:
Click here to try it out in the TypeScript Playground. Note that we have constrained
foo
to be of typeFoo
, so the result ofmerge
has to be a completeFoo
. So if you renamebar
tobad
then you get a type error.NB There is still one type hole here, however. TypeScript doesn't provide a way to constrain a type parameter to be "not a function". So you could get confused and pass your function as the second argument to
merge
, and that wouldn't work. So until this can be declared, we have to catch it at runtime: