Header menu logo FSharp.TypeProviders.SDK

Providing Types, Members, and Features

Static Parameters

Static parameters let consumers specialise a type at compile time:

type MyParameterisedProvider(config: TypeProviderConfig) as this =
    inherit TypeProviderForNamespaces(config)

    let ns  = "MyProvider.Provided"
    let asm = Assembly.GetExecutingAssembly()

    let createType typeName (count: int) =
        let t = ProvidedTypeDefinition(asm, ns, typeName, Some typeof<obj>)
        for i in 1 .. count do
            let prop =
                ProvidedProperty(
                    "Property" + string i,
                    typeof<int>,
                    isStatic = true,
                    getterCode = fun _args -> <@@ i @@>)
            t.AddMember prop
        t

    do
        let containerType = ProvidedTypeDefinition(asm, ns, "Schema", Some typeof<obj>)
        containerType.DefineStaticParameters(
            [ ProvidedStaticParameter("Count", typeof<int>) ],
            fun typeName args -> createType typeName (args.[0] :?> int))
        this.AddNamespace(ns, [containerType])

Usage in user code:

type MySchema = MyProvider.Provided.Schema<Count = 5>
printfn "%d" MySchema.Property3   // 3

Optional Static Parameters with Defaults

containerType.DefineStaticParameters(
    [ ProvidedStaticParameter("Count", typeof<int>)
      ProvidedStaticParameter("Prefix", typeof<string>, parameterDefaultValue = "Col") ],
    fun typeName args ->
        let count  = args.[0] :?> int
        let prefix = args.[1] :?> string
        createType typeName count prefix)

Warning: if all static parameters have defaults the SDK emits a compiler warning to alert you that the unapplied base type would be the same as the applied type with defaults. This is intentional behaviour. Test coverage: ErasingProviderWithStaticParams in tests/BasicErasedProvisionTests.fs; GenerativePropertyProviderWithStaticParams in tests/BasicGenerativeProvisionTests.fs.


Quotations in Type Providers

Quotation literals (<@@ ... @@>) are the primary way to specify the runtime behaviour of erased members. The F# compiler splices the quotation body at the call site.

// Access the 'this' parameter of an instance getter
let prop =
    ProvidedProperty("Count", typeof<int>,
        getterCode = fun args ->
            // args.[0] is 'this', typed as the erased base type
            <@@ (%%(args.[0]) :> obj :?> System.Collections.Generic.List<int>).Count @@>)

For cross-targeting rules and UncheckedQuotations see Technical Notes.


Your First Generative Type Provider

type MyGenerativeProvider(config: TypeProviderConfig) as this =
    inherit TypeProviderForNamespaces(
        config,
        assemblyReplacementMap = [("MyProvider.DesignTime", "MyProvider.Runtime")])

    let ns  = "MyProvider.Provided"
    let asm = Assembly.GetExecutingAssembly()

    let createType typeName (count: int) =
        let provAsm = ProvidedAssembly()
        let myType  = ProvidedTypeDefinition(provAsm, ns, typeName, Some typeof<obj>, isErased = false)

        let ctor = ProvidedConstructor([], invokeCode = fun _args -> <@@ () @@>)
        myType.AddMember ctor

        for i in 1 .. count do
            let prop =
                ProvidedProperty(
                    "Property" + string i, typeof<int>,
                    getterCode = fun _args -> <@@ i @@>)
            myType.AddMember prop

        provAsm.AddTypes [myType]
        myType

    do
        let containerType = ProvidedTypeDefinition(asm, ns, "GenerativeSchema", Some typeof<obj>, isErased = false)
        containerType.DefineStaticParameters(
            [ ProvidedStaticParameter("Count", typeof<int>) ],
            fun typeName args -> createType typeName (args.[0] :?> int))
        this.AddNamespace(ns, [containerType])

Key differences from erased providers:

Test coverage: GenerativePropertyProviderWithStaticParams in tests/BasicGenerativeProvisionTests.fs.


Providing Members in Depth

Constructors

// Default constructor
let ctor0 = ProvidedConstructor([], invokeCode = fun _args -> <@@ () @@>)

// Constructor with parameters (generative: args.[0] is 'this', args.[1] is first param)
let ctor1 =
    ProvidedConstructor(
        [ ProvidedParameter("value", typeof<int>) ],
        invokeCode = fun args -> <@@ ignore (%%(args.[1]) : int) @@>)

// Static constructor (type initialiser)
let cctor =
    ProvidedConstructor(
        [], invokeCode = fun _args -> <@@ () @@>,
        IsTypeInitializer = true)

For erased providers, args.[0] is the first explicit parameter (there is no this expression). For generative providers, args.[0] is this.

Events

let myEvent =
    ProvidedEvent(
        "DataChanged",
        typeof<System.EventHandler>,
        adderCode   = fun args -> <@@ ignore (%%(args.[1]) : System.EventHandler) @@>,
        removerCode = fun args -> <@@ ignore (%%(args.[1]) : System.EventHandler) @@>)
myType.AddMember myEvent

Test coverage: GenerativePropertyProviderWithStaticParams in tests/BasicGenerativeProvisionTests.fs.

Fields (Generative Only)

// Regular field
let field = ProvidedField("_count", typeof<int>)
myType.AddMember field

// Literal / enum constant
let litField = ProvidedField.Literal("MaxSize", typeof<int>, 100)
myType.AddMember litField

Test coverage: GenerativeEnumsProvisionTests.fs.

Delayed Member Generation

For large schemas it can be expensive to generate all members eagerly. Use AddMembersDelayed to defer generation until the compiler actually needs them:

myType.AddMembersDelayed(fun () ->
    [ for col in schema.Columns ->
        ProvidedProperty(col.Name, col.Type,
            getterCode = fun args -> <@@ fetchColumn (%%(args.[0]) : obj) col.Name @@>) ])

Interfaces (Generative)

// Declare the interface
let iface =
    ProvidedTypeDefinition("IContract", None, isErased = false, isInterface = true)
let meth = ProvidedMethod("Execute", [], typeof<unit>)
meth.AddMethodAttrs(MethodAttributes.Virtual ||| MethodAttributes.Abstract)
iface.AddMember meth

// Implement the interface on a generative type
myType.AddInterfaceImplementation iface
let impl = ProvidedMethod("Execute", [], typeof<unit>,
               invokeCode = fun _args -> <@@ () @@>)
myType.AddMember impl
myType.DefineMethodOverride(impl, iface.GetMethod("Execute"))

provAsm.AddTypes [iface; myType]

Test coverage: GenerativeInterfacesTests.fs.


Enumerations (Generative)

let enumType = ProvidedTypeDefinition("Status", Some typeof<Enum>, isErased = false)
enumType.SetEnumUnderlyingType(typeof<int>)

for (name, value) in [ "Active", 1; "Inactive", 2; "Pending", 3 ] do
    let field = ProvidedField.Literal(name, enumType, value)
    enumType.AddMember field

provAsm.AddTypes [enumType]

Test coverage: GenerativeEnumsProvisionTests.fs.


Nested Types

Both erased and generative providers support nested types:

// Erased nested type
let outerType = ProvidedTypeDefinition(asm, ns, "Outer", Some typeof<obj>)
let innerType = ProvidedTypeDefinition("Inner", Some typeof<obj>)
innerType.AddMember(ProvidedProperty("Value", typeof<int>, isStatic = true,
    getterCode = fun _args -> <@@ 42 @@>))
outerType.AddMember innerType

For generative providers, nested types must also be registered with the ProvidedAssembly using AddNestedTypes:

provAsm.AddNestedTypes([innerType], ["Outer"])

XML Documentation

Use AddXmlDoc to supply IntelliSense documentation for any provided member:

let myProp = ProvidedProperty("Status", typeof<string>, ...)
myProp.AddXmlDoc "Gets the current connection status."

// Delayed – evaluated only when IntelliSense requests it
myProp.AddXmlDocDelayed(fun () ->
    sprintf "Gets the '%s' column value." columnName)

