Skip to content

Nesting multiple, separate objects defined with oneOf in allOf does not work as expected #544

@joshAg

Description

@joshAg

I think this is related to #513. If I have an object that is all of X and Y, and X is one of X1 or X2 and Y is one of Y1 or Y2, I'd expect that the resulting object would be (X1 | X2) & (Y1 | Y2) (or X & Y, since i don't have a preference how much decomposition there is), not (X1 | X2 | Y1 | Y2).

I'm not entirely sure how to fix this, but I have a test you can add to test/allof_oneof_test.ts that hits this issue and that continues the the player/team example of the other tests.

    it("multiple 'allOf' and 'oneOf' nestings schema", async () => {
        const base: JsonSchemaDraft07.Schema = {
            $id: '/test/multiple/allOf/oneOf',
            $schema: 'http://json-schema.org/draft-07/schema#',
            type: 'object',
            allOf: [
                { $ref: '/test/multiple/allOf/oneOf/general' },
                { $ref: '/test/multiple/allOf/oneOf/team' },
                { $ref: '/test/multiple/allOf/oneOf/player' },
            ],
        };
        const general: JsonSchemaDraft07.Schema = {
            $id: '/test/multiple/allOf/oneOf/general',
            $schema: 'http://json-schema.org/draft-07/schema#',
            type: 'object',
            properties: {
                id: { type: 'integer' },
                type: { type: 'string' },
            },
            required: ['id', 'type'],
        };
        const team: JsonSchemaDraft07.Schema = {
            $id: '/test/multiple/allOf/oneOf/team',
            $schema: 'http://json-schema.org/draft-07/schema#',
            type: 'object',
            oneOf: [
                { $ref: '/test/multiple/allOf/oneOf/team1' },
                { $ref: '/test/multiple/allOf/oneOf/team2' },
            ],
        };
        const team1: JsonSchemaDraft07.Schema = {
            $id: '/test/multiple/allOf/oneOf/team1',
            $schema: 'http://json-schema.org/draft-07/schema#',
            type: 'object',
            properties: {
                name: { type: 'string' },
            },
            required: ['id', 'name'],
        };
        const team2: JsonSchemaDraft07.Schema = {
            $id: '/test/multiple/allOf/oneOf/team2',
            $schema: 'http://json-schema.org/draft-07/schema#',
            type: 'object',
            properties: {
                founded: { type: 'string' },
            },
            required: ['id', 'founded'],
        };
        const player: JsonSchemaDraft07.Schema = {
            $id: '/test/multiple/allOf/oneOf/player',
            $schema: 'http://json-schema.org/draft-07/schema#',
            type: 'object',
            oneOf: [
                { $ref: '/test/multiple/allOf/oneOf/player1' },
                { $ref: '/test/multiple/allOf/oneOf/player2' },
            ],
        };
        const player1: JsonSchemaDraft07.Schema = {
            $id: '/test/multiple/allOf/oneOf/player1',
            $schema: 'http://json-schema.org/draft-07/schema#',
            type: 'object',
            properties: {
                firstName: { type: 'string' },
                lastName: { type: 'string' },
            },
            required: ['id', 'firstName', 'lastName'],
        };
        const player2: JsonSchemaDraft07.Schema = {
            $id: '/test/multiple/allOf/oneOf/player2',
            $schema: 'http://json-schema.org/draft-07/schema#',
            type: 'object',
            properties: {
                name: { type: 'string' },
                born: { type: 'string' },
            },
            required: ['id', 'name', 'born'],
        };

        const result = await dtsGenerator({
            contents: [
                base,
                general,
                team,
                player,
                team1,
                team2,
                player1,
                player2,
            ].map((s) => parseSchema(s)),
        });

        const expected = `declare namespace Test {
    namespace Multiple {
        namespace AllOf {
            export type OneOf = {
                id: number;
                type: string;
            } & (OneOf.Team1 | OneOf.Team2) & (OneOf.Player1 | OneOf.Player2);
            namespace OneOf {
                export interface General {
                    id: number;
                    type: string;
                }
                export type Player = Player1 | Player2;
                export interface Player1 {
                    firstName: string;
                    lastName: string;
                }
                export interface Player2 {
                    name: string;
                    born: string;
                }
                export type Team = Team1 | Team2;
                export interface Team1 {
                    name: string;
                }
                export interface Team2 {
                    founded: string;
                }
            }
        }
    }
}
`;
        assert.strictEqual(result, expected);
    });

