Run-time Types
Types can be represented at run-time.
To create a type value, use the constructor function Type<T>(), which accepts the static type as a type argument.
This is similar to e.g. T.self in Swift, T::class/KClass<T> in Kotlin, and T.class/Class<T> in Java.
For example, to represent the type Int at run-time:
_10let intType: Type = Type<Int>()
This works for both built-in and user-defined types. For example, to get the type value for a resource:
_10resource Collectible {}_10_10let collectibleType = Type<@Collectible>()_10_10// `collectibleType` has type `Type`
Type values are comparable.
_10_10Type<Int>() == Type<Int>()_10_10Type<Int>() != Type<String>()
The method fun isSubtype(of: Type): Bool can be used to compare the run-time types of values.
_10Type<Int>().isSubtype(of: Type<Int>()) // true_10_10Type<Int>().isSubtype(of: Type<String>()) // false_10_10Type<Int>().isSubtype(of: Type<Int?>()) // true
To get the run-time type's fully qualified type identifier, use the let identifier: String field:
_10let type = Type<Int>()_10type.identifier  // is "Int"
_10// in account 0x1_10_10struct Test {}_10_10let type = Type<Test>()_10type.identifier  // is "A.0000000000000001.Test"
Getting the Type from a Value
The method fun getType(): Type can be used to get the runtime type of a value.
_10let something = "hello"_10_10let type: Type = something.getType()_10// `type` is `Type<String>()`
This method returns the concrete run-time type of the object, not the static type.
_10// Declare a variable named `something` that has the *static* type `AnyResource`_10// and has a resource of type `Collectible`_10//_10let something: @AnyResource <- create Collectible()_10_10// The resource's concrete run-time type is `Collectible`_10//_10let type: Type = something.getType()_10// `type` is `Type<@Collectible>()`
Constructing a Run-time Type
Run-time types can also be constructed from type identifier strings using built-in constructor functions.
_10fun CompositeType(_ identifier: String): Type?_10fun InterfaceType(_ identifier: String): Type?_10fun RestrictedType(identifier: String?, restrictions: [String]): Type?
Given a type identifier (as well as a list of identifiers for restricting interfaces
in the case of RestrictedType), these functions will look up nominal types and
produce their run-time equivalents. If the provided identifiers do not correspond
to any types, or (in the case of RestrictedType) the provided combination of
identifiers would not type-check statically, these functions will produce nil.
_10struct Test {}_10struct interface I {}_10let type: Type = CompositeType("A.0000000000000001.Test")_10// `type` is `Type<Test>`_10_10let type2: Type = RestrictedType(_10    identifier: type.identifier,_10    restrictions: ["A.0000000000000001.I"]_10)_10// `type2` is `Type<Test{I}>`
Other built-in functions will construct compound types from other run-types.
_10fun OptionalType(_ type: Type): Type_10fun VariableSizedArrayType(_ type: Type): Type_10fun ConstantSizedArrayType(type: Type, size: Int): Type_10fun FunctionType(parameters: [Type], return: Type): Type_10// returns `nil` if `key` is not valid dictionary key type_10fun DictionaryType(key: Type, value: Type): Type?_10// returns `nil` if `type` is not a reference type_10fun CapabilityType(_ type: Type): Type?_10fun ReferenceType(authorized: bool, type: Type): Type
Asserting the Type of a Value
The method fun isInstance(_ type: Type): Bool can be used to check if a value has a certain type,
using the concrete run-time type, and considering subtyping rules,
_19// Declare a variable named `collectible` that has the *static* type `Collectible`_19// and has a resource of type `Collectible`_19//_19let collectible: @Collectible <- create Collectible()_19_19// The resource is an instance of type `Collectible`,_19// because the concrete run-time type is `Collectible`_19//_19collectible.isInstance(Type<@Collectible>())  // is `true`_19_19// The resource is an instance of type `AnyResource`,_19// because the concrete run-time type `Collectible` is a subtype of `AnyResource`_19//_19collectible.isInstance(Type<@AnyResource>())  // is `true`_19_19// The resource is *not* an instance of type `String`,_19// because the concrete run-time type `Collectible` is *not* a subtype of `String`_19//_19collectible.isInstance(Type<String>())  // is `false`
Note that the concrete run-time type of the object is used, not the static type.
_19// Declare a variable named `something` that has the *static* type `AnyResource`_19// and has a resource of type `Collectible`_19//_19let something: @AnyResource <- create Collectible()_19_19// The resource is an instance of type `Collectible`,_19// because the concrete run-time type is `Collectible`_19//_19something.isInstance(Type<@Collectible>())  // is `true`_19_19// The resource is an instance of type `AnyResource`,_19// because the concrete run-time type `Collectible` is a subtype of `AnyResource`_19//_19something.isInstance(Type<@AnyResource>())  // is `true`_19_19// The resource is *not* an instance of type `String`,_19// because the concrete run-time type `Collectible` is *not* a subtype of `String`_19//_19something.isInstance(Type<String>())  // is `false`
For example, this allows implementing a marketplace sale resource:
_67pub resource SimpleSale {_67_67    /// The resource for sale._67    /// Once the resource is sold, the field becomes `nil`._67    ///_67    pub var resourceForSale: @AnyResource?_67_67    /// The price that is wanted for the purchase of the resource._67    ///_67    pub let priceForResource: UFix64_67_67    /// The type of currency that is required for the purchase._67    ///_67    pub let requiredCurrency: Type_67    pub let paymentReceiver: Capability<&{FungibleToken.Receiver}>_67_67    /// `paymentReceiver` is the capability that will be borrowed_67    /// once a valid purchase is made._67    /// It is expected to target a resource that allows depositing the paid amount_67    /// (a vault which has the type in `requiredCurrency`)._67    ///_67    init(_67        resourceForSale: @AnyResource,_67        priceForResource: UFix64,_67        requiredCurrency: Type,_67        paymentReceiver: Capability<&{FungibleToken.Receiver}>_67    ) {_67        self.resourceForSale <- resourceForSale_67        self.priceForResource = priceForResource_67        self.requiredCurrency = requiredCurrency_67        self.paymentReceiver = paymentReceiver_67    }_67_67    destroy() {_67        // When this sale resource is destroyed,_67        // also destroy the resource for sale._67        // Another option could be to transfer it back to the seller._67        destroy self.resourceForSale_67    }_67_67    /// buyObject allows purchasing the resource for sale by providing_67    /// the required funds._67    /// If the purchase succeeds, the resource for sale is returned._67    /// If the purchase fails, the program aborts._67    ///_67    pub fun buyObject(with funds: @FungibleToken.Vault): @AnyResource {_67        pre {_67            // Ensure the resource is still up for sale_67            self.resourceForSale != nil: "The resource has already been sold"_67            // Ensure the paid funds have the right amount_67            funds.balance >= self.priceForResource: "Payment has insufficient amount"_67            // Ensure the paid currency is correct_67            funds.isInstance(self.requiredCurrency): "Incorrect payment currency"_67        }_67_67        // Transfer the paid funds to the payment receiver_67        // by borrowing the payment receiver capability of this sale resource_67        // and depositing the payment into it_67_67        let receiver = self.paymentReceiver.borrow()_67            ?? panic("failed to borrow payment receiver capability")_67_67        receiver.deposit(from: <-funds)_67        let resourceForSale <- self.resourceForSale <- nil_67        return <-resourceForSale_67    }_67}