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

fix: expose referred DefinitionType if it wraps an AliasType #1047

Closed
wants to merge 3 commits into from

Conversation

tuner
Copy link
Contributor

@tuner tuner commented Dec 2, 2021

Closes #1046.

The UnionType object representing the Union type in the example was buried deep in the type hierarchy. The getChildren() method of ReferenceTypeFormatter ignored DefinitionTypes and thus, didn't to expose the hierarchically referred type.

This PR exposes the referred DefinitionType if it wraps an AliasType. This seems to work, but I'm not 100% sure that this is the proper way to fix this – mainly because understanding the purpose of different types is quite difficult. However, all tests pass.

image

@@ -16,8 +17,12 @@ export class ReferenceTypeFormatter implements SubTypeFormatter {
return { $ref: `#/definitions/${this.encodeRefs ? encodeURIComponent(ref) : ref}` };
}
public getChildren(type: ReferenceType): BaseType[] {
if (type.getType() instanceof DefinitionType) {
return [];
const referredType = type.getType();
Copy link
Member

Choose a reason for hiding this comment

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

Does derefType(...) work here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think it goes too deep in the hierarchy. It returns the UnionType but we need the DefinitionType that wraps it.

Copy link
Member

Choose a reason for hiding this comment

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

Are you saying we need the last definition type right above the non definition type? Maybe it's with pulling that functionality into a until function. We could add a flag to the deref function.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So, we have a piece of path from a RefType to a UnionType:

ReferenceType -> DefinitionType -> AliasType -> UnionType

Originally the traversal didn't reach the DefinitionType or its children, but we need to return a/the DefinitionType that wraps the UnionType. The intervening AliasType is a bit mystery to me. I guess we could also return an instance of a new DefinitionType that wraps the the UnionType directly(?) Anyway, I'm not sure which are exactly the situations where the traversal in ReferenceTypeFormatter should proceed to the DefinitionType. Now I used the presence of an AliasType as an indication for that. Although it works, it may not make much sense.

It actually seems that we could always traverse the DefinitionType. Tests seem to pass. I just thought that there was some reason for stopping propagation there. Maybe it was again an optimization that isn't needed any more?

Copy link
Member

Choose a reason for hiding this comment

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

If we can just not have this check here, let's delete it. I don't understand the logic otherwise ;-)

Copy link
Member

Choose a reason for hiding this comment

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

Hmm I spent some time stepping through the execution but didn't see the obvious bug yet. I added a breakpoint in ReferenceTypeFormatter.ts and then debugged your new test case and the existing test cases. I then set the code to generate the schema files from the tests and disabled the reachability check.

Alternatively, I created a test file and then ran yarn run run -p test.ts -t Intersection

I'm not satisfied with the hack above tbh.

The alias type makes sense. It's the union.

Screen Shot 2022-01-13 at 18 59 50

It goes away if you inline the type.

I would really like to understand why we don't format the definitions for the children.

Copy link
Member

Choose a reason for hiding this comment

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

I have a hunch what's happening.

We can expand the Intersection type to export type Intersection = (A & C) | (B & C);. See #985 and d9b9a15.

This means, we think that we don't need the Union type anymore, since we transformed it in d9b9a15#diff-79b658f531e0da963f97358d1c4ab65d18674a91e460f86500cca7cbdee966b3R23.

We addressed part of this for aliases that are inside the union but not for the union itself in https://github.com/vega/ts-json-schema-generator/pull/985/files.

So I think the fix needs to be in the Intersection/Union code, not the reference type.

Copy link
Member

Choose a reason for hiding this comment

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

We return [] because we know that definitions should already have been created. They usually already are but here is the exception.

Copy link
Member

Choose a reason for hiding this comment

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

Hmm, or maybe we can just return this.childTypeFormatter.getChildren(referredType) instead of doing the check. Maybe we do this because we think the definition is already through some other way defined. However, since we do caching and detect duplicated, I think we should be fine just returning the children.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you for spending time on this issue/pr!

Copy link
Member

@domoritz domoritz left a comment

Choose a reason for hiding this comment

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

Let's just make sure this is the right fix. Otherwise looks good.

y: number;
}

export type Union = Container | Silly;
Copy link
Member

Choose a reason for hiding this comment

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

Let's just test two things. First, what if we inline this type or not export it. Second, what if we define another type that just aliases Union?

You can test it locally. No need to write a test case unless you notice that it fails.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure. I tested various combinations of inlining, not exporting, aliasing, etc. No missing definitions occurred and the produced schemas looked okay to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hi @domoritz! What do you think, is there still something else to test or adjust?

Copy link
Member

Choose a reason for hiding this comment

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

No. I need to do a review. Thank you for your patience while I come back from the break and need to catch up on some other things.

I bumped up this review in my queue.

@domoritz
Copy link
Member

Moved to #1093.

@domoritz domoritz closed this Jan 14, 2022
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.

Crash on a union type in an intersection type
2 participants