codedownio / aeson-typescript

Generate TypeScript definition files from your ADTs
BSD 3-Clause "New" or "Revised" License
60 stars 27 forks source link

Example doesn't seem to work #24

Closed vcanadi closed 3 years ago

vcanadi commented 3 years ago

The example with type D doesn't seem to work on the type alone (without given type params). Similar behaviour (same error) happens with more simple example from tests:

data HigherKind a = HigherKind { higherKindList :: [a] }
$(deriveTypeScript A.defaultOptions ''HigherKind)
$(deriveJSON A.defaultOptions ''HigherKind)

main :: IO ()
main = putStrLn $ formatTSDeclarations $ getTypeScriptDeclarations (Proxy :: Proxy HigherKind)

which results in:

app/Main.hs:31:42: error:
    • No instance for (TypeScript HigherKind)
        arising from a use of ‘getTypeScriptDeclarations’
    • In the second argument of ‘($)’, namely
        ‘getTypeScriptDeclarations (Proxy :: Proxy HigherKind)’
      In the second argument of ‘($)’, namely
        ‘formatTSDeclarations
           $ getTypeScriptDeclarations (Proxy :: Proxy HigherKind)’
      In the expression:
        putStrLn
          $ formatTSDeclarations
              $ getTypeScriptDeclarations (Proxy :: Proxy HigherKind)
   |
31 | main = putStrLn $ formatTSDeclarations $ getTypeScriptDeclarations (Proxy :: Proxy HigherKind)i
thomasjm commented 3 years ago

Oh, after some recent changes you need to include the type parameter. So Proxy :: Proxy (D T) or Proxy :: Proxy (HigherKind T). For multiple params you can do T1, T2, etc. Sorry, I need to update the docs.

vcanadi commented 3 years ago

Thanks. I noticed that this is the intended usage from the tests, but I was unsure since I didn't manage to get it working in my code (not the one mentioned above). What I need is a higher kinded type parameter. e.g.

data X f = X (f Int)

Is this possible?

thomasjm commented 3 years ago

Yes, it's possible as long as when you call getTypeScriptDeclarations you fill in f with something such that you get a reasonable type to map to TypeScript. For example, see the tests in TypeFamilies.hs under the heading "Complicated Beam-like user type".

Somewhat related to this, there is special built-in support for type families, when get mapped to TypeScript lookup types. You can also see that in the TypeFamilies.hs tests, somewhat mixed together with the other higher kinded stuff. I need to write that up properly as well.

vcanadi commented 3 years ago

Could you write an simple example of what to pass to my X type for f argument in getTypeScriptDeclaratios? I tried making a type TtoT similar to yours T, but I didn't manage to get it to work.

thomasjm commented 3 years ago
data X f = X (f Int)
$(deriveTypeScript A.defaultOptions ''X)

-- Unfortunately we need to declare this funny instance because the constraints on
-- getTypeScriptDeclarations are a little too tight in this case
instance TypeScript (HashMap String) where
  getTypeScriptType _ = "void"

decls = getTypeScriptDeclarations (Proxy :: Proxy (X (HashMap String)))
> putStrLn $ formatTSDeclarations decls
type X = IX;

type IX = {[k: string]: number};
vcanadi commented 3 years ago

So is this something like making and typescript type for a "subtype" Y of X where type Y = X (HashMap String) but HashMap String is arbitrarily chosen and it could be any * -> * type like Identity or Maybe as well? As I understood, now the f as a type argument doesn't exist in TS? So it's just like a type without an argument, but I got to chose the specification (HashMap String) in Proxy (X (HashMap String))) it possible that f remains unchosen so it remains a higher level type argument (or some hacky equivalent of it) in the TS?

vcanadi commented 3 years ago

My ultimate goal is to have this type in haskell: data AlterList a b c d n = AlterList (a n) [(b n, c n)] (d n) (c n) and have the TS equivalent without worrying to much about the TS (which I am not very familiar with)

thomasjm commented 3 years ago

The part where you want f to remain unchosen sounds like you might want the closed type family stuff:

type family MyTypeFamily a = result | result -> a where
  MyTypeFamily Int = String
  MyTypeFamily String = Bool
  MyTypeFamily T = Int

data X a = X (MyTypeFamily a)
$(deriveTypeScript' A.defaultOptions ''X (ExtraTypeScriptOptions { typeFamiliesToMapToTypeScript = [''MyTypeFamily] }))

decls = getTypeScriptDeclarationsRecursively (Proxy :: Proxy (X T))
> putStrLn $ formatTSDeclarations decls
interface MyTypeFamily {
  number: string;
  string: boolean;
  T: number;
}

type IX<T extends keyof MyTypeFamily> = MyTypeFamily[T];

type X<T extends keyof MyTypeFamily> = IX<T>;

In general, TypeScript has a less powerful type system than Haskell, so it's hard to map your AlterList to TS generally, without knowing more about what a, b, c, d can be. But presumably you're also coming up with FromJSON/ToJSON instances, so that might help narrow down the types. (For example, a n couldn't be a function type.)

thomasjm commented 3 years ago

I updated the docs so I'm going to close this now, but feel free to let me know if you have any other questions.