-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[experimental] Support custom discriminator properties for discrimina…
…ted unions (#190) * Support custom discriminator properties for discriminated union * Regexify strings when testing code For more reliable code assertions * Add more tests * Fix name transformer input
- Loading branch information
1 parent
38943c1
commit 2f79032
Showing
6 changed files
with
201 additions
and
43 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@metaplex-foundation/kinobi": patch | ||
--- | ||
|
||
Support custom discriminator properties for discriminated union types in JS experimental renderer |
6 changes: 3 additions & 3 deletions
6
src/renderers/js-experimental/fragments/typeDiscriminatedUnionHelpers.njk
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
130 changes: 130 additions & 0 deletions
130
test/renderers/js-experimental/types/discriminatedUnions.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
import test from 'ava'; | ||
import { | ||
definedTypeNode, | ||
enumEmptyVariantTypeNode, | ||
enumStructVariantTypeNode, | ||
enumTupleVariantTypeNode, | ||
enumTypeNode, | ||
numberTypeNode, | ||
stringTypeNode, | ||
structFieldTypeNode, | ||
structTypeNode, | ||
tupleTypeNode, | ||
visit, | ||
} from '../../../../src'; | ||
import { getRenderMapVisitor } from '../../../../src/renderers/js-experimental/getRenderMapVisitor'; | ||
import { renderMapContains } from '../_setup'; | ||
|
||
// Given the following event discriminated union. | ||
const eventTypeNode = definedTypeNode({ | ||
name: 'event', | ||
type: enumTypeNode([ | ||
enumEmptyVariantTypeNode('quit'), | ||
enumTupleVariantTypeNode('write', tupleTypeNode([stringTypeNode()])), | ||
enumStructVariantTypeNode( | ||
'move', | ||
structTypeNode([ | ||
structFieldTypeNode({ name: 'x', type: numberTypeNode('u32') }), | ||
structFieldTypeNode({ name: 'y', type: numberTypeNode('u32') }), | ||
]) | ||
), | ||
]), | ||
}); | ||
|
||
test('it exports discriminated union types', (t) => { | ||
// When we render a discriminated union. | ||
const renderMap = visit(eventTypeNode, getRenderMapVisitor()); | ||
|
||
// Then we expect the following types to be exported. | ||
renderMapContains(t, renderMap, 'types/event.ts', [ | ||
'export type Event =', | ||
"| { __kind: 'Quit' }", | ||
"| { __kind: 'Write'; fields: readonly [string] }", | ||
"| { __kind: 'Move'; x: number; y: number }", | ||
]); | ||
}); | ||
|
||
test('it exports discriminated union codecs', (t) => { | ||
// When we render a discriminated union. | ||
const renderMap = visit(eventTypeNode, getRenderMapVisitor()); | ||
|
||
// Then we expect the following codec functions to be exported. | ||
renderMapContains(t, renderMap, 'types/event.ts', [ | ||
'export function getEventEncoder(): Encoder< EventArgs >', | ||
'export function getEventDecoder(): Decoder< Event >', | ||
'export function getEventCodec(): Codec< EventArgs, Event >', | ||
]); | ||
}); | ||
|
||
test('it exports discriminated union helpers', (t) => { | ||
// When we render a discriminated union. | ||
const renderMap = visit(eventTypeNode, getRenderMapVisitor()); | ||
|
||
// Then we expect the following helpers to be exported. | ||
renderMapContains(t, renderMap, 'types/event.ts', [ | ||
"export function event( kind: 'Quit' ): GetDiscriminatedUnionVariant< EventArgs, '__kind', 'Quit' >;", | ||
"export function event( kind: 'Write', data: GetDiscriminatedUnionVariantContent< EventArgs, '__kind', 'Write' >[ 'fields' ] ): GetDiscriminatedUnionVariant< EventArgs, '__kind', 'Write' >;", | ||
"export function event( kind: 'Move', data: GetDiscriminatedUnionVariantContent< EventArgs, '__kind', 'Move' > ): GetDiscriminatedUnionVariant< EventArgs, '__kind', 'Move' >;", | ||
"export function isEvent< K extends Event['__kind'] >( kind: K, value: Event ): value is Event & { __kind: K }", | ||
]); | ||
}); | ||
|
||
test('it exports discriminated union with custom discriminator properties', (t) => { | ||
// When we render a discriminated union with a custom discriminator property. | ||
const renderMap = visit( | ||
eventTypeNode, | ||
getRenderMapVisitor({ | ||
nameTransformers: { discriminatedUnionDiscriminator: () => `type` }, | ||
}) | ||
); | ||
|
||
// Then we expect the discriminator property to be used instead of __kind. | ||
renderMapContains(t, renderMap, 'types/event.ts', [ | ||
"{ discriminator: 'type' }", | ||
"| { type: 'Quit' }", | ||
"| { type: 'Write'; fields: readonly [string] }", | ||
"| { type: 'Move'; x: number; y: number }", | ||
"export function event( kind: 'Quit' ): GetDiscriminatedUnionVariant< EventArgs, 'type', 'Quit' >;", | ||
"export function event( kind: 'Write', data: GetDiscriminatedUnionVariantContent< EventArgs, 'type', 'Write' >[ 'fields' ] ): GetDiscriminatedUnionVariant< EventArgs, 'type', 'Write' >;", | ||
"export function event( kind: 'Move', data: GetDiscriminatedUnionVariantContent< EventArgs, 'type', 'Move' > ): GetDiscriminatedUnionVariant< EventArgs, 'type', 'Move' >;", | ||
"export function isEvent< K extends Event['type'] >( kind: K, value: Event ): value is Event & { type: K }", | ||
]); | ||
}); | ||
|
||
test('it use a custom discriminator property for selected unions', (t) => { | ||
// Given two discriminated unions A and B. | ||
const eventTypeNodeA = definedTypeNode({ ...eventTypeNode, name: 'eventA' }); | ||
const eventTypeNodeB = definedTypeNode({ ...eventTypeNode, name: 'eventB' }); | ||
|
||
// And given we use different discriminator properties for each union. | ||
const nameTransformers = { | ||
discriminatedUnionDiscriminator: (union: string) => | ||
union === 'eventA' ? 'typeA' : `typeB`, | ||
}; | ||
|
||
// When we render both discriminated unions. | ||
const renderMapA = visit( | ||
eventTypeNodeA, | ||
getRenderMapVisitor({ nameTransformers }) | ||
); | ||
const renderMapB = visit( | ||
eventTypeNodeB, | ||
getRenderMapVisitor({ nameTransformers }) | ||
); | ||
|
||
// Then we expect discriminated union A to use 'typeA' as its discriminator property. | ||
renderMapContains(t, renderMapA, 'types/eventA.ts', [ | ||
"{ discriminator: 'typeA' }", | ||
"| { typeA: 'Quit' }", | ||
"| { typeA: 'Write'; fields: readonly [string] }", | ||
"| { typeA: 'Move'; x: number; y: number }", | ||
]); | ||
|
||
// And discriminated union B to use 'typeB' as its discriminator property. | ||
renderMapContains(t, renderMapB, 'types/eventB.ts', [ | ||
"{ discriminator: 'typeB' }", | ||
"| { typeB: 'Quit' }", | ||
"| { typeB: 'Write'; fields: readonly [string] }", | ||
"| { typeB: 'Move'; x: number; y: number }", | ||
]); | ||
}); |