Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type annotations update #2184

Merged
merged 1 commit into from
Sep 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"name": "Run bench-compiler (.NET)",
"program": "${workspaceFolder}/src/fable-standalone/test/bench-compiler/bin/Debug/netcoreapp3.1/bench-compiler.dll",
// "args": ["${workspaceRoot}/tests/Main/Fable.Tests.fsproj", "out-tests"],
"args": ["${workspaceRoot}/../fable-test/fable-test.fsproj", "out-test"],
"args": ["${workspaceRoot}/../fable-test/fable-test.fsproj", "out-test", "--typescript"],
"cwd": "${workspaceFolder}/src/fable-standalone/test/bench-compiler"
},
{
Expand Down
3 changes: 1 addition & 2 deletions src/Fable.Transforms/AST/AST.Babel.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1112,12 +1112,11 @@ type ClassProperty(key, ?value, ?computed_, ?``static``, ?optional, ?typeAnnotat
printer.PrintOptional(typeAnnotation)
printer.PrintOptional(": ", value)

type ClassImplements(id, ?typeParameters, ?loc) =
type ClassImplements(id, ?typeParameters) =
member _.Id: Identifier = id
member _.TypeParameters: TypeParameterInstantiation option = typeParameters
interface Expression with
member _.Print(printer) =
printer.Print(" implements ", ?loc=loc)
printer.Print(id)
printer.PrintOptional(typeParameters)

Expand Down
198 changes: 51 additions & 147 deletions src/Fable.Transforms/Fable2Babel.fs
Original file line number Diff line number Diff line change
Expand Up @@ -347,21 +347,6 @@ module Annotation =
GenericTypeAnnotation(Identifier name, ?typeParameters=typeParamInst) :> TypeAnnotationInfo
|> TypeAnnotation |> Some

let makeInterfaceDecl (com: IBabelCompiler) ctx (ent: Fable.Entity) (entName: string) (baseExpr: Expression option) =
let genTypeParams = getEntityGenParams ent
let newTypeParams = Set.difference genTypeParams ctx.ScopedTypeParams
let ctx = { ctx with ScopedTypeParams = Set.union ctx.ScopedTypeParams newTypeParams }
let attached = Util.getEntityExplicitInterfaceMembers com ctx ent
let extends =
Util.getInterfaceExtends com ctx ent
|> Seq.toArray
|> function [||] -> None | e -> Some e
// Type declaration merging only works well with class declarations, not class expressions
let id = Identifier(entName)
let body = ObjectTypeAnnotation([| yield! attached |])
let typeParamDecl = genTypeParams |> makeTypeParamDecl
InterfaceDeclaration(id, body, ?extends_=extends, ?typeParameters=typeParamDecl)

let typeAnnotation com ctx typ: TypeAnnotationInfo =
match typ with
| Fable.MetaType -> upcast AnyTypeAnnotation()
Expand Down Expand Up @@ -1214,7 +1199,7 @@ module Util =

let transformBindingAsStatements (com: IBabelCompiler) ctx (var: Fable.Ident) (value: Fable.Expr) =
if isJsStatement ctx false value then
let var = typedIdent com ctx var
let var = ident var
let decl = VariableDeclaration(var) :> Statement
let body = com.TransformAsStatements(ctx, Some(Assign var), value)
Array.append [|decl|] body
Expand Down Expand Up @@ -1645,7 +1630,7 @@ module Util =
let tailcallChance =
Option.map (fun name ->
NamedTailCallOpportunity(com, ctx, name, args) :> ITailCallOpportunity) name
let args = discardUnitArg args |> List.map (typedIdent com ctx)
let args = discardUnitArg args
let declaredVars = ResizeArray()
let mutable isTailCallOptimized = false
let ctx =
Expand All @@ -1663,21 +1648,21 @@ module Util =
match isTailCallOptimized, tailcallChance with
| true, Some tc ->
// Replace args, see NamedTailCallOpportunity constructor
let args, body =
let tcArgs =
tc.Args
|> List.map (fun arg -> Identifier(arg))
let varDecls =
tcArgs
|> List.map (fun arg -> Some (arg :> Expression))
|> List.zip args
|> multiVarDeclaration Const
tcArgs, BlockStatement(Array.append [|varDecls|] body.Body)
let args' =
List.zip args tc.Args
|> List.map (fun (id, tcArg) ->
makeTypedIdent id.Type tcArg |> typedIdent com ctx)
let varDecls =
List.zip args tc.Args
|> List.map (fun (id, tcArg) ->
id |> typedIdent com ctx, Some (Identifier(tcArg) :> Expression))
|> multiVarDeclaration Const
let body = BlockStatement(Array.append [|varDecls|] body.Body)
// Make sure we don't get trapped in an infinite loop, see #1624
let body = BlockStatement(Array.append body.Body [|BreakStatement()|])
args, LabeledStatement(Identifier tc.Label, WhileStatement(BooleanLiteral true, body))
args', LabeledStatement(Identifier tc.Label, WhileStatement(BooleanLiteral true, body))
:> Statement |> Array.singleton |> BlockStatement
| _ -> args, body
| _ -> args |> List.map (typedIdent com ctx), body
let body =
if declaredVars.Count = 0 then body
else
Expand Down Expand Up @@ -1719,121 +1704,26 @@ module Util =
else
None

let getInterfaceExtends com ctx (ent: Fable.Entity) =
let getClassImplements com ctx (ent: Fable.Entity) =
let mkNative genArgs typeName =
let id = Identifier(typeName)
let typeParamInst = makeGenTypeParamInst com ctx genArgs
InterfaceExtends(id, ?typeParameters=typeParamInst) |> Some
ClassImplements(id, ?typeParameters=typeParamInst) |> Some
let mkImport genArgs moduleName typeName =
let id = makeImportTypeId com ctx moduleName typeName
let typeParamInst = makeGenTypeParamInst com ctx genArgs
InterfaceExtends(id, ?typeParameters=typeParamInst) |> Some

// let isIEquatable = FSharp2Fable.Util.hasInterface Types.iequatable ent
// let isIComparable = FSharp2Fable.Util.hasInterface Types.icomparable ent

ClassImplements(id, ?typeParameters=typeParamInst) |> Some
ent.AllInterfaces |> Seq.choose (fun ifc ->
match ifc.Definition.FullName with
| Types.ienumerableGeneric ->
mkImport ifc.GenericArgs "Seq" "IEnumerable"
| Types.ienumeratorGeneric ->
mkImport ifc.GenericArgs "Seq" "IEnumerator"
| Types.iequatable ->
mkImport [Fable.Any] "Util" "IEquatable"
| Types.icomparable ->
mkImport [Fable.Any] "Util" "IComparable"
// | Types.iequatableGeneric when not isIEquatable ->
// mkImport ifc.GenericArgs "Util" "IEquatable"
// | Types.icomparableGeneric when not isIComparable ->
// mkImport ifc.GenericArgs "Util" "IComparable"
| Types.comparer ->
mkImport ifc.GenericArgs "Util" "IComparer"
// this is not needed, as it's already included in every object
// | Types.equalityComparer ->
// mkImport ifc.GenericArgs "Util" "IEqualityComparer"
| Types.idisposable ->
mkImport [] "Util" "IDisposable"
| Types.icollectionGeneric ->
mkImport ifc.GenericArgs "Util" "ICollection"
| "Fable.Collections.IMutableSet`1" ->
mkImport ifc.GenericArgs "Util" "IMutableSet"
| "Fable.Collections.IMutableMap`2" ->
mkImport ifc.GenericArgs "Util" "IMutableMap"
// TODO: add other interfaces
| "Fable.Collections.IMutableSet`1" -> mkNative ifc.GenericArgs "Set"
| "Fable.Collections.IMutableMap`2" -> mkNative ifc.GenericArgs "Map"
| _ -> None
)

// must match the above list (with the exception of IEqualityComparer)
let alreadyDeclaredInterfaces =
set [
Types.ienumerableGeneric
Types.ienumeratorGeneric
Types.iequatable
Types.icomparable
// Types.iequatableGeneric
// Types.icomparableGeneric
Types.comparer
Types.equalityComparer
Types.idisposable
Types.icollectionGeneric
"Fable.Collections.IMutableSet`1"
"Fable.Collections.IMutableMap`2"
]

let isOtherInterfaceMember (memb: Fable.MemberFunctionOrValue) =
let isInterface, fullName =
if memb.IsExplicitInterfaceImplementation then
true, memb.CompiledName.Replace("-",".")
else
let ent = memb.ApparentEnclosingEntity
ent.IsInterface, memb.FullName
let lastDot = fullName.LastIndexOf(".")
let entName = if lastDot < 0 then fullName else fullName.Substring(0, lastDot)
isInterface && not (alreadyDeclaredInterfaces.Contains entName)

let getEntityExplicitInterfaceMembers com ctx (ent: Fable.Entity) =
ent.MembersFunctionsAndValues
|> Seq.filter isOtherInterfaceMember
|> Seq.map (fun memb ->
let args =
List.concat memb.CurriedParameterGroups
|> List.mapi (fun i p ->
let name =
defaultArg p.Name ("arg" + (string i))
|> Naming.sanitizeIdentForbiddenChars |> Naming.checkJsKeywords
name, p.Type
)
let argTypes = args |> List.map snd
let retType = memb.ReturnParameter.Type
let genTypeParams = getGenericTypeParams (argTypes @ [retType])
let newTypeParams = Set.difference genTypeParams ctx.ScopedTypeParams
let ctx = { ctx with ScopedTypeParams = Set.union ctx.ScopedTypeParams newTypeParams }
let funcArgs =
args
|> Seq.map (fun (name, typ) ->
let typeInfo = typeAnnotation com ctx typ
FunctionTypeParam(Identifier(name), typeInfo)
) |> Seq.toArray
let returnType = retType |> typeAnnotation com ctx
let typeParamDecl = makeTypeParamDecl newTypeParams
let funcTypeInfo =
FunctionTypeAnnotation(funcArgs, returnType, ?typeParameters=typeParamDecl)
:> TypeAnnotationInfo
// TODO!!! This should be the compiled name if the interface is not mangled
let name = memb.DisplayName
let membId = Identifier(name)
ObjectTypeProperty(membId, funcTypeInfo)
)
|> Seq.toArray

let getEntityFieldsAsProps (com: IBabelCompiler) ctx (ent: Fable.Entity) =
ent.FSharpFields
|> Seq.map (fun field ->
let id, computed = memberFromName field.Name
let ta = typeAnnotation com ctx field.FieldType
let isStatic = if field.IsStatic then Some true else None
ObjectTypeProperty(id, ta, computed_=computed, ?``static``=isStatic))
|> Seq.toArray
let getUnionFieldsAsIdents (com: IBabelCompiler) ctx (ent: Fable.Entity) =
let tagId = makeTypedIdent (Fable.Number Int32) "tag"
let fieldsId = makeTypedIdent (Fable.Array Fable.Any) "fields"
[| tagId; fieldsId |]

let getEntityFieldsAsIdents com (ent: Fable.Entity) =
ent.FSharpFields
Expand All @@ -1844,9 +1734,30 @@ module Util =
id)
|> Seq.toArray

let getEntityFieldsAsProps (com: IBabelCompiler) ctx (ent: Fable.Entity) =
if (ent.IsFSharpUnion) then
getUnionFieldsAsIdents com ctx ent
|> Array.map (fun id ->
let prop = ident id
let ta = typeAnnotation com ctx id.Type
ObjectTypeProperty(prop, ta))
else
ent.FSharpFields
|> Seq.map (fun field ->
let prop, computed = memberFromName field.Name
let ta = typeAnnotation com ctx field.FieldType
let isStatic = if field.IsStatic then Some true else None
ObjectTypeProperty(prop, ta, computed_=computed, ?``static``=isStatic))
|> Seq.toArray

let declareClassType (com: IBabelCompiler) ctx (ent: Fable.Entity) entName (consArgs: Pattern[]) (consBody: BlockStatement) (baseExpr: Expression option) classMembers =
let consId = Identifier "constructor"
let typeParamDecl = makeEntityTypeParamDecl com ctx ent
let implements =
if com.Options.Typescript then
let implements = Util.getClassImplements com ctx ent |> Seq.toArray
if Array.isEmpty implements then None else Some implements
else None
let consBody =
if ent.IsFSharpExceptionDeclaration then
let super = callSuperConstructor None [] |> ExpressionStatement :> Statement
Expand All @@ -1862,13 +1773,13 @@ module Util =
else Array.empty
let classMembers = Array.append [| classCons |] classMembers
let classBody = ClassBody([| yield! classFields; yield! classMembers |])
let classExpr = ClassExpression(classBody, ?superClass=baseExpr, ?typeParameters=typeParamDecl)
let classExpr = ClassExpression(classBody, ?superClass=baseExpr, ?typeParameters=typeParamDecl, ?implements=implements)
classExpr |> declareModuleMember ent.IsPublic entName false

let declareType (com: IBabelCompiler) ctx (ent: Fable.Entity) entName (consArgs: Pattern[]) (consBody: BlockStatement) baseExpr classMembers: ModuleDeclaration list =
let typeDeclaration = declareClassType com ctx ent entName consArgs consBody baseExpr classMembers
let reflectionDeclaration =
let genArgs = Array.init (ent.GenericParameters.Length) (fun i -> "gen" + string i |> makeIdent |> typedIdent com ctx)
let genArgs = Array.init (ent.GenericParameters.Length) (fun i -> "gen" + string i |> makeIdent |> ident)
let body = transformReflectionInfo com ctx None ent (Array.map (fun x -> x :> _) genArgs)
let returnType =
if com.Options.Typescript then
Expand All @@ -1878,12 +1789,7 @@ module Util =
let args = genArgs |> Array.map (fun x -> x :> Pattern)
makeFunctionExpression None (args, body, returnType, None)
|> declareModuleMember ent.IsPublic (entName + Naming.reflectionSuffix) false
if com.Options.Typescript then
let interfaceDecl = makeInterfaceDecl com ctx ent entName baseExpr
let interfaceDeclaration = ExportNamedDeclaration(interfaceDecl) :> ModuleDeclaration
[interfaceDeclaration; typeDeclaration; reflectionDeclaration]
else
[typeDeclaration; reflectionDeclaration]
[typeDeclaration; reflectionDeclaration]

let transformModuleFunction (com: IBabelCompiler) ctx (info: Fable.MemberInfo) (membName: string) args body =
let args, body, returnType, typeParamDecl =
Expand Down Expand Up @@ -1930,14 +1836,12 @@ module Util =
|]

let transformUnion (com: IBabelCompiler) ctx (ent: Fable.Entity) (entName: string) classMembers =

let tagId = makeTypedIdent (Fable.Number Int32) "tag"
let fieldsId = makeTypedIdent (Fable.Array Fable.Any) "fields"
let fieldIds = getUnionFieldsAsIdents com ctx ent
let args =
[| typedIdent com ctx tagId :> Pattern
typedIdent com ctx fieldsId |> restElement |]
[| typedIdent com ctx fieldIds.[0] :> Pattern
typedIdent com ctx fieldIds.[1] |> restElement |]
let body =
[| tagId; fieldsId |]
fieldIds
|> Array.map (fun id ->
let left = get None thisExpr id.Name
let right =
Expand Down
4 changes: 2 additions & 2 deletions src/fable-library/Option.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,13 @@ export function value<T>(x: Option<T>) {
}
}

export function ofNullable<T>(x: T|null): Option<T> {
export function ofNullable<T>(x: T | null): Option<T> {
// This will fail with unit probably, an alternative would be:
// return x === null ? undefined : (x === undefined ? new Some(x) : x);
return x == null ? undefined : x;
}

export function toNullable<T>(x: Option<T>): T|null {
export function toNullable<T>(x: Option<T>): T | null {
return x == null ? null : value(x);
}

Expand Down
Loading