Visibility
Visibility is a language feature that allows you to share a model between multiple operations and define in which contexts properties of the model are “visible.” Visibility is a very powerful feature that allows you to define different “views” of a model within different operations or contexts.
Basic concepts
- Visibility applies to model properties only. It is used to determine when an emitter should include or exclude a property in a certain context.
- Visibility is defined using a visibility class. A visibility class is an
enum
that defines the visibility modifiers (or flags) that can be applied to a property. Anyenum
can serve as a visibility class. - Visibility classes have a default visibility, which is the set of visibility modifiers that are applied by default to a property if the visibility is not explicitly set.
Lifecycle visibility
TypeSpec provides a built-in visibility called “resource lifecycle visibility.” This visibility allows you to declare whether properties are visible when a creating, updating, or reading a resource from an API endpoint. For example:
In the above example, each property of the Example
model has a lifecycle visibility that instructs emitters to include
or exclude the property when creating, updating, or reading the Example
resource.
TypeSpec’s HTTP library, OpenAPI emitter, and other standard functionality use the Lifecycle
visibility to create
different views of the Example
model based on which lifecycle phase is used in a particular operation.
In the following example, the type of the input and output of each operation is affected by the lifecycle visibility
of the properties in the Example
model.
The above interface generates the following OpenAPIv3 schemas:
Notice:
- The
id
property is markedreadOnly: true
because it is only visible when reading the resource. - The
ExampleUpdate
schema only includes thedescription
property because it is the only property that is visible when updating the resource. - Each of the
paths
reference the correct schema based on the lifecycle phase that the operations use. - The TypeSpec model is only defined once, and any changes in the output schemas are derived from the lifecycle visibility of the properties in the model.
Lifecycle visibility transforms
You can explicitly compute the shape of a model within a specific lifecycle phase by using the four built-in templates for lifecycle transforms:
Create<T extends Model>
: creates a copy ofT
with only the properties that are visible in theCreate
lifecycle phase, recursively.Read<T extends Model>
: creates a copy ofT
with only the properties that are visible in theRead
lifecycle phase, recursively.Update<T extends Model>
: creates a copy ofT
with only the properties that are visible in theUpdate
lifecycle phase, with the types of the properties set toCreateOrUpdate<T>
, recursively.CreateOrUpdate<T>
: creates a copy ofT
with only the properties that have either theCreate
orUpdate
visibility modifiers enabled, recursively.
For example:
When you use these templates, the resulting models have no Lifecycle
visibility modifiers applied, so that any
emitters or libraries that use lifecycle visibility will not alter them further.
Visibility modifiers
Each property has its own set of active visibility modifiers for each visibility class. The active modifiers can be changed using the decorators described in this section.
Note: Changing the visibility for one visibility class does not affect other visibility classes. If you change the
visibility for the Lifecycle
visibility class, it will not affect the modifiers that are active for any other
visibility classes.
@visibility
The @visibility
decorator enables visibility modifiers. It takes a list of visibility modifiers as arguments and
sets them on the property. For example:
In this example, the name
property has the Create
and Read
visibility modifiers enabled.
If visibility has already been set explicitly on a property, the @visibility
decorator ADDS its own visibility
modifiers to the currently-active modifiers. It does not replace the existing modifiers. For example:
In this example, the name
property has both the Create
and Read
visibility modifiers enabled, but not the Update
visibility modifier. The @visibility
decorator starts from an empty set of modifiers and adds the Create
modifier,
then adds the Read
modifier.
@removeVisibility
The @removeVisibility
decorator disables visibility modifiers. It takes a list of visibility modifiers as arguments
and removes them from the property. For example:
This use of @removeVisibility
is equivalent to the above examples with the @visibility
decorator, but it uses the @removeVisibility
decorator to remove the Update
visibility modifier from the name
property rather than adding the Create
and Read
visibility modifiers. The @removeVisibility
decorator starts from the default set of visibility modifiers and removes
the Update
modifier.
If the visibility has already been set on a property, the @removeVisibility
decorator removes its visibility from
the currently-active modifiers. It does not replace the existing modifiers. For example:
In this example, the id
property has the Update
and Create
visibility modifiers removed, but it retains the Read
visibility modifier.
@invisible
The @invisible
decorator disables all visibility modifiers on a property within a given visibility class. For example:
In this example, the invisible
property has no visibility modifiers enabled in the Lifecycle
visibility class.
Visibility filters
The @withVisibilityFilter
decorator allows you to transform a model by applying a visibility filter to it. A
visibility filter is an object that defines constraints on which visibility modifiers must be enabled/disabled for a
property to be visible. For example:
In the above example, the CreateAndReadExample
model is a copy of the Example
model with only the the properties
that have BOTH the Create
and Read
visibility modifiers enabled (i.e. only the name
property). The
CreateOrUpdateExample
model is a copy of the Example
model with only the properties that have EITHER the Create
or Update
visibility modifiers enabled (i.e. the id
and name
properties). The NonUpdateExample
model is a copy
of the Example
model with only the properties that do not have the Update
visibility modifier enabled (i.e. the
id
and name
properties).
Note: For Lifecycle
visibility, you should ordinarily use the Create
, Read
, Update
, and CreateOrUpdate
templates instead of @withVisibilityFilter
directly, but you can use @withVisibilityFilter
to create custom “views”
of a model that use visibility classes other than Lifecycle
or custom filter logic.
Visibility classes
Any TypeSpec enum
can serve as a visibility class. The members of the enum
define the visibility modifiers in the
class. For example, the following is the definition of the Lifecycle
visibility class defined in the TypeSpec standard
library:
This visibility class defines three visibility modifiers: Create
, Read
, and Update
. By default, all properties
have ALL three visibilities in the Lifecycle
enum enabled.
Setting default visibility
You can set the default visibility for a visibility class by declaring it on the enum using the @defaultVisibility
decorator:
In this example, any property that does not declare an Example
visibility modifier will have the A
visibility by
default.
Note: While you can define your own visibility classes, emitters will not recognize them unless they have been
programmed to do so. You can leverage custom visibility classes in your own emitters, but they will have no effect on
the standard emitters unless those emitters choose to adopt and recognize those visibility classes as meaningful. The
Lifecycle
visibility class is a standard visibility class that is recognized by several emitters. You can, however,
use your own visibility classes with the built in @withVisibilityFilter
decorator to transform your models in whatever
ways you see fit.