Skip to content

Function augmented types are enforced inconsistentlyΒ #61554

Closed as not planned
Closed as not planned
@PartMan7

Description

@PartMan7

πŸ”Ž Search Terms

function, interface, augment, parentheses, inconsistent

πŸ•— Version & Regression Information

  • This is the behavior in every version I tried (all the way down to 3.3.3), and I reviewed the FAQ for entries about pretty much everything.

⏯ Playground Link

https://www.typescriptlang.org/play/?#code/CYUwxgNghgTiAEBzCB7ARlC8DeAoe8AlgHYAuIMAZlGAgGICuxYphKxO+B85AzqQH4AXPH4wSiANxcAvrjm4w7fvAAqIfo2YBGeAF54lJizYcAFAEocM6UuIr1m4wCZ98M0eat27q9hkW0p4mPo6kWmAAzJbWtsqkahqkAIIwMCgA7hG6BjF6AHyxivGJ-KnpWS5uZnmF-oG4uGHZAHR8CQba0s0ubUluXU1JEZF9Kp3dSeWZre0Dk2VpM71zE40gAB4ADigwCf7SQA

πŸ’» Code

declare global {
  interface Function {
    test?: string;
  }
}

const TestFunc1 = function () {};
const TestFunc2 = (function () {});
function TestFunc3() {};
const TestArrowFunc1 = () => {};
const TestArrowFunc2 = (() => {});

TestFunc1.test = 1;
TestFunc2.test = 1;
TestFunc3.test = 1;
TestArrowFunc1.test = 1;
TestArrowFunc2.test = 1;

export {};

πŸ™ Actual behavior

Only TestFunc2 and TestArrowFunc2 (the ones with ()) are flagged as errors. All other cases are perfectly fine according to TypeScript.

πŸ™‚ Expected behavior

All five of the .test = 1 lines should be flagged as errors, since 1 is not compatible with string | undefined.

Additional information about the issue

I've only found this issue with properties on functions. Others (String, Number) work correctly in all cases, and even Function works correctly when the value is wrapped with () - but wrapping it in parentheses doesn't seem to have any semantic differences here.


My use case is trying to augment functions (specifically React components) in a massive codebase while enforcing types on the metadata being passed.

const Component = () => {};
Component.__metadata = metadata; // Function has the type so I don't have to cast or check!

Because of this bug, I have to use this instead:

import type { Metadata } from 'path/to/metadata/type';

const Component = () => {};
Component.__metadata = metadata satisfies Metadata as Metadata;

which is poor DX on three fronts (frustrating to read/write, appears redundant but is needed, and introduces a required import on every use).


Looked at:

Metadata

Metadata

Assignees

No one assigned

    Labels

    Not a DefectThis behavior is one of several equally-correct options

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions