Closed joprice closed 3 weeks ago
I don't think we have an equivalent in F#, do we?
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.
type ClientLikeCtr<T extends pg.Client> = new(config?: string) => T;
now generates
type ClientLikeCtr<'T when 'T :> pg.Client> =
obj
but this is not a valid F# code.
We could instead generate:
type ClientLikeCtr = obj
But we lose the generic type information and also when encountering a TypeReference
Glutinum doesn't have access to the full type information. This is because, a type use itself then an infinite loop occurs. Because of that, we would not be able to apply the optimisation of removing the generics from the type when the type is used as a type signature.
To workaround that, what we can do is for
interface Client {
port : number
}
type ClientLikeCtr<T extends Client> = new(config?: string) => T;
declare class Pool<T extends Client> {
Client: ClientLikeCtr<T>;
}
generates
[<AllowNullLiteral>]
[<Interface>]
type Client =
abstract member port: float with get, set
[<Erase>]
type ClientLikeCtr<'T when 'T :> Client> =
ClientLikeCtr of 'T
[<AllowNullLiteral>]
[<Interface>]
type Pool<'T when 'T :> Client> =
abstract member Client: ClientLikeCtr<'T> with get, set
When using [<Erase>]
on a single case DUs Fable, will use the underlying type directly. Doing so allow to make F# compiler happy and still have access to the generics information.
[<Erase>]
type Test<'T> = Test of 'T
let value = Test "dwd"
let (Test.Test v) = value
printfn "%A" v
generates
import { printf, toConsole } from "fable-library-js/String.js";
export const t = "dwd";
export const patternInput$004010 = t;
export const v = patternInput$004010;
toConsole(printf("%A"))(v);
The draw back is that the user will need to unwrap the DU case to access the value. To improve the situation we could generates a
[<Erase>]
type Test<'T> =
| Test of 'T
member inline this.Value =
let (Test.Test v) = this
v
let value = Test "dwd"
printfn "%A" value.Value // unwrapping is hidden to the user
Issue created from Glutinum Tool
Glutinum version - 0.11.0-preview
TypeScript
FSharp (with warnings/errors)
Problem description
When
new
appears in a type alias, it fails to get handled and results in the fsharp typeobj
. The above is taken frompg-pool
. A simpler version that triggers this is: