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

prepare typescript 4.5 #1407

Merged
merged 13 commits into from
May 28, 2022
Merged

prepare typescript 4.5 #1407

merged 13 commits into from
May 28, 2022

Conversation

PodaruDragos
Copy link
Member

Prepare for TS 4.5

replace "" with '' to make it future eslint compatible
repace some of the any's
update all packages
@codecov-commenter
Copy link

codecov-commenter commented Mar 13, 2022

Codecov Report

Merging #1407 (ef9b8e7) into master (1df31d6) will decrease coverage by 0.29%.
The diff coverage is 97.87%.

@@            Coverage Diff             @@
##           master    #1407      +/-   ##
==========================================
- Coverage   99.85%   99.55%   -0.30%     
==========================================
  Files          53       53              
  Lines        1367     1363       -4     
  Branches      192      196       +4     
==========================================
- Hits         1365     1357       -8     
- Partials        2        6       +4     
Impacted Files Coverage Δ
src/annotation/lazy_service_identifier.ts 100.00% <ø> (ø)
src/constants/literal_types.ts 100.00% <ø> (ø)
src/container/container_snapshot.ts 100.00% <ø> (ø)
src/interfaces/interfaces.ts 100.00% <ø> (ø)
src/planning/plan.ts 100.00% <ø> (ø)
src/syntax/binding_to_syntax.ts 100.00% <ø> (ø)
src/utils/clonable.ts 100.00% <ø> (ø)
src/resolution/instantiation.ts 96.66% <77.77%> (-1.67%) ⬇️
src/container/container.ts 98.85% <92.50%> (-1.15%) ⬇️
src/annotation/decorator_utils.ts 100.00% <100.00%> (ø)
... and 43 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 1df31d6...ef9b8e7. Read the comment docs.

Copy link
Contributor

@jakehamtexas jakehamtexas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @PodaruDragos for your beautiful work in this PR 🙏

I have a few things that I would like to see changes about, and I am happy to discuss them more:

  1. Please reconsider the usage of never when defining a function type of unknown arity. (this tends to introduce complications with using the type and interacting with other types that depend on ...unknown[]).
  2. Please reconsider making a member public without addressing the underlying code smell (please see my comment for a writeup about this).


export function isStackOverflowExeption(error: Error) {
export function isStackOverflowExeption(error: unknown | Error): error is RangeError {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since unknown is a top type, unknown | Error is the equivalent of simply unknown to the compiler.

Lovely to see the usage of the is RangeError type predicate though 👍

Suggested change
export function isStackOverflowExeption(error: unknown | Error): error is RangeError {
export function isStackOverflowExeption(error: unknown): error is RangeError {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have noticed this situation of using SomeType | unknown at least one other time, and I will try to find it, but I know I may miss it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would Error | unknown work better?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, unfortunately it is the same no matter the order @dcavanagh

Any type union with unknown will simply be unknown without any of the other union members

@@ -6,7 +6,7 @@ import { BindingWhenOnSyntax } from "./binding_when_on_syntax";

class BindingToSyntax<T> implements interfaces.BindingToSyntax<T> {

private _binding: interfaces.Binding<T>;
public _binding: interfaces.Binding<T>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this, I think we should keep this member private and use the same tricks that are in use elsewhere to make it accessible with an as unknown as { _binding: interfaces.Binding<T> }, but only for now :). Please see my following comment.

Copy link
Contributor

@jakehamtexas jakehamtexas Mar 15, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that this is uncovering a smell.

I have noticed in the past that this member is private, but a type assertion is used in order to allow it to be accessed from a public location. This is a pattern that can be observed with other members, not just this one (for instance, _bindingDictionary).

A few places I have noticed this are among test files:

test/container/container.test.ts
test/syntax/binding_when_on_syntax.test.ts

Another place is in src/planning/planner.ts.

The reality of this smell is that there is a need for two forms of the containing types (in this case, I am referring to BindingToSyntax since this is where we have concentrated this specific example):

  1. A presentational version which maintains a correct API for the consumers of the package, and does not expose unnecessary members like _binding.
  2. An internal version which allows a more exposed API for the purposes of testing and proper layer integration with planner and friends.

I think that a good way of accomplishing this which resolves the smell is to rename the class with a _ prefix, as in BindingToSyntax => _BindingToSyntax, etc. This means that the current definition of this class will become the internal form of the class. Then, any members which are meant to be public may be accomplished in the following manner:

// Please ignore the lack of generic type vars, etc.,
// as this is only meant to be an example
class BindingToSyntax {
  constructor(
    private _internal = new _BindingToSyntax(...args)
  ) {}

  get somePublicMemberFromOriginalImplementation() {
    return this._internal.somePublicMemberFromOriginalImplementation;
  }
}

etc.

Finally, with a presentational/internal pattern like this, we are free to fix the original problem for those classes which have the behavior of needing a private member exposed for testing, or for use in planner or similar. We can simply make the members that needed this special access despite being private members into public ones instead.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you agree @PodaruDragos then I will spin the above comment off as an issue for us to work on at some time.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

totally agree with this. Do you also agree that we should just move everything to actual #private fields since you know they are really private ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is worth taking a look at cleaning this up

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@PodaruDragos I think we should definitely do #private type of vars in the future, for now let's not increase the scope of this PR

Comment on lines 78 to +80
let serviceIdentifier = serviceIdentifiers[index];
const injectIdentifier = (metadata.inject || metadata.multiInject);
serviceIdentifier = (injectIdentifier) ? (injectIdentifier) : serviceIdentifier;
const injectIdentifier = metadata.inject || metadata.multiInject;
serviceIdentifier = (injectIdentifier ? injectIdentifier : serviceIdentifier) as interfaces.ServiceIdentifier<unknown> | undefined;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know it was just simple type annotations being added here, but since injectIdentifier isn't used outside of the assignment on line 80, it would be great if someday we could rewrite this in the following manner:

let serviceIdentifier = metadata.inject ?? metadata.multiInject ?? serviceIdentifiers[index];

Ideally this would avoid needing an explicit cast to interfaces.ServiceIdentifier<unknown> | undefined, since (I hope) we have strictNullChecks enabled.

This would be better enabled by making the return type of formatTargetMetadata use interfaces.ServiceIdentifier<unknown> for the inject and multiInject properties.

@@ -90,7 +92,7 @@ namespace interfaces {
factory: FactoryTypeFunction | null
};

export type Provider<T> = (...args: any[]) => (((...args: any[]) => Promise<T>) | Promise<T>);
export type Provider<T> = (...args: never[]) => (((...args: never[]) => Promise<T>) | Promise<T>);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would highly recommend leaving this type as any. never is not really a good candidate for this use case.

In my mind, any is acceptable for defining function args with unknown arity.

@@ -40,6 +40,8 @@ namespace interfaces {

export type Newable<T> = new (...args: never[]) => T;

export type Instance<T> = T & Record<string, () => void>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting choice here. I think there may be a better name for this type as it relates to the context it is used in.

  1. I like the idea of encapsulating this type with type keyword.
  2. Calling it Instance feels a little undercooked since the right hand side of the assignment does not really imply to me that this type is exlusively Instance-like. Does it seem possible that we could infer a better name from its usage?
  3. Should we consider whether this should be exported publicly from the package? We tend to put all interfaces in this module, which makes them available to consumers of the package, but thinking of proper encapsulation, does a consumer of the package care about this type? Is there a better/closer place to put?

I'm personally ready to challenge the assumption that using interfaces.ts for all interfaces and types is the correct pattern, but it's certainly not in scope for this one issue :) Maybe we should keep it here for now and thinking about breaking apart interfaces in a future breaking changes release?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jakehamtexas I agree about the interfaces. interfaces.ts should really only export the interfaces we want to see in the public api.

@@ -1,7 +1,5 @@
const BindingCount = {
export const BindingCount = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this a lot better. Maybe in the future this is better as an enum?

Copy link
Member

@dcavanagh dcavanagh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for all the work. Looking good and generating some discussion!

@@ -91,4 +91,4 @@ jobs:
uses: codecov/codecov-action@f32b3a3741e1053eb607407145bc9619351dc93b # renovate: tag=v2
with:
directory: coverage/
fail_ci_if_error: true
fail_ci_if_error: true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing new line

};

export { BindingCount };
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing new line

@@ -1,48 +1,9 @@
{
"name": "inversify",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if we need this reorganized.

@@ -40,6 +40,8 @@ namespace interfaces {

export type Newable<T> = new (...args: never[]) => T;

export type Instance<T> = T & Record<string, () => void>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jakehamtexas I agree about the interfaces. interfaces.ts should really only export the interfaces we want to see in the public api.

@@ -6,7 +6,7 @@ import { BindingWhenOnSyntax } from "./binding_when_on_syntax";

class BindingToSyntax<T> implements interfaces.BindingToSyntax<T> {

private _binding: interfaces.Binding<T>;
public _binding: interfaces.Binding<T>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is worth taking a look at cleaning this up

@@ -40,7 +40,7 @@ describe("Context", () => {
const plan = new Plan(context, ninjaRequest);
context.addPlan(plan);

expect(context.plan.rootRequest.serviceIdentifier).eql("Ninja");
expect(context.plan.rootRequest.serviceIdentifier).eql('Ninja');
});

});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing new line

it('should thrown an exception non factory binding.type', () => {
const binding = new Binding('', 'Singleton');
binding.type = 'Instance';
expect(() => getFactoryDetails(binding)).to.throw('Unexpected factory type Instance');
});
})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing new line

const id1 = id();
expect(id1).to.be.a("number");
expect(id1).to.be.a('number');
});

});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing newline


const symbolWithoutDescription = Symbol();
expect(getSymbolDescription(symbolWithoutDescription)).to.equal("");
expect(getSymbolDescription(symbolWithoutDescription)).to.equal('');
});

});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing new line

@@ -20,7 +20,10 @@
"max-file-line-count": false,
"max-line-length": [
true,
140
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I would rather have the imports have line breaks after 3

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps better under a different scope? (See previous formatting comment)

dependabot bot and others added 4 commits May 21, 2022 12:21
Bumps [minimist](https://github.com/substack/minimist) from 1.2.5 to 1.2.6.
- [Release notes](https://github.com/substack/minimist/releases)
- [Commits](https://github.com/substack/minimist/compare/1.2.5...1.2.6)

---
updated-dependencies:
- dependency-name: minimist
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Podaru Dragos <[email protected]>
@PodaruDragos PodaruDragos merged commit 3975ab2 into master May 28, 2022
@PodaruDragos PodaruDragos deleted the update-packages branch May 28, 2022 08:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants