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:
1let intType: Type = Type<Int>()
This works for both built-in and user-defined types. For example, to get the type value for a resource:
1resource Collectible {}23let collectibleType = Type<@Collectible>()45// `collectibleType` has type `Type`
Type values are comparable.
1Type<Int>() == Type<Int>()23Type<Int>() != Type<String>()
The method fun isSubtype(of otherType: Type): Bool
can be used to compare the run-time types of values.
1Type<Int>().isSubtype(of: Type<Int>()) // true23Type<Int>().isSubtype(of: Type<String>()) // false45Type<Int>().isSubtype(of: Type<Int?>()) // true
To get the run-time type's fully qualified type identifier, use the let identifier: String
field:
1let type = Type<Int>()2type.identifier // is "Int"
1// in account 0x123struct Test {}45let type = Type<Test>()6type.identifier // is "A.0000000000000001.Test"
The method fun getType(): Type
can be used to get the runtime type of a value.
1let something = "hello"23let type: Type = something.getType()4// `type` is `Type<String>()`
This method returns the concrete run-time type of the object, not the static type.
1// Declare a variable named `something` that has the *static* type `AnyResource`2// and has a resource of type `Collectible`3//4let something: @AnyResource <- create Collectible()56// The resource's concrete run-time type is `Collectible`7//8let type: Type = something.getType()9// `type` is `Type<@Collectible>()`
Run-time types can also be constructed from type identifier strings using built-in constructor functions.
1fun CompositeType(_ identifier: String): Type?2fun InterfaceType(_ identifier: String): Type?3fun RestrictedType(identifier: String?, restrictions: [String]): Type?
Given a type identifer (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
.
1struct Test {}2struct interface I {}3let type: Type = CompositeType("A.0000000000000001.Test")4// `type` is `Type<Test>`56let type2: Type = RestrictedType(7identifier: type.identifier,8restrictions: ["A.0000000000000001.I"]9)10// `type2` is `Type<Test{I}>`
Other built-in functions will construct compound types from other run-types.
1fun OptionalType(_ type: Type): Type2fun VariableSizedArrayType(_ type: Type): Type3fun ConstantSizedArrayType(type: Type, size: Int): Type4fun FunctionType(parameters: [Type], return: Type): Type5// returns `nil` if `key` is not valid dictionary key type6fun DictionaryType(key: Type, value: Type): Type?7// returns `nil` if `type` is not a reference type8fun CapabilityType(_ type: Type): Type?9fun ReferenceType(authorized: bool, type: Type): Type
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,
1// Declare a variable named `collectible` that has the *static* type `Collectible`2// and has a resource of type `Collectible`3//4let collectible: @Collectible <- create Collectible()56// The resource is an instance of type `Collectible`,7// because the concrete run-time type is `Collectible`8//9collectible.isInstance(Type<@Collectible>()) // is `true`1011// The resource is an instance of type `AnyResource`,12// because the concrete run-time type `Collectible` is a subtype of `AnyResource`13//14collectible.isInstance(Type<@AnyResource>()) // is `true`1516// The resource is *not* an instance of type `String`,17// because the concrete run-time type `Collectible` is *not* a subtype of `String`18//19collectible.isInstance(Type<String>()) // is `false`
Note that the concrete run-time type of the object is used, not the static type.
1// Declare a variable named `something` that has the *static* type `AnyResource`2// and has a resource of type `Collectible`3//4let something: @AnyResource <- create Collectible()56// The resource is an instance of type `Collectible`,7// because the concrete run-time type is `Collectible`8//9something.isInstance(Type<@Collectible>()) // is `true`1011// The resource is an instance of type `AnyResource`,12// because the concrete run-time type `Collectible` is a subtype of `AnyResource`13//14something.isInstance(Type<@AnyResource>()) // is `true`1516// The resource is *not* an instance of type `String`,17// because the concrete run-time type `Collectible` is *not* a subtype of `String`18//19something.isInstance(Type<String>()) // is `false`
For example, this allows implementing a marketplace sale resource:
1pub resource SimpleSale {23/// The resource for sale.4/// Once the resource is sold, the field becomes `nil`.5///6pub var resourceForSale: @AnyResource?78/// The price that is wanted for the purchase of the resource.9///10pub let priceForResource: UFix641112/// The type of currency that is required for the purchase.13///14pub let requiredCurrency: Type15pub let paymentReceiver: Capability<&{FungibleToken.Receiver}>1617/// `paymentReceiver` is the capability that will be borrowed18/// once a valid purchase is made.19/// It is expected to target a resource that allows depositing the paid amount20/// (a vault which has the type in `requiredCurrency`).21///22init(23resourceForSale: @AnyResource,24priceForResource: UFix64,25requiredCurrency: Type,26paymentReceiver: Capability<&{FungibleToken.Receiver}>27) {28self.resourceForSale <- resourceForSale29self.priceForResource = priceForResource30self.requiredCurrency = requiredCurrency31self.paymentReceiver = paymentReceiver32}3334destroy() {35// When this sale resource is destroyed,36// also destroy the resource for sale.37// Another option could be to transfer it back to the seller.38destroy self.resourceForSale39}4041/// buyObject allows purchasing the resource for sale by providing42/// the required funds.43/// If the purchase succeeds, the resource for sale is returned.44/// If the purchase fails, the program aborts.45///46pub fun buyObject(with funds: @FungibleToken.Vault): @AnyResource {47pre {48// Ensure the resource is still up for sale49self.resourceForSale != nil: "The resource has already been sold"50// Ensure the paid funds have the right amount51funds.balance >= self.priceForResource: "Payment has insufficient amount"52// Ensure the paid currency is correct53funds.isInstance(self.requiredCurrency): "Incorrect payment currency"54}5556// Transfer the paid funds to the payment receiver57// by borrowing the payment receiver capability of this sale resource58// and depositing the payment into it5960let receiver = self.paymentReceiver.borrow()61?? panic("failed to borrow payment receiver capability")6263receiver.deposit(from: <-funds)64let resourceForSale <- self.resourceForSale <- nil65return <-resourceForSale66}67}