From 8da7427aa62ccb20bac98131ff7196b24474c2d1 Mon Sep 17 00:00:00 2001 From: Kalle Virtaneva Date: Thu, 31 Jul 2025 18:05:47 -0400 Subject: [PATCH 1/2] test: try to replicate issue with Maybe in tuple --- test/Basic.hs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/Basic.hs b/test/Basic.hs index a6c7a86..ca4c6d2 100644 --- a/test/Basic.hs +++ b/test/Basic.hs @@ -20,6 +20,9 @@ $(deriveTypeScript (A.defaultOptions { A.tagSingleConstructors = True data Test1 = Test1 (Maybe Int) deriveTypeScript A.defaultOptions ''Test1 +data Test2 = Test2 String [Int] (Maybe String) +deriveTypeScript A.defaultOptions ''Test2 + tests :: SpecWith () tests = describe "Basic tests" $ do describe "tagSingleConstructors and constructorTagModifier" $ do @@ -40,6 +43,12 @@ tests = describe "Basic tests" $ do , TSTypeAlternatives "ITest1" [] ["number | null"] Nothing ]) + it [i|Maybe in multi-field tuple includes null option|] $ do + (getTypeScriptDeclarations (Proxy :: Proxy Test2)) `shouldBe` ([ + TSTypeAlternatives "Test2" [] ["ITest2"] Nothing + , TSTypeAlternatives "ITest2" [] ["[string, number[], string | null]"] Nothing + ]) + main :: IO () main = hspec tests From 808896ed6ed3600fa8ffed470110791e74a53998 Mon Sep 17 00:00:00 2001 From: Kalle Virtaneva Date: Thu, 31 Jul 2025 18:20:37 -0400 Subject: [PATCH 2/2] feat: try to get test to pass --- src/Data/Aeson/TypeScript/TH.hs | 17 +++++++++++++---- test/Basic.hs | 22 ++++++++++++++++++++++ 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/Data/Aeson/TypeScript/TH.hs b/src/Data/Aeson/TypeScript/TH.hs index 3cd7227..3ce3491 100644 --- a/src/Data/Aeson/TypeScript/TH.hs +++ b/src/Data/Aeson/TypeScript/TH.hs @@ -308,10 +308,19 @@ handleConstructor (ExtraTypeScriptOptions {..}) options (DatatypeInfo {..}) gene interfaceName = "I" <> (lastNameComponent' $ constructorName ci) tupleEncoding = do - let typ = contentsTupleTypeSubstituted genericVariables ci - stringExp <- lift $ case typ of - (AppT (ConT name) t) | name == ''Maybe -> [|$(getTypeAsStringExp t) <> " | null"|] - _ -> getTypeAsStringExp typ + let fields = constructorFields ci + stringExp <- lift $ case fields of + [] -> [|"void[]"|] + [x] -> case mapType genericVariables x of + (AppT (ConT name) t) | name == ''Maybe -> [|$(getTypeAsStringExp t) <> " | null"|] + mappedType -> getTypeAsStringExp mappedType + xs -> do + -- Process each field individually to handle Maybe types + fieldStrings <- forM (fmap (mapType genericVariables) xs) $ \fieldType -> case fieldType of + (AppT (ConT name) t) | name == ''Maybe -> [|$(getTypeAsStringExp t) <> " | null"|] + _ -> getTypeAsStringExp fieldType + let fieldExps = map return fieldStrings + [|"[" <> $(foldr1 (\a b -> [|$a <> ", " <> $b|]) fieldExps) <> "]"|] lift [|TSTypeAlternatives $(TH.stringE interfaceName) $(genericVariablesListExpr True genericVariables) diff --git a/test/Basic.hs b/test/Basic.hs index ca4c6d2..72a72c2 100644 --- a/test/Basic.hs +++ b/test/Basic.hs @@ -4,6 +4,7 @@ module Basic (tests) where import Data.Aeson as A import Data.Aeson.TypeScript.TH import Data.Aeson.TypeScript.Types +import Data.List.NonEmpty (NonEmpty) import Data.Proxy import Data.String.Interpolate import Prelude hiding (Double) @@ -23,6 +24,21 @@ deriveTypeScript A.defaultOptions ''Test1 data Test2 = Test2 String [Int] (Maybe String) deriveTypeScript A.defaultOptions ''Test2 +-- Test case for Maybe types in multi-field tuples +data PromptKey = PromptKey String deriving (Eq, Show) + +$(deriveTypeScript A.defaultOptions ''PromptKey) + +newtype ExtraInputPrompt = ExtraInputPrompt String deriving (Eq, Show) + +$(deriveTypeScript A.defaultOptions ''ExtraInputPrompt) + +data WidgetActionPayload + = InfoRequest String [PromptKey] (Maybe (NonEmpty ExtraInputPrompt)) + deriving (Eq, Show) + +$(deriveTypeScript A.defaultOptions ''WidgetActionPayload) + tests :: SpecWith () tests = describe "Basic tests" $ do describe "tagSingleConstructors and constructorTagModifier" $ do @@ -49,6 +65,12 @@ tests = describe "Basic tests" $ do , TSTypeAlternatives "ITest2" [] ["[string, number[], string | null]"] Nothing ]) + it [i|WidgetActionPayload tuple includes null for Maybe list|] $ do + (getTypeScriptDeclarations (Proxy :: Proxy WidgetActionPayload)) + `shouldBe` ( [ TSTypeAlternatives "WidgetActionPayload" [] ["IInfoRequest"] Nothing, + TSTypeAlternatives "IInfoRequest" [] ["[string, PromptKey[], ExtraInputPrompt[] | null]"] Nothing + ] + ) main :: IO () main = hspec tests