哦哈哈哈哈 發表於 2020-1-31 21:34:00

用 F# 手写 TypeScript 转 C# 类型绑定生成器

<h2>前言</h2>
<p>我们经常会遇到这样的事情:有时候我们找到了一个库,但是这个库是用 TypeScript 写的,但是我们想在 C# 调用,于是我们需要设法将原来的 TypeScript 类型声明翻译成 C# 的代码,然后如果是 UI 组件的话,我们需要将其封装到一个 WebView 里面,然后通过 JavaScript 和 C# 的互操作功能来调用该组件的各种方法,支持该组件的各种事件等等。</p>
<p>但是这是一个苦力活,尤其是类型翻译这一步。</p>
<p>这个是我最近在帮助维护一个开源 UWP 项目&nbsp;monaco-editor-uwp&nbsp;所需要的,该项目将微软的 monaco 编辑器封装成了 UWP 组件。</p>
<p>然而它的 monaco.d.ts 足足有 1.5 mb,并且 API 经常会变化,如果人工翻译,不仅工作量十分大,还可能会漏掉新的变化,但是如果有一个自动生成器的话,那么人工的工作就会少很多。</p>
<p>目前 GitHub 上面有一个叫做 QuickType 的项目,但是这个项目对 TypeScript 的支持极其有限,仍然停留在 TypeScript 3.2,而且遇到不认识的类型就会报错,比如 DOM 类型等等。</p>
<p>因此我决定手写一个代码生成器 TypedocConverter:https://github.com/hez2010/TypedocConverter</p>
<h2>构思</h2>
<p>本来是打算从 TypeScript 词法和语义分析开始做的,但是发现有一个叫做&nbsp;Typedoc&nbsp;的项目已经帮我们完成了这一步,而且支持输出 JSON schema,那么剩下的事情就简单了:我们只需要将 TypeScript 的 AST 转换成 C# 的 AST,然后再将 AST 还原成代码即可。</p>
<p>那么话不多说,这就开写。</p>
<h2>构建 Typescipt AST 类型绑定</h2>
<p>借助于 F# 更加强大的类型系统,类型的声明和使用非常简单,并且具有完善的recursive pattern。pattern matching、option types 等支持,这也是该项目选用 F# 而不是 C# 的原因,虽然 C# 也支持这些,也有一定的 FP 能力,但是它还是偏 OOP,写起来会有很多的样板代码,非常的繁琐。</p>
<p>我们将 Typescipt 的类型绑定定义到 Definition.fs 中,这一步直接将 Typedoc 的定义翻译到 F# 即可:</p>
<p>首先是 ReflectionKind 枚举,该枚举表示了 JSON Schema 中各节点的类型:</p>
<div class="highlight">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">type</span><span style="color: rgba(0, 0, 0, 1)"> ReflectionKind =
</span>| Global = <span style="color: rgba(128, 0, 128, 1)">0</span>
| ExternalModule = <span style="color: rgba(128, 0, 128, 1)">1</span>
| Module = <span style="color: rgba(128, 0, 128, 1)">2</span>
| Enum = <span style="color: rgba(128, 0, 128, 1)">4</span>
| EnumMember = <span style="color: rgba(128, 0, 128, 1)">16</span>
| Variable = <span style="color: rgba(128, 0, 128, 1)">32</span>
| Function = <span style="color: rgba(128, 0, 128, 1)">64</span>
| Class = <span style="color: rgba(128, 0, 128, 1)">128</span>
| Interface = <span style="color: rgba(128, 0, 128, 1)">256</span>
| Constructor = <span style="color: rgba(128, 0, 128, 1)">512</span>
| Property = <span style="color: rgba(128, 0, 128, 1)">1024</span>
| Method = <span style="color: rgba(128, 0, 128, 1)">2048</span>
| CallSignature = <span style="color: rgba(128, 0, 128, 1)">4096</span>
| IndexSignature = <span style="color: rgba(128, 0, 128, 1)">8192</span>
| ConstructorSignature = <span style="color: rgba(128, 0, 128, 1)">16384</span>
| Parameter = <span style="color: rgba(128, 0, 128, 1)">32768</span>
| TypeLiteral = <span style="color: rgba(128, 0, 128, 1)">65536</span>
| TypeParameter = <span style="color: rgba(128, 0, 128, 1)">131072</span>
| Accessor = <span style="color: rgba(128, 0, 128, 1)">262144</span>
| GetSignature = <span style="color: rgba(128, 0, 128, 1)">524288</span>
| SetSignature = <span style="color: rgba(128, 0, 128, 1)">1048576</span>
| ObjectLiteral = <span style="color: rgba(128, 0, 128, 1)">2097152</span>
| TypeAlias = <span style="color: rgba(128, 0, 128, 1)">4194304</span>
| Event = <span style="color: rgba(128, 0, 128, 1)">8388608</span>
| Reference = <span style="color: rgba(128, 0, 128, 1)">16777216</span></pre>
</div>
<p>&nbsp;</p>
</div>
<p>然后是类型修饰标志 ReflectionFlags,注意该 record 所有的成员都是 option 的</p>
<div class="highlight">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">type</span><span style="color: rgba(0, 0, 0, 1)"> ReflectionFlags = {
    IsPrivate: </span><span style="color: rgba(0, 0, 255, 1)">bool</span><span style="color: rgba(0, 0, 0, 1)"> option
    IsProtected: </span><span style="color: rgba(0, 0, 255, 1)">bool</span><span style="color: rgba(0, 0, 0, 1)"> option
    IsPublic: </span><span style="color: rgba(0, 0, 255, 1)">bool</span><span style="color: rgba(0, 0, 0, 1)"> option
    IsStatic: </span><span style="color: rgba(0, 0, 255, 1)">bool</span><span style="color: rgba(0, 0, 0, 1)"> option
    IsExported: </span><span style="color: rgba(0, 0, 255, 1)">bool</span><span style="color: rgba(0, 0, 0, 1)"> option
    IsExternal: </span><span style="color: rgba(0, 0, 255, 1)">bool</span><span style="color: rgba(0, 0, 0, 1)"> option
    IsOptional: </span><span style="color: rgba(0, 0, 255, 1)">bool</span><span style="color: rgba(0, 0, 0, 1)"> option
    IsReset: </span><span style="color: rgba(0, 0, 255, 1)">bool</span><span style="color: rgba(0, 0, 0, 1)"> option
    HasExportAssignment: </span><span style="color: rgba(0, 0, 255, 1)">bool</span><span style="color: rgba(0, 0, 0, 1)"> option
    IsConstructorProperty: </span><span style="color: rgba(0, 0, 255, 1)">bool</span><span style="color: rgba(0, 0, 0, 1)"> option
    IsAbstract: </span><span style="color: rgba(0, 0, 255, 1)">bool</span><span style="color: rgba(0, 0, 0, 1)"> option
    IsConst: </span><span style="color: rgba(0, 0, 255, 1)">bool</span><span style="color: rgba(0, 0, 0, 1)"> option
    IsLet: </span><span style="color: rgba(0, 0, 255, 1)">bool</span><span style="color: rgba(0, 0, 0, 1)"> option
}</span></pre>
</div>
<p>&nbsp;</p>
</div>
<p>然后到了我们的 Reflection,由于每一种类型的 Reflection 都可以由 ReflectionKind 来区分,因此我选择将所有类型的 Reflection 合并成为一个 record,而不是采用 Union Types,因为后者虽然看上去清晰,但是在实际 parse AST 的时候会需要大量 pattern matching 的代码。</p>
<p>由于部分 records 相互引用,因此我们使用&nbsp;<code>and</code>&nbsp;来定义 recursive records。</p>
<div class="highlight">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">type</span><span style="color: rgba(0, 0, 0, 1)"> Reflection = {
    Id: </span><span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)">
    Name: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">
    OriginalName: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">
    Kind: ReflectionKind
    KindString: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> option
    Flags: ReflectionFlags
    Parent: Reflection option
    Comment: Comment option
    Sources: SourceReference list option
    Decorators: Decorator option
    Decorates: Type list option
    Url: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> option
    Anchor: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> option
    HasOwnDocument: </span><span style="color: rgba(0, 0, 255, 1)">bool</span><span style="color: rgba(0, 0, 0, 1)"> option
    CssClasses: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> option
    DefaultValue: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> option
    Type: Type option
    TypeParameter: Reflection list option
    Signatures: Reflection list option
    IndexSignature: Reflection list option
    GetSignature: Reflection list option
    SetSignature: Reflection list option
    Overwrites: Type option
    InheritedFrom: Type option
    ImplementationOf: Type option
    ExtendedTypes: Type list option
    ExtendedBy: Type list option
    ImplementedTypes: Type list option
    ImplementedBy: Type list option
    TypeHierarchy: DeclarationHierarchy option
    Children: Reflection list option
    Groups: ReflectionGroup list option
    Categories: ReflectionCategory list option
    Reflections: Map&lt;</span><span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)">, Reflection&gt; option
    Directory: SourceDirectory option
    Files: SourceFile list option
    Readme: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> option
    PackageInfo: obj option
    Parameters: Reflection list option
}
</span><span style="color: rgba(0, 0, 255, 1)">and</span><span style="color: rgba(0, 0, 0, 1)"> DeclarationHierarchy = {
    Type: Type list
    Next: DeclarationHierarchy option
    IsTarget: </span><span style="color: rgba(0, 0, 255, 1)">bool</span><span style="color: rgba(0, 0, 0, 1)"> option
}
</span><span style="color: rgba(0, 0, 255, 1)">and</span><span style="color: rgba(0, 0, 0, 1)"> Type = {
    Type: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">
    Id: </span><span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> option
    Name: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> option
    ElementType: Type option
    Value: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> option
    Types: Type list option
    TypeArguments: Type list option
    Constraint: Type option
    Declaration: Reflection option
}
</span><span style="color: rgba(0, 0, 255, 1)">and</span><span style="color: rgba(0, 0, 0, 1)"> Decorator = {
    Name: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">
    Type: Type option
    Arguments: obj option
}
</span><span style="color: rgba(0, 0, 255, 1)">and</span><span style="color: rgba(0, 0, 0, 1)"> ReflectionGroup = {
    Title: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">
    Kind: ReflectionKind
    Children: </span><span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> list
    CssClasses: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> option
    AllChildrenHaveOwnDocument: </span><span style="color: rgba(0, 0, 255, 1)">bool</span><span style="color: rgba(0, 0, 0, 1)"> option
    AllChildrenAreInherited: </span><span style="color: rgba(0, 0, 255, 1)">bool</span><span style="color: rgba(0, 0, 0, 1)"> option
    AllChildrenArePrivate: </span><span style="color: rgba(0, 0, 255, 1)">bool</span><span style="color: rgba(0, 0, 0, 1)"> option
    AllChildrenAreProtectedOrPrivate: </span><span style="color: rgba(0, 0, 255, 1)">bool</span><span style="color: rgba(0, 0, 0, 1)"> option
    AllChildrenAreExternal: </span><span style="color: rgba(0, 0, 255, 1)">bool</span><span style="color: rgba(0, 0, 0, 1)"> option
    SomeChildrenAreExported: </span><span style="color: rgba(0, 0, 255, 1)">bool</span><span style="color: rgba(0, 0, 0, 1)"> option
    Categories: ReflectionCategory list option
}
</span><span style="color: rgba(0, 0, 255, 1)">and</span><span style="color: rgba(0, 0, 0, 1)"> ReflectionCategory = {
    Title: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">
    Children: </span><span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> list
    AllChildrenHaveOwnDocument: </span><span style="color: rgba(0, 0, 255, 1)">bool</span><span style="color: rgba(0, 0, 0, 1)"> option
}
</span><span style="color: rgba(0, 0, 255, 1)">and</span><span style="color: rgba(0, 0, 0, 1)"> SourceDirectory = {
    Parent: SourceDirectory option
    Directories: Map&lt;</span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">, SourceDirectory&gt;
    Groups: ReflectionGroup list option
    Files: SourceFile list
    Name: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> option
    DirName: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> option
    Url: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> option
}
</span><span style="color: rgba(0, 0, 255, 1)">and</span><span style="color: rgba(0, 0, 0, 1)"> SourceFile = {
    FullFileName: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">
    FileName: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">
    Name: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">
    Url: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> option
    Parent: SourceDirectory option
    Reflections: Reflection list option
    Groups: ReflectionGroup list option
}
</span><span style="color: rgba(0, 0, 255, 1)">and</span><span style="color: rgba(0, 0, 0, 1)"> SourceReference = {
    File: SourceFile option
    FileName: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">
    Line: </span><span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)">
    Character: </span><span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)">
    Url: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> option
}
</span><span style="color: rgba(0, 0, 255, 1)">and</span><span style="color: rgba(0, 0, 0, 1)"> Comment = {
    ShortText: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">
    Text: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> option
    Returns: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> option
    Tags: CommentTag list option
}
</span><span style="color: rgba(0, 0, 255, 1)">and</span><span style="color: rgba(0, 0, 0, 1)"> CommentTag = {
    TagName: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">
    ParentName: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">
    Text: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">
}</span></pre>
</div>
<p>&nbsp;</p>
</div>
<p>这样,我们就简单的完成了类型绑定的翻译,接下来要做的就是将 Typedoc 生成的 JSON 反序列化成我们所需要的东西即可。</p>
<h2>反序列化</h2>
<p>虽然想着好像一切都很顺利,但是实际上 System.Text.Json、Newtonsoft.JSON 等均不支持 F# 的 option types,所需我们还需要一个 JsonConverter 处理 option types。</p>
<p>本项目采用 Newtonsoft.Json,因为 System.Text.Json 目前尚不成熟。得益于 F# 对 OOP 的兼容,我们可以很容易的实现一个&nbsp;<code>OptionConverter</code>。</p>
<div class="highlight">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">type</span><span style="color: rgba(0, 0, 0, 1)"> OptionConverter() =
    </span><span style="color: rgba(0, 0, 255, 1)">inherit</span><span style="color: rgba(0, 0, 0, 1)"> JsonConverter()
    </span><span style="color: rgba(0, 0, 255, 1)">override</span> __.CanConvert(objectType: Type) : <span style="color: rgba(0, 0, 255, 1)">bool</span><span style="color: rgba(0, 0, 0, 1)"> =
      </span><span style="color: rgba(0, 0, 255, 1)">match</span> objectType.IsGenericType <span style="color: rgba(0, 0, 255, 1)">with</span>
      | <span style="color: rgba(0, 0, 255, 1)">false</span> -&gt; <span style="color: rgba(0, 0, 255, 1)">false</span>
      | <span style="color: rgba(0, 0, 255, 1)">true</span> -&gt;<span style="color: rgba(0, 0, 0, 1)"> typedefof&lt;_ option&gt; = objectType.GetGenericTypeDefinition()
    </span><span style="color: rgba(0, 0, 255, 1)">override</span><span style="color: rgba(0, 0, 0, 1)"> __.WriteJson(writer: JsonWriter, value: obj, serializer: JsonSerializer) : unit =
      serializer.Serialize(writer,
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> isNull value <span style="color: rgba(0, 0, 255, 1)">then</span> <span style="color: rgba(0, 0, 255, 1)">null</span>
            <span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">let</span><span style="color: rgba(0, 0, 0, 1)"> _, fields = FSharpValue.GetUnionFields(value, value.GetType())
               fields.[</span><span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">]
      )
    </span><span style="color: rgba(0, 0, 255, 1)">override</span><span style="color: rgba(0, 0, 0, 1)"> __.ReadJson(reader: JsonReader, objectType: Type, _existingValue: obj, serializer: JsonSerializer) : obj =
      </span><span style="color: rgba(0, 0, 255, 1)">let</span> innerType = objectType.GetGenericArguments().[<span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">]
      </span><span style="color: rgba(0, 0, 255, 1)">let</span><span style="color: rgba(0, 0, 0, 1)"> value =
            serializer.Deserialize(
                reader,
                </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> innerType.IsValueType
                </span><span style="color: rgba(0, 0, 255, 1)">then</span> (typedefof&lt;_ Nullable&gt;).MakeGenericType([|innerType|<span style="color: rgba(0, 0, 0, 1)">])
                </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> innerType
      )
      </span><span style="color: rgba(0, 0, 255, 1)">let</span><span style="color: rgba(0, 0, 0, 1)"> cases = FSharpType.GetUnionCases objectType
      </span><span style="color: rgba(0, 0, 255, 1)">if</span> isNull value <span style="color: rgba(0, 0, 255, 1)">then</span> FSharpValue.MakeUnion(cases.[<span style="color: rgba(128, 0, 128, 1)">0</span>], [||<span style="color: rgba(0, 0, 0, 1)">])
      </span><span style="color: rgba(0, 0, 255, 1)">else</span> FSharpValue.MakeUnion(cases.[<span style="color: rgba(128, 0, 128, 1)">1</span>], [|value|])</pre>
</div>
<p>&nbsp;</p>
</div>
<p>这样所有的工作就完成了。</p>
<p>我们可以去&nbsp;monaco-editor&nbsp;仓库下载 monaco.d.ts 测试一下我们的 JSON Schema deserializer,可以发现 JSON Sechma 都被正确地反序列化了。</p>
<p><img class="origin_image zh-lightbox-thumb lazy" src="https://pic3.zhimg.com/80/v2-e8e1e4aa3a5366a9fc29c561831a8862_hd.jpg" alt="" width="760" height="595" data-size="normal" data-rawwidth="1916" data-rawheight="1501" data-original="https://pic3.zhimg.com/v2-e8e1e4aa3a5366a9fc29c561831a8862_r.jpg" data-actualsrc="https://pic3.zhimg.com/v2-e8e1e4aa3a5366a9fc29c561831a8862_b.jpg" data-lazy-status="ok"></p>
<p>反序列化结果</p>
<p class="ztext-empty-paragraph">&nbsp;</p>
<h2>构建 C# AST 类型</h2>
<p>当然,此 "AST" 非彼 AST,我们没有必要其细化到语句层面,因为我们只是要写一个简单的代码生成器,我们只需要构建实体结构即可。</p>
<p>我们将实体结构定义到 Entity.fs 中,在此我们只需支持 interface、class、enum 即可,对于 class 和 interface,我们只需要支持 method、property 和 event 就足够了。</p>
<p>当然,代码中存在泛型的可能,这一点我们也需要考虑。</p>
<div class="highlight">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">type</span><span style="color: rgba(0, 0, 0, 1)"> EntityBodyType = {
    Type: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">
    Name: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> option
    InnerTypes: EntityBodyType list
}

</span><span style="color: rgba(0, 0, 255, 1)">type</span><span style="color: rgba(0, 0, 0, 1)"> EntityMethod = {
    Comment: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">
    Modifier: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> list
    Type: EntityBodyType
    Name: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">
    TypeParameter: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> list
    Parameter: EntityBodyType list
}

</span><span style="color: rgba(0, 0, 255, 1)">type</span><span style="color: rgba(0, 0, 0, 1)"> EntityProperty = {
    Comment: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">
    Modifier: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> list
    Name: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">
    Type: EntityBodyType
    WithGet: </span><span style="color: rgba(0, 0, 255, 1)">bool</span><span style="color: rgba(0, 0, 0, 1)">
    WithSet: </span><span style="color: rgba(0, 0, 255, 1)">bool</span><span style="color: rgba(0, 0, 0, 1)">
    IsOptional: </span><span style="color: rgba(0, 0, 255, 1)">bool</span><span style="color: rgba(0, 0, 0, 1)">
    InitialValue: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> option
}

</span><span style="color: rgba(0, 0, 255, 1)">type</span><span style="color: rgba(0, 0, 0, 1)"> EntityEvent = {
    Comment: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">
    Modifier: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> list
    DelegateType: EntityBodyType
    Name: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">
    IsOptional: </span><span style="color: rgba(0, 0, 255, 1)">bool</span><span style="color: rgba(0, 0, 0, 1)">
}

</span><span style="color: rgba(0, 0, 255, 1)">type</span><span style="color: rgba(0, 0, 0, 1)"> EntityEnum = {
    Comment: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">
    Name: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">
    Value: int64 option
}

</span><span style="color: rgba(0, 0, 255, 1)">type</span><span style="color: rgba(0, 0, 0, 1)"> EntityType =
</span>|<span style="color: rgba(0, 0, 0, 1)"> Interface
</span>|<span style="color: rgba(0, 0, 0, 1)"> Class
</span>|<span style="color: rgba(0, 0, 0, 1)"> Enum
</span>|<span style="color: rgba(0, 0, 0, 1)"> StringEnum

</span><span style="color: rgba(0, 0, 255, 1)">type</span><span style="color: rgba(0, 0, 0, 1)"> Entity = {
    Namespace: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">
    Name: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">
    Comment: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">
    Methods: EntityMethod list
    Properties: EntityProperty list
    Events: EntityEvent list
    Enums: EntityEnum list
    InheritedFrom: EntityBodyType list
    Type: EntityType
    TypeParameter: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> list
    Modifier: </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> list
}</span></pre>
</div>
<p>&nbsp;</p>
</div>
<h2>文档化注释生成器</h2>
<p>文档化注释也是少不了的东西,能极大方便开发者后续使用生成的类型绑定,而无需参照原 typescript 类型声明上的注释。</p>
<p>代码很简单,只需要将文本处理成 xml 即可。</p>
<div class="highlight">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">let</span> escapeSymbols (text: <span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">) =
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> isNull text <span style="color: rgba(0, 0, 255, 1)">then</span> <span style="color: rgba(128, 0, 0, 1)">""</span>
    <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> text
            .Replace(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">&amp;</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">&amp;amp;</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
            .Replace(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">&amp;lt;</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
            .Replace(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">&gt;</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">&amp;gt;</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)

</span><span style="color: rgba(0, 0, 255, 1)">let</span> toCommentText (text: <span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">) =
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> isNull text <span style="color: rgba(0, 0, 255, 1)">then</span> <span style="color: rgba(128, 0, 0, 1)">""</span>
    <span style="color: rgba(0, 0, 255, 1)">else</span> text.Split <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">\n</span><span style="color: rgba(128, 0, 0, 1)">"</span> |&gt; Array.map (<span style="color: rgba(0, 0, 255, 1)">fun</span> t -&gt; <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">/// </span><span style="color: rgba(128, 0, 0, 1)">"</span> + escapeSymbols t) |&gt; Array.reduce(<span style="color: rgba(0, 0, 255, 1)">fun</span> accu next -&gt; accu + <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">\n</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)"> + next)

</span><span style="color: rgba(0, 0, 255, 1)">let</span><span style="color: rgba(0, 0, 0, 1)"> getXmlDocComment (comment: Comment) =
    </span><span style="color: rgba(0, 0, 255, 1)">let</span> prefix = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">/// &lt;summary&gt;\n</span><span style="color: rgba(128, 0, 0, 1)">"</span>
    <span style="color: rgba(0, 0, 255, 1)">let</span> suffix = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">\n/// &lt;/summary&gt;</span><span style="color: rgba(128, 0, 0, 1)">"</span>
    <span style="color: rgba(0, 0, 255, 1)">let</span><span style="color: rgba(0, 0, 0, 1)"> summary =
      </span><span style="color: rgba(0, 0, 255, 1)">match</span> comment.Text <span style="color: rgba(0, 0, 255, 1)">with</span>
      | Some text -&gt;<span style="color: rgba(0, 0, 0, 1)"> prefix + toCommentText comment.ShortText + toCommentText text + suffix
      </span>| _ -&gt;
            <span style="color: rgba(0, 0, 255, 1)">match</span> comment.ShortText <span style="color: rgba(0, 0, 255, 1)">with</span>
            | <span style="color: rgba(128, 0, 0, 1)">""</span> -&gt; <span style="color: rgba(128, 0, 0, 1)">""</span>
            | _ -&gt;<span style="color: rgba(0, 0, 0, 1)"> prefix + toCommentText comment.ShortText + suffix
    </span><span style="color: rgba(0, 0, 255, 1)">let</span><span style="color: rgba(0, 0, 0, 1)"> returns =
      </span><span style="color: rgba(0, 0, 255, 1)">match</span> comment.Returns <span style="color: rgba(0, 0, 255, 1)">with</span>
      | Some text -&gt; <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">\n/// &lt;returns&gt;\n</span><span style="color: rgba(128, 0, 0, 1)">"</span> + toCommentText text + <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">\n/// &lt;/returns&gt;</span><span style="color: rgba(128, 0, 0, 1)">"</span>
      | _ -&gt; <span style="color: rgba(128, 0, 0, 1)">""</span><span style="color: rgba(0, 0, 0, 1)">
    summary + returns</span></pre>
</div>
<p>&nbsp;</p>
</div>
<h2>类型生成器</h2>
<p>Typescript 的类型系统较为灵活,包括 union types、intersect types 等等,这些即使是目前的 C# 8 都不能直接表达,需要等到 C# 9 才行。当然我们可以生成一个 struct 并为其编写隐式转换操作符重载,支持 union types,但是目前尚未实现,我们就先用 union types 中的第一个类型代替,而对于 intersect types,我们姑且先使用 object。</p>
<p>然而 union types 有一个特殊情况:string literals types alias。就是这样的东西:</p>
<div class="highlight">
<pre><code class="language-text">type Size = "XS" | "S" | "M" | "L" | "XL";</code></pre>
</div>
<p>即纯 string 值组合的 type alias,这个我们还是有必要支持的,因为在 typescript 中用的非常广泛。</p>
<p>C# 在没有对应语法的时候要怎么支持呢?很简单,我们创建一个 enum,该 enum 包含该类型中的所有元素,然后我们为其编写 JsonConverter,这样就能确保序列化后,typescript 方能正确识别类型,而在 C# 又有 type sound 的编码体验。</p>
<p>另外,我们需要提供一些常用的类型转换:</p>
<ul>
<li><code>Array&lt;T&gt;</code>&nbsp;-&gt;&nbsp;<code>T[]</code>&nbsp;</li>
<li><code>Set&lt;T&gt;</code>&nbsp;-&gt;&nbsp;<code>System.Collections.Generic.ISet&lt;T&gt;</code></li>
<li><code></code><code>Map&lt;T&gt;</code>&nbsp;-&gt;&nbsp;<code>System.Collections.Generic.IDictionary&lt;T&gt;</code>&nbsp;</li>
<li><code>Promise&lt;T&gt;</code>&nbsp;-&gt;&nbsp;<code>System.Threading.Tasks.Task&lt;T&gt;</code>&nbsp;</li>
<li>callbacks -&gt;&nbsp;<code>System.Func&lt;T...&gt;</code>,&nbsp;<code>System.Action&lt;T...&gt;</code>&nbsp;</li>
<li>Tuple 类型</li>
<li>其他的数组类型如&nbsp;<code>Uint32Array</code>&nbsp;</li>
<li>对于&nbsp;<code>&lt;void&gt;</code>,我们需要解除泛型,即&nbsp;<code>T&lt;void&gt;</code>&nbsp;-&gt;&nbsp;<code>T</code></li>
</ul>
<p>那么实现如下:</p>
<div class="highlight">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">let</span> <span style="color: rgba(0, 0, 255, 1)">rec</span><span style="color: rgba(0, 0, 0, 1)"> getType (typeInfo: Type): EntityBodyType =
    </span><span style="color: rgba(0, 0, 255, 1)">let</span><span style="color: rgba(0, 0, 0, 1)"> genericType =
      </span><span style="color: rgba(0, 0, 255, 1)">match</span> typeInfo.Type <span style="color: rgba(0, 0, 255, 1)">with</span>
      | <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">intrinsic</span><span style="color: rgba(128, 0, 0, 1)">"</span> -&gt;
            <span style="color: rgba(0, 0, 255, 1)">match</span> typeInfo.Name <span style="color: rgba(0, 0, 255, 1)">with</span>
            | Some name -&gt;
                <span style="color: rgba(0, 0, 255, 1)">match</span> name <span style="color: rgba(0, 0, 255, 1)">with</span>
                | <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">number</span><span style="color: rgba(128, 0, 0, 1)">"</span> -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">double</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; InnerTypes = []; Name = None }
                </span>| <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">boolean</span><span style="color: rgba(128, 0, 0, 1)">"</span> -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">bool</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; InnerTypes = []; Name = None }
                </span>| <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">string</span><span style="color: rgba(128, 0, 0, 1)">"</span> -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">string</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; InnerTypes = []; Name = None }
                </span>| <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">void</span><span style="color: rgba(128, 0, 0, 1)">"</span> -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">void</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; InnerTypes = []; Name = None }
                </span>| _ -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">object</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; InnerTypes = []; Name = None }
            </span>| _ -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">object</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; InnerTypes = []; Name = None }
      </span>| <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">reference</span><span style="color: rgba(128, 0, 0, 1)">"</span> | <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">typeParameter</span><span style="color: rgba(128, 0, 0, 1)">"</span> -&gt;
            <span style="color: rgba(0, 0, 255, 1)">match</span> typeInfo.Name <span style="color: rgba(0, 0, 255, 1)">with</span>
            | Some name -&gt;
                <span style="color: rgba(0, 0, 255, 1)">match</span> name <span style="color: rgba(0, 0, 255, 1)">with</span>
                | <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Promise</span><span style="color: rgba(128, 0, 0, 1)">"</span> -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">System.Threading.Tasks.Task</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; InnerTypes = []; Name = None }
                </span>| <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Set</span><span style="color: rgba(128, 0, 0, 1)">"</span> -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">System.Collections.Generic.ISet</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; InnerTypes = []; Name = None }
                </span>| <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Map</span><span style="color: rgba(128, 0, 0, 1)">"</span> -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">System.Collections.Generic.IDictionary</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; InnerTypes = []; Name = None }
                </span>| <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Array</span><span style="color: rgba(128, 0, 0, 1)">"</span> -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">System.Array</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; InnerTypes = []; Name = None }
                </span>| <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">BigUint64Array</span><span style="color: rgba(128, 0, 0, 1)">"</span> -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">System.Array</span><span style="color: rgba(128, 0, 0, 1)">"</span>; InnerTypes = [{ Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">ulong</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; InnerTypes = [ ]; Name = None };]; Name = None };
                </span>| <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Uint32Array</span><span style="color: rgba(128, 0, 0, 1)">"</span> -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">System.Array</span><span style="color: rgba(128, 0, 0, 1)">"</span>; InnerTypes = [{ Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">uint</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; InnerTypes = [ ]; Name = None };]; Name = None };
                </span>| <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Uint16Array</span><span style="color: rgba(128, 0, 0, 1)">"</span> -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">System.Array</span><span style="color: rgba(128, 0, 0, 1)">"</span>; InnerTypes = [{ Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">ushort</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; InnerTypes = [ ]; Name = None };]; Name = None };
                </span>| <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Uint8Array</span><span style="color: rgba(128, 0, 0, 1)">"</span> -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">System.Array</span><span style="color: rgba(128, 0, 0, 1)">"</span>; InnerTypes = [{ Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">byte</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; InnerTypes = [ ]; Name = None };]; Name = None };
                </span>| <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">BigInt64Array</span><span style="color: rgba(128, 0, 0, 1)">"</span> -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">System.Array</span><span style="color: rgba(128, 0, 0, 1)">"</span>; InnerTypes = [{ Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">long</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; InnerTypes = [ ]; Name = None };]; Name = None };
                </span>| <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Int32Array</span><span style="color: rgba(128, 0, 0, 1)">"</span> -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">System.Array</span><span style="color: rgba(128, 0, 0, 1)">"</span>; InnerTypes = [{ Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">int</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; InnerTypes = [ ]; Name = None };]; Name = None };
                </span>| <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Int16Array</span><span style="color: rgba(128, 0, 0, 1)">"</span> -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">System.Array</span><span style="color: rgba(128, 0, 0, 1)">"</span>; InnerTypes = [{ Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">short</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; InnerTypes = [ ]; Name = None };]; Name = None };
                </span>| <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Int8Array</span><span style="color: rgba(128, 0, 0, 1)">"</span> -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">System.Array</span><span style="color: rgba(128, 0, 0, 1)">"</span>; InnerTypes = [{ Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">char</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; InnerTypes = [ ]; Name = None };]; Name = None };
                </span>| <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">RegExp</span><span style="color: rgba(128, 0, 0, 1)">"</span> -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">string</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; InnerTypes = []; Name = None };
                </span>| x -&gt;<span style="color: rgba(0, 0, 0, 1)"> { Type = x; InnerTypes = []; Name = None };
            </span>| _ -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">object</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; InnerTypes = []; Name = None }
      </span>| <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">array</span><span style="color: rgba(128, 0, 0, 1)">"</span> -&gt;
            <span style="color: rgba(0, 0, 255, 1)">match</span> typeInfo.ElementType <span style="color: rgba(0, 0, 255, 1)">with</span>
            | Some elementType -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">System.Array</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; InnerTypes = ; Name = None }
            </span>| _ -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">System.Array</span><span style="color: rgba(128, 0, 0, 1)">"</span>; InnerTypes = [{ Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">object</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; InnerTypes = []; Name = None }]; Name = None }
      </span>| <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">stringLiteral</span><span style="color: rgba(128, 0, 0, 1)">"</span> -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">string</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; InnerTypes = []; Name = None }
      </span>| <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">tuple</span><span style="color: rgba(128, 0, 0, 1)">"</span> -&gt;
            <span style="color: rgba(0, 0, 255, 1)">match</span> typeInfo.Types <span style="color: rgba(0, 0, 255, 1)">with</span>
            | Some innerTypes -&gt;
                <span style="color: rgba(0, 0, 255, 1)">match</span> innerTypes <span style="color: rgba(0, 0, 255, 1)">with</span>
                | [] -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">object</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; InnerTypes = []; Name = None }
                </span>| _ -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">System.ValueTuple</span><span style="color: rgba(128, 0, 0, 1)">"</span>; InnerTypes = innerTypes |<span style="color: rgba(0, 0, 0, 1)">&gt; List.map getType; Name = None }
            </span>| _ -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">object</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; InnerTypes = []; Name = None }
      </span>| <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">union</span><span style="color: rgba(128, 0, 0, 1)">"</span> -&gt;
            <span style="color: rgba(0, 0, 255, 1)">match</span> typeInfo.Types <span style="color: rgba(0, 0, 255, 1)">with</span>
            | Some innerTypes -&gt;
                <span style="color: rgba(0, 0, 255, 1)">match</span> innerTypes <span style="color: rgba(0, 0, 255, 1)">with</span>
                | [] -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">object</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; InnerTypes = []; Name = None }
                </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)">
                  printWarning (</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Taking only the first type </span><span style="color: rgba(128, 0, 0, 1)">"</span> + innerTypes.[<span style="color: rgba(128, 0, 128, 1)">0</span>].Type + <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)"> for the entire union type.</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
                  getType innerTypes.[</span><span style="color: rgba(128, 0, 128, 1)">0</span>] <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> TODO: generate unions</span>
| _ -&gt;{ Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">object</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; InnerTypes = []; Name = None }
      </span>| <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">intersection</span><span style="color: rgba(128, 0, 0, 1)">"</span> -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">object</span><span style="color: rgba(128, 0, 0, 1)">"</span>; InnerTypes = []; Name = None } <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> TODO: generate intersections</span>
| <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">reflection</span><span style="color: rgba(128, 0, 0, 1)">"</span> -&gt;
            <span style="color: rgba(0, 0, 255, 1)">match</span> typeInfo.Declaration <span style="color: rgba(0, 0, 255, 1)">with</span>
            | Some dec -&gt;
                <span style="color: rgba(0, 0, 255, 1)">match</span> dec.Signatures <span style="color: rgba(0, 0, 255, 1)">with</span>
                | Some -&gt;
                  <span style="color: rgba(0, 0, 255, 1)">let</span><span style="color: rgba(0, 0, 0, 1)"> paras =
                        </span><span style="color: rgba(0, 0, 255, 1)">match</span> signature.Parameters <span style="color: rgba(0, 0, 255, 1)">with</span>
                        | Some p -&gt;<span style="color: rgba(0, 0, 0, 1)">
                            p
                            </span>|<span style="color: rgba(0, 0, 0, 1)">&gt; List.map
                              (</span><span style="color: rgba(0, 0, 255, 1)">fun</span> pi -&gt;
                                    <span style="color: rgba(0, 0, 255, 1)">match</span> pi.Type <span style="color: rgba(0, 0, 255, 1)">with</span>
                                    | Some pt -&gt;<span style="color: rgba(0, 0, 0, 1)"> Some (getType pt)
                                    </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)"> None
                              )
                            </span>|<span style="color: rgba(0, 0, 0, 1)">&gt; List.collect
                              (</span><span style="color: rgba(0, 0, 255, 1)">fun</span> x -&gt;
                                    <span style="color: rgba(0, 0, 255, 1)">match</span> x <span style="color: rgba(0, 0, 255, 1)">with</span>
                                    | Some s -&gt;<span style="color: rgba(0, 0, 0, 1)">
                                    </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)"> []
                              )
                        </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)"> []
                  </span><span style="color: rgba(0, 0, 255, 1)">let</span> <span style="color: rgba(0, 0, 255, 1)">rec</span><span style="color: rgba(0, 0, 0, 1)"> getDelegateParas (paras: EntityBodyType list): EntityBodyType list =
                        </span><span style="color: rgba(0, 0, 255, 1)">match</span> paras <span style="color: rgba(0, 0, 255, 1)">with</span>
                        | -&gt;<span style="color: rgba(0, 0, 0, 1)"> [{ Type = x.Type; InnerTypes = x.InnerTypes; Name = None }]
                        </span>| (front::tails) -&gt;<span style="color: rgba(0, 0, 0, 1)"> @ getDelegateParas tails
                        </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)"> []
                  </span><span style="color: rgba(0, 0, 255, 1)">let</span><span style="color: rgba(0, 0, 0, 1)"> returnsType =
                        </span><span style="color: rgba(0, 0, 255, 1)">match</span> signature.Type <span style="color: rgba(0, 0, 255, 1)">with</span>
                        | Some t -&gt;<span style="color: rgba(0, 0, 0, 1)"> getType t
                        </span>| _ -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">void</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; InnerTypes = []; Name = None }
                  </span><span style="color: rgba(0, 0, 255, 1)">let</span><span style="color: rgba(0, 0, 0, 1)"> typeParas = getDelegateParas paras
                  </span><span style="color: rgba(0, 0, 255, 1)">match</span> typeParas <span style="color: rgba(0, 0, 255, 1)">with</span>
                  | [] -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">System.Action</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; InnerTypes = []; Name = None }
                  </span>| _ -&gt;
                        <span style="color: rgba(0, 0, 255, 1)">if</span> returnsType.Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">void</span><span style="color: rgba(128, 0, 0, 1)">"</span>
                        <span style="color: rgba(0, 0, 255, 1)">then</span> { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">System.Action</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; InnerTypes = typeParas; Name = None }
                        </span><span style="color: rgba(0, 0, 255, 1)">else</span> { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">System.Func</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; InnerTypes = typeParas @ ; Name = None }
                </span>| _ -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">object</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; InnerTypes = []; Name = None }
            </span>| _ -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">object</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; InnerTypes = []; Name = None }
      </span>| _ -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">object</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; InnerTypes = []; Name = None }
    </span><span style="color: rgba(0, 0, 255, 1)">let</span> <span style="color: rgba(0, 0, 255, 1)">mutable</span><span style="color: rgba(0, 0, 0, 1)"> innerTypes =
      </span><span style="color: rgba(0, 0, 255, 1)">match</span> typeInfo.TypeArguments <span style="color: rgba(0, 0, 255, 1)">with</span>
      | Some args -&gt;<span style="color: rgba(0, 0, 0, 1)"> getGenericTypeArguments args
      </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)"> []
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> genericType.Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">System.Threading.Tasks.Task</span><span style="color: rgba(128, 0, 0, 1)">"</span>
    <span style="color: rgba(0, 0, 255, 1)">then</span>
      <span style="color: rgba(0, 0, 255, 1)">match</span> innerTypes <span style="color: rgba(0, 0, 255, 1)">with</span>
      | (front::_) -&gt; <span style="color: rgba(0, 0, 255, 1)">if</span> front.Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">void</span><span style="color: rgba(128, 0, 0, 1)">"</span> <span style="color: rgba(0, 0, 255, 1)">then</span> innerTypes &lt;- [] <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> ()
      </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)"> ()
    </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> ()
    {
      Type = genericType.Type;
      Name = None;
      InnerTypes = </span><span style="color: rgba(0, 0, 255, 1)">if</span> innerTypes = [] <span style="color: rgba(0, 0, 255, 1)">then</span> genericType.InnerTypes <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> innerTypes;
    }
