Skip to content

Generate a nicer NonEmpty for TypeScript #60

@ekilah

Description

@ekilah

Is there appetite for changing the NonEmpty instance?

It currently generates the same way as any other list (e.g. NonEmpty Text and [Text] both generate to string[]):

instance {-# OVERLAPPABLE #-} (TypeScript a) => TypeScript [a] where
getTypeScriptType _ = (getTypeScriptType (Proxy :: Proxy a)) ++ "[]"
getParentTypes _ = [TSType (Proxy :: Proxy a)]
instance (TypeScript a) => TypeScript (NonEmpty a) where
getTypeScriptType _ = getTypeScriptType (Proxy :: Proxy [a])
getParentTypes _ = [TSType (Proxy :: Proxy a)]

In TypeScript, NonEmpty Text can actually be represented natively, like so:

// (not a built-in type)
type NonEmpty<T> = [T, ...T[]]

and then used like this:

interface Foo {
    nonEmptyList: [string, ...string[]]

    orAnotherWay: NonEmpty<string>
}

These fancier tuple types are compatible in all of the important ways in TS (e.g. you can use a [T, ...T] in a position expecting T[]), so this wouldn't really be a breaking change, but it would indeed change people's generated outputs in a way people might not be super familiar with (or care about), so I'm sympathetic to any caution you might express here.


Our typescript client actually represents this NonEmpty<T> in with its own little named type, so another option that might (?) work for me personally would be to mark your instance as OVERLAPPABLE (docs).

That said, I am not very practiced in Haskell to know how likely you are to do that, or if there are downsides, etc. I see it's already there for the regular, not-NonEmpty list type, so maybe this isn't weird?

Supposedly, that'd make it so users of this library could write their own, fancier instance as desired, without a "breaking" sort of change like the above could be seen as.

As-is, I tried this:

instance {-# OVERLAPPING #-} TypeScript a => TypeScript (NE.NonEmpty a) where
  getTypeScriptType _ = "NEList<" <> getTypeScriptType (Proxy @a) <> ">"
  getParentTypes _ = TSType (Proxy @NEList) : getParentTypes (Proxy @a)

but Haskell seems to not like that my instance and the one from this library have the same specificity, or something.

edit: Claude suggests that OVERLAPPABLE would not solve my problem because of the specificity issue.

(Again, not really much of a Haskeller, so apologies if there are some misunderstandings here)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions