From 2aeb539f72ce046fb29183871d18004fe060cbf0 Mon Sep 17 00:00:00 2001 From: Attila Mihaly Date: Wed, 27 Oct 2021 08:32:32 -0400 Subject: [PATCH 01/11] Added some docs about the project setup in the contribution guide. --- docs/contribution-guide.md | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/docs/contribution-guide.md b/docs/contribution-guide.md index 18b7d98c0..762bf8205 100644 --- a/docs/contribution-guide.md +++ b/docs/contribution-guide.md @@ -2,7 +2,35 @@ The purpose of this document is to make it easier for new contributors to get up-to-speed on the project. -## Prerequisites +## Project Setup + +### JavaScript Tooling + +The project uses Node.js and NPM as the runtime and package manager. You can download them from +[this link](https://nodejs.org/en/download/). + +We use [Gulp](https://gulpjs.com/) as our build tool. You can use NPM to install it: + +``` +npm install -g gulp +``` + +### Elm Tooling + +The easiest way to install the Elm tooling is using NPM. Here's a list of Elm tools we use: + +- `npm install -g elm` +- `npm install -g elm-test` +- `npm install -g elm-format` +- `npm install -g elm-live` + +### IDE + +Most contributors are using [IntelliJ](https://www.jetbrains.com/idea/download) with the +[Elm plugin](https://plugins.jetbrains.com/plugin/10268-elm). [VS Code](https://code.visualstudio.com/download) with the +[Elm plugin](https://marketplace.visualstudio.com/items?itemName=Elmtooling.elm-ls-vscode) is another popular choice. + +## Learning Material In order to contribute to this project you need to be familiar with Elm and understand some language processing / compiler concepts that are core to Morphir. We collected a series of learning materials for you to make it easier to fill any knowledge gaps. Feel free to skip any of these if you feel like you are an expert. @@ -24,6 +52,3 @@ If you have any doubts though it's better to glance through them. We included th - [Deeper dive](https://www.youtube.com/watch?v=VKM1eLoN-gI) (12 mins) -## Project Setup - -TODO From 5c5a4d593b98f517fefd960d36ea6fd37b9c657a Mon Sep 17 00:00:00 2001 From: Sam Thursfield Date: Wed, 20 Oct 2021 18:56:55 +0200 Subject: [PATCH 02/11] Add documentation for TypeScript backend --- docs/typescript.md | 131 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 docs/typescript.md diff --git a/docs/typescript.md b/docs/typescript.md new file mode 100644 index 000000000..795c61f7b --- /dev/null +++ b/docs/typescript.md @@ -0,0 +1,131 @@ +# TypeScript API + +The purpose of this document is describing the TypeScript API generated for Morphir +models by running `morphir-elm gen --target=TypeScript`. + +## Generating TypeScript + +Given a model represented in `morphir-ir.json`, you can generate a TypeScript +representation by running: + + morphir-elm gen --input morphir-ir.json --output ./generated --target=TypeScript + +Note that at present only the types are converted. Data values and functions +are not. + +You can generate a TypeScript representation of the Morphir IR itself by +running this in the morphir-elm repo: + + morphir-elm make ./morphir-make --types-only + morphir-elm gen --input=morphir-ir.json --output=./generated --target=TypeScript + +## Using the generated types + +The TypeScript backend outputs a top-level module per package, which your own +code should import. The namespaces correspond with the package and module names +in the IR. Only namespaces and symbols marked as public will be exported in the +TypeScript API. + +For example, you can use the `IR` types from the `Morphir` package like this: + + import { Morphir } from './generated/Morphir' + + const myName: Morphir.IR.Name.Name = ["this", "is", "a", "great", "name"] + +Internally the types map to TypeScript type definitions. This is how a Morphir +IR `Name` would be represented in `generated/morphir/ir/Name.ts`: + + export type Name = Array + +You benefit from all the usual TypeScript type checking. For example, a Path +must be a list of Name instances, so this example will raise an error: + + import { Morphir } from './generated/Morphir' + + const myName: Morphir.IR.Path.Path = "This is the wrong type." + +You should see this message when compiling: + + test.ts:3:7 - error TS2322: Type 'string' is not assignable to type 'Path'. + +Most Morphir types correspond directly to JavaScript types. The +[JSON mapping](https://github.com/finos/morphir-elm/blob/main/docs/json-mapping.md) +gives a useful reference. There are some special cases, which are documented below. + +### Type mapping details + +#### Dict + +A `Morphir.SDK.Dict.Dict K V` maps to a TypeScript `Map`. + +#### Custom types + +We follow the example +["Tagged Union Types in TypeScript"](https://mariusschulz.com/blog/tagged-union-types-in-typescript) +to implement custom types. + +Each type variant is a TypeScript `interface`, with a `kind` and maybe some +fields. The fields names are defined in the IR, and if you used `morphir-elm` +make to build the IR then the names will follow the pattern `arg1`, `arg2`, +`arg3` and so on. + +Constructor functions are provided for these. Here's an example using the +Morphir IR `Value` custom type, creating an instance of its `Reference` +variant: + + import { Morphir } from './generated/Morphir' + + const exampleFQName: Morphir.IR.FQName.FQName = [[], [[]], ["excellent", "name"]]; + + type AttrType = []; + let myReference = new Morphir.IR.Value.Reference([], exampleFQName); + +Calling the constructor function is equivalent to manually constructing an object +and setting the relevant properties: + + let myReference: Morphir.IR.Value.Reference = { + kind: "Reference", + arg1: [], + arg2: exampleFQName, + } + +Constructor functions are only provided for custom types. + +#### Type variables + +Morphir's custom types and type aliases can use type variables. These map to +TypeScript [generics](https://www.typescriptlang.org/docs/handbook/2/generics.html). + +Here's an example using Morphir IR's `AccessControlled` type, which is a type +alias that maps to a Record. + + import { Morphir } from './generated/Morphir' + + const myAccess = new Morphir.IR.AccessControlled.Public(); + + let myAccessControlled: Morphir.IR.AccessControlled.AccessControlled = { + access: myAccess, + value: "I'm a string", + } + +## JSON serialization and deserialization + +The generated TypeScript API includes `decode` and `encode` functions for each +type, used to serialize and deserialize instances of the types according to the +[standard Morphir JSON mapping](https://github.com/finos/morphir-elm/blob/master/docs/json-mapping.md). + +With the generated Morphir.IR API, this allows you to read entire `morphir-ir.json` files +into your TypeScript program and create instances of the appropriate types. Here's how you +might do that: + + import { Morphir } from './generated/Morphir' + + function loadMorphirIR(text) { + let data = JSON.parse(text); + + if (data['format-version'] != 2) { + throw "Unsupported morphir-ir.json format"; + } + + return Morphir.IR.Distribution.decodeDistribution(data['distribution']); + } From bc0eebd010341cda8bb83c8ae4f5316218718768 Mon Sep 17 00:00:00 2001 From: Attila Mihaly Date: Wed, 27 Oct 2021 16:09:52 +0200 Subject: [PATCH 03/11] Skeleton implementation of multiary decision tree data structure. --- .../Components/MultiaryDecisionTree.elm | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/Morphir/Visual/Components/MultiaryDecisionTree.elm diff --git a/src/Morphir/Visual/Components/MultiaryDecisionTree.elm b/src/Morphir/Visual/Components/MultiaryDecisionTree.elm new file mode 100644 index 000000000..4dc73348a --- /dev/null +++ b/src/Morphir/Visual/Components/MultiaryDecisionTree.elm @@ -0,0 +1,32 @@ +module Morphir.Visual.Components.MultiaryDecisionTree exposing (..) + +import Morphir.IR.Type as Type +import Morphir.IR.Value as Value exposing (Pattern, RawValue, Value) +import Morphir.Visual.VisualTypedValue exposing (VisualTypedValue) + + +type Node + = Branch BranchNode + | Leaf VisualTypedValue + + +type alias BranchNode = + { subject : VisualTypedValue + , subjectEvaluationResult : Maybe RawValue + , branches : List ( Pattern (), Node ) + } + + +{-| Sample data structure. Should be moved into a test module. +-} +exampleTree : Node +exampleTree = + Branch + { subject = Value.Variable ( 0, Type.Unit () ) [ "foo" ] + , subjectEvaluationResult = Nothing + , branches = + [ ( Value.ConstructorPattern () ( [], [], [ "yes" ] ) [], Leaf (Value.Variable ( 0, Type.Unit () ) [ "foo" ]) ) + , ( Value.WildcardPattern (), Leaf (Value.Variable ( 0, Type.Unit () ) [ "foo" ]) ) + , ( Value.WildcardPattern (), Leaf (Value.Variable ( 0, Type.Unit () ) [ "foo" ]) ) + ] + } From 392d2ff636a2e6888b6ef98aec7c5fe69d344506 Mon Sep 17 00:00:00 2001 From: Douglas Winship Date: Mon, 25 Oct 2021 18:08:31 +0100 Subject: [PATCH 04/11] TypeScript AST: type annotations on functions Makes it possible for any function declaration to have generic type variables, and a return-type annotation. --- src/Morphir/TypeScript/AST.elm | 2 ++ src/Morphir/TypeScript/Backend/Types.elm | 14 ++++++++++++ src/Morphir/TypeScript/PrettyPrinter.elm | 29 ++++++++++++++++++++++-- 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/src/Morphir/TypeScript/AST.elm b/src/Morphir/TypeScript/AST.elm index aa0c20f88..f7b480fa2 100644 --- a/src/Morphir/TypeScript/AST.elm +++ b/src/Morphir/TypeScript/AST.elm @@ -108,6 +108,8 @@ parameter modifiers name typeAnnotation = type Statement = FunctionDeclaration { name : String + , typeVariables : List TypeExp + , returnType : Maybe TypeExp , scope : FunctionScope , parameters : List Parameter , body : List Statement diff --git a/src/Morphir/TypeScript/Backend/Types.elm b/src/Morphir/TypeScript/Backend/Types.elm index 4c57bba8b..2f0833962 100644 --- a/src/Morphir/TypeScript/Backend/Types.elm +++ b/src/Morphir/TypeScript/Backend/Types.elm @@ -470,6 +470,8 @@ generateDecoderFunction variables typeName access typeExp = in TS.FunctionDeclaration { name = prependDecodeToName typeName + , typeVariables = [] + , returnType = Nothing , scope = TS.ModuleFunction , parameters = variableParams ++ [ inputParam ] , privacy = access |> mapPrivacy @@ -525,6 +527,8 @@ generateConstructorDecoderFunction constructor = in TS.FunctionDeclaration { name = prependDecodeToName constructor.name + , typeVariables = [] + , returnType = Nothing , scope = TS.ModuleFunction , privacy = constructor.privacy , parameters = decoderParams ++ [ inputParam ] @@ -576,6 +580,8 @@ generateUnionDecoderFunction typeName privacy typeVariables constructors = in TS.FunctionDeclaration { name = prependDecodeToName typeName + , typeVariables = [] + , returnType = Nothing , scope = TS.ModuleFunction , privacy = privacy , parameters = decoderParams ++ [ inputParam ] @@ -716,6 +722,8 @@ generateEncoderFunction variables typeName access typeExp = in TS.FunctionDeclaration { name = prependEncodeToName typeName + , typeVariables = [] + , returnType = Nothing , scope = TS.ModuleFunction , parameters = variableParams ++ [ valueParam ] , privacy = access |> mapPrivacy @@ -767,6 +775,8 @@ generateConstructorEncoderFunction constructor = in TS.FunctionDeclaration { name = prependEncodeToName constructor.name + , typeVariables = [] + , returnType = Nothing , scope = TS.ModuleFunction , privacy = constructor.privacy , parameters = encoderParams ++ [ valueParam ] @@ -818,6 +828,8 @@ generateUnionEncoderFunction typeName privacy typeVariables constructors = in TS.FunctionDeclaration { name = prependEncodeToName typeName + , typeVariables = [] + , returnType = Nothing , scope = TS.ModuleFunction , privacy = privacy , parameters = encoderParams ++ [ valueParam ] @@ -838,6 +850,8 @@ generateConstructorConstructorFunction { name, privacy, args, typeVariables, typ in TS.FunctionDeclaration { name = "constructor" + , typeVariables = [] + , returnType = Nothing , scope = TS.ClassMemberFunction , privacy = privacy , parameters = argParams diff --git a/src/Morphir/TypeScript/PrettyPrinter.elm b/src/Morphir/TypeScript/PrettyPrinter.elm index 9308ac077..563732792 100644 --- a/src/Morphir/TypeScript/PrettyPrinter.elm +++ b/src/Morphir/TypeScript/PrettyPrinter.elm @@ -224,7 +224,7 @@ mapMaybeStatement maybeStatement = mapStatement : Statement -> String mapStatement statement = case statement of - FunctionDeclaration { name, scope, parameters, body, privacy } -> + FunctionDeclaration { name, typeVariables, returnType, scope, parameters, body, privacy } -> let prefaceKeywords : String prefaceKeywords = @@ -242,13 +242,38 @@ mapStatement statement = _ -> "" + + typeVariablesString : String + typeVariablesString = + case typeVariables of + [] -> + "" + + _ -> + concat + [ "<" + , String.join ", " (typeVariables |> List.map mapTypeExp) + , ">" + ] + + returnTypeExpression : String + returnTypeExpression = + case returnType of + Nothing -> + "" + + Just typeExp -> + concat [ ": ", mapTypeExp typeExp ] in concat [ prefaceKeywords , name + , typeVariablesString , "(" , String.join ", " (parameters |> List.map mapParameter) - , ") {" + , ")" + , returnTypeExpression + , " {" , newLine , body |> List.map mapStatement |> indentLines defaultIndent , newLine From 7b64028340cdbe8517ada84f9ca31f933f787516 Mon Sep 17 00:00:00 2001 From: Douglas Winship Date: Tue, 26 Oct 2021 12:55:40 +0100 Subject: [PATCH 05/11] TypeScript AST: add more language features IntLiteralExpression: An expression consisting of a literal integer IndexedExpression: An expression for square bracket indexing (eg foo[bar]) SwitchStatement: A switch statement, with a list of cases FunctionTypeExpression: a Type Expression for a function type (a list of parameter types, and a return type) --- src/Morphir/TypeScript/AST.elm | 7 +++ src/Morphir/TypeScript/PrettyPrinter.elm | 51 ++++++++++++------- .../TypeScript/PrettyPrinter/Expressions.elm | 32 +++++++++++- 3 files changed, 69 insertions(+), 21 deletions(-) diff --git a/src/Morphir/TypeScript/AST.elm b/src/Morphir/TypeScript/AST.elm index f7b480fa2..cd8e9a654 100644 --- a/src/Morphir/TypeScript/AST.elm +++ b/src/Morphir/TypeScript/AST.elm @@ -66,6 +66,11 @@ type Expression = ArrayLiteralExpression (List Expression) | Call CallExpression | Identifier String + | IntLiteralExpression Int + | IndexedExpression + { object : Expression + , index : Expression + } | MemberExpression { object : Expression , member : Expression @@ -119,6 +124,7 @@ type Statement | AssignmentStatement Expression (Maybe TypeExp) Expression | ExpressionStatement Expression | ReturnStatement Expression + | SwitchStatement Expression (List ( Expression, List Statement )) {-| Represents a type definition. @@ -166,6 +172,7 @@ Only a small subset of the type-system is currently implemented. type TypeExp = Any | Boolean + | FunctionTypeExp (List Parameter) TypeExp | List TypeExp {- Represents a Morphir 'List' type, as a Typescript 'Array' type -} | LiteralString String | Map TypeExp TypeExp diff --git a/src/Morphir/TypeScript/PrettyPrinter.elm b/src/Morphir/TypeScript/PrettyPrinter.elm index 563732792..9110f95f5 100644 --- a/src/Morphir/TypeScript/PrettyPrinter.elm +++ b/src/Morphir/TypeScript/PrettyPrinter.elm @@ -7,6 +7,7 @@ representation. -} +import Elm.Syntax.Expression exposing (Expression(..)) import Morphir.File.SourceCode exposing (Doc, concat, indentLines, newLine) import Morphir.IR.Path exposing (Path) import Morphir.TypeScript.AST exposing (CompilationUnit, Expression(..), FunctionScope(..), ImportDeclaration, NamespacePath, Parameter, Privacy(..), Statement(..), TypeDef(..), TypeExp(..)) @@ -168,6 +169,17 @@ mapExpression expression = Identifier name -> name + IntLiteralExpression num -> + String.fromInt num + + IndexedExpression { object, index } -> + concat + [ mapExpression object + , "[" + , mapExpression index + , "]" + ] + MemberExpression { object, member } -> concat [ mapExpression object @@ -305,22 +317,23 @@ mapStatement statement = ExpressionStatement expression -> concat [ mapExpression expression, ";" ] - -mapParameter : Parameter -> String -mapParameter { modifiers, name, typeAnnotation } = - concat - [ modifiers |> String.join " " - , " " - , name - , mapMaybeAnnotation typeAnnotation - ] - - -mapMaybeAnnotation : Maybe TypeExp -> String -mapMaybeAnnotation maybeTypeExp = - case maybeTypeExp of - Nothing -> - "" - - Just typeExp -> - ": " ++ mapTypeExp typeExp + SwitchStatement condition cases -> + let + mapCase : ( Expression, List Statement ) -> String + mapCase ( caseExpr, statementList ) = + concat + [ "case " + , mapExpression caseExpr + , ":" + , statementList |> List.map mapStatement |> indentLines defaultIndent + ] + in + concat + [ "switch (" + , mapExpression condition + , ") {" + , newLine + , cases |> List.map mapCase |> indentLines defaultIndent + , newLine + , "}" + ] diff --git a/src/Morphir/TypeScript/PrettyPrinter/Expressions.elm b/src/Morphir/TypeScript/PrettyPrinter/Expressions.elm index b7cad2858..f0a30a992 100644 --- a/src/Morphir/TypeScript/PrettyPrinter/Expressions.elm +++ b/src/Morphir/TypeScript/PrettyPrinter/Expressions.elm @@ -1,9 +1,9 @@ -module Morphir.TypeScript.PrettyPrinter.Expressions exposing (mapField, mapGenericVariables, mapObjectExp, mapTypeExp, namespaceNameFromPackageAndModule) +module Morphir.TypeScript.PrettyPrinter.Expressions exposing (..) import Morphir.File.SourceCode exposing (Doc, concat, indentLines, newLine) import Morphir.IR.Name as Name import Morphir.IR.Path exposing (Path) -import Morphir.TypeScript.AST exposing (ObjectExp, Privacy(..), TypeDef(..), TypeExp(..), namespaceNameFromPackageAndModule) +import Morphir.TypeScript.AST exposing (ObjectExp, Parameter, Privacy(..), TypeDef(..), TypeExp(..), namespaceNameFromPackageAndModule) defaultIndent = @@ -24,6 +24,26 @@ mapGenericVariables variables = ] +mapParameter : Parameter -> String +mapParameter { modifiers, name, typeAnnotation } = + concat + [ modifiers |> String.join " " + , " " + , name + , mapMaybeAnnotation typeAnnotation + ] + + +mapMaybeAnnotation : Maybe TypeExp -> String +mapMaybeAnnotation maybeTypeExp = + case maybeTypeExp of + Nothing -> + "" + + Just typeExp -> + ": " ++ mapTypeExp typeExp + + {-| Map a field to text (from an object or interface) -} mapField : ( String, TypeExp ) -> Doc @@ -57,6 +77,14 @@ mapTypeExp typeExp = Boolean -> "boolean" + FunctionTypeExp params rtnTypeExp -> + concat + [ "(" + , params |> List.map mapParameter |> String.join ", " + , ") => " + , mapTypeExp rtnTypeExp + ] + List listType -> "Array<" ++ mapTypeExp listType ++ ">" From 43113651a3579000d48532d5cfcd316470911eda Mon Sep 17 00:00:00 2001 From: Douglas Winship Date: Tue, 26 Oct 2021 12:59:28 +0100 Subject: [PATCH 06/11] Typescript: prettify codecs.ts --- .../TypeScript/morphir/internal/Codecs.ts | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/redistributable/TypeScript/morphir/internal/Codecs.ts b/redistributable/TypeScript/morphir/internal/Codecs.ts index b7e6a031c..33fb5aa16 100644 --- a/redistributable/TypeScript/morphir/internal/Codecs.ts +++ b/redistributable/TypeScript/morphir/internal/Codecs.ts @@ -35,7 +35,9 @@ type CodecMap = Map; // * https://github.com/Microsoft/TypeScript/issues/3369 // * https://stackoverflow.com/a/53136686 // -export function buildCodecMap(entries: Array<[string, CodecFunction]>): CodecMap { +export function buildCodecMap( + entries: Array<[string, CodecFunction]> +): CodecMap { return new Map(entries); } @@ -141,14 +143,16 @@ export function decodeDict( const inputArray: Array = input; - return new Map(inputArray.map((item: any) => { - if (!(item instanceof Array)) { - throw new DecodeError(`Expected array, got ${typeof item}`); - } + return new Map( + inputArray.map((item: any) => { + if (!(item instanceof Array)) { + throw new DecodeError(`Expected array, got ${typeof item}`); + } - const itemArray: Array = item; - return [decodeKey(itemArray[0]), decodeValue(itemArray[1])]; - })); + const itemArray: Array = item; + return [decodeKey(itemArray[0]), decodeValue(itemArray[1])]; + }) + ); } export function decodeList(decodeElement: (any) => T, input: any): Array { From 4171c1b9120eb1b80528215d5bef46ae4335799d Mon Sep 17 00:00:00 2001 From: Douglas Winship Date: Tue, 26 Oct 2021 12:56:13 +0100 Subject: [PATCH 07/11] TypeScript: type annotations for variant decoders --- .../TypeScript/morphir/internal/Codecs.ts | 24 +++++ src/Morphir/TypeScript/Backend/Types.elm | 96 ++++++++++++------- 2 files changed, 85 insertions(+), 35 deletions(-) diff --git a/redistributable/TypeScript/morphir/internal/Codecs.ts b/redistributable/TypeScript/morphir/internal/Codecs.ts index 33fb5aa16..b5e15efad 100644 --- a/redistributable/TypeScript/morphir/internal/Codecs.ts +++ b/redistributable/TypeScript/morphir/internal/Codecs.ts @@ -296,3 +296,27 @@ export function encodeTuple( } return result; } + +export function validateCustomTypeVariantInput( + kindString: String, + numArgs: number, + input: any +): void { + if (typeof input == "string") input = [input]; + if (!(input instanceof Array)) { + throw new DecodeError(`Expected Array, got ${typeof input}`); + } + if (!(input.length == numArgs + 1)) { + throw new DecodeError( + `Expected Array of length ${numArgs + 1}, got ${input.length}` + ); + } + if (typeof input[0] != "string") { + throw new DecodeError( + `Expected first argument to be ${kindString}, got ${input[0]}` + ); + } + if (input[0] != kindString) { + throw new DecodeError(`Expected kind ${kindString}, got ${input[0]}`); + } +} diff --git a/src/Morphir/TypeScript/Backend/Types.elm b/src/Morphir/TypeScript/Backend/Types.elm index 2f0833962..6861125b2 100644 --- a/src/Morphir/TypeScript/Backend/Types.elm +++ b/src/Morphir/TypeScript/Backend/Types.elm @@ -28,6 +28,14 @@ type alias ConstructorDetail a = } +inputIndexArg : Int -> TS.Expression +inputIndexArg index = + TS.IndexedExpression + { object = TS.Identifier "input" + , index = TS.IntLiteralExpression index + } + + prependDecodeToName : Name -> String prependDecodeToName name = ("decode" :: name) |> Name.toCamelCase @@ -294,6 +302,13 @@ mapTypeExp tpe = TS.UnhandledType "Function" +genericDecoder : TS.TypeExp -> TS.TypeExp +genericDecoder typeExp = + TS.FunctionTypeExp + [ TS.Parameter [] "input" (Just TS.Any) ] + typeExp + + {-| Reference a symbol in the Morphir.Internal.Codecs module. -} codecsModule : String -> TS.Expression @@ -320,12 +335,8 @@ buildCodecMap array = } -decoderExpression : TypeVariablesList -> Type.Type a -> TS.CallExpression -decoderExpression customTypeVars typeExp = - let - inputArg = - TS.Identifier "input" - in +decoderExpression : TypeVariablesList -> Type.Type a -> TS.Expression -> TS.CallExpression +decoderExpression customTypeVars typeExp inputArg = case typeExp of Type.Reference _ ( [ [ "morphir" ], [ "s", "d", "k" ] ], [ [ "basics" ] ], [ "bool" ] ) [] -> { function = codecsModule "decodeBoolean", arguments = [ inputArg ] } @@ -441,7 +452,7 @@ specificDecoderForType : TypeVariablesList -> Type.Type ta -> TS.Expression specificDecoderForType customTypeVars typeExp = let expression = - decoderExpression customTypeVars typeExp + decoderExpression customTypeVars typeExp (TS.Identifier "input") removeInputArg arguments = arguments |> List.take (List.length arguments - 1) @@ -454,7 +465,7 @@ generateDecoderFunction variables typeName access typeExp = let call : TS.CallExpression call = - decoderExpression variables typeExp + decoderExpression variables typeExp (TS.Identifier "input") variableParams : List TS.Parameter variableParams = @@ -482,57 +493,72 @@ generateDecoderFunction variables typeName access typeExp = generateConstructorDecoderFunction : ConstructorDetail ta -> TS.Statement generateConstructorDecoderFunction constructor = let + variableTypeExpressions : List TS.TypeExp + variableTypeExpressions = + constructor.typeVariableNames |> List.map Name.toTitleCase |> List.map (\var -> TS.Variable var) + decoderParams : List TS.Parameter decoderParams = constructor.typeVariableNames |> List.map (\var -> - TS.parameter [] (prependDecodeToName var) Nothing + TS.parameter + [] + (prependDecodeToName var) + (Just (genericDecoder (TS.Variable (Name.toTitleCase var)))) ) inputParam : TS.Parameter inputParam = - TS.parameter [] "input" Nothing + TS.parameter [] "input" (Just TS.Any) kind = TS.StringLiteralExpression (constructor.name |> Name.toTitleCase) - argNames = - TS.ArrayLiteralExpression - (constructor.args - |> List.map (Tuple.first >> Name.toCamelCase >> TS.StringLiteralExpression) - ) - - argDecoders = - TS.ArrayLiteralExpression - (constructor.args - |> List.map Tuple.second - |> List.map (specificDecoderForType constructor.typeVariableNames) - ) - - input = - TS.Identifier "input" - - call : TS.Expression - call = + validateCall : TS.Expression + validateCall = TS.Call - { function = codecsModule "decodeCustomTypeVariant" + { function = codecsModule "validateCustomTypeVariantInput" , arguments = [ kind - , argNames - , argDecoders - , input + , constructor.args |> List.length |> TS.IntLiteralExpression + , TS.Identifier "input" ] } + + argDecoderCalls : List TS.Expression + argDecoderCalls = + constructor.args + |> List.map Tuple.second + |> List.indexedMap + (\index -> + \typExp -> + TS.Call + (decoderExpression + constructor.typeVariableNames + typExp + (inputIndexArg (index + 1)) + ) + ) + + newCall : TS.Expression + newCall = + TS.NewExpression + { constructor = constructor.name |> Name.toTitleCase + , arguments = argDecoderCalls + } in TS.FunctionDeclaration { name = prependDecodeToName constructor.name - , typeVariables = [] - , returnType = Nothing + , typeVariables = variableTypeExpressions + , returnType = Just (TS.TypeRef ( [], [], constructor.name ) variableTypeExpressions) , scope = TS.ModuleFunction , privacy = constructor.privacy , parameters = decoderParams ++ [ inputParam ] - , body = [ TS.ReturnStatement call ] + , body = + [ TS.ExpressionStatement validateCall + , TS.ReturnStatement newCall + ] } From 4852463aa35575051bf3338c6847a3a4d585ff5e Mon Sep 17 00:00:00 2001 From: Douglas Winship Date: Tue, 26 Oct 2021 14:16:19 +0100 Subject: [PATCH 08/11] TypeScript: type annotations for other decoders Puts type annotations on custom type decoders and on type alias decoders Updates how custom type decoders work. --- .../TypeScript/morphir/internal/Codecs.ts | 33 ++++++- src/Morphir/TypeScript/Backend/Types.elm | 88 ++++++++++++------- 2 files changed, 86 insertions(+), 35 deletions(-) diff --git a/redistributable/TypeScript/morphir/internal/Codecs.ts b/redistributable/TypeScript/morphir/internal/Codecs.ts index b5e15efad..5af21bc32 100644 --- a/redistributable/TypeScript/morphir/internal/Codecs.ts +++ b/redistributable/TypeScript/morphir/internal/Codecs.ts @@ -164,7 +164,10 @@ export function decodeList(decodeElement: (any) => T, input: any): Array { return inputArray.map(decodeElement); } -export function decodeRecord(fieldDecoders: CodecMap, input: any): object { +export function decodeRecord( + fieldDecoders: CodecMap, + input: any +): recordType { if (!(input instanceof Object)) { throw new DecodeError(`Expected Object, got ${typeof input}`); } @@ -192,14 +195,14 @@ export function decodeRecord(fieldDecoders: CodecMap, input: any): object { } result[name] = decoder(inputObject[name]); }); - + // @ts-ignore return result; } -export function decodeTuple( +export function decodeTuple( elementDecoders: CodecList, input: any -): Array { +): tupleType { if (!(input instanceof Array)) { throw new DecodeError(`Expected Array, got ${typeof input}`); } @@ -209,6 +212,7 @@ export function decodeTuple( for (var i = 0; i < inputArray.length; i++) { result.push(elementDecoders[i](inputArray[i])); } + // @ts-ignore return result; } @@ -320,3 +324,24 @@ export function validateCustomTypeVariantInput( throw new DecodeError(`Expected kind ${kindString}, got ${input[0]}`); } } + +export function parseKindFromCustomTypeInput(input: any): string { + if (typeof input == "string") input = [input]; + if (!(input instanceof Array)) { + throw new DecodeError(`Expected Array, got ${typeof input}`); + } + if (!(typeof input[0] == "string")) { + throw new DecodeError(`Expected String, got ${typeof input}`); + } + return input[0]; +} + +export function raiseDecodeErrorFromCustomType( + customTypeName: string, + kind: string +): void { + throw new DecodeError( + `Error while attempting to decode an instance of ${customTypeName}.` + + ` "${kind}" is not a valid 'kind' field for ${customTypeName}.` + ); +} diff --git a/src/Morphir/TypeScript/Backend/Types.elm b/src/Morphir/TypeScript/Backend/Types.elm index 6861125b2..e760aa06f 100644 --- a/src/Morphir/TypeScript/Backend/Types.elm +++ b/src/Morphir/TypeScript/Backend/Types.elm @@ -463,6 +463,10 @@ specificDecoderForType customTypeVars typeExp = generateDecoderFunction : TypeVariablesList -> Name -> Access -> Type.Type ta -> TS.Statement generateDecoderFunction variables typeName access typeExp = let + variableTypeExpressions : List TS.TypeExp + variableTypeExpressions = + variables |> List.map Name.toTitleCase |> List.map (\var -> TS.Variable var) + call : TS.CallExpression call = decoderExpression variables typeExp (TS.Identifier "input") @@ -472,17 +476,20 @@ generateDecoderFunction variables typeName access typeExp = variables |> List.map (\var -> - TS.parameter [] (prependDecodeToName var) Nothing + TS.parameter + [] + (prependDecodeToName var) + (Just (genericDecoder (TS.Variable (Name.toTitleCase var)))) ) inputParam : TS.Parameter inputParam = - TS.parameter [] "input" Nothing + TS.parameter [] "input" (Just TS.Any) in TS.FunctionDeclaration { name = prependDecodeToName typeName - , typeVariables = [] - , returnType = Nothing + , typeVariables = variableTypeExpressions + , returnType = Just (TS.TypeRef ( [], [], typeName ) variableTypeExpressions) , scope = TS.ModuleFunction , parameters = variableParams ++ [ inputParam ] , privacy = access |> mapPrivacy @@ -565,53 +572,72 @@ generateConstructorDecoderFunction constructor = generateUnionDecoderFunction : Name -> TS.Privacy -> List Name -> List (ConstructorDetail ta) -> TS.Statement generateUnionDecoderFunction typeName privacy typeVariables constructors = let + variableTypeExpressions : List TS.TypeExp + variableTypeExpressions = + typeVariables |> List.map Name.toTitleCase |> List.map (\var -> TS.Variable var) + decoderParams : List TS.Parameter decoderParams = typeVariables |> List.map (\var -> - TS.parameter [] (prependDecodeToName var) Nothing + TS.parameter + [] + (prependDecodeToName var) + (Just (genericDecoder (TS.Variable (Name.toTitleCase var)))) ) inputParam : TS.Parameter inputParam = - TS.parameter [] "input" Nothing - - getCodecMapEntry : ConstructorDetail ta -> TS.Expression - getCodecMapEntry constructor = - TS.ArrayLiteralExpression - [ TS.StringLiteralExpression (constructor.name |> Name.toTitleCase) - , bindArgumentsToFunction - (constructor.name |> prependDecodeToName |> TS.Identifier) - (constructor.typeVariableNames |> List.map (prependDecodeToName >> TS.Identifier)) - ] + TS.parameter [] "input" (Just TS.Any) - codecMap : TS.Expression - codecMap = - constructors |> List.map getCodecMapEntry |> TS.ArrayLiteralExpression |> buildCodecMap + kindCall : TS.Statement + kindCall = + TS.LetStatement + (TS.Identifier "kind") + Nothing + (TS.Call + { function = codecsModule "parseKindFromCustomTypeInput" + , arguments = [ inputArg ] + } + ) - call : TS.Expression - call = - TS.Call - { function = - TS.MemberExpression - { object = TS.Identifier "codecs" - , member = TS.Identifier "decodeCustomType" - } + errorCall : TS.Statement + errorCall = + (TS.Call >> TS.ExpressionStatement) + { function = codecsModule "raiseDecodeErrorFromCustomType" , arguments = - [ codecMap - , TS.Identifier "input" + [ TS.StringLiteralExpression (typeName |> Name.toTitleCase) + , TS.Identifier "kind" ] } + + constructorToCase : ConstructorDetail ta -> ( TS.Expression, List TS.Statement ) + constructorToCase constructor = + ( constructor.name |> Name.toTitleCase |> TS.StringLiteralExpression + , [ TS.ReturnStatement + (TS.Call + { function = constructor.name |> prependDecodeToName |> TS.Identifier + , arguments = (constructor.typeVariableNames |> List.map (prependDecodeToName >> TS.Identifier)) ++ [ inputArg ] + } + ) + ] + ) + + switchStatement : TS.Statement + switchStatement = + TS.SwitchStatement + (TS.Identifier "kind") + (constructors |> List.map constructorToCase) in TS.FunctionDeclaration { name = prependDecodeToName typeName - , typeVariables = [] - , returnType = Nothing + , typeVariables = variableTypeExpressions + , returnType = Just (TS.TypeRef ( [], [], typeName ) variableTypeExpressions) , scope = TS.ModuleFunction , privacy = privacy , parameters = decoderParams ++ [ inputParam ] - , body = [ TS.ReturnStatement call ] + , body = [ kindCall, switchStatement, errorCall ] } From 34ea6f6d27eb208e4f6967348865e671fd8b3b90 Mon Sep 17 00:00:00 2001 From: Douglas Winship Date: Tue, 26 Oct 2021 18:25:00 +0100 Subject: [PATCH 09/11] TypeScript: annotate encoder functions --- src/Morphir/TypeScript/Backend/Types.elm | 52 ++++++++++++++++++------ 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/src/Morphir/TypeScript/Backend/Types.elm b/src/Morphir/TypeScript/Backend/Types.elm index e760aa06f..508c85264 100644 --- a/src/Morphir/TypeScript/Backend/Types.elm +++ b/src/Morphir/TypeScript/Backend/Types.elm @@ -309,6 +309,13 @@ genericDecoder typeExp = typeExp +genericEncoder : TS.TypeExp -> TS.TypeExp +genericEncoder typeExp = + TS.FunctionTypeExp + [ TS.Parameter [] "value" (Just typeExp) ] + TS.Any + + {-| Reference a symbol in the Morphir.Internal.Codecs module. -} codecsModule : String -> TS.Expression @@ -757,6 +764,10 @@ specificEncoderForType customTypeVars typeExp = generateEncoderFunction : TypeVariablesList -> Name -> Access -> Type.Type ta -> TS.Statement generateEncoderFunction variables typeName access typeExp = let + variableTypeExpressions : List TS.TypeExp + variableTypeExpressions = + variables |> List.map Name.toTitleCase |> List.map (\var -> TS.Variable var) + call = encoderExpression variables typeExp @@ -765,17 +776,20 @@ generateEncoderFunction variables typeName access typeExp = variables |> List.map (\var -> - TS.parameter [] (prependEncodeToName var) Nothing + TS.parameter + [] + (prependEncodeToName var) + (Just (genericEncoder (TS.Variable (Name.toTitleCase var)))) ) valueParam : TS.Parameter valueParam = - TS.parameter [] "value" Nothing + TS.parameter [] "value" (Just (TS.TypeRef ( [], [], typeName ) variableTypeExpressions)) in TS.FunctionDeclaration { name = prependEncodeToName typeName - , typeVariables = [] - , returnType = Nothing + , typeVariables = variableTypeExpressions + , returnType = Just TS.Any , scope = TS.ModuleFunction , parameters = variableParams ++ [ valueParam ] , privacy = access |> mapPrivacy @@ -786,17 +800,24 @@ generateEncoderFunction variables typeName access typeExp = generateConstructorEncoderFunction : ConstructorDetail ta -> TS.Statement generateConstructorEncoderFunction constructor = let + variableTypeExpressions : List TS.TypeExp + variableTypeExpressions = + constructor.typeVariableNames |> List.map Name.toTitleCase |> List.map (\var -> TS.Variable var) + encoderParams : List TS.Parameter encoderParams = constructor.typeVariableNames |> List.map (\var -> - TS.parameter [] (prependEncodeToName var) Nothing + TS.parameter + [] + (prependEncodeToName var) + (Just (genericEncoder (TS.Variable (Name.toTitleCase var)))) ) valueParam : TS.Parameter valueParam = - TS.parameter [] "value" Nothing + TS.parameter [] "value" (Just (TS.TypeRef ( [], [], constructor.name ) variableTypeExpressions)) argNames = TS.ArrayLiteralExpression @@ -827,8 +848,8 @@ generateConstructorEncoderFunction constructor = in TS.FunctionDeclaration { name = prependEncodeToName constructor.name - , typeVariables = [] - , returnType = Nothing + , typeVariables = variableTypeExpressions + , returnType = Just TS.Any , scope = TS.ModuleFunction , privacy = constructor.privacy , parameters = encoderParams ++ [ valueParam ] @@ -839,17 +860,24 @@ generateConstructorEncoderFunction constructor = generateUnionEncoderFunction : Name -> TS.Privacy -> List Name -> List (ConstructorDetail ta) -> TS.Statement generateUnionEncoderFunction typeName privacy typeVariables constructors = let + variableTypeExpressions : List TS.TypeExp + variableTypeExpressions = + typeVariables |> List.map Name.toTitleCase |> List.map (\var -> TS.Variable var) + encoderParams : List TS.Parameter encoderParams = typeVariables |> List.map (\var -> - TS.parameter [] (prependEncodeToName var) Nothing + TS.parameter + [] + (prependEncodeToName var) + (Just (genericEncoder (TS.Variable (Name.toTitleCase var)))) ) valueParam : TS.Parameter valueParam = - TS.parameter [] "value" Nothing + TS.parameter [] "value" (Just (TS.TypeRef ( [], [], typeName ) variableTypeExpressions)) getCodecMapEntry : ConstructorDetail ta -> TS.Expression getCodecMapEntry constructor = @@ -880,8 +908,8 @@ generateUnionEncoderFunction typeName privacy typeVariables constructors = in TS.FunctionDeclaration { name = prependEncodeToName typeName - , typeVariables = [] - , returnType = Nothing + , typeVariables = variableTypeExpressions + , returnType = Just TS.Any , scope = TS.ModuleFunction , privacy = privacy , parameters = encoderParams ++ [ valueParam ] From c943a4baf3323dc062432d700260f1e840eab553 Mon Sep 17 00:00:00 2001 From: Douglas Winship Date: Tue, 26 Oct 2021 19:13:40 +0100 Subject: [PATCH 10/11] TypeScript: change custom type encoder functions Update the encoder function for custom types and custom type variants. These functions now create the return values directly, and do not have to call on codec.encodeCustomType and codec.encodeCustomTypeVariant. --- src/Morphir/TypeScript/Backend/Types.elm | 102 ++++++++++------------- 1 file changed, 45 insertions(+), 57 deletions(-) diff --git a/src/Morphir/TypeScript/Backend/Types.elm b/src/Morphir/TypeScript/Backend/Types.elm index 508c85264..45cf39ec2 100644 --- a/src/Morphir/TypeScript/Backend/Types.elm +++ b/src/Morphir/TypeScript/Backend/Types.elm @@ -648,12 +648,8 @@ generateUnionDecoderFunction typeName privacy typeVariables constructors = } -encoderExpression : TypeVariablesList -> Type.Type a -> TS.CallExpression -encoderExpression customTypeVars typeExp = - let - valueArg = - TS.Identifier "value" - in +encoderExpression : TypeVariablesList -> Type.Type a -> TS.Expression -> TS.CallExpression +encoderExpression customTypeVars typeExp valueArg = case typeExp of Type.Reference _ ( [ [ "morphir" ], [ "s", "d", "k" ] ], [ [ "basics" ] ], [ "bool" ] ) [] -> { function = codecsModule "encodeBoolean", arguments = [ valueArg ] } @@ -753,7 +749,7 @@ specificEncoderForType : TypeVariablesList -> Type.Type ta -> TS.Expression specificEncoderForType customTypeVars typeExp = let expression = - encoderExpression customTypeVars typeExp + encoderExpression customTypeVars typeExp (TS.Identifier "value") removeValueArg arguments = arguments |> List.take (List.length arguments - 1) @@ -769,7 +765,7 @@ generateEncoderFunction variables typeName access typeExp = variables |> List.map Name.toTitleCase |> List.map (\var -> TS.Variable var) call = - encoderExpression variables typeExp + encoderExpression variables typeExp (TS.Identifier "value") variableParams : List TS.Parameter variableParams = @@ -819,32 +815,33 @@ generateConstructorEncoderFunction constructor = valueParam = TS.parameter [] "value" (Just (TS.TypeRef ( [], [], constructor.name ) variableTypeExpressions)) - argNames = - TS.ArrayLiteralExpression - (constructor.args - |> List.map (Tuple.first >> Name.toCamelCase >> TS.StringLiteralExpression) + argToEncoderCall : ( Name, Type a ) -> TS.Expression + argToEncoderCall ( argName, argType ) = + TS.Call + (encoderExpression + constructor.typeVariableNames + argType + (TS.MemberExpression + { object = TS.Identifier "value" + , member = argName |> Name.toCamelCase |> TS.Identifier + } + ) ) - argEncoders = - TS.ArrayLiteralExpression - (constructor.args - |> List.map Tuple.second - |> List.map (specificEncoderForType constructor.typeVariableNames) - ) + kindExpression : TS.Expression + kindExpression = + TS.MemberExpression { object = TS.Identifier "value", member = TS.Identifier "kind" } - value = - TS.Identifier "value" + returnList : TS.Expression + returnList = + if (constructor.args |> List.length) == 0 then + kindExpression - call : TS.Expression - call = - TS.Call - { function = codecsModule "encodeCustomTypeVariant" - , arguments = - [ argNames - , argEncoders - , value - ] - } + else + TS.ArrayLiteralExpression + (kindExpression + :: (constructor.args |> List.map argToEncoderCall) + ) in TS.FunctionDeclaration { name = prependEncodeToName constructor.name @@ -853,7 +850,7 @@ generateConstructorEncoderFunction constructor = , scope = TS.ModuleFunction , privacy = constructor.privacy , parameters = encoderParams ++ [ valueParam ] - , body = [ TS.ReturnStatement call ] + , body = [ TS.ReturnStatement returnList ] } @@ -879,32 +876,23 @@ generateUnionEncoderFunction typeName privacy typeVariables constructors = valueParam = TS.parameter [] "value" (Just (TS.TypeRef ( [], [], typeName ) variableTypeExpressions)) - getCodecMapEntry : ConstructorDetail ta -> TS.Expression - getCodecMapEntry constructor = - TS.ArrayLiteralExpression - [ TS.StringLiteralExpression (constructor.name |> Name.toTitleCase) - , bindArgumentsToFunction - (constructor.name |> prependEncodeToName |> TS.Identifier) - (constructor.typeVariableNames |> List.map (prependEncodeToName >> TS.Identifier)) - ] - - codecMap : TS.Expression - codecMap = - constructors |> List.map getCodecMapEntry |> TS.ArrayLiteralExpression |> buildCodecMap - - call : TS.Expression - call = - TS.Call - { function = - TS.MemberExpression - { object = TS.Identifier "codecs" - , member = TS.Identifier "encodeCustomType" + constructorToCase : ConstructorDetail ta -> ( TS.Expression, List TS.Statement ) + constructorToCase constructor = + ( constructor.name |> Name.toTitleCase |> TS.StringLiteralExpression + , [ TS.ReturnStatement + (TS.Call + { function = constructor.name |> prependEncodeToName |> TS.Identifier + , arguments = (constructor.typeVariableNames |> List.map (prependEncodeToName >> TS.Identifier)) ++ [ TS.Identifier "value" ] } - , arguments = - [ codecMap - , TS.Identifier "value" - ] - } + ) + ] + ) + + switchStatement : TS.Statement + switchStatement = + TS.SwitchStatement + (TS.MemberExpression { object = TS.Identifier "value", member = TS.Identifier "kind" }) + (constructors |> List.map constructorToCase) in TS.FunctionDeclaration { name = prependEncodeToName typeName @@ -913,7 +901,7 @@ generateUnionEncoderFunction typeName privacy typeVariables constructors = , scope = TS.ModuleFunction , privacy = privacy , parameters = encoderParams ++ [ valueParam ] - , body = [ TS.ReturnStatement call ] + , body = [ switchStatement ] } From 5ddaabc6b3b3551579cc37006ba453eef2d3dce1 Mon Sep 17 00:00:00 2001 From: Douglas Winship Date: Tue, 26 Oct 2021 19:38:17 +0100 Subject: [PATCH 11/11] TypeScript: remove unused functions from Codecs.ts --- .../TypeScript/morphir/internal/Codecs.ts | 77 ------------------- 1 file changed, 77 deletions(-) diff --git a/redistributable/TypeScript/morphir/internal/Codecs.ts b/redistributable/TypeScript/morphir/internal/Codecs.ts index 5af21bc32..6eb405c0e 100644 --- a/redistributable/TypeScript/morphir/internal/Codecs.ts +++ b/redistributable/TypeScript/morphir/internal/Codecs.ts @@ -83,55 +83,6 @@ export function decodeFloat(input: any): number { return input; } -export function decodeCustomType(decoderMap: CodecMap, input: any): object { - if (typeof input == "string") input = [input]; - if (!(input instanceof Array)) { - throw new DecodeError(`Expected Array, got ${typeof input}`); - } - if (!(typeof input[0] == "string")) { - throw new DecodeError(`Expected String, got ${typeof input}`); - } - if (!decoderMap.has(input[0])) { - let variantNames = Array.from(decoderMap.keys()); - let variantNameString = variantNames.join(", "); - throw new DecodeError( - `Expected one of "${variantNameString}", got ${input[0]}` - ); - } - return decoderMap.get(input[0])(input); -} - -export function decodeCustomTypeVariant( - kind: string, - argNames: Array, - argDecoders: CodecList, - input: any -): object { - if (typeof input == "string") input = [input]; - - if (input[0] != kind) { - throw new DecodeError(`Expected kind ${kind}, got ${input[0]}`); - } - - const argCount = input.length - 1; - if (argCount != argDecoders.length) { - throw new DecodeError( - `Expected ${argDecoders.length} args for custom type "${kind}", got ${argCount}` - ); - } - - var result = { - kind: kind, - }; - - for (var i = 0; i < argDecoders.length; i++) { - var paramName = argNames[i]; - result[paramName] = argDecoders[i](input[i + 1]); - } - - return result; -} - export function decodeDict( decodeKey: (any) => K, decodeValue: (any) => V, @@ -240,34 +191,6 @@ export function encodeFloat(value: number): number { return value; } -export function encodeCustomType(encoderMap: CodecMap, value: any): any { - if (encoderMap.has(value["kind"])) { - const encoderFn: any = encoderMap.get(value["kind"]); - return encoderFn(value); - } else { - throw new DecodeError( - `Didn't find encoder for type variant: ${value["kind"]}` - ); - } -} - -export function encodeCustomTypeVariant( - argNames: Array, - argEncoders: CodecList, - value: object -): Array { - if (argNames.length == 0) { - return value["kind"]; - } else { - var result = [value["kind"]]; - for (var i = 0; i < argNames.length; i++) { - const name = argNames[i]; - result.push(argEncoders[i](value[name])); - } - return result; - } -} - export function encodeDict( encodeKey: (any) => K, encodeValue: (any) => V,