or (to show that the issue persists even when X and Y parenthesize the oneOf correctly

    it("multiple 'allOf' and 'oneOf' nestings schema", async () => {
        const base: JsonSchemaDraft07.Schema = {
            $id: '/test/multiple/allOf/oneOf',
            $schema: 'http://json-schema.org/draft-07/schema#',
            type: 'object',
            allOf: [
                { $ref: '/test/multiple/allOf/oneOf/team' },
                { $ref: '/test/multiple/allOf/oneOf/player' },
            ],
        };
        const general: JsonSchemaDraft07.Schema = {
            $id: '/test/multiple/allOf/oneOf/general',
            $schema: 'http://json-schema.org/draft-07/schema#',
            type: 'object',
            properties: {
                id: { type: 'integer' },
                type: { type: 'string' },
            },
            required: ['id', 'type'],
        };
        const team: JsonSchemaDraft07.Schema = {
            $id: '/test/multiple/allOf/oneOf/team',
            $schema: 'http://json-schema.org/draft-07/schema#',
            type: 'object',
            allOf: [
                { $ref: '/test/multiple/allOf/oneOf/general' },
                {
                    oneOf: [
                        { $ref: '/test/multiple/allOf/oneOf/team1' },
                        { $ref: '/test/multiple/allOf/oneOf/team2' },
                    ],
                },
            ],
        };
        const team1: JsonSchemaDraft07.Schema = {
            $id: '/test/multiple/allOf/oneOf/team1',
            $schema: 'http://json-schema.org/draft-07/schema#',
            type: 'object',
            properties: {
                name: { type: 'string' },
            },
            required: ['id', 'name'],
        };
        const team2: JsonSchemaDraft07.Schema = {
            $id: '/test/multiple/allOf/oneOf/team2',
            $schema: 'http://json-schema.org/draft-07/schema#',
            type: 'object',
            properties: {
                founded: { type: 'string' },
            },
            required: ['id', 'founded'],
        };
        const player: JsonSchemaDraft07.Schema = {
            $id: '/test/multiple/allOf/oneOf/player',
            $schema: 'http://json-schema.org/draft-07/schema#',
            type: 'object',
            allOf: [
                { $ref: '/test/multiple/allOf/oneOf/general' },
                {
                    oneOf: [
                        { $ref: '/test/multiple/allOf/oneOf/player1' },
                        { $ref: '/test/multiple/allOf/oneOf/player2' },
                    ],
                },
            ],
        };
        const player1: JsonSchemaDraft07.Schema = {
            $id: '/test/multiple/allOf/oneOf/player1',
            $schema: 'http://json-schema.org/draft-07/schema#',
            type: 'object',
            properties: {
                firstName: { type: 'string' },
                lastName: { type: 'string' },
            },
            required: ['id', 'firstName', 'lastName'],
        };
        const player2: JsonSchemaDraft07.Schema = {
            $id: '/test/multiple/allOf/oneOf/player2',
            $schema: 'http://json-schema.org/draft-07/schema#',
            type: 'object',
            properties: {
                name: { type: 'string' },
                born: { type: 'string' },
            },
            required: ['id', 'name', 'born'],
        };

        const result = await dtsGenerator({
            contents: [
                base,
                general,
                team,
                player,
                team1,
                team2,
                player1,
                player2,
            ].map((s) => parseSchema(s)),
        });

        const expected = `declare namespace Test {
    namespace Multiple {
        namespace AllOf {
            export type OneOf = {
                id: number;
                type: string;
            } & (OneOf.Team1 | OneOf.Team2) & (OneOf.Player1 | OneOf.Player2);
            namespace OneOf {
                export interface General {
                    id: number;
                    type: string;
                }
                export type Player = {
                    id: number;
                    type: string;
                } & (Player1 | Player2);
                export interface Player1 {
                    firstName: string;
                    lastName: string;
                }
                export interface Player2 {
                    name: string;
                    born: string;
                }
                export type Team = {
                    id: number;
                    type: string;
                } & (Team1 | Team2);
                export interface Team1 {
                    name: string;
                }
                export interface Team2 {
                    founded: string;
                }
            }
        }
    }
}
`;
        assert.strictEqual(result, expected);
    });

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions