Skip to content

Commit

Permalink
Merge pull request #28 from GoNZooo/r.typescript-basic-tests
Browse files Browse the repository at this point in the history
fix: output correct type for optional constructor arguments in TypeScript
  • Loading branch information
GoNZooo authored Oct 13, 2022
2 parents 5cea7d7 + de9f40b commit bbadd18
Show file tree
Hide file tree
Showing 12 changed files with 409 additions and 7 deletions.
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
# Changelog

## 2.2.0

### Added

- More tests for TypeScript output; the "basic" suite

### Fixes
- Fixed bug where TypeScript's optional parameters to constructors would not
use `?`, i.e. `function Constructor(data?: string)`.

## 2.1.0

### Fixes

- Made it so Kotlin started using types from enum definitions instead of `Any`.

## 2.0.0

### Breaking changes
Expand Down
9 changes: 8 additions & 1 deletion gotyno-hs.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ cabal-version: 1.12
-- see: https://github.com/sol/hpack

name: gotyno-hs
version: 2.1.0
version: 2.2.0
synopsis: A type definition compiler supporting multiple output languages.
description: Compiles type definitions into F#, TypeScript and Python, with validators, decoders and encoders.
category: Compiler
Expand All @@ -31,8 +31,15 @@ extra-source-files:
examples/python.gotyno
test/reference-output/arrayOfArraysOfNullableStrings.ts
test/reference-output/basic.ts
test/reference-output/basicEnumeration.ts
test/reference-output/basicImport.ts
test/reference-output/basicOptional.ts
test/reference-output/basicStruct.ts
test/reference-output/basicUnion.ts
test/reference-output/external.ts
test/reference-output/generics.ts
test/reference-output/genericStruct.ts
test/reference-output/genericUnion.ts
test/reference-output/github.ts
test/reference-output/hasGeneric.ts
test/reference-output/importExample.ts
Expand Down
2 changes: 1 addition & 1 deletion package.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: gotyno-hs
version: 2.1.0
version: 2.2.0
synopsis: A type definition compiler supporting multiple output languages.
description: Compiles type definitions into F#, TypeScript and Python, with validators, decoders and encoders.
license: BSD2
Expand Down
9 changes: 7 additions & 2 deletions src/CodeGeneration/TypeScript.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1038,7 +1038,10 @@ outputCaseType
unionName
(FieldName tag)
(Constructor (ConstructorName name) maybePayload) =
let payloadLine = maybe "" (\p -> " data: " <> outputFieldType p <> ";\n") maybePayload
let payloadLine = maybe "" outputDataField maybePayload
outputDataField (ComplexType (OptionalType t)) =
mconcat [" data?: ", outputFieldType t, ";\n"]
outputDataField t = mconcat [" data: ", outputFieldType t, ";\n"]
maybeTypeVariables = maybe "" (typeVariablesFrom >>> maybeJoinTypeVariables) maybePayload
in mconcat
[ mconcat ["export type ", upperCaseFirst name, maybeTypeVariables, " = {\n"],
Expand All @@ -1058,7 +1061,9 @@ outputCaseConstructor
unionName
(FieldName tag)
(Constructor (ConstructorName name) maybePayload) =
let argumentFieldAndType = maybe "" (\p -> "data: " <> outputFieldType p) maybePayload
let argumentFieldAndType = maybe "" outputDataParameter maybePayload
outputDataParameter (ComplexType (OptionalType t)) = mconcat ["data?: ", outputFieldType t]
outputDataParameter t = mconcat ["data: ", outputFieldType t]
maybeTypeVariables = maybe "" joinTypeVariables (maybePayload >>= typeVariablesFrom)
in mconcat
[ mconcat
Expand Down
54 changes: 51 additions & 3 deletions test/ParsingSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,14 @@ import Test.Hspec
import Types

data TypeScriptReferenceOutput = TypeScriptReferenceOutput
{ basic :: !Text,
{ basicStruct :: !Text,
basicUnion :: !Text,
genericStruct :: !Text,
genericUnion :: !Text,
basicEnumeration :: !Text,
basicImport :: !Text,
basicOptional :: !Text,
basic :: !Text,
import' :: !Text,
hasGeneric :: !Text,
generics :: !Text,
Expand Down Expand Up @@ -66,12 +73,33 @@ data DLangReferenceOutput = DLangReferenceOutput

typeScriptReferenceOutput :: IO TypeScriptReferenceOutput
typeScriptReferenceOutput = do
basicStruct <- basicStructReferenceOutput "ts"
basicUnion <- basicUnionReferenceOutput "ts"
genericStruct <- genericStructReferenceOutput "ts"
genericUnion <- genericUnionReferenceOutput "ts"
basicEnumeration <- basicEnumerationReferenceOutput "ts"
basicImport <- basicImportReferenceOutput "ts"
basicOptional <- basicOptionalReferenceOutput "ts"
basic <- basicReferenceOutput "ts"
import' <- importReferenceOutput "ts"
hasGeneric <- hasGenericReferenceOutput "ts"
generics <- genericsReferenceOutput "ts"
gitHub <- gitHubReferenceOutput "ts"
pure TypeScriptReferenceOutput {basic, import', hasGeneric, generics, gitHub}
pure
TypeScriptReferenceOutput
{ basicStruct,
basicUnion,
genericStruct,
genericUnion,
basicEnumeration,
basicImport,
basicOptional,
basic,
import',
hasGeneric,
generics,
gitHub
}

haskellReferenceOutput :: IO HaskellReferenceOutput
haskellReferenceOutput = do
Expand Down Expand Up @@ -191,7 +219,20 @@ spec ::
DLangReferenceOutput ->
Spec
spec
(TypeScriptReferenceOutput tsBasic tsImport tsHasGeneric tsGenerics tsGitHub)
( TypeScriptReferenceOutput
tsBasicStruct
tsBasicUnion
tsGenericStruct
tsGenericUnion
tsBasicEnumeration
tsBasicImport
tsBasicOptional
tsBasic
tsImport
tsHasGeneric
tsGenerics
tsGitHub
)
(HaskellReferenceOutput hsBasic hsImport hsHasGeneric hsGenerics hsGitHub)
(FSharpReferenceOutput fsBasic fsImport fsHasGeneric fsGenerics fsGitHub)
(PythonReferenceOutput pyPython pyBasic pyGenerics)
Expand Down Expand Up @@ -513,37 +554,44 @@ spec
it "Mirrors reference output for `basicStruct.gotyno`" $ do
basicStructModule <-
(getRight >>> PartialList.head) <$> parseModules ["examples/basicStruct.gotyno"]
TypeScript.outputModule basicStructModule `shouldBe` tsBasicStruct
DLang.outputModule basicStructModule `shouldBe` dBasicStruct

it "Mirrors reference output for `basicUnion.gotyno`" $ do
basicUnionModule <-
(getRight >>> PartialList.head) <$> parseModules ["examples/basicUnion.gotyno"]
TypeScript.outputModule basicUnionModule `shouldBe` tsBasicUnion
DLang.outputModule basicUnionModule `shouldBe` dBasicUnion

it "Mirrors reference output for `genericStruct.gotyno`" $ do
genericStructModule <-
(getRight >>> PartialList.head) <$> parseModules ["examples/genericStruct.gotyno"]
TypeScript.outputModule genericStructModule `shouldBe` tsGenericStruct
DLang.outputModule genericStructModule `shouldBe` dGenericStruct

it "Mirrors reference output for `genericUnion.gotyno`" $ do
genericUnionModule <-
(getRight >>> PartialList.head) <$> parseModules ["examples/genericUnion.gotyno"]
TypeScript.outputModule genericUnionModule `shouldBe` tsGenericUnion
DLang.outputModule genericUnionModule `shouldBe` dGenericUnion

it "Mirrors reference output for `basicEnumeration.gotyno`" $ do
enumerationModule <-
(getRight >>> PartialList.head) <$> parseModules ["examples/basicEnumeration.gotyno"]
TypeScript.outputModule enumerationModule `shouldBe` tsBasicEnumeration
DLang.outputModule enumerationModule `shouldBe` dBasicEnumeration

it "Mirrors reference output for `basicImport.gotyno`" $ do
basicImportModule <-
(getRight >>> PartialList.last)
<$> parseModules ["examples/basicStruct.gotyno", "examples/basicImport.gotyno"]
TypeScript.outputModule basicImportModule `shouldBe` tsBasicImport
DLang.outputModule basicImportModule `shouldBe` dBasicImport

it "Mirrors reference output for `basicOptional.gotyno`" $ do
basicOptionalModule <-
(getRight >>> PartialList.head) <$> parseModules ["examples/basicOptional.gotyno"]
TypeScript.outputModule basicOptionalModule `shouldBe` tsBasicOptional
DLang.outputModule basicOptionalModule `shouldBe` dBasicOptional

it "Mirrors reference output for `basic.gotyno`" $ do
Expand Down
31 changes: 31 additions & 0 deletions test/reference-output/basicEnumeration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import * as svt from "simple-validation-tools";

export enum StringValues {
first = "first",
second = "second",
third = "Third",
fourth = "Fourth",
}

export function isStringValues(value: unknown): value is StringValues {
return [StringValues.first, StringValues.second, StringValues.third, StringValues.fourth].some((v) => v === value);
}

export function validateStringValues(value: unknown): svt.ValidationResult<StringValues> {
return svt.validateOneOfLiterals<StringValues>(value, [StringValues.first, StringValues.second, StringValues.third, StringValues.fourth]);
}

export enum IntValues {
first = 1,
second = 2,
third = 3,
fourth = 4,
}

export function isIntValues(value: unknown): value is IntValues {
return [IntValues.first, IntValues.second, IntValues.third, IntValues.fourth].some((v) => v === value);
}

export function validateIntValues(value: unknown): svt.ValidationResult<IntValues> {
return svt.validateOneOfLiterals<IntValues>(value, [IntValues.first, IntValues.second, IntValues.third, IntValues.fourth]);
}
46 changes: 46 additions & 0 deletions test/reference-output/basicImport.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import * as svt from "simple-validation-tools";

import * as basicStruct from "./basicStruct";

export type StructUsingImport = {
field: basicStruct.BasicStruct;
};

export function isStructUsingImport(value: unknown): value is StructUsingImport {
return svt.isInterface<StructUsingImport>(value, {field: basicStruct.isBasicStruct});
}

export function validateStructUsingImport(value: unknown): svt.ValidationResult<StructUsingImport> {
return svt.validate<StructUsingImport>(value, {field: basicStruct.validateBasicStruct});
}

export type UnionUsingImport = ConstructorWithPayload;

export enum UnionUsingImportTag {
ConstructorWithPayload = "ConstructorWithPayload",
}

export type ConstructorWithPayload = {
type: UnionUsingImportTag.ConstructorWithPayload;
data: basicStruct.BasicStruct;
};

export function ConstructorWithPayload(data: basicStruct.BasicStruct): ConstructorWithPayload {
return {type: UnionUsingImportTag.ConstructorWithPayload, data};
}

export function isUnionUsingImport(value: unknown): value is UnionUsingImport {
return [isConstructorWithPayload].some((typePredicate) => typePredicate(value));
}

export function isConstructorWithPayload(value: unknown): value is ConstructorWithPayload {
return svt.isInterface<ConstructorWithPayload>(value, {type: UnionUsingImportTag.ConstructorWithPayload, data: basicStruct.isBasicStruct});
}

export function validateUnionUsingImport(value: unknown): svt.ValidationResult<UnionUsingImport> {
return svt.validateWithTypeTag<UnionUsingImport>(value, {[UnionUsingImportTag.ConstructorWithPayload]: validateConstructorWithPayload}, "type");
}

export function validateConstructorWithPayload(value: unknown): svt.ValidationResult<ConstructorWithPayload> {
return svt.validate<ConstructorWithPayload>(value, {type: UnionUsingImportTag.ConstructorWithPayload, data: basicStruct.validateBasicStruct});
}
82 changes: 82 additions & 0 deletions test/reference-output/basicOptional.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import * as svt from "simple-validation-tools";

export type HasOptionalString = {
stringField?: string;
optionalArrayField?: number[];
arrayOfOptionalField: (number | null | undefined)[];
};

export function isHasOptionalString(value: unknown): value is HasOptionalString {
return svt.isInterface<HasOptionalString>(value, {stringField: svt.optional(svt.isString), optionalArrayField: svt.optional(svt.arrayOf(svt.isNumber)), arrayOfOptionalField: svt.arrayOf(svt.optional(svt.isNumber))});
}

export function validateHasOptionalString(value: unknown): svt.ValidationResult<HasOptionalString> {
return svt.validate<HasOptionalString>(value, {stringField: svt.validateOptional(svt.validateString), optionalArrayField: svt.validateOptional(svt.validateArray(svt.validateNumber)), arrayOfOptionalField: svt.validateArray(svt.validateOptional(svt.validateNumber))});
}

export type HasOptionalConstructor = DoesNot | Does | HasOptionalStruct;

export enum HasOptionalConstructorTag {
DoesNot = "DoesNot",
Does = "Does",
HasOptionalStruct = "HasOptionalStruct",
}

export type DoesNot = {
type: HasOptionalConstructorTag.DoesNot;
data: number;
};

export type Does = {
type: HasOptionalConstructorTag.Does;
data?: number;
};

export type HasOptionalStruct = {
type: HasOptionalConstructorTag.HasOptionalStruct;
data?: HasOptionalString;
};

export function DoesNot(data: number): DoesNot {
return {type: HasOptionalConstructorTag.DoesNot, data};
}

export function Does(data?: number): Does {
return {type: HasOptionalConstructorTag.Does, data};
}

export function HasOptionalStruct(data?: HasOptionalString): HasOptionalStruct {
return {type: HasOptionalConstructorTag.HasOptionalStruct, data};
}

export function isHasOptionalConstructor(value: unknown): value is HasOptionalConstructor {
return [isDoesNot, isDoes, isHasOptionalStruct].some((typePredicate) => typePredicate(value));
}

export function isDoesNot(value: unknown): value is DoesNot {
return svt.isInterface<DoesNot>(value, {type: HasOptionalConstructorTag.DoesNot, data: svt.isNumber});
}

export function isDoes(value: unknown): value is Does {
return svt.isInterface<Does>(value, {type: HasOptionalConstructorTag.Does, data: svt.optional(svt.isNumber)});
}

export function isHasOptionalStruct(value: unknown): value is HasOptionalStruct {
return svt.isInterface<HasOptionalStruct>(value, {type: HasOptionalConstructorTag.HasOptionalStruct, data: svt.optional(isHasOptionalString)});
}

export function validateHasOptionalConstructor(value: unknown): svt.ValidationResult<HasOptionalConstructor> {
return svt.validateWithTypeTag<HasOptionalConstructor>(value, {[HasOptionalConstructorTag.DoesNot]: validateDoesNot, [HasOptionalConstructorTag.Does]: validateDoes, [HasOptionalConstructorTag.HasOptionalStruct]: validateHasOptionalStruct}, "type");
}

export function validateDoesNot(value: unknown): svt.ValidationResult<DoesNot> {
return svt.validate<DoesNot>(value, {type: HasOptionalConstructorTag.DoesNot, data: svt.validateNumber});
}

export function validateDoes(value: unknown): svt.ValidationResult<Does> {
return svt.validate<Does>(value, {type: HasOptionalConstructorTag.Does, data: svt.validateOptional(svt.validateNumber)});
}

export function validateHasOptionalStruct(value: unknown): svt.ValidationResult<HasOptionalStruct> {
return svt.validate<HasOptionalStruct>(value, {type: HasOptionalConstructorTag.HasOptionalStruct, data: svt.validateOptional(validateHasOptionalString)});
}
14 changes: 14 additions & 0 deletions test/reference-output/basicStruct.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import * as svt from "simple-validation-tools";

export type BasicStruct = {
field1: number;
field2: string;
};

export function isBasicStruct(value: unknown): value is BasicStruct {
return svt.isInterface<BasicStruct>(value, {field1: svt.isNumber, field2: svt.isString});
}

export function validateBasicStruct(value: unknown): svt.ValidationResult<BasicStruct> {
return svt.validate<BasicStruct>(value, {field1: svt.validateNumber, field2: svt.validateString});
}
Loading

0 comments on commit bbadd18

Please sign in to comment.