Skip to content

Function Statements - interface assignment, errorless property assignment #46941

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking β€œSign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
brandon942 opened this issue Nov 29, 2021 Β· 5 comments
Closed
Labels
Question An issue which isn't directly actionable in code

Comments

@brandon942
Copy link

brandon942 commented Nov 29, 2021 β€’

Suggestion

πŸ” Search Terms

Function Statement , static variables

⭐ Suggestion

Problem case:

function func(a, b) {
	func.counter = 1 // Property 'counter' does not exist on type '(a: any, b: any) => void'.  ts(2339)
}

My 3 suggestions related to this problem.

  1. Allow access/assignment of properties on a function object inside of the function body
  2. A way to attach a type/interface/typehint onto a function in a function statement (Type Cast or Assertions on the FunctionDeclaration #40378)
  3. Inference of arguments and the return type of a function

πŸ“ƒ Motivating Example

πŸ’» Use Cases

If you need static variables in func then using the func object as a bag is the only good solution in javascript.
I am not sure if this typescript error is a bug or a feature since assigning properties to func in the outer scope does not cause errors

function func(a, b) {}
func.counter = 1 // ok

I found the best way to get rid of the error is to add // @ts-ignore in front of the property assignment. However doing this everywhere just to deal with a typescript shortcoming isn't ideal.

My failed attempts to work around this error by working with typescript:

// trying an interface
// One shouldn't create an outside interface for properties that are only ever used inside of func
interface anObject {  (...args:any):any;   [x:string]:any  }

// Currently there is no syntax for assigning a type or interface onto func
// function func(a, b) {  func.counter = 1  } // Hence Suggestion 2.

// One has to break the function statement and turn it into an expression
// Typescript should not force one to change the structure of existing code to accommodate it. it should be simply additive.
var func : anObject = function (a, b) {  func.counter = 1  } // ok

// But now the info on arguments and return type of func is lost  (...args:any)=>any
// Attempt to fix this:
// fails
var func: (typeof func) & {counter:number} = function func__(params) {  func.counter = 1  } // 'func' is referenced directly or indirectly in its own type annotation.  ts(2502)
// fails
var func: { <T extends (...args: any) => any>(...args:Parameters<T>):ReturnType<T>; counter:number}  = function (a, b) {  func.counter = 1  }
// fails
var func: {(a, b):void; counter:number} = function (a, b) {  func.counter = 1  } // Property 'counter' is missing ...  ts(2741)
// Hence suggestion 3.
@whzx5byb
Copy link

I think what you need is Declaration Merging.

function func(a, b) {
    func.counter = 1
}
declare namespace func {
    export var counter: number
}

@RyanCavanaugh RyanCavanaugh added the Question An issue which isn't directly actionable in code label Nov 29, 2021
@brandon942
Copy link
Author

@whzx5byb Your solution seems elegant. The function remains untouched. Very good. But elegant here is relative. I haven't seen namespaces used in a hacky way like that - just to make an error go away. I'd rather not have that error in the first place. You're still dealing with an outside declaration that is strongly coupled with the function. You want that function to be atomic and easy to move.

What I need is a typescript that is opt in. One that doesn't insert itself where it isn't needed and shout error where there is none.
Better control over error emissions via config, and probably something like //@ts-off and //@ts-on for disabling typescript over un-annotated sections where you neither want nor need it. Types are supposed to be helpful in situations where they make sense, not something that makes you deal with them everywhere. You should be the one to decide where that is.

@nmain
Copy link

nmain commented Dec 1, 2021

Typescript has a lot of tools to deal with gradual typing. The scenario you've given has a strongly typed answer, the declaration merge; as well as weakly typed answers throughts-ignore, any casts, or noEmitOnError: false.

@typescript-bot
Copy link
Collaborator

This issue has been marked as 'Question' and has seen no recent activity. It has been automatically closed for house-keeping purposes. If you're still waiting on a response, questions are usually better suited to stackoverflow or the TypeScript Discord community.

@brandon942
Copy link
Author

Update:
The ultimate non-workaround solution: Do not use .ts files! (#22063 (comment))
(#47559)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Question An issue which isn't directly actionable in code
Projects
None yet
Development

No branches or pull requests

5 participants