It's not clear to me what are you trying to achieve. Are you trying to restrict the wrapped types to Int, String and Float?
1) If so you can check at runtime like this:
[<Struct>]
type Wrapper<'T> =
val raw:'T
new(v:'T) = {raw = v}
let wrap x =
match box x with
| :? float -> ()
| :? int -> ()
| :? string -> ()
| _ -> failwith "invalid type"
Wrapper x
let a = wrap 90
let b = wrap "hi"
let c = wrap true // fails at runtime
2) If you want to restrict at compile-time an easy way is to add static members as constructors:
[<Struct>]
type Wrapper<'T> =
val raw:'T
private new(v:'T) = {raw = v}
with
static member wrap (x:float) = Wrapper x
static member wrap (x:int) = Wrapper x
static member wrap (x:string) = Wrapper x
let a = Wrapper<_>.wrap 90
let b = Wrapper<_>.wrap "hi"
let c = Wrapper<_>.wrap true // doesn't compile
3) Or may be, using the DU inside the wrapper makes more sense for you:
type Value =
| Float of float
| Int of int
| String of string
[<Struct>]
type Wrapper =
val raw:Value
new(v:Value) = {raw = v}
Of all solutions 3) is the only one that really restricts your wrapper. Solutions 1) and 2) restrict the way you construct it.
From 2) you can use some tricks with statically resolved type parameters in order to come up with an inline function (not a method) that will wrap only on those types. Still that will not constraint the type itself, but since the constructor is private the code that consumes your type will be forced through your restricted constructors.
Statically resolved type parameters work with functions or methods but not on types, since they are a compile time F# feature, not a .NET type system feature.