</span><span style="color: rgba(0, 0, 255, 1)">and</span><span style="color: rgba(0, 0, 0, 1)"> getGenericTypeArguments (typeInfos: Type list): EntityBodyType list =
    typeInfos </span>|<span style="color: rgba(0, 0, 0, 1)">&gt; List.map getType
</span><span style="color: rgba(0, 0, 255, 1)">and</span> getGenericTypeParameters (nodes: Reflection list) = <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> TODO: generate constaints</span>
<span style="color: rgba(0, 0, 255, 1)">let</span><span style="color: rgba(0, 0, 0, 1)"> types =
      nodes
      </span>|&gt; List.where(<span style="color: rgba(0, 0, 255, 1)">fun</span> x -&gt;<span style="color: rgba(0, 0, 0, 1)"> x.Kind = ReflectionKind.TypeParameter)
      </span>|&gt; List.map (<span style="color: rgba(0, 0, 255, 1)">fun</span> x -&gt;<span style="color: rgba(0, 0, 0, 1)"> x.Name)
    types </span>|&gt; List.map (<span style="color: rgba(0, 0, 255, 1)">fun</span> x -&gt; {| Type = x; Constraint = <span style="color: rgba(128, 0, 0, 1)">""</span> |})</pre>
</div>
<p>&nbsp;</p>
</div>
<p>当然,目前尚不支持生成泛型约束,如果以后有时间的话会考虑添加。</p>
<h2>修饰生成器</h2>
<p>例如&nbsp;<code>public</code>、<code>private</code>、<code>protected</code>、<code>static</code>&nbsp;等等。这一步很简单,直接将 ReflectionFlags 转换一下即可,个人觉得使用 mutable 代码会让代码变得非常不优雅,但是有的时候还是需要用一下的,不然会极大地提高代码的复杂度。</p>
<div class="highlight">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">let</span><span style="color: rgba(0, 0, 0, 1)"> getModifier (flags: ReflectionFlags) =
    </span><span style="color: rgba(0, 0, 255, 1)">let</span> <span style="color: rgba(0, 0, 255, 1)">mutable</span><span style="color: rgba(0, 0, 0, 1)"> modifier = []
    </span><span style="color: rgba(0, 0, 255, 1)">match</span> flags.IsPublic <span style="color: rgba(0, 0, 255, 1)">with</span>
    | Some flag -&gt; <span style="color: rgba(0, 0, 255, 1)">if</span> flag <span style="color: rgba(0, 0, 255, 1)">then</span> modifier &lt;- modifier |&gt; List.append [ <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">public</span><span style="color: rgba(128, 0, 0, 1)">"</span> ] <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> ()
    </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)"> ()
    </span><span style="color: rgba(0, 0, 255, 1)">match</span> flags.IsAbstract <span style="color: rgba(0, 0, 255, 1)">with</span>
    | Some flag -&gt; <span style="color: rgba(0, 0, 255, 1)">if</span> flag <span style="color: rgba(0, 0, 255, 1)">then</span> modifier &lt;- modifier |&gt; List.append [ <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">abstract</span><span style="color: rgba(128, 0, 0, 1)">"</span> ] <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> ()
    </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)"> ()
    </span><span style="color: rgba(0, 0, 255, 1)">match</span> flags.IsPrivate <span style="color: rgba(0, 0, 255, 1)">with</span>
    | Some flag -&gt; <span style="color: rgba(0, 0, 255, 1)">if</span> flag <span style="color: rgba(0, 0, 255, 1)">then</span> modifier &lt;- modifier |&gt; List.append [ <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">private</span><span style="color: rgba(128, 0, 0, 1)">"</span> ] <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> ()
    </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)"> ()
    </span><span style="color: rgba(0, 0, 255, 1)">match</span> flags.IsProtected <span style="color: rgba(0, 0, 255, 1)">with</span>
    | Some flag -&gt; <span style="color: rgba(0, 0, 255, 1)">if</span> flag <span style="color: rgba(0, 0, 255, 1)">then</span> modifier &lt;- modifier |&gt; List.append [ <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">protected</span><span style="color: rgba(128, 0, 0, 1)">"</span> ] <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> ()
    </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)"> ()
    </span><span style="color: rgba(0, 0, 255, 1)">match</span> flags.IsStatic <span style="color: rgba(0, 0, 255, 1)">with</span>
    | Some flag -&gt; <span style="color: rgba(0, 0, 255, 1)">if</span> flag <span style="color: rgba(0, 0, 255, 1)">then</span> modifier &lt;- modifier |&gt; List.append [ <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">static</span><span style="color: rgba(128, 0, 0, 1)">"</span> ] <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> ()
    </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)"> ()
    modifier</span></pre>
</div>
<p>&nbsp;</p>
</div>
<h2>Enum 生成器</h2>
<p>终于到 parse 实体的部分了,我们先从最简单的做起:枚举。 代码很简单,直接将原 AST 中的枚举部分转换一下即可。</p>
<div class="highlight">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">let</span> parseEnum (section: <span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">) (node: Reflection): Entity =
    </span><span style="color: rgba(0, 0, 255, 1)">let</span> values = <span style="color: rgba(0, 0, 255, 1)">match</span> node.Children <span style="color: rgba(0, 0, 255, 1)">with</span>
               | Some children -&gt;<span style="color: rgba(0, 0, 0, 1)">
                     children
                     </span>|&gt; List.where (<span style="color: rgba(0, 0, 255, 1)">fun</span> x -&gt;<span style="color: rgba(0, 0, 0, 1)"> x.Kind = ReflectionKind.EnumMember)
               </span>| None -&gt;<span style="color: rgba(0, 0, 0, 1)"> []
    {
      Type = EntityType.Enum;
      Namespace = </span><span style="color: rgba(0, 0, 255, 1)">if</span> section = <span style="color: rgba(128, 0, 0, 1)">""</span> <span style="color: rgba(0, 0, 255, 1)">then</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">TypeDocGenerator</span><span style="color: rgba(128, 0, 0, 1)">"</span> <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> section;
      Modifier = getModifier node.Flags;
      Name = node.Name
      Comment =
            </span><span style="color: rgba(0, 0, 255, 1)">match</span> node.Comment <span style="color: rgba(0, 0, 255, 1)">with</span>
            | Some comment -&gt;<span style="color: rgba(0, 0, 0, 1)"> getXmlDocComment comment
            </span>| _ -&gt; <span style="color: rgba(128, 0, 0, 1)">""</span><span style="color: rgba(0, 0, 0, 1)">
      Methods = []; Properties = []; Events = []; InheritedFrom = [];
      Enums = values </span>|&gt; List.map (<span style="color: rgba(0, 0, 255, 1)">fun</span> x -&gt;
            <span style="color: rgba(0, 0, 255, 1)">let</span><span style="color: rgba(0, 0, 0, 1)"> comment =
                </span><span style="color: rgba(0, 0, 255, 1)">match</span> x.Comment <span style="color: rgba(0, 0, 255, 1)">with</span>
                | Some comment -&gt;<span style="color: rgba(0, 0, 0, 1)"> getXmlDocComment comment
                </span>| _ -&gt; <span style="color: rgba(128, 0, 0, 1)">""</span>
            <span style="color: rgba(0, 0, 255, 1)">let</span> <span style="color: rgba(0, 0, 255, 1)">mutable</span> intValue = <span style="color: rgba(128, 0, 128, 1)">0L</span>
            <span style="color: rgba(0, 0, 255, 1)">match</span> x.DefaultValue <span style="color: rgba(0, 0, 255, 1)">with</span>
            <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> ?????</span>
            | Some value -&gt; <span style="color: rgba(0, 0, 255, 1)">if</span> Int64.TryParse(value, &amp;intValue) <span style="color: rgba(0, 0, 255, 1)">then</span><span style="color: rgba(0, 0, 0, 1)"> { Comment = comment; Name = toPascalCase x.Name; Value = Some intValue; }
                            </span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">match</span> getEnumReferencedValue values value x.Name <span style="color: rgba(0, 0, 255, 1)">with</span>
                                 | Some t -&gt;<span style="color: rgba(0, 0, 0, 1)"> { Comment = comment; Name = x.Name; Value = Some (int64 t); }
                                 </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)"> { Comment = comment; Name = x.Name; Value = None; }
            </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)"> { Comment = comment; Name = x.Name; Value = None; }
      );
      TypeParameter = []
    }</span></pre>
</div>
<p>&nbsp;</p>
</div>
<p>你会注意到一个上面我有一处标了个&nbsp;<code>?????</code>,这是在干什么呢?</p>
<p>其实,TypeScript 的 enum 是 recursive 的,也就意味着定义的时候,一个元素可以引用另一个元素,比如这样:</p>
<div class="highlight">
<pre><code class="language-text">enum MyEnum {
    A = 1,
    B = 2,
    C = A
}</code></pre>
</div>
<p>这个时候,我们需要查找它引用的枚举值,比如在上面的例子里面,处理 C 的时候,需要将它的值 A 用真实值 1 代替。所以我们还需要一个查找函数:</p>
<div class="highlight">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">let</span> <span style="color: rgba(0, 0, 255, 1)">rec</span><span style="color: rgba(0, 0, 0, 1)"> getEnumReferencedValue (nodes: Reflection list) value name =
    </span><span style="color: rgba(0, 0, 255, 1)">match</span><span style="color: rgba(0, 0, 0, 1)"> nodes
          </span>|&gt; List.where(<span style="color: rgba(0, 0, 255, 1)">fun</span> x -&gt;
            <span style="color: rgba(0, 0, 255, 1)">match</span> x.DefaultValue <span style="color: rgba(0, 0, 255, 1)">with</span>
            | Some v -&gt;<span style="color: rgba(0, 0, 0, 1)"> v &lt;&gt; value &amp;&amp; not (name = x.Name)
            </span>| _ -&gt; <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">
          )
          </span>|&gt; List.where(<span style="color: rgba(0, 0, 255, 1)">fun</span> x -&gt;<span style="color: rgba(0, 0, 0, 1)"> x.Name = value)
          </span>|&gt; List.tryFind(<span style="color: rgba(0, 0, 255, 1)">fun</span> x -&gt;
                            <span style="color: rgba(0, 0, 255, 1)">let</span> <span style="color: rgba(0, 0, 255, 1)">mutable</span> intValue = <span style="color: rgba(128, 0, 128, 1)">0</span>
                            <span style="color: rgba(0, 0, 255, 1)">match</span> x.DefaultValue <span style="color: rgba(0, 0, 255, 1)">with</span>
                            | Some y -&gt;<span style="color: rgba(0, 0, 0, 1)"> Int32.TryParse(y, &amp;intValue)
                            </span>| _ -&gt; <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">
         ) </span><span style="color: rgba(0, 0, 255, 1)">with</span>
    | Some t -&gt;<span style="color: rgba(0, 0, 0, 1)"> t.DefaultValue
    </span>| _ -&gt; None</pre>
</div>
<p>&nbsp;</p>
</div>
<p>这样我们的 Enum parser 就完成了。</p>
<h2>Interface 和 Class 生成器</h2>
<p>下面到了重头戏,interface 和 class 才是类型绑定的关键。</p>
<p>我们的函数签名是这样的:</p>
<div class="highlight">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">let</span> parseInterfaceAndClass (section: <span style="color: rgba(0, 0, 255, 1)">string</span>) (node: Reflection) (isInterface: <span style="color: rgba(0, 0, 255, 1)">bool</span>): Entity = ...</pre>
</div>
<p>&nbsp;</p>
</div>
<p>首先我们从 Reflection 节点中查找并生成注释、修饰、名称、泛型参数、继承关系、方法、属性和事件:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">let</span><span style="color: rgba(0, 0, 0, 1)"> comment =
    </span><span style="color: rgba(0, 0, 255, 1)">match</span> node.Comment <span style="color: rgba(0, 0, 255, 1)">with</span>
    | Some comment -&gt;<span style="color: rgba(0, 0, 0, 1)"> getXmlDocComment comment
    </span>| _ -&gt; <span style="color: rgba(128, 0, 0, 1)">""</span>
<span style="color: rgba(0, 0, 255, 1)">let</span><span style="color: rgba(0, 0, 0, 1)"> exts =
    (</span><span style="color: rgba(0, 0, 255, 1)">match</span> node.ExtendedTypes <span style="color: rgba(0, 0, 255, 1)">with</span>
    | Some types -&gt; types |&gt; List.map(<span style="color: rgba(0, 0, 255, 1)">fun</span> x -&gt;<span style="color: rgba(0, 0, 0, 1)"> getType x)
    </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)"> []) @
    (</span><span style="color: rgba(0, 0, 255, 1)">match</span> node.ImplementedTypes <span style="color: rgba(0, 0, 255, 1)">with</span>
    | Some types -&gt; types |&gt; List.map(<span style="color: rgba(0, 0, 255, 1)">fun</span> x -&gt;<span style="color: rgba(0, 0, 0, 1)"> getType x)
    </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)"> [])
</span><span style="color: rgba(0, 0, 255, 1)">let</span><span style="color: rgba(0, 0, 0, 1)"> genericType =
    </span><span style="color: rgba(0, 0, 255, 1)">let</span><span style="color: rgba(0, 0, 0, 1)"> types =
          </span><span style="color: rgba(0, 0, 255, 1)">match</span> node.TypeParameter <span style="color: rgba(0, 0, 255, 1)">with</span>
          | Some tp -&gt;<span style="color: rgba(0, 0, 0, 1)"> Some (getGenericTypeParameters tp)
          </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)"> None
    </span><span style="color: rgba(0, 0, 255, 1)">match</span> types <span style="color: rgba(0, 0, 255, 1)">with</span>
    | Some result -&gt;<span style="color: rgba(0, 0, 0, 1)"> result
    </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)"> []
</span><span style="color: rgba(0, 0, 255, 1)">let</span><span style="color: rgba(0, 0, 0, 1)"> properties =
    </span><span style="color: rgba(0, 0, 255, 1)">match</span> node.Children <span style="color: rgba(0, 0, 255, 1)">with</span>
    | Some children -&gt;
      <span style="color: rgba(0, 0, 255, 1)">if</span> isInterface <span style="color: rgba(0, 0, 255, 1)">then</span><span style="color: rgba(0, 0, 0, 1)">
            children
            </span>|&gt; List.where(<span style="color: rgba(0, 0, 255, 1)">fun</span> x -&gt;<span style="color: rgba(0, 0, 0, 1)"> x.Kind = ReflectionKind.Property)
            </span>|&gt; List.where(<span style="color: rgba(0, 0, 255, 1)">fun</span> x -&gt; x.InheritedFrom = None) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> exclude inhreited properties</span>
            |&gt; List.where(<span style="color: rgba(0, 0, 255, 1)">fun</span> x -&gt; x.Overwrites = None) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> exclude overrites properties</span>
      <span style="color: rgba(0, 0, 255, 1)">else</span> children |&gt; List.where(<span style="color: rgba(0, 0, 255, 1)">fun</span> x -&gt;<span style="color: rgba(0, 0, 0, 1)"> x.Kind = ReflectionKind.Property)
    </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)"> []
</span><span style="color: rgba(0, 0, 255, 1)">let</span><span style="color: rgba(0, 0, 0, 1)"> events =
    </span><span style="color: rgba(0, 0, 255, 1)">match</span> node.Children <span style="color: rgba(0, 0, 255, 1)">with</span>
    | Some children -&gt;
      <span style="color: rgba(0, 0, 255, 1)">if</span> isInterface <span style="color: rgba(0, 0, 255, 1)">then</span><span style="color: rgba(0, 0, 0, 1)">
            children
            </span>|&gt; List.where(<span style="color: rgba(0, 0, 255, 1)">fun</span> x -&gt;<span style="color: rgba(0, 0, 0, 1)"> x.Kind = ReflectionKind.Event)
            </span>|&gt; List.where(<span style="color: rgba(0, 0, 255, 1)">fun</span> x -&gt; x.InheritedFrom = None) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> exclude inhreited events</span>
            |&gt; List.where(<span style="color: rgba(0, 0, 255, 1)">fun</span> x -&gt; x.Overwrites = None) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> exclude overrites events</span>
      <span style="color: rgba(0, 0, 255, 1)">else</span> children |&gt; List.where(<span style="color: rgba(0, 0, 255, 1)">fun</span> x -&gt;<span style="color: rgba(0, 0, 0, 1)"> x.Kind = ReflectionKind.Event)
    </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)"> []
</span><span style="color: rgba(0, 0, 255, 1)">let</span><span style="color: rgba(0, 0, 0, 1)"> methods =
    </span><span style="color: rgba(0, 0, 255, 1)">match</span> node.Children <span style="color: rgba(0, 0, 255, 1)">with</span>
    | Some children -&gt;
      <span style="color: rgba(0, 0, 255, 1)">if</span> isInterface <span style="color: rgba(0, 0, 255, 1)">then</span><span style="color: rgba(0, 0, 0, 1)">
            children
            </span>|&gt; List.where(<span style="color: rgba(0, 0, 255, 1)">fun</span> x -&gt;<span style="color: rgba(0, 0, 0, 1)"> x.Kind = ReflectionKind.Method)
            </span>|&gt; List.where(<span style="color: rgba(0, 0, 255, 1)">fun</span> x -&gt; x.InheritedFrom = None) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> exclude inhreited methods</span>
            |&gt; List.where(<span style="color: rgba(0, 0, 255, 1)">fun</span> x -&gt; x.Overwrites = None) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> exclude overrites methods</span>
      <span style="color: rgba(0, 0, 255, 1)">else</span> children |&gt; List.where(<span style="color: rgba(0, 0, 255, 1)">fun</span> x -&gt;<span style="color: rgba(0, 0, 0, 1)"> x.Kind = ReflectionKind.Method)
    </span>| _ -&gt; []</pre>
</div>
<p>&nbsp;</p>
<p>有一点要注意,就是对于 interface 来说,子 interface 无需重复父 interface 的成员,因此需要排除。</p>
<p>然后我们直接返回一个 record,代表该节点的实体即可。</p>
<div class="highlight">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">{
    Type = </span><span style="color: rgba(0, 0, 255, 1)">if</span> isInterface <span style="color: rgba(0, 0, 255, 1)">then</span> EntityType.Interface <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> EntityType.Class;
    Namespace = </span><span style="color: rgba(0, 0, 255, 1)">if</span> section = <span style="color: rgba(128, 0, 0, 1)">""</span> <span style="color: rgba(0, 0, 255, 1)">then</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">TypedocConverter</span><span style="color: rgba(128, 0, 0, 1)">"</span> <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> section;
    Name = node.Name;
    Comment = comment;
    Modifier = getModifier node.Flags;
    InheritedFrom = exts;
    Methods =
      methods
      </span>|<span style="color: rgba(0, 0, 0, 1)">&gt; List.map (
            </span><span style="color: rgba(0, 0, 255, 1)">fun</span> x -&gt;
                <span style="color: rgba(0, 0, 255, 1)">let</span><span style="color: rgba(0, 0, 0, 1)"> retType =
                  </span><span style="color: rgba(0, 0, 255, 1)">match</span><span style="color: rgba(0, 0, 0, 1)"> (
                            </span><span style="color: rgba(0, 0, 255, 1)">match</span> x.Signatures <span style="color: rgba(0, 0, 255, 1)">with</span>
                            | Some signatures -&gt;<span style="color: rgba(0, 0, 0, 1)">
                              signatures </span>|&gt; List.where(<span style="color: rgba(0, 0, 255, 1)">fun</span> x -&gt;<span style="color: rgba(0, 0, 0, 1)"> x.Kind = ReflectionKind.CallSignature)
                            </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)"> [])
                        </span><span style="color: rgba(0, 0, 255, 1)">with</span>
                        | [] -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">object</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; InnerTypes = []; Name = None }
                        </span>| (front::_) -&gt;
                            <span style="color: rgba(0, 0, 255, 1)">match</span> front.Type <span style="color: rgba(0, 0, 255, 1)">with</span>
                            | Some typeInfo -&gt;<span style="color: rgba(0, 0, 0, 1)"> getType typeInfo
                            </span>| _ -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">object</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; InnerTypes = []; Name = None }
                </span><span style="color: rgba(0, 0, 255, 1)">let</span><span style="color: rgba(0, 0, 0, 1)"> typeParameter =
                  </span><span style="color: rgba(0, 0, 255, 1)">match</span> x.Signatures <span style="color: rgba(0, 0, 255, 1)">with</span>
                  | Some (sigs::_) -&gt;
                        <span style="color: rgba(0, 0, 255, 1)">let</span><span style="color: rgba(0, 0, 0, 1)"> types =
                              </span><span style="color: rgba(0, 0, 255, 1)">match</span> sigs.TypeParameter <span style="color: rgba(0, 0, 255, 1)">with</span>
                              | Some tp -&gt;<span style="color: rgba(0, 0, 0, 1)"> Some (getGenericTypeParameters tp)
                              </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)"> None
                        </span><span style="color: rgba(0, 0, 255, 1)">match</span> types <span style="color: rgba(0, 0, 255, 1)">with</span>
                        | Some result -&gt;<span style="color: rgba(0, 0, 0, 1)"> result
                        </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)"> []
                  </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)"> []
                  </span>|&gt; List.map (<span style="color: rgba(0, 0, 255, 1)">fun</span> x -&gt;<span style="color: rgba(0, 0, 0, 1)"> x.Type)
                </span><span style="color: rgba(0, 0, 255, 1)">let</span><span style="color: rgba(0, 0, 0, 1)"> parameters =
                  getMethodParameters
                        (</span><span style="color: rgba(0, 0, 255, 1)">match</span> x.Signatures <span style="color: rgba(0, 0, 255, 1)">with</span>
                        | Some signatures -&gt;<span style="color: rgba(0, 0, 0, 1)">
                            signatures
                            </span>|&gt; List.where(<span style="color: rgba(0, 0, 255, 1)">fun</span> x -&gt;<span style="color: rgba(0, 0, 0, 1)"> x.Kind = ReflectionKind.CallSignature)
                            </span>|<span style="color: rgba(0, 0, 0, 1)">&gt; List.map(
                              </span><span style="color: rgba(0, 0, 255, 1)">fun</span> x -&gt;
                                    <span style="color: rgba(0, 0, 255, 1)">match</span> x.Parameters <span style="color: rgba(0, 0, 255, 1)">with</span>
                                    | Some parameters -&gt; parameters |&gt; List.where(<span style="color: rgba(0, 0, 255, 1)">fun</span> p -&gt;<span style="color: rgba(0, 0, 0, 1)"> p.Kind = ReflectionKind.Parameter)
                                    </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)"> []
                              )
                            </span>|&gt; List.reduce(<span style="color: rgba(0, 0, 255, 1)">fun</span> accu next -&gt;<span style="color: rgba(0, 0, 0, 1)"> accu @ next)
                        </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)"> [])
                {
                  Comment =
                        </span><span style="color: rgba(0, 0, 255, 1)">match</span> x.Comment <span style="color: rgba(0, 0, 255, 1)">with</span>
                        | Some comment -&gt;<span style="color: rgba(0, 0, 0, 1)"> getXmlDocComment comment
                        </span>| _ -&gt; <span style="color: rgba(128, 0, 0, 1)">""</span><span style="color: rgba(0, 0, 0, 1)">
                  Modifier = </span><span style="color: rgba(0, 0, 255, 1)">if</span> isInterface <span style="color: rgba(0, 0, 255, 1)">then</span> [] <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> getModifier x.Flags;
                  Type = retType
                  Name = x.Name
                  TypeParameter = typeParameter
                  Parameter = parameters
                }
      );
    Events =
      events
      </span>|<span style="color: rgba(0, 0, 0, 1)">&gt; List.map (
            </span><span style="color: rgba(0, 0, 255, 1)">fun</span> x -&gt;
                <span style="color: rgba(0, 0, 255, 1)">let</span><span style="color: rgba(0, 0, 0, 1)"> paras =
                  </span><span style="color: rgba(0, 0, 255, 1)">match</span> x.Signatures <span style="color: rgba(0, 0, 255, 1)">with</span>
                  | Some sigs -&gt;<span style="color: rgba(0, 0, 0, 1)">
                        sigs
                        </span>|&gt; List.where (<span style="color: rgba(0, 0, 255, 1)">fun</span> x -&gt;<span style="color: rgba(0, 0, 0, 1)"> x.Kind = ReflectionKind.Event)
                        </span>|&gt; List.map(<span style="color: rgba(0, 0, 255, 1)">fun</span> x -&gt;<span style="color: rgba(0, 0, 0, 1)"> x.Parameters)
                        </span>|&gt; List.collect (<span style="color: rgba(0, 0, 255, 1)">fun</span> x -&gt;
                            <span style="color: rgba(0, 0, 255, 1)">match</span> x <span style="color: rgba(0, 0, 255, 1)">with</span>
                            | Some paras -&gt;<span style="color: rgba(0, 0, 0, 1)"> paras
                            </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)"> [])
                  </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)"> []
                {
                  Name = x.Name;
                  IsOptional =
                        </span><span style="color: rgba(0, 0, 255, 1)">match</span> x.Flags.IsOptional <span style="color: rgba(0, 0, 255, 1)">with</span>
                        | Some optional -&gt;<span style="color: rgba(0, 0, 0, 1)"> optional
                        </span>| _ -&gt; <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">
                        ;
                  DelegateType =
                        </span><span style="color: rgba(0, 0, 255, 1)">match</span> paras <span style="color: rgba(0, 0, 255, 1)">with</span>
                        | (front::_) -&gt;
                            <span style="color: rgba(0, 0, 255, 1)">match</span> front.Type <span style="color: rgba(0, 0, 255, 1)">with</span>
                            | Some typeInfo -&gt;<span style="color: rgba(0, 0, 0, 1)"> getType typeInfo
                            </span>| _ -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">System.Delegate</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; Name = None; InnerTypes = [] }
                        </span>| _ -&gt;
                            <span style="color: rgba(0, 0, 255, 1)">match</span> x.Type <span style="color: rgba(0, 0, 255, 1)">with</span>
                            | Some typeInfo -&gt;<span style="color: rgba(0, 0, 0, 1)"> getType typeInfo
                            </span>| _ -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">System.Delegate</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; Name = None; InnerTypes = [] }
                        ;
                  Comment =
                        </span><span style="color: rgba(0, 0, 255, 1)">match</span> x.Comment <span style="color: rgba(0, 0, 255, 1)">with</span>
                        | Some comment -&gt;<span style="color: rgba(0, 0, 0, 1)"> getXmlDocComment comment
                        </span>| _ -&gt; <span style="color: rgba(128, 0, 0, 1)">""</span><span style="color: rgba(0, 0, 0, 1)">
                        ;
                  Modifier = </span><span style="color: rgba(0, 0, 255, 1)">if</span> isInterface <span style="color: rgba(0, 0, 255, 1)">then</span> [] <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> getModifier x.Flags;
                }
      );
    Properties =
      properties
      </span>|<span style="color: rgba(0, 0, 0, 1)">&gt; List.map (
            </span><span style="color: rgba(0, 0, 255, 1)">fun</span> x -&gt;<span style="color: rgba(0, 0, 0, 1)">
                {
                  Comment =
                        </span><span style="color: rgba(0, 0, 255, 1)">match</span> x.Comment <span style="color: rgba(0, 0, 255, 1)">with</span>
                        | Some comment -&gt;<span style="color: rgba(0, 0, 0, 1)"> getXmlDocComment comment
                        </span>| _ -&gt; <span style="color: rgba(128, 0, 0, 1)">""</span><span style="color: rgba(0, 0, 0, 1)">
                  Modifier = </span><span style="color: rgba(0, 0, 255, 1)">if</span> isInterface <span style="color: rgba(0, 0, 255, 1)">then</span> [] <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> getModifier x.Flags;
                  Name = x.Name
                  Type =
                        </span><span style="color: rgba(0, 0, 255, 1)">match</span> x.Type <span style="color: rgba(0, 0, 255, 1)">with</span>
                        | Some typeInfo -&gt;<span style="color: rgba(0, 0, 0, 1)"> getType typeInfo
                        </span>| _ -&gt; { Type = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">object</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">; Name = None; InnerTypes = [] }
                  WithGet = </span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
                  WithSet = </span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
                  IsOptional =
                        </span><span style="color: rgba(0, 0, 255, 1)">match</span> x.Flags.IsOptional <span style="color: rgba(0, 0, 255, 1)">with</span>
                        | Some optional -&gt;<span style="color: rgba(0, 0, 0, 1)"> optional
                        </span>| _ -&gt; <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">
                        ;
                  InitialValue =
                        </span><span style="color: rgba(0, 0, 255, 1)">match</span> x.DefaultValue <span style="color: rgba(0, 0, 255, 1)">with</span>
                        | Some value -&gt;<span style="color: rgba(0, 0, 0, 1)"> Some value
                        </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)"> None
                }
      );
    Enums = [];
    TypeParameter = genericType </span>|&gt; List.map(<span style="color: rgba(0, 0, 255, 1)">fun</span> x -&gt;<span style="color: rgba(0, 0, 0, 1)"> x.Type);
}</span></pre>
</div>
<p>&nbsp;</p>
</div>
<p>注意处理 event 的时候,委托的类型需要特殊处理一下。</p>
<h2>Type alias 生诚器</h2>
<p>还记得我们最上面说的一种特殊的 union types 吗?这里就是处理纯 string 的 type alias 的。</p>
<div class="highlight">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">let</span> parseUnionTypeAlias (section: <span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">) (node: Reflection) (nodes: Type list): Entity list =
    </span><span style="color: rgba(0, 0, 255, 1)">let</span> notStringLiteral = nodes |&gt; List.tryFind(<span style="color: rgba(0, 0, 255, 1)">fun</span> x -&gt; x.Type &lt;&gt; <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">stringLiteral</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
    </span><span style="color: rgba(0, 0, 255, 1)">let</span><span style="color: rgba(0, 0, 0, 1)"> enums =
      </span><span style="color: rgba(0, 0, 255, 1)">match</span> notStringLiteral <span style="color: rgba(0, 0, 255, 1)">with</span>
      | Some _ -&gt;<span style="color: rgba(0, 0, 0, 1)">
            printWarning (</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Type alias </span><span style="color: rgba(128, 0, 0, 1)">"</span> + node.Name + <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)"> is not supported.</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
            []
      </span>| None -&gt;<span style="color: rgba(0, 0, 0, 1)">
            nodes
            </span>|<span style="color: rgba(0, 0, 0, 1)">&gt; List.collect
                (</span><span style="color: rgba(0, 0, 255, 1)">fun</span> x -&gt;
                  <span style="color: rgba(0, 0, 255, 1)">match</span> x.Value <span style="color: rgba(0, 0, 255, 1)">with</span>
                  | Some value -&gt;<span style="color: rgba(0, 0, 0, 1)">
                        [{
                            Name = toPascalCase value
                            Comment = </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">///&lt;summary&gt;\n</span><span style="color: rgba(128, 0, 0, 1)">"</span> + toCommentText value + <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">\n///&lt;/summary&gt;</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
                            Value = None
                        }]
                  </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)"> []
                )
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> enums = [] <span style="color: rgba(0, 0, 255, 1)">then</span><span style="color: rgba(0, 0, 0, 1)"> []
    </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">
      [
            {
                Namespace = section
                Name = node.Name
                Comment =
                  </span><span style="color: rgba(0, 0, 255, 1)">match</span> node.Comment <span style="color: rgba(0, 0, 255, 1)">with</span>
                  | Some comment -&gt;<span style="color: rgba(0, 0, 0, 1)"> getXmlDocComment comment
                  </span>| _ -&gt; <span style="color: rgba(128, 0, 0, 1)">""</span><span style="color: rgba(0, 0, 0, 1)">
                Methods = []
                Events = []
                Properties = []
                Enums = enums
                InheritedFrom = []
                Type = EntityType.StringEnum
                TypeParameter = []
                Modifier = getModifier node.Flags
            }
      ]

</span><span style="color: rgba(0, 0, 255, 1)">let</span> parseTypeAlias (section: <span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">) (node: Reflection): Entity list =
    </span><span style="color: rgba(0, 0, 255, 1)">let</span><span style="color: rgba(0, 0, 0, 1)"> typeInfo = node.Type
    </span><span style="color: rgba(0, 0, 255, 1)">match</span> typeInfo <span style="color: rgba(0, 0, 255, 1)">with</span>
    | Some aliasType -&gt;
      <span style="color: rgba(0, 0, 255, 1)">match</span> aliasType.Type <span style="color: rgba(0, 0, 255, 1)">with</span>
      | <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">union</span><span style="color: rgba(128, 0, 0, 1)">"</span> -&gt;
            <span style="color: rgba(0, 0, 255, 1)">match</span> aliasType.Types <span style="color: rgba(0, 0, 255, 1)">with</span>
            | Some types -&gt;<span style="color: rgba(0, 0, 0, 1)"> parseUnionTypeAlias section node types
            </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)">
                printWarning (</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Type alias </span><span style="color: rgba(128, 0, 0, 1)">"</span> + node.Name + <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)"> is not supported.</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
                []
      </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)">
            printWarning (</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Type alias </span><span style="color: rgba(128, 0, 0, 1)">"</span> + node.Name + <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)"> is not supported.</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
            []
    </span>| _ -&gt; []</pre>
</div>
<p>&nbsp;</p>
</div>
<h2>组合 Prasers</h2>
<p>我们最后将以上 parsers 组合起来就 ojbk 了:</p>
<div class="highlight">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">let</span> <span style="color: rgba(0, 0, 255, 1)">rec</span> parseNode (section: <span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">) (node: Reflection): Entity list =
    </span><span style="color: rgba(0, 0, 255, 1)">match</span> node.Kind <span style="color: rgba(0, 0, 255, 1)">with</span>
    | ReflectionKind.Global -&gt;
      <span style="color: rgba(0, 0, 255, 1)">match</span> node.Children <span style="color: rgba(0, 0, 255, 1)">with</span>
      | Some children -&gt;<span style="color: rgba(0, 0, 0, 1)"> parseNodes section children
      </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)"> []
    </span>| ReflectionKind.Module -&gt;
      <span style="color: rgba(0, 0, 255, 1)">match</span> node.Children <span style="color: rgba(0, 0, 255, 1)">with</span>
      | Some children -&gt;<span style="color: rgba(0, 0, 0, 1)">
            parseNodes (</span><span style="color: rgba(0, 0, 255, 1)">if</span> section = <span style="color: rgba(128, 0, 0, 1)">""</span> <span style="color: rgba(0, 0, 255, 1)">then</span> node.Name <span style="color: rgba(0, 0, 255, 1)">else</span> section + <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">.</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)"> + node.Name) children
      </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)"> []
    </span>| ReflectionKind.ExternalModule -&gt;
      <span style="color: rgba(0, 0, 255, 1)">match</span> node.Children <span style="color: rgba(0, 0, 255, 1)">with</span>
      | Some children -&gt;<span style="color: rgba(0, 0, 0, 1)"> parseNodes section children
      </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)"> []
    </span>| ReflectionKind.Enum -&gt;<span style="color: rgba(0, 0, 0, 1)">
    </span>| ReflectionKind.Interface -&gt;
    </span>| ReflectionKind.Class -&gt;
    </span>| ReflectionKind.TypeAlias -&gt;
      <span style="color: rgba(0, 0, 255, 1)">match</span> node.Type <span style="color: rgba(0, 0, 255, 1)">with</span>
      | Some _ -&gt;<span style="color: rgba(0, 0, 0, 1)"> parseTypeAlias section node
      </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)"> []
    </span>| _ -&gt;<span style="color: rgba(0, 0, 0, 1)"> []

</span><span style="color: rgba(0, 0, 255, 1)">and</span><span style="color: rgba(0, 0, 0, 1)"> parseNodes section (nodes: Reflection list): Entity list =
    </span><span style="color: rgba(0, 0, 255, 1)">match</span> nodes <span style="color: rgba(0, 0, 255, 1)">with</span>
    | ([ front ]) -&gt;<span style="color: rgba(0, 0, 0, 1)"> parseNode section front
    </span>| (front :: tails) -&gt;<span style="color: rgba(0, 0, 0, 1)">
      parseNode section front @ parseNodes section tails
    </span>| _ -&gt; []</pre>
</div>
<p>&nbsp;</p>
</div>
<p>至此,我们的 parse 工作全部搞定,完结撒花~~~</p>
<h2>代码生成</h2>
<p>有了 C# 的实体类型,代码生成还困难吗?</p>
<p>不过有一点要注意的是,我们需要将名称转换为 Pascal Case,还需要生成 string literals union types 的 JsonConverter。不过这些都是样板代码,非常简单。</p>
<p>这里就不放代码了,感兴趣的同学可以自行去我的 GitHub 仓库查看。</p>
<h2>测试效果</h2>
<p>原 typescipt 代码:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">declare namespace test {
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
   * The declaration of an enum
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
export enum MyEnum {
    A </span>= 0<span style="color: rgba(0, 0, 0, 1)">,
    B </span>= 1<span style="color: rgba(0, 0, 0, 1)">,
    C </span>= 2<span style="color: rgba(0, 0, 0, 1)">,
    D </span>=<span style="color: rgba(0, 0, 0, 1)"> C
}

</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
   * The declaration of an interface
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
export interface MyInterface1 {
    </span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
   * A method
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
    testMethod(arg: string, callback: () </span>=&gt; <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)">): string;
    </span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
   * An event
   * @event
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
    onTest(listener: (e: MyInterface1) </span>=&gt; <span style="color: rgba(0, 0, 255, 1)">void</span>): <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
   * An property
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
    readonly testProp: string;
}

</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
   * Another declaration of an interface
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
export interface MyInterface2</span>&lt;T&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    </span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
   * A method
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
    testMethod(arg: T, callback: () </span>=&gt; <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)">): T;
    </span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
   * An event
   * @event
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
    onTest(listener: (e: MyInterface2</span>&lt;T&gt;) =&gt; <span style="color: rgba(0, 0, 255, 1)">void</span>): <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
   * An property
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
    readonly testProp: T;
}

</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
   * The declaration of a class
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
export class MyClass1</span>&lt;T&gt;<span style="color: rgba(0, 0, 0, 1)"> implements MyInterface1 {
    </span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
   * A method
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
    testMethod(arg: string, callback: () </span>=&gt; <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)">): string;
    </span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
   * An event
   * @event
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
    onTest(listener: (e: MyInterface1) </span>=&gt; <span style="color: rgba(0, 0, 255, 1)">void</span>): <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
   * An property
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
    readonly testProp: string;
    static staticMethod(value: string, isOption</span>?: <span style="color: rgba(0, 0, 255, 1)">boolean</span><span style="color: rgba(0, 0, 0, 1)">): UnionStr;
}

</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
   * Another declaration of a class
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
export class MyClass2</span>&lt;T&gt; implements MyInterface2&lt;T&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    </span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
   * A method
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
    testMethod(arg: T, callback: () </span>=&gt; <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)">): T;
    </span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
   * An event
   * @event
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
    onTest(listener: (e: MyInterface2</span>&lt;T&gt;) =&gt; <span style="color: rgba(0, 0, 255, 1)">void</span>): <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
   * An property
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
    readonly testProp: T;
    static staticMethod(value: string, isOption</span>?: <span style="color: rgba(0, 0, 255, 1)">boolean</span><span style="color: rgba(0, 0, 0, 1)">): UnionStr;
}

