Description
There is a discrepancy in metamodel.ts
between how we declare the generic parameters of a type, and how they are later referenced in its definition:
- declaration uses
generics: string[]
- usage in the definition uses a
TypeName
like for any other type, consisting of a name and a namespace. For open generic parameters references, the name is the one used in the declaration, which is expected, but the namespace is the one of the enclosing type, which is more surprising.
Example:
{
"kind": "interface",
"name": {
"name": "KeyedBucket",
"namespace": "aggregations"
},
"generics": [
"TKey"
],
"properties": [
{
"name": "key",
"required": true,
"type": {
"kind": "instance_of",
"type": {
"name": "TKey",
"namespace": "aggregations"
}
}
},
...
The fact that declarations use a simple name and references use a namespace that depends on the current type makes processing generic parameters more complicated than needed.
To resolve this inconsistency, we can consider different approaches:
-
change the declaration to also use namespaces:
generics: TypeName
with the same namespace as the one used when they are referenced:{ "kind": "interface", "name": { "name": "KeyedBucket", "namespace": "aggregations" }, "generics": [ { "name": "TKey", // <------ "namespace": "aggregations" // <------ } ], "properties": [ { "name": "key", "required": true, "type": { "kind": "instance_of", "type": { "name": "TKey", "namespace": "aggregations" } } }, ...
-
keep the
generics? : string[]
declaration and consider that open generic parameters all live in a builtingeneric_parameters
namespace. References would then use that namespace:{ "kind": "interface", "name": { "name": "KeyedBucket", "namespace": "aggregations" }, "generics": [ "TKey" ], "properties": [ { "name": "key", "required": true, "type": { "kind": "instance_of", "type": { "name": "TKey", "namespace": "generic_parameters" // <------ } } }, ...
The first approach makes the model more self-contained (no magic builtin namespace) but requires code generators to keep track of type names that are open generic parameters in the enclosing scope, while the second approach introduces a convention but avoids code generators to build that open generics context.
A 3rd approach could be to combine the two, using full type names for declarations so that the model is internally consisitent, but making sure (and validating in validate-model.ts
) that open generics live in the generic_parameters
namespace so that code generators can just match on that namespace to distinguish them:
{
"kind": "interface",
"name": {
"name": "KeyedBucket",
"namespace": "aggregations"
},
"generics": [
{
"name": "TKey", // <------
"namespace": "generic_parameters" // <------
}
],
"properties": [
{
"name": "key",
"required": true,
"type": {
"kind": "instance_of",
"type": {
"name": "TKey",
"namespace": "generic_parameters" // <------
}
}
},
...
Thoughts?
/cc @Mpdreamz @delvedor @stevejgordon
Note: TypeAlias
generics use a ValueOf[]
. This is a bug in the model and it should use the same approach as other type declarations.