Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does TypeScript track mutation of function static properties?

Tags:

typescript

I always thought that TypeScript does not track object mutations. For example:

type DescribableObject = {
  name: string;
  age: number;
};

// error
const obj: DescribableObject = {
  name: 'sdf'
}
obj.age = 2

But, it seems that in some circumstances it tracks mutation of function static properties.

type DescribableFunction = {
  description: string;
  (): boolean;
};

// error
const fn: DescribableFunction = () => true

//fn.description = 'hello';

If you uncomment //fn.description = 'hello';, the TypeScript error will disappear.

Furthermore, if you hover over fn you will see that TS treats fn as some kind of module.

enter image description here

What kind of module is the fn function? Is this behaviour documented?

like image 468
captain-yossarian Avatar asked Aug 03 '21 21:08

captain-yossarian


1 Answers

Starting with TypeScript 3.1, you are allowed to define "expando" properties on functions. This was implemented in microsoft/TypeScript#26368 as a fix to microsoft/TypeScript#15868. Apparently there are common patterns where developers add properties to functions in JavaScript, but before this feature there was no idiomatic way to do this in TypeScript.

Right now you can do this:

function foo(x: string) { foo.callCount++; return x.length }
foo.callCount = 0;

But before TypeScript 3.1 you had to do something like this:

function bar(x: string) { bar.callCount++; return x.length }
namespace bar {
  export var callCount = 0;
}

or this:

const baz = Object.assign(
  (x: string) => { baz.callCount++; return x.length },
  { callCount: 0 }
);

or even this:

const qux = ((x: string) => { qux.callCount++; return x.length }) as
  { (x: string): number; callCount: number };
qux.callCount = 0;

all of which were fairly ugly.


While it might be nice to allow expando properties on all objects (like DescribableObject) and not just functions, as requested in microsoft/TypeScript#12416, it seems that this is not as common or important a pattern to support. Generally speaking you can use an object literal to add all the properties you want at once (although sometimes dependencies can be difficult to deal with) but you can't do that with a function.

Playground link to code

like image 106
jcalz Avatar answered Oct 29 '22 02:10

jcalz