Custom Attributes

Attach arbitrary custom attributes to provided members using AddCustomAttribute:

open System
open System.Reflection

// Mark a type as [<Obsolete("use NewType instead")>]
myType.AddCustomAttribute {
    new CustomAttributeData() with
        member _.Constructor = typeof<ObsoleteAttribute>.GetConstructor([| typeof<string> |])
        member _.ConstructorArguments =
            [| CustomAttributeTypedArgument(typeof<string>, "use NewType instead" :> obj) |] :> _
        member _.NamedArguments = [||] :> _
}

// Mark a parameter as [<ReflectedDefinition>]
let param = ProvidedParameter("expr", typeof<Microsoft.FSharp.Quotations.Expr<int>>)
param.AddCustomAttribute {
    new CustomAttributeData() with
        member _.Constructor = typeof<ReflectedDefinitionAttribute>.GetConstructor([||])
        member _.ConstructorArguments = [||] :> _
        member _.NamedArguments = [||] :> _
}

AddCustomAttribute is available on ProvidedTypeDefinition, ProvidedMethod, ProvidedProperty, and ProvidedParameter. ProvidedConstructor does not support it; use AddObsoleteAttribute for the common case of marking a constructor as obsolete.

When using cross-targeting (the normal mode), custom attributes whose types exist in the target reference assemblies will appear in GetCustomAttributesData() on the translated (target) type. Attributes whose types are not present in the target assemblies are silently omitted from the cross-targeted model.

Test coverage: GenerativeEnumsProvisionTests.fs (enum field attributes); tests/BasicErasedProvisionTests.fs (parameter custom attributes in the NameOf example).


Units of Measure

See Units of Measure in F# Type Providers for SI units, compound units, custom units, and multi-argument annotation.


Non-Nullability and HideObjectMethods

nonNullable

Set nonNullable = true when constructing a ProvidedTypeDefinition to mark the type with [<AllowNullLiteral(false)>]. This prevents the F# null literal from being used with the provided type, which is the standard F# behaviour for non-nullable reference types.

let myType = ProvidedTypeDefinition(asm, ns, "Row", Some typeof<obj>, nonNullable = true)
// myType.NonNullable = true
// myType.GetCustomAttributesData() contains AllowNullLiteralAttribute

The NonNullable property can be read back to check whether the flag was set.

hideObjectMethods

Set hideObjectMethods = true to suppress the standard .NET Object methods (ToString, GetHashCode, Equals) from appearing in IntelliSense for instances of the provided type. This is useful for simple record-like or data types where the inherited methods add noise.

let myType =
    ProvidedTypeDefinition(asm, ns, "Connection", Some typeof<obj>, hideObjectMethods = true)
// myType.HideObjectMethods = true

Abstract Classes and Base Constructors (Generative)

let baseType =
    ProvidedTypeDefinition(provAsm, ns, "AnimalBase", Some typeof<obj>,
        isErased = false, isAbstract = true, isSealed = false)

// Implicit constructor (fields become accessible with Expr.GlobalVar)
let baseCtor =
    ProvidedConstructor(
        [ ProvidedParameter("name", typeof<string>) ],
        invokeCode = fun _args -> <@@ () @@>,
        IsImplicitConstructor = true)
baseType.AddMember baseCtor

// Derived type
let derivedType =
    ProvidedTypeDefinition(provAsm, ns, "Dog", Some (baseType :> System.Type), isErased = false)
let derivedCtor =
    ProvidedConstructor(
        [ ProvidedParameter("name", typeof<string>) ],
        invokeCode = fun args -> <@@ () @@>)
derivedCtor.BaseConstructorCall <- fun args -> (baseCtor :> System.Reflection.ConstructorInfo), [args.[1]]
derivedType.AddMember derivedCtor

provAsm.AddTypes [baseType; derivedType]

Test coverage: GenerativeAbstractClassesTests.fs.

Multiple items
type MyParameterisedProvider = new: config: obj -> MyParameterisedProvider

--------------------
new: config: obj -> MyParameterisedProvider
val config: obj
val this: MyParameterisedProvider
val ns: string
val asm: obj
val createType: typeName: 'a -> count: int -> 'b
val typeName: 'a
val count: int
Multiple items
val int: value: 'T -> int (requires member op_Explicit)

--------------------
type int = int32

--------------------
type int<'Measure> = int
val t: 'b
union case Option.Some: Value: 'T -> Option<'T>
val typeof<'T> : System.Type
type obj = System.Object
val i: int32
val prop: obj
Multiple items
val string: value: 'T -> string

--------------------
type string = System.String
val containerType: obj
type MySchema = obj
val printfn: format: Printf.TextWriterFormat<'T> -> 'T
namespace System
namespace System.Collections
namespace System.Collections.Generic
Multiple items
type List<'T> = interface ICollection<'T> interface IEnumerable<'T> interface IEnumerable interface IList<'T> interface IReadOnlyCollection<'T> interface IReadOnlyList<'T> interface ICollection interface IList new: unit -> unit + 2 overloads member Add: item: 'T -> unit ...
<summary>Represents a strongly typed list of objects that can be accessed by index. Provides methods to search, sort, and manipulate lists.</summary>
<typeparam name="T">The type of elements in the list.</typeparam>


