microsoft / microsoft/typespec
core: array typekits are brittle and error-prone
- Dominant language
- Java
- Stars
- 5.9k
- Forks
- 394
- Avg merge
- 1d 23h
- Merged PRs (30d)
- 104
Description
## `$.array.is`
We have this typekit function: `$(program).array.is(t)`, which says it "checks if a type is an array," but what does that mean?
Programmatically, what the function does is check:
- `t` is a Type; and
- `t` is a Model; and
- `t` has no properties; and
- `t` has an indexer; and
- `t`'s indexer key is named `integer`.
So the typekit considers `Foo` an array in the following example:
```ts
// This is my own custom scalar that just happens to be named `integer`
// obviously no one should do this, but there's no law against it
scalar integer;
// You're not supposed to use `@indexer` either, but you can
@indexer(global.integer, {})
model Foo {}
```
This makes it pretty unintuitive to recognize specifically array _literals_, and not `model is TypeSpec.Array<...>` declarations. To do so, you need to check also that the name of the type is `"Array"`, and probably also that it is defined in the `TypeSpec` namespace.
## `$.array.create`
Second, we have a typekit `$(program).array.create(t)`, which says it "creates an array type." However, it does not create an actual instance of `TypeSpec.Array`. What it does is:
- Creates a new model type with `$.model.create`; where
- `name` is "Array"; and
- `properties` is empty; and
- there is an indexer; where
- the indexer `key` is `getStdType("integer")`; and
- the indexer `value` is `t`
So the result of `$.array.create` is a type that _looks like_ an array, but it:
- is not in the `TypeSpec` namespace.
- is not an instance of `TypeSpec.Array`, but rather a completely separate model detached from any namespace.
- is not recognized as a standard type by `isStdType`.
## Bugs this Causes
Much worse than just these typekits being a bit confusing is that we have several different systems in the lineage of TypeSpec that all calculate array-ness _slightly differently_, which has caused discordance among the emitters. For example, until very recently, an Array created with `$.array.create` _was not recognized_ as an array literal by the EFv1 asset-emitter. It would always treat it as just some model named "Array" that looked like an Array declaration.
This discordance around the classification of what is and is not an array has led to several bugs in the openapi3 emitter, core visibility system, etc.
I'd like to solve this problem by highly specifying what is and is not an array or array-like entity in the typekit interface, and checking everywhere we test for array-ness across the repos to use the following classifications:
- **True Array**: a type that is a _true instance_ of `TypeSpec.Array`, i.e. it is an _array literal_.
- **Array Declaration**: a model that `is TypeSpec.Array`, or `is A` where `A` is an **Array Declaration**.
- **Array-Like**: a model that is assignable to `TypeSpec.Array`.
With that classification in mind, here is what I propose:
- We need a typekit that tells us if a type is a True Array: `$.array.isArrayLiteral`. This would be used in the asset emitter for determining that a type is an array literal (and therefore, for the OpenAPIv3 emitter to be able to emit an inline `type: array` schema).
- We may need a _different_ typekit that tells us if a type is equivalent to an array literal, i.e. it is an Array Literal **or** it is an Array Declaration that has no additional properties: I think this is the intent of `$.array.is`.
- We may need additional typekits to determine if a type is array-like, an array-like model with no additional properties, etc.
- `$.array.create` should return a _true instance_ of `TypeSpec.Array` instead of an array-like entity.
Contributor guide
Assessment
This issue has not been assessed yet.