Skip to content

Commit

Permalink
(docs): Add discriminated union section (#3763)
Browse files Browse the repository at this point in the history
  • Loading branch information
amckinney authored Jun 3, 2024
1 parent ba29916 commit f08f122
Show file tree
Hide file tree
Showing 2 changed files with 219 additions and 23 deletions.
2 changes: 2 additions & 0 deletions fern/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ navigation:
path: ./pages/sdks/features/idiomatic-method-names.mdx
- page: Schema validation
path: ./pages/sdks/features/schema-validation.mdx
- page: Discriminated unions
path: ./pages/sdks/features/discriminated-unions.mdx
- page: Multipart form data
path: ./pages/sdks/features/multipart-form-data.mdx
- page: Forward compatibility
Expand Down
240 changes: 217 additions & 23 deletions fern/pages/sdks/features/discriminated-unions.mdx
Original file line number Diff line number Diff line change
@@ -1,33 +1,227 @@
### Discriminated Unions
The SDKs natively support discriminated unions.
---
title: Discriminated Unions
description: Fern SDKs include idiomatic support for discriminated unions
---

```ts maxLines=0 {1}
export type Shape = Triangle | Square;
The SDKs natively support discriminated [unions](../../api-definition/fern/types#unions) for both OpenAPI and Fern APIs.

export interface Triangle {
type: "triangle";
a: number;
b: number;
c: number;
<AccordionGroup>
<Accordion title="Fern definition">
Discriminated unions are defined with the `union` key. For example, a simple
`Shape` type that can either be a `Triangle` or a `Square` can be defined as follows:

<CodeBlock title="fern/definition/shape.yml">
```yaml
types:
Shape:
union:
triangle: Triangle
square: Square

Triangle:
properties:
a: double
b: double
c: double

Square:
properties:
side: double
```
</CodeBlock>
With this, the JSON representation for a `Shape` looks like the following:

<CodeBlock title="triangle.json">
```json
{
"type": "triangle",
"a": 3,
"b": 4,
"c": 5
}
```
</CodeBlock>

or

export interface Square {
type: "square";
/* length of a single side */
side: number;
<CodeBlock title="square.json">
```json
{
"type": "sqaure",
"side": 5
}
```
</CodeBlock>
</Accordion>

Consumers can easily write branching logic by checking the discriminant.
<Accordion title="OpenAPI spec">
Discriminated unions are defined with the `oneOf` and `anyOf` keys. For example, consider
the following `Shape` definition:

<CodeBlock title="openapi.yml">
```yaml
components:
schemas:
Shape:
oneOf:
- $ref: "#/components/schemas/Triangle"
- $ref: "#/components/schemas/Square"
Triangle:
type: object
properties:
type:
type: string
enum:
- triangle
a:
type: number
b:
type: number
c:
type: number
required:
- type
- a
- b
- c
Square:
type: object
properties:
type:
type: string
enum:
- square
side:
type: number
required:
- type
- side
```
</CodeBlock>

```ts {4, 6}
import { Shape } from "sdk";
With this, the JSON representation for a `Shape` looks like the following:

export function computeArea(shape: Shape): number {
if (shape.type === "triangle") {
// compute triangle area
} else if (shape.type === "square") {
// compute square area
}
<CodeBlock title="triangle.json">
```json
{
"type": "triangle",
"a": 3,
"b": 4,
"c": 5
}
```
```
</CodeBlock>

or

<CodeBlock title="square.json">
```json
{
"type": "sqaure",
"side": 5
}
```
</CodeBlock>
</Accordion>
</AccordionGroup>

<Tabs>
<Tab title="TypeScript">

```ts maxLines=0 {1}
export type Shape = Triangle | Square;
export interface Triangle {
type: "triangle";
a: number;
b: number;
c: number;
}
export interface Square {
type: "square";
side: number;
}
```

Callers can create a `Shape` object by simply constructing the appropriate type. For example, creating
a `Triangle` shape looks like the following:

```ts
const shape: Shape = {
type: "triangle",
a: 3,
b: 4,
c: 5,
};
```

Consumers can easily write branching logic by checking the discriminant.

```ts {4, 6}
import { Shape } from "sdk";
export function computeArea(shape: Shape): number {
if (shape.type === "triangle") {
// compute triangle area
} else if (shape.type === "square") {
// compute square area
}
}
```

</Tab>

<Tab title="Go">

Go does not have a built-in support for discriminated unions. However, you can define a union struct
to achieve the same effect:

```go maxLines=0 {1-5}
type Shape struct {
Type string
Triangle *Triangle
Square *Square
}
type Triangle struct {
A float64 `json:"a" url:"a"`
B float64 `json:"b" url:"b"`
C float64 `json:"c" url:"c"`
}

type Square struct {
Side float64 `json:"side" url:"side"`
}
```

Callers can create a `Shape` object by simply setting the appropriate key. For example, creating
a `Triangle` shape looks like the following:

```go
shape := &Shape{
// You do not need to set the Type field manually, the SDK will automatically set it for you.
Triangle: &Triangle{
A: 3,
B: 4,
C: 5,
},
}
```

Consumers can easily write a `switch` statement by checking the discriminant:

```go {3, 5}
func ComputeArea(shape *Shape) float64 {
switch shape.Type {
case "triangle":
// compute triangle area
case "square":
// compute square areair
}
}
```

</Tab>
</Tabs>

0 comments on commit f08f122

Please sign in to comment.