-
Notifications
You must be signed in to change notification settings - Fork 716
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
Improve annotations #1343
Improve annotations #1343
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps could permit inject and multi inject to have two addditional parameters as a shortcut for the named and tagged decorators.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks great, I just added some comments
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great, we only need to update the changelog and this PR should be ready :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great! I just added some comments :)
I have one other commit to come regarding an unhelpful error message that can arise for decorated properties missing inject / multi inject. |
@tonyhallett that introduces another problem for
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What should we do with
export interface MetadataReader { getPropertiesMetadata(constructorFunc: Function): MetadataMap; }
Given that symbols can be keys ?
I would not change it. It's something Typescript has to fix in order to allow symbol index signatures
Should the wiki have an example with symbol property injection ?
Probably, but that's something we could add in another PR
Are you happy with the name of the additional Target property identifier ?
I'm not sure, do we need it? Maybe I'm missing something
src/annotation/inject_base.ts
Outdated
|
||
export function injectBase(metadataKey:string){ | ||
return (serviceIdentifier: ServiceIdentifierOrFunc) => { | ||
return (target: Object, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think Record<string | symbol, unknown> | interfaces.Newable<unknown>
is a better type for target
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not acceptable by the compiler
is not assignable to type 'Record<string | symbol, unknown>'. Index signature is missing in type
I think that I have provided a solution
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
interface ConstructorFunction {
new(...args:any[]):unknown,
readonly prototype:unknown
}
interface Prototype {
constructor: Function;
}
export type DecoratorTarget = Prototype | ConstructorFunction;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's indeed a valid solution. We want to introduce some linter rules in the future and that's the reason to ban some types. If you want I can create a PR to your branch with equivalent types which respect this rule :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you elaborate on CI ?
By all means change the typing. I hadn't seen the banning of Function. I look forward to seeing the equivalent types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can also just add this into out tslint
setup right now and then just try out what works best, since ban-types
are somehow related to both linters.
What was the problem with Record<string, unknown
anyway ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What was the problem with Record<string, unknown anyway ?
When applied to a property typescript will complain
is not assignable to type 'Record<string | symbol, unknown>'. Index signature is missing in type CLASSNAME
This is why
class ClassWithDecorators {
constructor(@decorator param:Dependency){}
@decorator
//@ts-ignore
prop:Dependency
@decorator
static staticProp:Dependency
}
// here is the error
const typescriptError:Record<string,unknown> = ClassWithDecorators.prototype
the js
var ClassWithDecorators = /** @class */ (function () {
function ClassWithDecorators(param) {
}
__decorate([
decorator,
__metadata("design:type", Dependency)
], ClassWithDecorators.prototype, "prop", void 0);
__decorate([
decorator,
__metadata("design:type", Dependency)
], ClassWithDecorators, "staticProp", void 0);
ClassWithDecorators = __decorate([
__param(0, decorator),
__metadata("design:paramtypes", [Dependency])
], ClassWithDecorators);
return ClassWithDecorators;
}());
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They differ. object is allowed in tslint.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@PodaruDragos
tell eslint to ignore the object and the Function types where they are used. The errors is there for lazy typing and errors that arise because of that.
In this instance the type signatures are ok and definitely better than unknown and of couse any.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the class being decorated had
[key:string]:unknown
Then Record<string | symbol, unknown> | interfaces.Newable<unknown>
will not error
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please see #1343 (comment).
src/annotation/decorator_utils.ts
Outdated
prototype:T | ||
} | ||
|
||
export type DecoratorTarget<T = Object> = ConstructorFunction<T> | T |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe that this is the best way of typing the target.
We differentiate between constructor function and prototype to prevent static properties and the prototype property is the proper way of doing so.
_tagParameterOrProperty
requires annotationTarget: Function
( this cannot be typed better with generics ) and when target is a constructor function it is obviously so. Although it is through a cast in tagParameter
_tagParameterOrProperty(METADATA_KEY.TAGGED, annotationTarget as ConstructorFunction, parameterIndex.toString(), metadata);
the type predicate could have been used instead.
src/annotation/decorator_utils.ts
Outdated
|
||
interface ConstructorFunction<T = Object>{ | ||
new (...args:unknown[]): T, | ||
prototype:T |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think prototype should not be T
since T is not an instance of the type, but the prototype instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
var ClassWithDecorators = /** @class */ (function () {
function ClassWithDecorators(param) {
}
__decorate([
decorator,
__metadata("design:type", Dependency)
], ClassWithDecorators.prototype, "prop", void 0);
__decorate([
decorator,
__metadata("design:type", Dependency)
], ClassWithDecorators, "staticProp", void 0);
ClassWithDecorators = __decorate([
__param(0, decorator),
__metadata("design:paramtypes", [Dependency])
], ClassWithDecorators);
return ClassWithDecorators;
}());
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mapped T to Prototype<T>
, properties now optional.
type Prototype<T> = {
[Property in keyof T ]:
T[Property] extends Function?
T[Property] :
T[Property] | undefined
} & {constructor:Function}
interface ConstructorFunction<T = Object>{
new (...args:unknown[]): T,
prototype:Prototype<T>
}
export type DecoratorTarget<T = Object> = ConstructorFunction<T> | Prototype<T>
Co-authored-by: notaphplover <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is definitely better ( nice work). feel free to merge when able. LGTM.
Thanks for your contributions on this and other pull requests ! Add this to the changelog as exports the helper createTaggedDecorator ? Are we doing merge commits or rebase and merge ? |
Description
I have created a new function that is used for all the decorators that are applicable to parameters and properties ( including accessors ).
I have also exported createTaggedDecorator from inversify.ts to be available for anyone to use.
Note that this function allows multiple metadata to be provided through a single decorator.
I have also refactored _tagParameterOrProperty as it was checking if it was being applied for a parameter or a property. Now it is passed only the information that it requires.
I also changed the type to include undefined
let paramOrPropertyMetadata: interfaces.Metadata[] | undefined = paramsOrPropertiesMetadata[key];
and then checked against undefined.
Related Issue
#1342
Motivation and Context
#1342
How Has This Been Tested?
Properly
Checklist: