@@ -1156,20 +1156,15 @@ module TypeHelpers =
11561156 | Choice1Of2 t -> t
11571157 | Choice2Of2 fullName -> makeRuntimeTypeWithMeasure genArgs fullName
11581158 | _ ->
1159- let mkDeclType () =
1160- Fable.DeclaredType( FsEnt.Ref tdef, makeTypeGenArgsWithConstraints withConstraints ctxTypeArgs genArgs)
1161- // Emit attribute
1162- if tdef.Attributes |> hasAttribute Atts.emitAttr then
1163- mkDeclType ()
1164- else
1165- // other special attributes
1166- tdef.Attributes |> tryPickAttribute [
1167- Atts.stringEnum, Fable.String
1168- Atts.erase, Fable.Any
1169- Atts.tsTaggedUnion, Fable.Any
1170- ]
1171- // Rest of declared types
1172- |> Option.defaultWith mkDeclType
1159+ let transformAttrs =
1160+ match Compiler.Language with
1161+ | Language.JavaScript | Language.TypeScript -> [ Atts.stringEnum, Fable.String ]
1162+ // Other languages can type erased unions too after fixing tests
1163+ | _ -> [ Atts.stringEnum, Fable.String; Atts.erase, Fable.Any ]
1164+ tdef.Attributes
1165+ |> tryPickAttribute transformAttrs
1166+ |> Option.defaultWith ( fun () ->
1167+ Fable.DeclaredType( FsEnt.Ref tdef, makeTypeGenArgsWithConstraints withConstraints ctxTypeArgs genArgs))
11731168
11741169 let rec makeTypeWithConstraints withConstraints ( ctxTypeArgs : Map < string , Fable.Type >) ( NonAbbreviatedType t ) =
11751170 // Generic parameter (try to resolve for inline functions)
@@ -1262,22 +1257,10 @@ module TypeHelpers =
12621257 /// Enums in F# are uint32
12631258 /// -> Allow into all int & uint
12641259 | EnumIntoInt = 0b0001
1265- /// Erased Unions are reduced to `Any`
1266- /// -> Cannot distinguish between 'normal' Any (like `obj`) and Erased Union (like Erased Union with string field)
1267- ///
1268- /// For interface members the FSharp Type is available
1269- /// -> `Ux<...>` receive special treatment and its types are extracted
1270- /// -> `abstract Value: U2<int,string>` -> extract `int` & `string`
1271- /// BUT: for Expressions in Anon Records that's not possible, and `U2<int,string>` is only recognized as `Any`
1272- /// -> `{| Value = v |}`: `v: int` and `v: string` are recognized as matching,
1273- /// but `v: U2<int,string>` isn't: only `Any`/`obj` as Type available
1274- /// To recognize as matching, we must allow all `Any` expressions for `U2` in interface place.
1275- ///
1276- /// Note: Only `Ux<...>` are currently handled (on interface side), not other Erased Unions!
1277- | AnyIntoErased = 0b0010
1278- /// Unlike `AnyIntoErased`, this allows all expressions of type `Any` in all interface properties.
1279- /// (The other way is always allow: Expression of all Types fits into `Any`)
1280- | AlwaysAny = 0b0100
1260+
1261+ // We could try to identify all erased unions (without tag) instead of only handling Fable.Core.Ux ones
1262+ // but it's more complex because we cannot simply extra the alternative types from the generics
1263+ let ERASED_UNION = Regex( @" ^Fable\.Core\.U\d+`\d+$" )
12811264
12821265 let fitsAnonRecordInInterface
12831266 ( _com : IFableCompiler )
@@ -1293,60 +1276,20 @@ module TypeHelpers =
12931276 getAllInterfaceMembers interface_
12941277 |> Seq.toList
12951278
1296- let makeType = makeType Map.empty
12971279 /// Returns for:
12981280 /// * `Ux<...>`: extracted types from `<....>`: `U2<string,int>` -> `[String; Int]`
12991281 /// * `Option<Ux<...>>`: extracted types from `<...>`, then made Optional: `Option<U2<string,int>>` -> `[Option String; Option Int]`
13001282 /// * 'normal' type: `makeType`ed type: `string` -> `[String]`
1301- /// Note: Erased Unions (except handled `Ux<...>`) are reduced to `Any`
1302- ///
1303- /// Extracting necessary: Erased Unions are reduced to `Any` -> special handling for `Ux<...>`
13041283 ///
13051284 /// Note: nested types aren't handled: `U2<string, U<int, float>>` -> `[Int; Any]`
13061285 let rec collectTypes ( ty : FSharpType ) : Fable.Type list =
13071286 // Special treatment for Ux<...> and Option<Ux<...>>: extract types in Ux
1308- // This is necessary because: `makeType` reduces Erased Unions (including Ux) to `Any` -> no type info any more
1309- //
13101287 // Note: no handling of nested types: `U2<string, U<int, float>>` -> `int` & `float` don't get extract
1311- match ty with
1312- | UType tys ->
1313- tys
1314- |> List.map makeType
1315- |> List.distinct
1316- | OptionType ( UType tys, isStruct) ->
1317- tys
1318- |> List.map ( fun t -> Fable.Option( makeType t, isStruct))
1319- |> List.distinct
1320- | _ ->
1321- makeType ty
1322- |> List.singleton
1323- and (| OptionType | _ |) ( ty : FSharpType ) =
1324- match ty with
1325- | TypeDefinition tdef ->
1326- match FsEnt.FullName tdef with
1327- | Types.valueOption -> Some( ty.GenericArguments[ 0 ], true )
1328- | Types.option -> Some( ty.GenericArguments[ 0 ], false )
1329- | _ -> None
1330- | _ -> None
1331- and (| UType | _ |) ( ty : FSharpType ) =
1332- let (| UName | _ |) ( tdef : FSharpEntity ) =
1333- if
1334- tdef.Namespace = Some " Fable.Core"
1335- &&
1336- (
1337- let name = tdef.DisplayName
1338- name.Length = 2 && name[ 0 ] = 'U' && Char.IsDigit name[ 1 ]
1339- )
1340- then
1341- Some ()
1342- else
1343- None
1344- match ty with
1345- | TypeDefinition UName ->
1346- ty.GenericArguments
1347- |> Seq.toList
1348- |> Some
1349- | _ -> None
1288+ match makeType Map.empty ty with
1289+ | Fable.DeclaredType({ FullName = Naming.Regex ERASED_ UNION _ }, genArgs) -> genArgs
1290+ | Fable.Option( Fable.DeclaredType({ FullName = Naming.Regex ERASED_ UNION _ }, genArgs), isStruct) ->
1291+ genArgs |> List.map ( fun t -> Fable.Option( t, isStruct))
1292+ | t -> [ t]
13501293
13511294 /// Special Rules mostly for Indexers:
13521295 /// For direct interface member implementation we want to be precise (-> exact_ish match)
@@ -1358,14 +1301,10 @@ module TypeHelpers =
13581301 function
13591302 | Fable.Number(( Int8 | UInt8 | Int16 | UInt16 | Int32 | UInt32), _) -> Some ()
13601303 | _ -> None
1304+
13611305 let fitsIntoSingle ( rules : Allow ) ( expected : Fable.Type ) ( actual : Fable.Type ) =
13621306 match expected, actual with
13631307 | Fable.Any, _ -> true
1364- | _, Fable.Any when rules.HasFlag Allow.AlwaysAny ->
1365- // Erased Unions are reduced to `Any`
1366- // -> cannot distinguish between 'normal' Any (like 'obj')
1367- // and Erased Union (like Erased Union with string field)
1368- true
13691308 | IntNumber, Fable.Number(_, Fable.NumberInfo.IsEnum _) when rules.HasFlag Allow.EnumIntoInt ->
13701309 // the underlying type of enum in F# is uint32
13711310 // For practicality: allow in all uint & int fields
@@ -1374,22 +1313,14 @@ module TypeHelpers =
13741313 | Fable.Option( t1,_), t2
13751314 | t1, t2 ->
13761315 typeEquals false t1 t2
1316+
13771317 let fitsIntoMulti ( rules : Allow ) ( expected : Fable.Type list ) ( actual : Fable.Type ) =
13781318 expected |> List.contains Fable.Any
1379- ||
1380- (
1381- // special treatment for actual=Any & multiple expected:
1382- // multiple expected -> `Ux<...>` -> extracted types
1383- // BUT: in actual that's not possible -> in actual `Ux<...>` = `Any`
1384- // -> no way to distinguish Ux (or other Erased Unions) from 'normal` Any (like obj)
1385- rules.HasFlag Allow.AnyIntoErased
1386- &&
1387- expected |> List.isMultiple
1388- &&
1389- actual = Fable.Any
1390- )
1391- ||
1392- expected |> List.exists ( fun expected -> fitsIntoSingle rules expected actual)
1319+ || ( match actual with
1320+ | Fable.DeclaredType({ FullName = Naming.Regex ERASED_ UNION _ }, actual) when List.sameLength expected actual ->
1321+ List.zip expected actual |> List.forall ( fun ( expected , actual ) -> fitsIntoSingle rules expected actual)
1322+ | _ -> false )
1323+ || expected |> List.exists ( fun expected -> fitsIntoSingle rules expected actual)
13931324
13941325 fitsIntoMulti rules expected actual
13951326
@@ -1460,10 +1391,10 @@ module TypeHelpers =
14601391 | [] -> unreachable ()
14611392 | [ expectedType] ->
14621393 let expectedType = expectedType |> formatType
1463- $" Expected type '{expectedType}' for field '{fieldName}' because of Indexer '{indexerName}' in interface '{interfaceName}', but is '{actualType}'"
1394+ $" Expected type '{expectedType}' for field '{fieldName}' because of indexer '{indexerName}' in interface '{interfaceName}', but is '{actualType}'"
14641395 | _ ->
14651396 let expectedTypes = expectedTypes |> formatTypes
1466- $" Expected any type of [{expectedTypes}] for field '{fieldName}' because of Indexer '{indexerName}' in interface '{interfaceName}', but is '{actualType}'"
1397+ $" Expected any type of [{expectedTypes}] for field '{fieldName}' because of indexer '{indexerName}' in interface '{interfaceName}', but is '{actualType}'"
14671398 | _ ->
14681399 let indexerNames =
14691400 indexers
@@ -1473,10 +1404,10 @@ module TypeHelpers =
14731404 | [] -> unreachable ()
14741405 | [ expectedType] ->
14751406 let expectedType = expectedType |> formatType
1476- $" Expected type '{expectedType}' for field '{fieldName}' because of Indexers [{indexerNames}] in interface '{interfaceName}', but is '{actualType}'"
1407+ $" Expected type '{expectedType}' for field '{fieldName}' because of indexers [{indexerNames}] in interface '{interfaceName}', but is '{actualType}'"
14771408 | _ ->
14781409 let expectedTypes = expectedTypes |> formatTypes
1479- $" Expected any type of [{expectedTypes}] for field '{fieldName}' because of Indexers [{indexerNames}] in interface '{interfaceName}', but is '{actualType}'"
1410+ $" Expected any type of [{expectedTypes}] for field '{fieldName}' because of indexers [{indexerNames}] in interface '{interfaceName}', but is '{actualType}'"
14801411
14811412 let r = r |> Option.orElse range // fall back to anon record range
14821413
@@ -1504,7 +1435,7 @@ module TypeHelpers =
15041435 | Some i ->
15051436 let expr = List.item i argExprs
15061437 let ty = expr.Type
1507- if ty |> fitsInto ( Allow.TheUsual ||| Allow.AnyIntoErased ) expectedTypes then
1438+ if ty |> fitsInto Allow.TheUsual expectedTypes then
15081439 None
15091440 else
15101441 formatUnexpectedTypeError None m.DisplayName expectedTypes ty expr.Range
@@ -1537,7 +1468,7 @@ module TypeHelpers =
15371468 |> List.filter ( fun ( fieldName , _ ) -> fieldsToIgnore |> Set.contains fieldName |> not )
15381469 |> List.choose ( fun ( name , expr ) ->
15391470 let ty = expr.Type
1540- if fitsInto ( Allow.TheUsual ||| Allow.EnumIntoInt ||| Allow.AnyIntoErased ) validTypes ty then
1471+ if fitsInto ( Allow.TheUsual ||| Allow.EnumIntoInt) validTypes ty then
15411472 None
15421473 else
15431474 formatUnexpectedTypeError ( Some indexers) name validTypes ty expr.Range
@@ -1804,13 +1735,13 @@ module Util =
18041735 let isErasedOrStringEnumEntity ( ent : Fable.Entity ) =
18051736 ent.Attributes |> Seq.exists ( fun att ->
18061737 match att.Entity.FullName with
1807- | Atts.erase | Atts.stringEnum | Atts.tsTaggedUnion -> true
1738+ | Atts.erase | Atts.stringEnum | Atts.tsTaggedUnion | Atts.emit -> true
18081739 | _ -> false )
18091740
18101741 let isErasedOrStringEnumFSharpEntity ( ent : FSharpEntity ) =
18111742 ent.Attributes |> Seq.exists ( fun att ->
18121743 match ( nonAbbreviatedDefinition att.AttributeType) .TryFullName with
1813- | Some( Atts.erase | Atts.stringEnum | Atts.tsTaggedUnion) -> true
1744+ | Some( Atts.erase | Atts.stringEnum | Atts.tsTaggedUnion | Atts.emit ) -> true
18141745 | _ -> false )
18151746
18161747 let isGlobalOrImportedEntity ( ent : Fable.Entity ) =
0 commit comments