--------------------
System.Collections.Generic.List() : System.Collections.Generic.List<'T>
System.Collections.Generic.List(collection: System.Collections.Generic.IEnumerable<'T>) : System.Collections.Generic.List<'T>
System.Collections.Generic.List(capacity: int) : System.Collections.Generic.List<'T>
Multiple items
type MyGenerativeProvider = new: config: obj -> MyGenerativeProvider

--------------------
new: config: obj -> MyGenerativeProvider
val this: MyGenerativeProvider
val provAsm: obj
val myType: 'b
val ctor: obj
val ctor0: obj
val ctor1: obj
val ignore: value: 'T -> unit
val cctor: obj
val myEvent: obj
Multiple items
type EventHandler<'TEventArgs> = new: object: obj * method: nativeint -> unit member BeginInvoke: sender: obj * e: 'TEventArgs * callback: AsyncCallback * object: obj -> IAsyncResult member EndInvoke: result: IAsyncResult -> unit member Invoke: sender: obj * e: 'TEventArgs -> unit
<summary>Represents the method that will handle an event when the event provides data.</summary>
<param name="sender">The source of the event.</param>
<param name="e">An object that contains the event data.</param>
<typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>


--------------------
type EventHandler = new: object: obj * method: nativeint -> unit member BeginInvoke: sender: obj * e: EventArgs * callback: AsyncCallback * object: obj -> IAsyncResult member EndInvoke: result: IAsyncResult -> unit member Invoke: sender: obj * e: EventArgs -> unit
<summary>Represents the method that will handle an event that has no event data.</summary>
<param name="sender">The source of the event.</param>
<param name="e">An object that contains no event data.</param>
val field: obj
val litField: obj
val iface: obj
union case Option.None: Option<'T>
val meth: obj
type unit = Unit
val impl: obj
val enumType: obj
val name: string
val value: int
val outerType: obj
val innerType: obj
val myProp: obj
val sprintf: format: Printf.StringFormat<'T> -> 'T
namespace System.Reflection
type CustomAttributeData = member Equals: obj: obj -> bool member GetHashCode: unit -> int member ToString: unit -> string static member GetCustomAttributes: target: Assembly -> IList<CustomAttributeData> + 3 overloads member AttributeType: Type member Constructor: ConstructorInfo member ConstructorArguments: IList<CustomAttributeTypedArgument> member NamedArguments: IList<CustomAttributeNamedArgument>
<summary>Provides access to custom attribute data for assemblies, modules, types, members and parameters that are loaded into the reflection-only context.</summary>
val typeof<'T> : Type
Multiple items
type ObsoleteAttribute = inherit Attribute new: unit -> unit + 2 overloads member DiagnosticId: string member IsError: bool member Message: string member UrlFormat: string
<summary>Marks the program elements that are no longer in use. This class cannot be inherited.</summary>

--------------------
ObsoleteAttribute() : ObsoleteAttribute
ObsoleteAttribute(message: string) : ObsoleteAttribute
ObsoleteAttribute(message: string, error: bool) : ObsoleteAttribute
Multiple items
val string: value: 'T -> string

--------------------
type string = String
Multiple items
[<Struct>] type CustomAttributeTypedArgument = new: value: obj -> unit + 1 overload member Equals: obj: obj -> bool + 1 overload member GetHashCode: unit -> int member ToString: unit -> string static member (<>) : left: CustomAttributeTypedArgument * right: CustomAttributeTypedArgument -> bool static member (=) : left: CustomAttributeTypedArgument * right: CustomAttributeTypedArgument -> bool member ArgumentType: Type member Value: obj
<summary>Represents an argument of a custom attribute in the reflection-only context, or an element of an array argument.</summary>

--------------------
CustomAttributeTypedArgument ()
CustomAttributeTypedArgument(value: obj) : CustomAttributeTypedArgument
CustomAttributeTypedArgument(argumentType: Type, value: obj) : CustomAttributeTypedArgument
type obj = Object
val param: obj
namespace Microsoft
namespace Microsoft.FSharp
namespace Microsoft.FSharp.Quotations
Multiple items
type Expr = override Equals: obj: objnull -> bool member GetFreeVars: unit -> Var seq member Substitute: substitution: (Var -> Expr option) -> Expr member ToString: full: bool -> string static member AddressOf: target: Expr -> Expr static member AddressSet: target: Expr * value: Expr -> Expr static member Application: functionExpr: Expr * argument: Expr -> Expr static member Applications: functionExpr: Expr * arguments: Expr list list -> Expr static member Call: methodInfo: MethodInfo * arguments: Expr list -> Expr + 1 overload static member CallWithWitnesses: methodInfo: MethodInfo * methodInfoWithWitnesses: MethodInfo * witnesses: Expr list * arguments: Expr list -> Expr + 1 overload ...

--------------------
type Expr<'T> = inherit Expr member Raw: Expr with get
Multiple items
type ReflectedDefinitionAttribute = inherit Attribute new: unit -> ReflectedDefinitionAttribute + 1 overload member IncludeValue: bool with get

--------------------
new: unit -> ReflectedDefinitionAttribute
new: includeValue: bool -> ReflectedDefinitionAttribute
val myType: obj
val baseType: obj
val baseCtor: obj
val derivedType: obj
type Type = inherit MemberInfo interface IReflect member Equals: o: obj -> bool + 1 overload member FindInterfaces: filter: TypeFilter * filterCriteria: obj -> Type array member FindMembers: memberType: MemberTypes * bindingAttr: BindingFlags * filter: MemberFilter * filterCriteria: obj -> MemberInfo array member GetArrayRank: unit -> int member GetConstructor: bindingAttr: BindingFlags * binder: Binder * callConvention: CallingConventions * types: Type array * modifiers: ParameterModifier array -> ConstructorInfo + 3 overloads member GetConstructors: unit -> ConstructorInfo array + 1 overload member GetDefaultMembers: unit -> MemberInfo array override GetElementType: unit -> Type ...
<summary>Represents type declarations: class types, interface types, array types, value types, enumeration types, type parameters, generic type definitions, and open or closed constructed generic types.</summary>
val derivedCtor: obj
type ConstructorInfo = inherit MethodBase member Equals: obj: obj -> bool member GetHashCode: unit -> int member Invoke: parameters: obj array -> obj + 1 overload static member (<>) : left: ConstructorInfo * right: ConstructorInfo -> bool static member (=) : left: ConstructorInfo * right: ConstructorInfo -> bool static val ConstructorName: string static val TypeConstructorName: string member MemberType: MemberTypes
<summary>Discovers the attributes of a class constructor and provides access to constructor metadata.</summary>

Type something to start searching.