Convert Haskell to Typescript in a highly configurable way with Generics
- First Derive a generic Instance of a type:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
import GHC.Generics
import Data.Text (Text)
data User =
User
{name :: Text
,age :: Int
} deriving (Generic, TypescriptType)
- Pick a flavor you'd like to generate the TS with, and run the print function
-- Note here we are using the flavor Vanilla
printUser :: IO ()
printUser =
putStrLn $ mkTypescriptDeclaration (Proxy :: Proxy Vanilla) (Proxy :: Proxy User)
This prints the following:
interface User {
name : string
age : number
}
Typescript has many ways of doing the same thing, and there are lots of opinions on how to do this. For example, product types can be represented by an interface or (immutable) classes. For this reason, configurability is considered a primary design goal. Here are all of them
-
Ability to customize how Haskell types are represented as TS types
-
Prebaked configurations for the most common ways people like to represent TS types
-
The use of well known libraries as configuration options (built-in flavors). Two examples I will provide default implementations for are fp-ts and unionize
-
A simple interface for providing your own custom translation
A flavor is just another name for a type that represents how you want your Typescript customized to. There are currently two supported flavors: Vanilla and FpTS. You can also write your own flavors
We will use the following example type to see how it varies across flavors
newtype AnOption = AnOption (Maybe Text) deriving (Generic, TypescriptType)
printVanillaOption =
putStrLn $ mkTypescriptDeclaration (Proxy :: Proxy Vanilla) (Proxy :: Proxy AnOption)
-- type AnOption = null | string
printAnOption =
putStrLn $ mkTypescriptDeclaration (Proxy :: Proxy FpTs) (Proxy :: Proxy AnOption)
-- type AnOption = Option<string>
TODO.....
The best place for up to date examples is probably just to look at test, but here's a basic one
Given these haskell types:
import Data.Text
import GHC.Generics
data ComplexRecord =
ComplexRecord
{anIntField :: Int
,aTextField :: Text
,aUnion :: SampleUnion
,aMaybeType :: Maybe Text
,aSimpleRecord :: SimpleRecord
} deriving (Generic, TypescriptType)
data SimpleUnTagged = F Int deriving (Generic, TypescriptType)
data SampleUnion = FirstCon Int | SecondCon Text deriving (Generic, TypescriptType)
Specify a flavor to print to TS. Here's an example using the Vanilla Flavor
printComplexRecord :: IO ()
printComplexRecord =
putStrLn $ mkTypescriptDeclaration (Proxy :: Proxy Vanilla) (Proxy :: Proxy ComplexRecord)
Generates the following typescript types
interface ComplexRecord {
anIntField : number
aTextField : string
aUnion : SampleUnion
aMaybeType : string | null
aSimpleRecord : SimpleRecord
}
- Smart file generators (declarations + imports)
- More complete FP-TS functionality
- Unionize library flavor
- Figure out a cleaner ADT interface for customizing TS
- I dunno, lots of stuff probably. Make it more production ready I guess