# Custom Types Support
## First iteration
### Glossary
* **Scalar type**: `String`, `Boolean`, `Bytes`, `UInt`, `UInt8`, `UInt16`, `UInt32`, `Int`, `Int8`, `Int16`, `Int32`
* **Complex type**: `Array`, `Enum`, `Object`
* **BaseUnion**: Union of `scalar` types + `BaseUnions`. It's represented in our IDL using brackets. Example: `[String, Boolean]` means "String or Boolean union"
* **ComplexUnion**: Union of `complex` types + `ComplexUnions` + `BaseUnions`. It's represented in the same way as `BaseUnions`. Scalar types can't be members of a `ComplexUnion`, but `BaseUnions` can; so `[String, Boolean, Object]` would be invalid; but `[[String, Boolean], Object]` would be valid.
* **CustomTemplate**: Represents all possible forms of a `CustomType` by using unions. Example: `Foo @custom(id: "0", types: "[String, Boolean]")` means "Foo is a custom type, that accepts either a String or a Boolean as type argument".
* **CustomType**: Particular instance of a `CustomTemplate` that contains no unions, but rather the specific types it expects, according to the `CustomTemplate` definition. Example: `Foo<String>` would be a particular `CustomType` derived from our template `Foo @custom(id: "0", types: "[String, Boolean]")` defined above.
* **Alias**: Alias of a `BaseUnion` for ease of use when defining `CustomTemplates` and `CustomTypes`. Example: `Numeric @alias(types: [UInt, Int])` means "wherever I write `Numeric`, it will be parsed as `[UInt, Int]`". So `Foo<Numeric>` would be equivalent to `Foo<[UInt, Int]>`
### Decisions
- **Introduced `@custom` annotation**
Custom types will be defined as scalars, annotated with the `@custom` directive:
```graphql
directive @custom(
id: String!
types: String!
) on Scalar
```
The `id` argument uniquely identifies this custom type and should be tied to an external package that contains the data translation logic to handle it. It is not local to the ABI in which the `CustomTemplate` is defined; it is universally unique.
The `types` argument defines, in the form of a string, a union of possible generic arguments that the type can take.
Example:
```graphql
scalar Bar @custom(id: 1, types: "=[[String, Boolean], Array<[String, Boolean]>]")
```
Here, `Bar` is a `CustomTemplate` that takes `2` generic arguments:
- The first is either a `String` or a `Boolean`
- The second is an `Array` of `Strings` or an `Array` of `Booleans`
Therefore, the syntax of `CustomTypes` that derive from this `CustomTemplate` would look like: `Bar<String, Array<String>` or `Bar<Boolean, Array<String>`, or `Bar<String, Array<Boolean>>`, etc.
- **Introduced `@alias` annotation**:
The `@alias` annotation isn't meant to be used as a way to define `CustomTypes` or `CustomTemplates` like `JSON`, `BigInt` or `BigNumber`, which are "aliases" of `Strings`. Instead, it's just meant to alias `BaseUnions` to ease writing definitions. It's syntax is:
```graphql
directive @alias(
id: String!
types: String!
) on Scalar
```
An example of its usage would look like this:
```graphql
scalar Numeric @alias(types: "[UInt, Int]")
```
So, with that, instead of writing:
```graphql
scalar Baz @custom(id: "0", types: "[[UInt, Int], [UInt, Int]]")
```
We would write:
```graphql
scalar Numeric @alias(types: "[UInt, Int]")
scalar Baz @custom(id: "0", types: "[Numeric, Numeric]")
```
And the `CustomTypes` that derive from the `Baz` `CustomTemplate` would look like: `Baz<Int, UInt>` or `Baz<Int, Int>`, etc.
- **Have a handful of useful aliases available by default**:
```graphql
scalar Scalar @alias(types: "[String, Boolean, Bytes, Numeric, UInt8, UInt16, UInt32, Int8, Int16, Int32]")
scalar Any @alias(types: "[Scalar, Array<Any>, Enum, Object]")
```
Instead of having users define these aliases everytime, we can simply make them available everytime (like we do with our Polywrap header)
- **Removal of `Map`, `JSON`, `BigInt` and `BigNumber` as a built-in types**
Instead of having them as a built-in types, they got removed in favor of these `CustomTemplates`:
```graphql
scalar Map @custom(id: "2", types: "[MapKey, Any]")
scalar BigInt @custom(id: "3", types: "[String]")
scalar BigNumber @custom(id: "4", types: "[String]")
scalar JSON @custom(id: "5", types: "[String]")
```
Jordan Thoughts:
Custom Type:
- Name
- Wasm: Type + serializer methods
- Base-Type Representation
Generic Type:
- Generic Type Annotations
```typescript=
/*
wrap/
Module/
serialization.ts
*/
function serializeBigNumber(data: string | u32 | i32) {
}
function serializeMap<TKey, TValue>(data: Array<{ key: ..., value: .... }>) {
const map = new Map<TKey, TValue>();
for (const element of data) {
map.push({
key: element.key.into(),
value: element.value.into()
});
}
}
```
```graphql
#Polywrap Maintained:
scalar Numeric @alias(type: "UInt | UInt8 | UInt16 | UInt32 | Int | Int8 | Int16 | Int32")
scalar Scalar @alias(type: "String | Boolean | Bytes | Numeric")
scalar Any @alias(type: "Scalar | Array | Enum | Object")
scalar BigInt @extension(data: "String")
scalar BigNumber @extension(data: "String | Numeric")
scalar JSON @extension(data: "String")
scalar MapKey @alias(type: "Numeric | String")
scalar MapValue @alias(type: "Any")
scalar MapEntry @alias(type: "{ key: MapKey, value: MapValue }")
scalar Map @extension(data: "Array<MapEntry>", generic: "<TKey, TValue>")
#Non Polywrap Maintained:
type Object {
prop: Map @annotate(type: "Map<String!, JSON!>")
}
# type MapOfArrays = Map<Numeric, String | Boolean | String[]>
scalar MapOfArrays @alias(type: "Map<Numeric, String | Boolean | Array<String>>")
type SomeObject {
baz: String!
}
type Module {
foo(s: String!): MapOfArrays! @annotate(type: "MapOfArrays<UInt, Array<String>>")
bar(s: String!): Map! @annotate(type: "Map<String!, SomeObject!>")
#When a custom type takes a single generic, it's ommited from the type annotation and always required
fez(s: String!): JSON! @annotate(type: "JSON")
}
```
User A:
`wrapper.eth`
```graphql!
# ID = 2
scalar JSON @extension(type: "String")
type Module {
method(json: JSON!): String!
}
```
```typescript!
import {
registerExtensions
} from "./wrap";
import { JSON } from "json-lib";
export {
JSON,
serializeJSON,
deserializeJSON
};
export function method(args: MethodArgs): string {
args.json // ?
}
```
User B:
```graphql!
#import { Module, JSON } into Wrapper from "wrapper.eth"
type Module {
foo: JSON!
}
```
```typescript!
import { Wrapper_Module } from "./wrap";
...JSON type + serialization handlers...
export function foo(): string {
Wrapper_Module.method({
json: `{ "prop": 4, "prop2": "foo" }`
});
}
```
User C:
```typescript=
client.invoke({
uri: "wrapper.eth",
method: "method",
args: {
json: {
id: 2,
value: `{ "prop": 4, "prop2": "foo" }`
},
json: `{ "prop": 4, "prop2": "foo" }`
}
})
```
ex: BigNumber = String | Numeric
```
[0xSTRING, "prop"][0xTYPE, 0xDATA]
[0xSTRING, "prop"][0xSTRING, 0xDATA]
[0xSTRING, "prop"][0xUINT32, 0xDATA]
```