</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
   * The declaration of a type alias
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
export type UnionStr </span>= "A" | "B" | "C" | "other"<span style="color: rgba(0, 0, 0, 1)">;
}</span></pre>
</div>
<p>&nbsp;</p>
<p>Typedoc 生成的 JSON 后,将其作为输入,生成 C# 代码:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">namespace</span><span style="color: rgba(0, 0, 0, 1)"> TypedocConverter.Test
{

    </span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)">&lt;summary&gt;</span>
    <span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> The declaration of an enum
    </span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)">&lt;/summary&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">enum</span><span style="color: rgba(0, 0, 0, 1)"> MyEnum
    {
      
      A </span>= <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">,
      
      B </span>= <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">,
      
      C </span>= <span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)">,
      
      D </span>= <span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)">
    }
}

</span><span style="color: rgba(0, 0, 255, 1)">namespace</span><span style="color: rgba(0, 0, 0, 1)"> TypedocConverter.Test
{

    </span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)">&lt;summary&gt;</span>
    <span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> The declaration of a class
    </span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)">&lt;/summary&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">class</span> MyClass1&lt;T&gt;<span style="color: rgba(0, 0, 0, 1)"> : MyInterface1
    {
      </span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)">&lt;summary&gt;</span>
      <span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> An property
      </span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)">&lt;/summary&gt;</span>
      
      </span><span style="color: rgba(0, 0, 255, 1)">string</span> TestProp { <span style="color: rgba(0, 0, 255, 1)">get</span> =&gt; <span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> System.NotImplementedException(); <span style="color: rgba(0, 0, 255, 1)">set</span> =&gt; <span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> System.NotImplementedException(); }
      </span><span style="color: rgba(0, 0, 255, 1)">event</span> System.Action&lt;MyInterface1&gt;<span style="color: rgba(0, 0, 0, 1)"> OnTest;
      </span><span style="color: rgba(0, 0, 255, 1)">string</span> TestMethod(<span style="color: rgba(0, 0, 255, 1)">string</span> arg, System.Action callback) =&gt; <span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> System.NotImplementedException();
      </span><span style="color: rgba(0, 0, 255, 1)">static</span> UnionStr StaticMethod(<span style="color: rgba(0, 0, 255, 1)">string</span> value, <span style="color: rgba(0, 0, 255, 1)">bool</span> isOption) =&gt; <span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> System.NotImplementedException();
    }
}

</span><span style="color: rgba(0, 0, 255, 1)">namespace</span><span style="color: rgba(0, 0, 0, 1)"> TypedocConverter.Test
{

    </span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)">&lt;summary&gt;</span>
    <span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> Another declaration of a class
    </span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)">&lt;/summary&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">class</span> MyClass2&lt;T&gt; : MyInterface2&lt;T&gt;<span style="color: rgba(0, 0, 0, 1)">
    {
      </span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)">&lt;summary&gt;</span>
      <span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> An property
      </span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)">&lt;/summary&gt;</span>
      
      T TestProp { </span><span style="color: rgba(0, 0, 255, 1)">get</span> =&gt; <span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> System.NotImplementedException(); <span style="color: rgba(0, 0, 255, 1)">set</span> =&gt; <span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> System.NotImplementedException(); }
      </span><span style="color: rgba(0, 0, 255, 1)">event</span> System.Action&lt;MyInterface2&lt;T&gt;&gt;<span style="color: rgba(0, 0, 0, 1)"> OnTest;
      T TestMethod(T arg, System.Action callback) </span>=&gt; <span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> System.NotImplementedException();
      </span><span style="color: rgba(0, 0, 255, 1)">static</span> UnionStr StaticMethod(<span style="color: rgba(0, 0, 255, 1)">string</span> value, <span style="color: rgba(0, 0, 255, 1)">bool</span> isOption) =&gt; <span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> System.NotImplementedException();
    }
}

</span><span style="color: rgba(0, 0, 255, 1)">namespace</span><span style="color: rgba(0, 0, 0, 1)"> TypedocConverter.Test
{

    </span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)">&lt;summary&gt;</span>
    <span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> The declaration of an interface
    </span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)">&lt;/summary&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">interface</span><span style="color: rgba(0, 0, 0, 1)"> MyInterface1
    {
      </span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)">&lt;summary&gt;</span>
      <span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> An property
      </span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)">&lt;/summary&gt;</span>
      
      </span><span style="color: rgba(0, 0, 255, 1)">string</span> TestProp { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
      </span><span style="color: rgba(0, 0, 255, 1)">event</span> System.Action&lt;MyInterface1&gt;<span style="color: rgba(0, 0, 0, 1)"> OnTest;
      </span><span style="color: rgba(0, 0, 255, 1)">string</span> TestMethod(<span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> arg, System.Action callback);
    }
}

</span><span style="color: rgba(0, 0, 255, 1)">namespace</span><span style="color: rgba(0, 0, 0, 1)"> TypedocConverter.Test
{

    </span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)">&lt;summary&gt;</span>
    <span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> Another declaration of an interface
    </span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)">&lt;/summary&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">interface</span> MyInterface2&lt;T&gt;<span style="color: rgba(0, 0, 0, 1)">
    {
      </span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)">&lt;summary&gt;</span>
      <span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> An property
      </span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)">&lt;/summary&gt;</span>
      
      T TestProp { </span><span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
      </span><span style="color: rgba(0, 0, 255, 1)">event</span> System.Action&lt;MyInterface2&lt;T&gt;&gt;<span style="color: rgba(0, 0, 0, 1)"> OnTest;
      T TestMethod(T arg, System.Action callback);
    }
}

</span><span style="color: rgba(0, 0, 255, 1)">namespace</span><span style="color: rgba(0, 0, 0, 1)"> TypedocConverter.Test
{

    </span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)">&lt;summary&gt;</span>
    <span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> The declaration of a type alias
    </span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)">&lt;/summary&gt;</span>
   
    </span><span style="color: rgba(0, 0, 255, 1)">enum</span><span style="color: rgba(0, 0, 0, 1)"> UnionStr
    {
      </span><span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(128, 128, 128, 1)">&lt;summary&gt;</span>
      <span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> A
      </span><span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(128, 128, 128, 1)">&lt;/summary&gt;</span>
      
      A,
      </span><span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(128, 128, 128, 1)">&lt;summary&gt;</span>
      <span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> B
      </span><span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(128, 128, 128, 1)">&lt;/summary&gt;</span>
      
      B,
      </span><span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(128, 128, 128, 1)">&lt;summary&gt;</span>
      <span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> C
      </span><span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(128, 128, 128, 1)">&lt;/summary&gt;</span>
      
      C,
      </span><span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(128, 128, 128, 1)">&lt;summary&gt;</span>
      <span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> other
      </span><span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(128, 128, 128, 1)">&lt;/summary&gt;</span>
      
      Other
    }
    </span><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> UnionStrConverter : Newtonsoft.Json.JsonConverter
    {
      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">override</span> <span style="color: rgba(0, 0, 255, 1)">bool</span> CanConvert(System.Type t) =&gt; t == <span style="color: rgba(0, 0, 255, 1)">typeof</span>(UnionStr) || t == <span style="color: rgba(0, 0, 255, 1)">typeof</span>(UnionStr?<span style="color: rgba(0, 0, 0, 1)">);
      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">override</span> <span style="color: rgba(0, 0, 255, 1)">object</span> ReadJson(Newtonsoft.Json.JsonReader reader, System.Type t, <span style="color: rgba(0, 0, 255, 1)">object</span>?<span style="color: rgba(0, 0, 0, 1)"> existingValue, Newtonsoft.Json.JsonSerializer serializer)
            </span>=&gt; reader.TokenType <span style="color: rgba(0, 0, 255, 1)">switch</span><span style="color: rgba(0, 0, 0, 1)">
            {
                Newtonsoft.Json.JsonToken.String </span>=&gt;<span style="color: rgba(0, 0, 0, 1)">
                  serializer.Deserialize</span>&lt;<span style="color: rgba(0, 0, 255, 1)">string</span>&gt;(reader) <span style="color: rgba(0, 0, 255, 1)">switch</span><span style="color: rgba(0, 0, 0, 1)">
                  {
                        </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">A</span><span style="color: rgba(128, 0, 0, 1)">"</span> =&gt;<span style="color: rgba(0, 0, 0, 1)"> UnionStr.A,
                        </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">B</span><span style="color: rgba(128, 0, 0, 1)">"</span> =&gt;<span style="color: rgba(0, 0, 0, 1)"> UnionStr.B,
                        </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">C</span><span style="color: rgba(128, 0, 0, 1)">"</span> =&gt;<span style="color: rgba(0, 0, 0, 1)"> UnionStr.C,
                        </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Other</span><span style="color: rgba(128, 0, 0, 1)">"</span> =&gt;<span style="color: rgba(0, 0, 0, 1)"> UnionStr.Other,
                        _ </span>=&gt; <span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> System.Exception(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Cannot unmarshal type UnionStr</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
                  },
                _ </span>=&gt; <span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> System.Exception(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Cannot unmarshal type UnionStr</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
            };
      </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">override</span> <span style="color: rgba(0, 0, 255, 1)">void</span> WriteJson(Newtonsoft.Json.JsonWriter writer, <span style="color: rgba(0, 0, 255, 1)">object</span>?<span style="color: rgba(0, 0, 0, 1)"> untypedValue, Newtonsoft.Json.JsonSerializer serializer)
      {
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (untypedValue <span style="color: rgba(0, 0, 255, 1)">is</span> <span style="color: rgba(0, 0, 255, 1)">null</span>) { serializer.Serialize(writer, <span style="color: rgba(0, 0, 255, 1)">null</span>); <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">; }
            </span><span style="color: rgba(0, 0, 255, 1)">var</span> value =<span style="color: rgba(0, 0, 0, 1)"> (UnionStr)untypedValue;
            </span><span style="color: rgba(0, 0, 255, 1)">switch</span><span style="color: rgba(0, 0, 0, 1)"> (value)
            {
                </span><span style="color: rgba(0, 0, 255, 1)">case</span> UnionStr.A: serializer.Serialize(writer, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">A</span><span style="color: rgba(128, 0, 0, 1)">"</span>); <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">;
                </span><span style="color: rgba(0, 0, 255, 1)">case</span> UnionStr.B: serializer.Serialize(writer, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">B</span><span style="color: rgba(128, 0, 0, 1)">"</span>); <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">;
                </span><span style="color: rgba(0, 0, 255, 1)">case</span> UnionStr.C: serializer.Serialize(writer, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">C</span><span style="color: rgba(128, 0, 0, 1)">"</span>); <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">;
                </span><span style="color: rgba(0, 0, 255, 1)">case</span> UnionStr.Other: serializer.Serialize(writer, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Other</span><span style="color: rgba(128, 0, 0, 1)">"</span>); <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">;
                </span><span style="color: rgba(0, 0, 255, 1)">default</span>: <span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">;
            }
            </span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> System.Exception(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Cannot marshal type UnionStr</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
      }
    }
}</span></pre>
</div>
<p>&nbsp;</p>
<h2>后记</h2>
<p>有了这个工具后,妈妈再也不用担心我封装 TypeScript 的库了。有了 TypedocConverter,任何 TypeScript 的库都能轻而易举地转换成 C# 的类型绑定,然后进行封装,非常方便。</p>
<p>感谢大家看到这里,最后,欢迎大家使用&nbsp;TypedocConverter。当然,如果能 star 一波甚至贡献代码,我会非常感谢的!</p><br><br>
来源:https://www.cnblogs.com/hez2010/p/12246841.html
頁: [1]
查看完整版本: 用 F# 手写 TypeScript 转 C# 类型绑定生成器