Skip to content

Commit

Permalink
Rework TypeAlias definition and better handled literal unions
Browse files Browse the repository at this point in the history
  • Loading branch information
MangelMaxime committed Dec 1, 2023
1 parent 0e76f42 commit a73e12e
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 33 deletions.
2 changes: 1 addition & 1 deletion src/Glutinum.Converter/GlueAST.fs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ type GlueEnum =
type GlueTypeAliasDeclaration =
{
Name: string
Types: GlueType list
Type: GlueType
}

type GlueFunctionDeclaration =
Expand Down
30 changes: 17 additions & 13 deletions src/Glutinum.Converter/Read.fs
Original file line number Diff line number Diff line change
Expand Up @@ -246,17 +246,21 @@ let rec private readUnionTypeCases
|> GlueType.TypeReference
|> List.singleton
|> Some

else
symbol.declarations
|> Seq.toList
|> List.collect (fun declaration ->
// We use the readUnionType to handle nested unions
let enum = readUnionType checker declaration?``type``
let declaration = symbol.declarations.[0]

[ enum ]
)
|> Some
readNode checker declaration |> List.singleton |> Some

// else
// symbol.declarations
// |> Seq.toList
// |> List.collect (fun declaration ->
// // We use the readUnionType to handle nested unions
// let enum = readUnionType checker declaration?``type``

// [ enum ]
// )
// |> Some
else
match node.kind with
| Ts.SyntaxKind.UnionType ->
Expand Down Expand Up @@ -336,7 +340,7 @@ let private readTypeAliasDeclaration
: GlueType
=

let types =
let typ =
match declaration.``type``.kind with
| Ts.SyntaxKind.UnionType ->
let unionTypeNode = declaration.``type`` :?> Ts.UnionTypeNode
Expand All @@ -355,7 +359,7 @@ let private readTypeAliasDeclaration

{
Name = declaration.name.getText ()
Types = [ types ]
Type = typ
}
|> GlueType.TypeAliasDeclaration

Expand Down Expand Up @@ -452,18 +456,18 @@ let private tryReadVariableStatement
let declaration =
statement.declarationList.declarations |> Seq.toList |> List.head


let name =
match declaration.name?kind with
| Ts.SyntaxKind.Identifier ->
let id: Ts.Identifier = !!declaration.name
id.getText ()
| _ -> failwith "readVariableStatement: Unsupported kind"

