glutinum-org / cli

https://glutinum.net/
58 stars 4 forks source link

Unsupported kind ConstructorType error when type alias contains new #130

Open joprice opened 2 weeks ago

joprice commented 2 weeks ago

Issue created from Glutinum Tool

Glutinum version - 0.11.0-preview

TypeScript

import * as pg from "pg";

  declare class Pool<T extends pg.Client> extends pg.Pool {
      readonly Client: Pool.ClientLikeCtr<T>;

      constructor(config?: Pool.Config<T>, client?: Pool.ClientLikeCtr<T>);

      connect(): Promise<T & pg.PoolClient>;
      connect(callback: (err?: Error, client?: T & pg.PoolClient, done?: (release?: any) => void) => void): void;

      on(event: "error", listener: (err: Error, client: T & pg.PoolClient) => void): this;
      on(event: "connect" | "acquire" | "remove", listener: (client: T & pg.PoolClient) => void): this;
  }

  declare namespace Pool {
      type ClientLikeCtr<T extends pg.Client> = new(config?: string | pg.ClientConfig) => T;

      interface Config<T extends pg.Client> extends pg.PoolConfig {
          Client?: ClientLikeCtr<T> | undefined;
      }
  }

  export = Pool;

FSharp (with warnings/errors)

module rec Glutinum

open Fable.Core
open Fable.Core.JsInterop
open System

[<AbstractClass>]
[<Erase>]
type Exports =
    [<Import("Pool", "REPLACE_ME_WITH_MODULE_NAME"); EmitConstructor>]
    static member Pool<'T when 'T :> pg.Client> (?config: Pool.Config<'T>, ?client: Pool.ClientLikeCtr<'T>) : Pool<'T when 'T :> pg.Client> = nativeOnly

[<AllowNullLiteral>]
[<Interface>]
type Pool<'T when 'T :> pg.Client> =
    inherit pg.Pool
    abstract member Client: Pool.ClientLikeCtr<'T> with get
    abstract member connect: unit -> JS.Promise<obj>
    abstract member connect: callback: (Error -> obj -> (obj -> unit) -> unit) -> unit
    abstract member on: event: string * listener: (Error -> obj -> unit) -> Pool
    abstract member on: event: Pool.on.event * listener: (obj -> unit) -> Pool

module Pool =

    type ClientLikeCtr<'T when 'T :> pg.Client> =
        obj

    [<AllowNullLiteral>]
    [<Interface>]
    type Config<'T when 'T :> pg.Client> =
        inherit pg.PoolConfig
        abstract member Client: ClientLikeCtr option with get, set

    module on =

        [<RequireQualifiedAccess>]
        [<StringEnum(CaseRules.None)>]
        type event =
            | connect
            | acquire
            | remove

[!WARNING]

./src/Glutinum.Converter/Reader/TypeNode.fs(397): Error while reading type node from:
/index.d.ts(16,48)

Unsupported kind ConstructorType

--- Text ---
 new(config?: string | pg.ClientConfig) => T
---

--- Parent text ---

      type ClientLikeCtr<T extends pg.Client> = new(config?: string | pg.ClientConfig) => T;
---
/index.d.ts(16,48)

Unsupported kind ConstructorType

--- Text ---
 new(config?: string | pg.ClientConfig) => T
---

--- Parent text ---

      type ClientLikeCtr<T extends pg.Client> = new(config?: string | pg.ClientConfig) => T;
---

Problem description

When new appears in a type alias, it fails to get handled and results in the fsharp type obj. The above is taken from pg-pool. A simpler version that triggers this is:

type Z = new() => number
MangelMaxime commented 3 days ago

I don't think we have an equivalent in F#, do we?

joprice commented 1 day ago

I don't think so. It's basically a function that is capturing a compile time proof that it can be called with new. Scalajs uses an implicit ConstructorTag trait along with a a helper function, where the trait can be used as a type bound similar to what can be done with SRTP:

Perhaps jsConstructor or a similar intrinsic could be designed to enable a comparable typesafe pattern, but as it currently stands, the example above new(config?: string | pg.ClientConfig) => T; would just have to be a function that doesn't track that it's source is a constructor.