Skip to content

Commit 297c7f9

Browse files
authored
feat: Add support for Mapped Types (#124)
Fix #119
1 parent 54913da commit 297c7f9

18 files changed

+396
-14
lines changed

Directory.Packages.props

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
<PackageVersion Include="Feliz" Version="2.7.0" />
1616
<PackageVersion Include="Feliz.Bulma" Version="3.0.0" />
1717
<PackageVersion Include="FSharp.Core" Version="8.0.101" />
18+
<PackageVersion Include="FsToolkit.ErrorHandling" Version="4.16.0" />
1819
<PackageVersion Include="Glutinum.Chalk" Version="1.0.0" />
1920
<PackageVersion Include="Glutinum.Feliz.Iconify" Version="2.0.0" />
2021
<PackageVersion Include="Glutinum.IconifyIcons.Lucide" Version="1.0.0" />

src/Glutinum.Converter.CLI/packages.lock.json

+10
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"glutinum.converter": {
3232
"type": "Project",
3333
"dependencies": {
34+
"FSToolkit.ErrorHandling": "[4.16.0, )",
3435
"Fable.Core": "[4.3.0, )",
3536
"Fable.Node": "[1.2.0, )",
3637
"Fable.Promise": "[3.2.0, )",
@@ -57,6 +58,15 @@
5758
"Fable.Core": "3.7.1"
5859
}
5960
},
61+
"FsToolkit.ErrorHandling": {
62+
"type": "CentralTransitive",
63+
"requested": "[4.16.0, )",
64+
"resolved": "4.16.0",
65+
"contentHash": "GG4LtvJ/UkdyjGQUd+uPYN/g1GNlHyzMPDmwcnYvBn0V1dD4xJiiRE5N8UCgq+TSRQP11NNQUbzpU0zdxkrNbw==",
66+
"dependencies": {
67+
"FSharp.Core": "4.7.2"
68+
}
69+
},
6070
"Glutinum.Chalk": {
6171
"type": "CentralTransitive",
6272
"requested": "[1.0.0, )",

src/Glutinum.Converter/GlueAST.fs

+15-1
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,18 @@ type GlueUtilityType =
266266
| Record of GlueRecord
267267
// | ReadOnly of GlueType
268268

269+
type GlueMappedType =
270+
{
271+
TypeParameter: GlueTypeParameter
272+
Type: GlueType option
273+
}
274+
275+
type GlueIndexedAccessType =
276+
{
277+
IndexType: GlueType
278+
ObjectType: GlueType
279+
}
280+
269281
[<RequireQualifiedAccess>]
270282
type GlueType =
271283
| Discard
@@ -278,7 +290,7 @@ type GlueType =
278290
| Union of GlueTypeUnion
279291
| Literal of GlueLiteral
280292
| KeyOf of GlueType
281-
| IndexedAccessType of GlueType
293+
| IndexedAccessType of GlueIndexedAccessType
282294
| ModuleDeclaration of GlueModuleDeclaration
283295
| ClassDeclaration of GlueClassDeclaration
284296
| TypeReference of GlueTypeReference
@@ -295,6 +307,7 @@ type GlueType =
295307
| ExportDefault of GlueType
296308
| TemplateLiteral
297309
| UtilityType of GlueUtilityType
310+
| MappedType of GlueMappedType
298311

299312
member this.Name =
300313
match this with
@@ -340,3 +353,4 @@ type GlueType =
340353
match utilityType with
341354
| GlueUtilityType.Partial _
342355
| GlueUtilityType.Record _ -> "obj"
356+
| MappedType _ -> "obj"

src/Glutinum.Converter/Glutinum.Converter.fsproj

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
<?xml version="1.0" encoding="utf-8"?>
21
<Project Sdk="Microsoft.NET.Sdk">
32
<PropertyGroup>
43
<Version>0.1.0-alpha-001</Version>
@@ -27,10 +26,12 @@
2726
<Compile Include="Reader/Node.fs" />
2827
<Compile Include="Reader/Parameters.fs" />
2928
<Compile Include="Reader/TypeAliasDeclaration.fs" />
29+
<Compile Include="Reader/TypeQueryNode.fs" />
3030
<Compile Include="Reader/TypeNode.fs" />
3131
<Compile Include="Reader/TypeOperatorNode.fs" />
3232
<Compile Include="Reader/TypeParameters.fs" />
3333
<Compile Include="Reader/UnionTypeNode.fs" />
34+
<Compile Include="Reader/MappedTypeNode.fs" />
3435
<Compile Include="Reader/VariableStatement.fs" />
3536
<Compile Include="Reader/ExportAssignment.fs" />
3637
<Compile Include="Reader/Documentation.fs" />
@@ -47,6 +48,7 @@
4748
<PackageReference Include="Fable.Core" />
4849
<PackageReference Include="Fable.Node" />
4950
<PackageReference Include="Fable.Promise" />
51+
<PackageReference Include="FSToolkit.ErrorHandling" />
5052
<PackageReference Include="Glutinum.Chalk" />
5153
</ItemGroup>
5254
</Project>

src/Glutinum.Converter/Reader/IndexedAccessType.fs

+33-5
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,52 @@ let readIndexedAccessType
1010
: GlueType
1111
=
1212

13-
let nodeType = declaration.indexType :?> Ts.TypeNode
13+
let indexType =
14+
let idxNodeType = declaration.indexType :?> Ts.TypeNode
1415

15-
let typ =
16-
match nodeType.kind with
16+
match idxNodeType.kind with
1717
| Ts.SyntaxKind.TypeOperator ->
1818
let typeOperatorNode = declaration.indexType :?> Ts.TypeOperatorNode
1919
reader.ReadTypeOperatorNode typeOperatorNode
2020

21+
| Ts.SyntaxKind.NumberKeyword ->
22+
let numberKeywordNode = declaration.indexType :?> Ts.KeywordTypeNode
23+
reader.ReadTypeNode(numberKeywordNode)
24+
25+
| unsupported ->
26+
let warning =
27+
Report.readerError (
28+
"readIndexedAccessType",
29+
$"Unsupported node kind {unsupported.Name}",
30+
idxNodeType
31+
)
32+
33+
reader.Warnings.Add warning
34+
35+
GlueType.Discard
36+
37+
let objectType =
38+
let objNodeType = declaration.objectType :?> Ts.TypeNode
39+
40+
match objNodeType.kind with
41+
| Ts.SyntaxKind.ParenthesizedType ->
42+
let pType = declaration.objectType :?> Ts.ParenthesizedTypeNode
43+
reader.ReadTypeNode pType
44+
2145
| unsupported ->
2246
let warning =
2347
Report.readerError (
2448
"readIndexedAccessType",
2549
$"Unsupported node kind %s{unsupported.Name}",
26-
nodeType
50+
objNodeType
2751
)
2852

2953
reader.Warnings.Add warning
3054

3155
GlueType.Discard
3256

33-
GlueType.IndexedAccessType typ
57+
GlueType.IndexedAccessType
58+
{
59+
IndexType = indexType
60+
ObjectType = objectType
61+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
module Glutinum.Converter.Reader.MappedTypeNode
2+
3+
open Glutinum.Converter.GlueAST
4+
open Glutinum.Converter.Reader.Types
5+
open TypeScript
6+
open FsToolkit.ErrorHandling
7+
8+
let readMappedTypeNode
9+
(reader: ITypeScriptReader)
10+
(mappedTypeNode: Ts.MappedTypeNode)
11+
: GlueType
12+
=
13+
result {
14+
let! typParam =
15+
// TODO: Make a single reader.ReadTypeParameter method
16+
let typeParameters =
17+
reader.ReadTypeParameters(
18+
Some(ResizeArray([ mappedTypeNode.typeParameter ]))
19+
)
20+
21+
match typeParameters with
22+
| [ tp ] -> Ok tp
23+
| _ ->
24+
Report.readerError (
25+
"readMappedTypeNode",
26+
$"Expected exactly one type parameter but was {List.length typeParameters}",
27+
mappedTypeNode
28+
)
29+
|> Error
30+
31+
return
32+
{
33+
TypeParameter = typParam
34+
Type = mappedTypeNode.``type`` |> Option.map reader.ReadNode
35+
}
36+
|> GlueType.MappedType
37+
}
38+
|> function
39+
| Ok glueType -> glueType
40+
| Error warning ->
41+
reader.Warnings.Add warning
42+
GlueType.Discard

src/Glutinum.Converter/Reader/Node.fs

+2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ let readNode (reader: ITypeScriptReader) (node: Ts.Node) : GlueType =
4747

4848
| Ts.SyntaxKind.ExportDeclaration -> GlueType.Discard
4949

50+
| Ts.SyntaxKind.BooleanKeyword -> reader.ReadTypeNode(node :?> Ts.TypeNode)
51+
5052
| unsupported ->
5153
let warning =
5254
Report.readerError (

src/Glutinum.Converter/Reader/TypeAliasDeclaration.fs

+4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ let readTypeAliasDeclaration
2525
let declaration = declaration.``type`` :?> Ts.IndexedAccessType
2626
reader.ReadIndexedAccessType declaration
2727

28+
| Ts.SyntaxKind.MappedType ->
29+
let mappedTypeNode = declaration.``type`` :?> Ts.MappedTypeNode
30+
reader.ReadMappedTypeNode mappedTypeNode
31+
2832
| _ -> reader.ReadTypeNode declaration.``type``
2933

3034
{

src/Glutinum.Converter/Reader/TypeNode.fs

+6-5
Original file line numberDiff line numberDiff line change
@@ -219,11 +219,8 @@ let readTypeNode
219219
|> GlueType.FunctionType
220220

221221
| Ts.SyntaxKind.TypeQuery ->
222-
let typeNodeQuery = typeNode :?> Ts.TypeQueryNode
223-
224-
let typ = checker.getTypeAtLocation !!typeNodeQuery.exprName
225-
226-
readTypeUsingFlags reader typ
222+
let typeQueryNode = typeNode :?> Ts.TypeQueryNode
223+
TypeQueryNode.readTypeQueryNode reader typeQueryNode
227224

228225
| Ts.SyntaxKind.LiteralType ->
229226
let literalTypeNode = typeNode :?> Ts.LiteralTypeNode
@@ -392,6 +389,10 @@ let readTypeNode
392389

393390
| Ts.SyntaxKind.TemplateLiteralType -> GlueType.TemplateLiteral
394391

392+
| Ts.SyntaxKind.IndexedAccessType ->
393+
let indexedAccessType = typeNode :?> Ts.IndexedAccessType
394+
reader.ReadIndexedAccessType indexedAccessType
395+
395396
| _ ->
396397
Report.readerError (
397398
"type node",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
module Glutinum.Converter.Reader.TypeQueryNode
2+
3+
open Glutinum.Converter.GlueAST
4+
open Glutinum.Converter.Reader.Types
5+
open TypeScript
6+
open Fable.Core.JsInterop
7+
open Glutinum.Converter.Reader.Utils
8+
open FsToolkit.ErrorHandling
9+
10+
let readTypeQueryNode
11+
(reader: ITypeScriptReader)
12+
(typeQueryNode: Ts.TypeQueryNode)
13+
=
14+
15+
let checker = reader.checker
16+
let typ = checker.getTypeAtLocation !!typeQueryNode.exprName
17+
18+
match typ.flags with
19+
| HasTypeFlags Ts.TypeFlags.Object ->
20+
// This is safe as both cases have a `kind` field
21+
let exprNameKind: Ts.SyntaxKind = typeQueryNode.exprName?kind
22+
23+
match typ.getSymbol (), exprNameKind with
24+
| None, Ts.SyntaxKind.Identifier ->
25+
26+
let exprName: Ts.Identifier = !!typeQueryNode.exprName
27+
28+
result {
29+
let! aliasSymbol =
30+
checker.getSymbolAtLocation exprName
31+
|> Result.requireSome (
32+
Report.readerError (
33+
"type node (TypeQuery)",
34+
"Missing symbol",
35+
typeQueryNode
36+
)
37+
)
38+
39+
let! declarations =
40+
aliasSymbol.declarations
41+
|> Result.requireSome (
42+
Report.readerError (
43+
"type node (TypeQuery)",
44+
"Missing declarations",
45+
typeQueryNode
46+
)
47+
)
48+
49+
let! declaration =
50+
if declarations.Count <> 1 then
51+
Report.readerError (
52+
53+
"type node (TypeQuery)",
54+
"Expected exactly one declaration",
55+
typeQueryNode
56+
)
57+
|> Error
58+
59+
else
60+
Ok(declarations.[0])
61+
62+
let! variableDeclaration =
63+
match declaration.kind with
64+
| Ts.SyntaxKind.VariableDeclaration ->
65+
Ok(declaration :?> Ts.VariableDeclaration)
66+
67+
| unsupported ->
68+
Report.readerError (
69+
"type node (TypeQuery)",
70+
$"Unsupported declaration kind {unsupported.Name}",
71+
typeQueryNode
72+
)
73+
|> Error
74+
75+
let! typeNode =
76+
variableDeclaration.``type``
77+
|> Result.requireSome (
78+
Report.readerError (
79+
"type node (TypeQuery)",
80+
"Missing type",
81+
typeQueryNode
82+
)
83+
)
84+
85+
match typeNode.kind with
86+
| Ts.SyntaxKind.TypeOperator ->
87+
let typeOperatorNode = typeNode :?> Ts.TypeOperatorNode
88+
return reader.ReadTypeOperatorNode typeOperatorNode
89+
90+
| unsupported ->
91+
return!
92+
Report.readerError (
93+
"type node (TypeQuery)",
94+
$"Unsupported declaration kind {unsupported.Name}",
95+
typeQueryNode
96+
)
97+
|> Error
98+
99+
}
100+
|> function
101+
| Ok glueType -> glueType
102+
| Error warning ->
103+
reader.Warnings.Add warning
104+
GlueType.Discard
105+
106+
| None, _ ->
107+
let warning =
108+
Report.readerError (
109+
"type node (TypeQuery)",
110+
"Expected an Identifier",
111+
typeQueryNode
112+
)
113+
114+
reader.Warnings.Add warning
115+
GlueType.Primitive GluePrimitive.Any
116+
117+
| Some symbol, _ ->
118+
// Try to find the declaration of the type, to get more information about it
119+
match symbol.declarations with
120+
| Some declarations ->
121+
let declaration = declarations.[0]
122+
123+
match declaration.kind with
124+
| Ts.SyntaxKind.ClassDeclaration ->
125+
{
126+
Name = symbol.name
127+
Constructors = []
128+
Members = []
129+
TypeParameters = []
130+
HeritageClauses = []
131+
}
132+
|> GlueType.ClassDeclaration
133+
134+
// We don't support TypeQuery for ModuleDeclaration yet
135+
// See https://github.com/glutinum-org/cli/issues/70 for a possible solution
136+
| Ts.SyntaxKind.ModuleDeclaration -> GlueType.Discard
137+
| _ -> reader.ReadNode declaration
138+
139+
| None -> GlueType.Primitive GluePrimitive.Any
140+
141+
| HasTypeFlags Ts.TypeFlags.String ->
142+
GlueType.Primitive GluePrimitive.String
143+
144+
| HasTypeFlags Ts.TypeFlags.Number ->
145+
GlueType.Primitive GluePrimitive.Number
146+
147+
| HasTypeFlags Ts.TypeFlags.Boolean -> GlueType.Primitive GluePrimitive.Bool
148+
149+
| HasTypeFlags Ts.TypeFlags.Any -> GlueType.Primitive GluePrimitive.Any
150+
151+
| HasTypeFlags Ts.TypeFlags.Void -> GlueType.Primitive GluePrimitive.Unit
152+
153+
| _ -> GlueType.Primitive GluePrimitive.Any

0 commit comments

Comments
 (0)