---
id: visibility
title: 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.

**Note** ⚠️: Enum-based visibility as described in this document _replaces_ visibility strings that you may have used
in the past. The system is backwards-compatible with visibility strings, but you should use enum-based visibility for
new specifications. String-based visibility (e.g. `@visibility("create")`) may be deprecated and removed in future
versions of TypeSpec.

## 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. Any `enum` 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 passing a resource to or reading a resource from an API endpoint. For example:

```typespec
model Example {
  /**
   * The unique identifier of this resource.
   *
   * The ID is automatically generated by the service, so it cannot be set when the resource is created or updated,
   * but the server will return it when the resource is read.
   */
  @visibility(Lifecycle.Read)
  id: string;

  /**
   * The name of this resource.
   *
   * The name can be set when the resource is created, but may not be changed.
   */
  @visibility(Lifecycle.Create, Lifecycle.Read)
  name: string;

  /**
   * The description of this resource.
   *
   * By default, properties are visible in all lifecycle phases, so this property
   * is present in all lifecycle phases.
   */
  description: string;
}
```

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.

```typespec
@route("/example")
interface Examples {
  /**
   * When an operation uses the POST verb, it uses the `Create` lifecycle visibility to determine which properties
   * are visible.
   */
  @post create(@body example: Example): Created<Example> | Error;

  /**
   * When an operation uses the GET verb, it uses the `Read` lifecycle visibility to determine which properties
   * are visible.
   */
  @get read(@path id: string): Ok<Example> | Error;

  /**
   * When an operation uses the PATCH verb, it uses the `Update` lifecycle visibility to determine which properties
   * are visible.
   */
  @patch update(@path id: string, @body example: Example): Ok<Example> | Error;
}
```

The above interface generates the following OpenAPIv3 schemas:

```yml
paths:
  /example:
    post:
      parameters: []
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Example"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/Example"
  /example/{id}:
    get:
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Example"
    patch:
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Example"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ExampleUpdate"
components:
  schemas:
    Example:
      type: object
      required:
        - id
        - name
        - description
      properties:
        id:
          type: string
          readOnly: true
        name:
          type: string
        description:
          type: string
    ExampleUpdate:
      type: object
      properties:
        description:
          type: string
```

Notice:

- The `id` property is marked `readOnly: true` because it is only visible when reading the resource.
- The `ExampleUpdate` schema only includes the `description` 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 modifiers

The following visibility modifiers are available in the `Lifecycle` visibility class:

- `Create`: The property is visible when the resource is created. This visibility is checked, for example, when a property
  is a parameter in an HTTP `POST` operation.
- `Read`: The property is visible when the resource is read. This visibility is checked, for example, when a property is
  returned in an HTTP `GET` operation.
- `Update`: The property is visible when the resource is updated. This visibility is checked, for example, when a property
  is a parameter in an HTTP `PATCH` or `PUT` operation.
- `Delete`: The property is visible when a resource is deleted. This visibility is checked, for example, when a property
  is a parameter in an HTTP `DELETE` operation.
- `Query`: The property is visible when a resource is passed as a parameter in a query. This visibility is checked, for
  example, when a property is a parameter in an HTTP `GET` operation (**this should not be confused with an HTTP query
  parameter defined using `@query`**).

### 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 of `T` with only the properties that are visible in the `Create` lifecycle
  phase, recursively.
- `Read<T extends Model>`: creates a copy of `T` with only the properties that are visible in the `Read` lifecycle phase,
  recursively.
- `Update<T extends Model>`: creates a copy of `T` with only the properties that are visible in the `Update` lifecycle
  phase, with the types of the properties set to `CreateOrUpdate<T>`, recursively.
- `CreateOrUpdate<T>`: creates a copy of `T` with only the properties that have _either_ the `Create` or `Update`
  visibility modifiers enabled, recursively.
- `Delete<T>`: creates a copy of `T` with only the properties that have the `Lifecycle.Delete` modifier enabled,
  recursively.
- `Query<T>`: creates a copy of `T` with only the properties that have the `Lifecycle.Query` modifier enabled,
  recursively.

For example:

```typespec
model Example {
  @visibility(Lifecycle.Create)
  id: string;

  @visibility(Lifecycle.Create, Lifecycle.Read)
  name: string;

  @visibility(Lifecycle.Update)
  description: string;
}

model ReadExample is Read<Example>;

model CreateExample is Create<Example>;

model UpdateExample is Update<Example>;

model CreateOrUpdateExample is CreateOrUpdate<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:

```typespec
@visibility(Lifecycle.Create, Lifecycle.Read)
name: string;
```

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:

```typespec
@visibility(Lifecycle.Create)
@visibility(Lifecycle.Read)
name: string;
```

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:

```typespec
@removeVisibility(Lifecycle.Update)
name: string;
```

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:

```typespec
@removeVisibility(Lifecycle.Update)
@removeVisibility(Lifecycle.Create)
id: string;
```

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:

```typespec
@invisible(Lifecycle)
invisible: string;
```

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:

```typespec
model Example {
  @visibility(Lifecycle.Create)
  id: string;

  @visibility(Lifecycle.Create, Lifecycle.Read)
  name: string;

  @visibility(Lifecycle.Update)
  description: string;
}

@withVisibilityFilter(#{ all: [Lifecycle.Create, Lifecycle.Read] })
model CreateAndReadExample {
  ...Example;
}

@withVisibilityFilter(#{ any: [Lifecycle.Create, Lifecycle.Update] })
model CreateOrUpdateExample {
  ...Example;
}

@withVisibilityFilter(#{ none: [Lifecycle.Update] })
model NonUpdateExample {
  ...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:

```typespec
enum Lifecycle {
  Create,
  Read,
  Update,
}
```

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:

```typespec
@defaultVisibility(Example.A)
enum Example {
  A,
  B,
}
```

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.