{
({
Name = name
Type = readTypeNode checker declaration.``type``
}
: GlueVariable)
|> Some
else
None
Expand Down
56 changes: 38 additions & 18 deletions src/Glutinum.Converter/Transform.fs
Original file line number Diff line number Diff line change
Expand Up @@ -401,31 +401,51 @@ let private transformTypeAliasDeclaration
=

// TODO: Make the transformation more robust
match glueTypeAliasDeclaration.Types with
| GlueType.Union cases :: [] ->
match glueTypeAliasDeclaration.Type with
| GlueType.Union cases ->
printfn "%A" cases

// let
// Unions can have nested unions, so we need to flatten them
// TODO: Is there cases where we don't want to flatten?
// U2<U2<int, string>, bool>
let rec flattenCases (cases: GlueType list) : GlueType list =
cases
|> List.collect (
function
// We are inside an union, and have access to the literal types
| GlueType.Literal _ as literal -> [ literal ]
| GlueType.Union cases -> flattenCases cases
| glueType -> [ glueType ]
| GlueType.TypeAliasDeclaration aliasCases ->
match aliasCases.Type with
| GlueType.Union cases -> flattenCases cases
| _ -> failwith "Should not happen"
// Can't find cases so we return an empty list to discard the type
// Should we do something if we fall in this state?
// I think the code below will be able to recover by generating
// an erased enum, but I don't know if there cases where we could
// be bitting ourselves in the foot
| _ -> []
)

let cases = flattenCases cases
let flattenedCases = flattenCases cases

let isStringOnly =
cases
|> List.forall (
function
| GlueType.Literal(GlueLiteral.String _) -> true
| _ -> false
)
// If the list is empty, it means that there was no candidates
// for string literals
not flattenedCases.IsEmpty
&& flattenedCases
|> List.forall (
function
| GlueType.Literal(GlueLiteral.String _) -> true
| _ -> false
)

let isNumericOnly =
cases
// If the list is empty, it means that there was no candidates
// for numeric literals
not flattenedCases.IsEmpty
&& flattenedCases
|> List.forall (
function
| GlueType.Literal(GlueLiteral.Int _) -> true
Expand All @@ -436,7 +456,7 @@ let private transformTypeAliasDeclaration
// we can transform it into a StringEnum
if isStringOnly then
let cases =
cases
flattenedCases
|> List.map (fun value ->
match value with
| GlueType.Literal(GlueLiteral.String value) ->
Expand Down Expand Up @@ -470,7 +490,7 @@ let private transformTypeAliasDeclaration
// we can transform it into a standard F# enum
else if isNumericOnly then
let cases =
cases
flattenedCases
|> List.map (fun value ->
match value with
| GlueType.Literal(GlueLiteral.Int value) ->
Expand Down Expand Up @@ -499,12 +519,12 @@ let private transformTypeAliasDeclaration
: FSharpTypeAlias)
|> FSharpType.Alias

| GlueType.KeyOf glueType :: [] ->
| GlueType.KeyOf glueType ->
TypeAliasDeclaration.transformKeyOf
glueTypeAliasDeclaration.Name
glueType

| GlueType.IndexedAccessType glueType :: [] ->
| GlueType.IndexedAccessType glueType ->
let typ =
match glueType with
| GlueType.KeyOf glueType ->
Expand Down Expand Up @@ -537,23 +557,23 @@ let private transformTypeAliasDeclaration
: FSharpTypeAlias)
|> FSharpType.Alias

| GlueType.Primitive primitiveInfo :: [] ->
| GlueType.Primitive primitiveInfo ->
({
Name = glueTypeAliasDeclaration.Name
Type = transformPrimitive primitiveInfo |> FSharpType.Primitive
}
: FSharpTypeAlias)
|> FSharpType.Alias

| GlueType.TypeReference typeReference :: [] ->
| GlueType.TypeReference typeReference ->
({
Name = glueTypeAliasDeclaration.Name
Type = transformType (GlueType.TypeReference typeReference)
}
: FSharpTypeAlias)
|> FSharpType.Alias

| GlueType.Array glueType :: [] ->
| GlueType.Array glueType ->
({
Name = glueTypeAliasDeclaration.Name
Type = transformType (GlueType.Array glueType)
Expand Down
2 changes: 1 addition & 1 deletion tests/libs/dayjs.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ declare namespace dayjs {

export interface FormatObject { locale?: string, format?: string, utc?: boolean }

export type OptionType = string | string[]
export type OptionType = FormatObject | string | string[]

}
12 changes: 12 additions & 0 deletions tests/specs/enums/literalNumericEnumWithNestedEnums.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export type NumberA =
| 1

export type NumberB =
| 2

export type NumberC = NumberA | NumberB;

export type NumberD =
| 3

export type NumberE = NumberC | NumberD;
31 changes: 31 additions & 0 deletions tests/specs/enums/literalNumericEnumWithNestedEnums.fsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
module rec Glutinum

(***)
#r "nuget: Fable.Core"
(***)

open Fable.Core
open System

[<RequireQualifiedAccess>]
type NumberA =
| ``1`` = 1

[<RequireQualifiedAccess>]
type NumberB =
| ``2`` = 2

[<RequireQualifiedAccess>]
type NumberC =
| ``1`` = 1
| ``2`` = 2

[<RequireQualifiedAccess>]
type NumberD =
| ``3`` = 3

[<RequireQualifiedAccess>]
type NumberE =
| ``1`` = 1
| ``2`` = 2
| ``3`` = 3
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface FormatObject { locale?: string, format?: string, utc?: boolean }

export type OptionType = FormatObject | string | string[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module rec Glutinum

(***)
#r "nuget: Fable.Core"
(***)

open Fable.Core
open System

[<AllowNullLiteral>]
type FormatObject =
abstract member locale: string with get, set
abstract member format: string with get, set
abstract member utc: bool with get, set

type OptionType =
U3<FormatObject, string, ResizeArray<string>>

0 comments on commit a73e12e

Please sign in to comment.