How to cast an object to a list of generic type in F#

Unfortunately, there’s no easy way to do what you want. Type tests can only be used with specific types, and even if the type test passed, the conversion operator :?> also only works to cast expressions to specific types so the right hand side of your match wouldn’t do what you want anyway. You can partially work around this issue using an active pattern:

open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.Patterns

let ( |GenericType|_| ) =
  (* methodinfo for typedefof<_> *)
  let tdo = 
    let (Call(None,t,[])) = <@ typedefof<_> @>
    t.GetGenericMethodDefinition()
  (* match type t against generic def g *)
  let rec tymatch t (g:Type) =
    if t = typeof<obj> then None
    elif g.IsInterface then
      let ints = if t.IsInterface then [|t|] else t.GetInterfaces()
      ints |> Seq.tryPick (fun t -> if (t.GetGenericTypeDefinition() = g) then Some(t.GetGenericArguments()) else None)
    elif t.IsGenericType && t.GetGenericTypeDefinition() = g then
      Some(t.GetGenericArguments())
    else
      tymatch (t.BaseType) g
  fun (e:Expr<Type>) (t:Type) ->
    match e with
    | Call(None,mi,[]) ->
        if (mi.GetGenericMethodDefinition() = tdo) then
          let [|ty|] = mi.GetGenericArguments()
          if ty.IsGenericType then
            let tydef = ty.GetGenericTypeDefinition()
            tymatch t tydef
          else None
        else
          None
    | _ -> None

This active pattern can be used as follows:

match o.GetType() with
| GenericType <@ typedefof<list<_>> @> [|t|] -> addChildListUntyped(t,o)
| _                                          -> addChild(o)

where you’ve created a variation of addChildList which takes a type t and an object o (with runtime type list<t>) instead of taking a generic list.

This is a bit clunky, but I can’t think of a cleaner solution.

Leave a Comment