Skip to content
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

Use of decorator factories as decorators is not flagged as an error #3246

Closed
mhegazy opened this issue May 21, 2015 · 7 comments · Fixed by #3249
Closed

Use of decorator factories as decorators is not flagged as an error #3246

mhegazy opened this issue May 21, 2015 · 7 comments · Fixed by #3249
Assignees
Labels
Breaking Change Would introduce errors in existing code Bug A bug in TypeScript Fixed A PR has been merged for this issue

Comments

@mhegazy
Copy link
Contributor

mhegazy commented May 21, 2015

As mentioned in #2249 (comment), consider this:

// decorator factory
function F(options?: {optional: boolean}) {
     // returns a property decorator
    return (target , propertyKey): void => {
        // decorator implementation
    }
}

class C {
    @F()    // OK, used as a factory
    prop1; 

    @F   // No error
    prop2; 
}

The problem here is that the compiler compares the signature of the decorator factory F against declare type PropertyDecorator = (target: Object, propertyKey: string | symbol) => void; which ends up to be assignable. I think we need a stronger check here to make sure we catch these issue.

@mhegazy mhegazy added API Relates to the public API for TypeScript Bug A bug in TypeScript and removed API Relates to the public API for TypeScript labels May 21, 2015
@mhegazy mhegazy assigned ahejlsberg and rbuckton and unassigned ahejlsberg May 21, 2015
@mhegazy mhegazy added this to the TypeScript 1.6 milestone May 21, 2015
@mhegazy mhegazy added the Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. label May 21, 2015
@mhegazy mhegazy mentioned this issue May 21, 2015
@JsonFreeman
Copy link
Contributor

Oh, it's the bivariance again. Ideally the signature of F would not be assignable to PropertyDecorator because Object is not assignable to {optional: boolean}. But because of signature bivariance we allow this.

The original issue raised in #2249 is different though, because the type of F's parameter is any.

What is the requirement for the return type of a property decorator? Could we require a property decorator to be void, or is that too drastic? That would fix this because F returns a function.

@mhegazy
Copy link
Contributor Author

mhegazy commented May 21, 2015

@JsonFreeman yes you are correct they are two different issues. the core is that just doing the assignablity check is not enough to catch common errors of using the decorator vs. the factory. @rbuckton is looking into treating it as a call expression instead to make it an error to pass extra arguments (i.e. as if we are calling F with (target, propetyKey) ).

@JsonFreeman
Copy link
Contributor

Yeah that is another possibility, but it breaks down if you have a decorator factory with two parameters... It seems like a fix that happens to work in this case, but doesn't really solve the overall problem of a decorator factory being confused with a decorator. That is why I think enforcing a void return might make more sense.

I also think this highlights an inconsistency between assignability and call resolution. Assignability allows "calling with too many arguments", but call resolution does not. I suppose the reason for the inconsistency is that calling something with too many arguments is totally useless, but assigning something with fewer arguments is useful if the assignment target is trying to be maximally generous to the source with its parameters.

@mhegazy
Copy link
Contributor Author

mhegazy commented May 21, 2015

the return type can not always be void, e.g. in the case of class, and method decorators. we talked about two way assignability (as a substitute to identity), but this seems to be too restrictive.

@rbuckton
Copy link
Member

I'm going the same route we went with tagged template expressions and resolving a decorator as a call.

For tagged template expressions, we create a synthetic argument in getEffectiveCallArguments that we later replace with globalTemplateStringsArrayType when we call checkApplicableSignature.

To make the same approach work for decorators, I'm looking into adding a getSyntheticArgumentType which will encompass both the TemplateStringsArray argument for a tagged template expression, and the arguments for a decorator based on its target. The result is much more reliable and flexible than the current approach, and resolves some other small issues with typing decorators today.

@JsonFreeman
Copy link
Contributor

Ok, yes. In general calling something is more reliable than checking assignability. You get better arity checking, you get parameter contravariance, and you even get type argument inference for the type parameters of the decorator 😃 It is a bit like contextual signature instantiation (which is instantiateSignatureInContextOfin the compiler), but it does the whole call, not just the type argument inference.

@rbuckton
Copy link
Member

I've created a PR for this issue, based on my proposed resolution above.

@mhegazy mhegazy added Fixed A PR has been merged for this issue and removed Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. labels Jun 24, 2015
@mhegazy mhegazy added the Breaking Change Would introduce errors in existing code label Sep 17, 2015
@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Breaking Change Would introduce errors in existing code Bug A bug in TypeScript Fixed A PR has been merged for this issue
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants