Co-authored-by: Mario Guerra <85648637+mario-guerra@users.noreply.github.com>
Co-authored-by: Libba Lawrence <llawrence@microsoft.com>
Co-authored-by: Allen Zhang <allenzhang@live.com>
Co-authored-by: Brian Terlson <brian.terlson@microsoft.com>
This commit is contained in:
Timothee Guerin 2024-03-11 18:56:35 -07:00 коммит произвёл GitHub
Родитель 57f0d47bef
Коммит d8576effd5
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
112 изменённых файлов: 1519 добавлений и 1586 удалений

Просмотреть файл

@ -0,0 +1,8 @@
---
# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking
changeKind: internal
packages:
- "@typespec/compiler"
- "@typespec/http"
- "@typespec/openapi3"
---

Просмотреть файл

@ -5,17 +5,17 @@ title: Creating a TypeSpec Library
# Creating a TypeSpec library
TypeSpec libraries are packages that contain TypeSpec types, decorators, emitters, linters, and other bits of reusable code. TypeSpec libraries are [npm packages](https://docs.npmjs.com/packages-and-modules/contributing-packages-to-the-registry) with some additional typespec-specific metadata and conventions. The following will show how to establish a new TypeSpec library, add some types to it, and distribute it on the public npm registry. Later sections will cover more details on how to write [decorators](create-decorators.md), [emitters](./emitters-basics.md) and [linters](./linters.md).
A TypeSpec library is a package that includes TypeSpec types, decorators, emitters or linters. These libraries are [npm packages](https://docs.npmjs.com/packages-and-modules/contributing-packages-to-the-registry) with some additional TypeSpec-specific metadata and conventions. This guide will walk you through the process of creating a new TypeSpec library, adding types to it, and distributing it on the public npm registry. Further sections will delve into the specifics of creating [decorators](create-decorators.md), [emitters](./emitters-basics.md) and [linters](./linters.md).
This document assumes you will be using [TypeScript](https://typescriptlang.org) to develop your library, but you should feel free to skip the TypeScript steps if you want to use plain JavaScript.
While this guide assumes that you'll be using [TypeScript](https://typescriptlang.org) to develop your library, you can skip the TypeScript-related steps if you prefer to use plain JavaScript.
## Prerequisites
You will need both Node and npm installed. Additionally, if you intend to develop multiple libraries together, you will likely want to establish a monorepo as this will make developing the libraries in tandem much easier. TypeSpec itself uses [pnpm](https://pnpm.io/).
You'll need to have both Node and npm installed. If you're planning to develop multiple libraries simultaneously, it's recommended to set up a monorepo to simplify the development process. TypeSpec itself uses [pnpm](https://pnpm.io/).
## Setup with templates
## Setting up with templates
Available templates:
You can use the following templates:
```bash
# Create a TypeSpec library (Decorators & Linters) with TypeScript enabled.
@ -25,21 +25,21 @@ tsp init --template library-ts
tsp init --template emitter-ts
```
## Canonical package structure
## Standard package structure
The following is a high level overview of the contents of a TypeSpec package. These files are explained in more detail in the subsequent sections.
Here's a high-level overview of what a TypeSpec package typically contains. Each of these files will be explained in more detail in the following sections.
- **dist/index.js** - the main file for your Node library
- **lib/main.tsp** - the main file for your TypeSpec types (optional)
- **src/index.ts** - the main file for your Node library in TypeScript
- **src/lib.ts** - the TypeSpec library definition file
- **package.json** - metadata about your TypeSpec package
- **dist/index.js** - The main file for your Node library
- **lib/main.tsp** - The main file for your TypeSpec types (optional)
- **src/index.ts** - The main file for your Node library in TypeScript
- **src/lib.ts** - The file that defines your TypeSpec library
- **package.json** - Metadata about your TypeSpec package
## 1 - Initial setup
## Step 1: Initial setup
You can skip this if you used one of the templates above.
You can skip this step if you've used one of the templates above.
### a. Initialize your package directory &amp; package.json
### a. Initialize your package directory & package.json
Run the following commands:
@ -49,9 +49,9 @@ Run the following commands:
> npm init
```
After filling out the wizard, you will have a package.json file that defines your TypeSpec library.
After completing the wizard, you'll have a package.json file that defines your TypeSpec library.
Unlike Node libraries which support CommonJS (cjs), TypeSpec libraries must be ECMAScript Modules. Open your `package.json` and add the following top-level configuration key:
Unlike Node libraries which support CommonJS (cjs), TypeSpec libraries must be ECMAScript Modules. To specify this, open your `package.json` and add the following top-level configuration key:
```jsonc
"type": "module"
@ -65,13 +65,13 @@ Run the following command:
npm install --save-peer @typespec/compiler
```
You may have need of other dependencies in the TypeSpec standard library depending on what you are doing (e.g. if you want to use the metadata found in `@typespec/openapi` you will need to install that as well).
You might need to install other dependencies from the TypeSpec standard library. For example, if you want to use the metadata found in `@typespec/openapi`, you'll need to install that as well.
See [dependency section](#defining-dependencies) for information on how to define your dependencies.
Refer to the [dependency section](#step-3-defining-dependencies) for more information on defining your dependencies.
### c. Define your main files
Your package.json needs to refer to two main files: your Node module main file, and your TypeSpec main. The Node module main file is the `"main"` key in your package.json file, and defines the entrypoint for your library when consumed as a Node library, and must reference a JS file. The TypeSpec main defines the entrypoint for your library when consumed from a TypeSpec program, and may reference either a JS file (when your library doesn't contain any TypeSpec types) or a TypeSpec file.
Your package.json needs to refer to two main files: your Node module main file, and your TypeSpec main. The Node module main file is specified by the `"main"` key in your package.json file, and it defines the entry point for your library when it's used as a Node library. This must reference a JS file. The TypeSpec main defines the entry point for your library when it's used from a TypeSpec program, and it can reference either a JS file (when your library doesn't contain any TypeSpec types) or a TypeSpec file.
```jsonc
"main": "dist/src/index.js",
@ -87,7 +87,7 @@ npm install -D typescript
npx tsc --init --strict
```
This will create `tsconfig.json`. But we need to make a couple changes to this. Open `tsconfig.json` and set the following settings:
This will create a `tsconfig.json` file. You'll need to make a few changes to this file. Open `tsconfig.json` and set the following settings:
```jsonc
"module": "Node16", // This and next setting tells TypeScript to use the new ESM import system to resolve types.
@ -103,10 +103,10 @@ This will create `tsconfig.json`. But we need to make a couple changes to this.
Open `./src/lib.ts` and create your library definition that registers your library with the TypeSpec compiler and defines any diagnostics your library will emit. Make sure to export the library definition as `$lib`.
:::warning
If `$lib` is not accessible from your library package (`import {$lib} from "my-library";`) some functionality will be unavailable like validation of emitter options, linter rules, etc.
If `$lib` is not accessible from your library package (for example, `import {$lib} from "my-library";`), some features such as linting and emitter option validation will not be available.
:::
The following shows an example:
Here's an example:
```typescript
import { createTypeSpecLibrary } from "@typespec/compiler";
@ -116,24 +116,24 @@ export const $lib = createTypeSpecLibrary({
diagnostics: {},
} as const);
// Optional but convenient, those are meant to be used locally in your library.
// Optional but convenient, these are meant to be used locally in your library.
export const { reportDiagnostic, createDiagnostic } = $lib;
```
Diagnostics are used for linters and decorators which are covered in subsequent topics.
Diagnostics are used for linters and decorators, which are covered in subsequent topics.
### f. Create `index.ts`
Open `./src/index.ts` and import your library definition:
```typescript
// Re-export $lib to the compiler can get access to it and register your library correctly.
// Re-export $lib so the compiler can access it and register your library correctly.
export { $lib } from "./lib.js";
```
### g. Build TypeScript
TypeSpec can only import JavaScript files, so any time changes are made to TypeScript sources, they need to be compiled before they are visible to TypeSpec. To do so, run `npx tsc -p .` in your library's root directory. You can also run `npx tsc -p . --watch` if you would like to re-run the TypeScript compiler whenever files are changed.
TypeSpec can only import JavaScript files, so any changes made to TypeScript sources need to be compiled before they are visible to TypeSpec. To do this, run `npx tsc -p .` in your library's root directory. If you want to re-run the TypeScript compiler whenever files are changed, you can run `npx tsc -p . --watch`.
Alternatively, you can add these as scripts in your `package.json` to make them easier to invoke. Consider adding the following:
@ -150,15 +150,15 @@ You can then run `npm run build` or `npm run watch` to build or watch your libra
### h. Add your main TypeSpec file
Open `./lib/main.tsp` and import your JS entrypoint. This ensures that when TypeSpec imports your library, the code to define the library is run. In later topics when we add decorators, this import will ensure those get exposed as well.
Open `./lib/main.tsp` and import your JS entrypoint. This ensures that when TypeSpec imports your library, the code to define the library is run. When we add decorators in later topics, this import will ensure those get exposed as well.
```typespec
import "../dist/index.js";
```
## 2. Adding TypeSpec types to your library
## Step 2: Adding TypeSpec types to your library
Open `./lib/main.tsp` and add any types you want to be available when users import this library. It is also strongly recommended you put these types in a namespace that corresponds with the library name. For example, your `./lib/main.tsp` file might look like:
Open `./lib/main.tsp` and add any types you want to be available when users import this library. It's strongly recommended to put these types in a namespace that corresponds with the library name. For example, your `./lib/main.tsp` file might look like:
```typespec
import "../dist/index.js";
@ -170,15 +170,15 @@ model Person {
}
```
## 3. Defining Dependencies
## Step 3: Defining dependencies
Defining dependencies in a TypeSpec library should be following these rules:
When defining dependencies in a TypeSpec library, follow these rules:
- use `peerDependencies` for all TypeSpec libraries (+ compiler) that you use in your own library/emitter
- use `devDependencies` for the other TypeSpec libraries used only in tests
- use `dependencies`/`devDependencies` for any other packages depending if using in library code or in test/dev scripts
- Use `peerDependencies` for all TypeSpec libraries (and the compiler) that you use in your own library or emitter.
- Use `devDependencies` for other TypeSpec libraries that are only used in tests.
- Use `dependencies` or `devDependencies` for any other packages, depending on whether they're used in library code or in test/dev scripts.
TypeSpec libraries are defined using `peerDependencies` so we don't end-up with multiple versions of the compiler/library running at the same time.
TypeSpec libraries are defined using `peerDependencies` to avoid having multiple versions of the compiler or library running at the same time.
**Example**
@ -188,7 +188,7 @@ TypeSpec libraries are defined using `peerDependencies` so we don't end-up with
"yaml": "~2.3.1", // This is a regular package this library/emitter will use
},
"peerDependencies": {
// Those are all TypeSpec libraries this library/emitter depend on
// These are all TypeSpec libraries this library/emitter depends on
"@typespec/compiler": "~0.43.0",
"@typespec/http": "~0.43.1",
"@typespec/openapi": "~0.43.0",
@ -196,19 +196,19 @@ TypeSpec libraries are defined using `peerDependencies` so we don't end-up with
"devDependencies": {
// This TypeSpec library is only used in the tests but is not required to use this library.
"@typespec/versioning": "~0.43.0",
// Typescript is only used during development
// TypeScript is only used during development
"typescript": "~5.0.2",
},
}
```
## 4. Testing your TypeSpec library
## Step 4: Testing your TypeSpec library
TypeSpec provides a testing framework to help testing libraries. Examples here are shown using Node.js's built-in test framework (available in Node 20+) but any other JS test framework can be used that will provide more advanced features like vitest which is used in this project.
TypeSpec provides a testing framework to assist in testing libraries. The examples here are shown using Node.js's built-in test framework (available in Node 20+), but any other JS test framework can be used that will provide more advanced features like vitest, which is used in this project.
### a. Add devDependencies
Verify that you have the following in your `package.json`:
Ensure that you have the following in your `package.json`:
```json
"devDependencies": {
@ -233,7 +233,7 @@ export default defineConfig({
### b. Define the testing library
The first step is to define how your library can be loaded from the test framework. This will let your library to be reused by other library tests.
The first step is to define how your library can be loaded from the test framework. This will allow your library to be reused by other library tests.
1. Create a new file `./src/testing/index.ts` with the following content
@ -272,14 +272,14 @@ export const MyTestLibrary = createTestLibrary({
Define some of the test framework base pieces that will be used in the tests. There are 2 functions:
- `createTestHost`: This is a lower level API that provides a virtual file system.
- `createTestHost`: This is a lower-level API that provides a virtual file system.
- `createTestRunner`: This is a wrapper on top of the test host that will automatically add a `main.tsp` file and automatically import libraries.
Create a new file `test/test-host.js` (change `test` to be your test folder)
```ts
import { createTestHost, createTestWrapper } from "@typespec/compiler/testing";
import { RestTestLibrary } from "@typespec/rest/testing";
import { RestTestLibrary } from "/rest/testing";
import { MyTestLibrary } from "../src/testing/index.js";
export async function createMyTestHost() {
@ -340,7 +340,7 @@ describe("my library", () => {
#### e. `@test` decorator
The `@test` decorator is a decorator loaded in the test environment. It can be used to collect any decorable type.
When using the `compile` method it will return a `Record<string, Type>` which is a map of all the types annoted with the `@test` decorator.
When using the `compile` method it will return a `Record<string, Type>` which is a map of all the types annotated with the `@test` decorator.
```ts
const { Foo, CustomName } = await runner.compile(`
@ -357,23 +357,23 @@ CustomName; // type of : Bar.name
#### f. Install vscode extension for the test framework
If you are using VSCode, you can install the [Node test runner](https://marketplace.visualstudio.com/items?itemName=connor4312.nodejs-testing) to run your tests from the editor. This will also allow you easily debug into your tests.
If you are using VSCode, you can install the [Node test runner](https://marketplace.visualstudio.com/items?itemName=connor4312.nodejs-testing) to run your tests from the editor. This will also allow you to easily debug your tests.
You should now be able to discover, run and debug into your tests from the test explorer.
After installing the extension, you should be able to discover, run, and debug your tests from the test explorer.
## 5. Publishing your TypeSpec library
## Step 5: Publishing your TypeSpec library
To publish to the public npm registry, follow [their documentation](https://docs.npmjs.com/creating-and-publishing-unscoped-public-packages).
To publish your library to the public npm registry, follow the instructions in the [npm documentation](https://docs.npmjs.com/creating-and-publishing-unscoped-public-packages).
## 6. Importing your TypeSpec library
## Step 6: Importing your TypeSpec library
Once your TypeSpec library is published, your users can install and use it just like any of the TypeSpec standard libraries. First, they have to install it:
Once your TypeSpec library is published, users can install and use it just like any of the standard TypeSpec libraries. First, they need to install it:
```bash
npm install $packageName
```
Next, they import it into their TypeSpec program and use the namespace (if desired):
Next, they can import it into their TypeSpec program and use the namespace (if desired):
```typespec
import "MyLibrary";
@ -384,6 +384,6 @@ model Employee extends Person {
}
```
## 7. Next steps
## Step 7: Next steps
TypeSpec libraries can contain more than just types. Read the subsequent topics for more details on how to write [decorators](./create-decorators.md), [emitters](./emitters-basics.md) and [linters](./linters.md).
TypeSpec libraries can contain more than just types. For more details on how to write [decorators](./create-decorators.md), [emitters](./emitters-basics.md) and [linters](./linters.md), refer to the subsequent topics.

Просмотреть файл

@ -41,7 +41,7 @@ reportDiagnostic({
## Test a diagnostic
[See here for testing a codefix inside a linter rule](./linters.md#test-a-codefix)
[See here for testing a codefix inside a linter rule](./linters.md#testing-linter-with-codefixes)
Testing a codefix is done by using the `expectCodeFixOnAst` function from the `@typespec/compiler/testing` package. It takes in the source code and a function that returns the codefix to apply.
It takes the input source code with a cursor defined by `┆` which will be used to resolve the node where the codefix should be applied. The callback function will receive that resolved node and is expected to return the codefix to test.

Просмотреть файл

@ -1,33 +1,33 @@
---
id: create-decorators
title: Creating TypeSpec Decorators
title: How to create TypeSpec decorators
---
# Creating TypeSpec decorators
# How to create TypeSpec decorators
TypeSpec decorator are implemented as JavaScript function. Declarating a decorator can be done in 1 or 2 part:
TypeSpec decorators are implemented as JavaScript functions. The process of creating a decorator can be divided into two parts:
1. [(Optional) Declare the decorator signature in typespec](#declaring-a-decorator-signature)
2. [Implement the decorator in Javascript](#implement-the-decorator-in-js)
1. [(Optional) Declare the decorator signature in TypeSpec](#declare-the-decorator-signature)
2. [Implement the decorator in JavaScript](#implement-the-decorator-in-javascript)
## Declaring a decorator signature
## Declare the decorator signature
This part is optional but provides great value:
While this step is optional, it offers significant benefits:
- Type checking for the parameters
- IDE IntelliSense
- It enables type checking for the parameters
- It provides IDE IntelliSense
A decorator signature can be declared using the `dec` keyword. As we are implementing the decorator in JS (only choice right now), we must apply the `extern` modifier as well.
You can declare a decorator signature using the `dec` keyword. Since we're implementing the decorator in JavaScript (the only option currently), we need to use the `extern` modifier as well.
```typespec
extern dec logType(target: unknown, name: string);
```
## Decorator target
## Specifying the decorator target
The first parameter of the decorator represents the TypeSpec type(s) that the decorator can be applied on.
The first parameter of the decorator represents the TypeSpec type(s) that the decorator can be applied to.
You can specify multiple potential target type using an `union expression`
You can specify multiple potential target types using a `union expression`.
```typespec
using TypeSpec.Reflection;
@ -37,31 +37,30 @@ extern dec track(target: Model | Enum);
### Optional parameters
A decorator parameter can be marked optional using `?`
You can mark a decorator parameter as optional using `?`.
```typespec
extern dec track(target: Model | Enum, name?: valueof string);
```
### REST parameters
### Rest parameters
A decorator's last parameter can be prefixed with `...` to collect all the remaining arguments. The type of that parameter must be an `array expression`
You can prefix the last parameter of a decorator with `...` to collect all the remaining arguments. The type of this parameter must be an `array expression`.
```typespec
extern dec track(target: Model | Enum, ...names: valueof string[]);
```
## Ask for a value type
## Requesting a value type
It is common that decorators parameter will expect a value(e.g. a string or a number). However just using `: string` as the type will also allow a user of the decorator to pass `string` itself or a custom scalar extending string as well as union of strings.
Instead the decorator can use `valueof <T>` to specify that it is expecting a value of that kind.
It's common for decorator parameters to expect a value (e.g., a string or a number). However, using `: string` as the type would also allow a user of the decorator to pass `string` itself or a custom scalar extending string, as well as a union of strings. Instead, the decorator can use `valueof <T>` to specify that it expects a value of that kind.
| Example | Description |
| ----------------- | ---------------- |
| `valueof string` | Expect a string |
| `valueof float64` | Expect a float |
| `valueof int32` | Expect a number |
| `valueof boolean` | Expect a boolean |
| Example | Description |
| ----------------- | ----------------- |
| `valueof string` | Expects a string |
| `valueof float64` | Expects a float |
| `valueof int32` | Expects a number |
| `valueof boolean` | Expects a boolean |
```tsp
extern dec tag(target: unknown, value: valueof string);
@ -73,7 +72,7 @@ extern dec tag(target: unknown, value: valueof string);
@tag("This is the tag name")
```
## Implement the decorator in JS
## Implement the decorator in JavaScript
Decorators can be implemented in JavaScript by prefixing the function name with `$`. A decorator function must have the following parameters:
@ -90,7 +89,7 @@ export function $logType(context: DecoratorContext, target: Type, name: valueof
}
```
or in pure JS
Or in pure JavaScript:
```ts
// model.js
@ -99,7 +98,7 @@ export function $logType(context, target, name) {
}
```
The decorator can then be consumed this way
The decorator can then be used like this:
```typespec
// main.tsp
@ -114,7 +113,7 @@ model Dog {
### Decorator parameter marshalling
For certain TypeSpec types (Literal types) the decorator do not receive the actual type but a marshalled value if the decorator parmaeter type is a `valueof`. This is to simplify the most common cases.
For certain TypeSpec types (Literal types), the decorator does not receive the actual type but a marshalled value if the decorator parameter type is a `valueof`. This simplifies the most common cases.
| TypeSpec Type | Marshalled value in JS |
| ----------------- | ---------------------- |
@ -122,7 +121,7 @@ For certain TypeSpec types (Literal types) the decorator do not receive the actu
| `valueof numeric` | `number` |
| `valueof boolean` | `boolean` |
for all the other types they are not transformed.
For all other types, they are not transformed.
Example:
@ -130,7 +129,7 @@ Example:
export function $tag(
context: DecoratorContext,
target: Type,
stringArg: string, // Here instead of receiving a `StringLiteral` the string value is being sent.
stringArg: string, // Here instead of receiving a `StringLiteral`, the string value is being sent.
modelArg: Model // Model has no special handling so we receive the Model type
) {}
```
@ -142,17 +141,13 @@ The TypeSpec type system will already validate the string template can be serial
```tsp
extern dec doc(target: unknown, name: valueof string);
alias world = "world!";
@doc("Hello ${world} ") // receive: "Hello world!"
@doc("Hello ${123} ") // receive: "Hello 123"
@doc("Hello ${true} ") // receive: "Hello true"
model Bar {}
@doc("Hello ${Bar} ") // not called error
^ String template cannot be serialized as a string.
```
#### Typescript type Reference
@ -168,7 +163,7 @@ model Bar {}
### Adding metadata with decorators
Decorators can be used to register some metadata. For this you can use the `context.program.stateMap` or `context.program.stateSet` to insert data that will be tied to the current execution.
Decorators can be used to register some metadata. For this, you can use the `context.program.stateMap` or `context.program.stateSet` to insert data that will be tied to the current execution.
❌ Do not save the data in a global variable.
@ -200,7 +195,7 @@ export const StateKeys = $lib.stateKeys;
### Reporting diagnostic on decorator or arguments
Decorator context provide the `decoratorTarget` and `getArgumentTarget` helpers
The decorator context provides the `decoratorTarget` and `getArgumentTarget` helpers.
```ts
import type { DecoratorContext, Type } from "@typespec/compiler";
@ -218,9 +213,9 @@ export function $customName(context: DecoratorContext, target: Type, name: strin
}
```
## Declaration - implementation link
## Linking declaration and implementation
Decorator signatures are linked to the implementation of the same name in the same namespace
Decorator signatures are linked to the implementation of the same name in the same namespace.
```typespec
import "./lib.js";
@ -231,7 +226,7 @@ namespace MyLib {
}
```
is linked the the following in `lib.js`
This is linked to the following in `lib.js`:
```ts
export function $customName(context: DecoratorContext, name: string) {}
@ -242,12 +237,12 @@ setTypeSpecNamespace("MyLib", $tableName);
## Troubleshooting
### Extern declation must have an implementation in JS file
### Extern declaration must have an implementation in JS file
Potential issues:
- JS function is not prefixed with `$`. For a decorator called `@decorate` the JS function must be called `$decoratate`
- JS function is not in the same namespace as the the `extern dec`
- Error is only showing in the IDE? Restart the TypeSpec server or the IDE.
- The JS function is not prefixed with `$`. For a decorator called `@decorate`, the JS function must be called `$decorate`.
- The JS function is not in the same namespace as the `extern dec`.
- Is the error only showing in the IDE? Try restarting the TypeSpec server or the IDE.
You can use `--trace bind.js.decorator` to log debug information about decorator loading in JS file that should help pinning down which of those is the issue.
You can use `--trace bind.js.decorator` to log debug information about decorator loading in the JS file, which should help identify the issue.

Просмотреть файл

@ -2,25 +2,25 @@
title: Diagnostics
---
The TypeSpec compiler reports errors and warnings in the spec using the diagnostic API.
The TypeSpec compiler uses the diagnostic API to report errors and warnings in the specification.
## Best practices
- ❌ Do not use `throw` to report errors. Any exception thrown like this will be presented as a bug in your library to the user.
- ✅ Use diagnostic API to report expected errors and warnings.
- ✅ Use `reportDiagnostic` in a decorator, `$onValidate` or `$onEmit`
- ❌ Do not use `reportDiagnostic` in an accessor (A function meant to be consumed in another library or emitter). See [collect diagnostics section](#collect-diagnostics)
- ❌ Avoid using `throw` to report errors. Any exceptions thrown in this manner will be perceived as bugs in your library by the user.
- ✅ Utilize the diagnostic API to report anticipated errors and warnings.
- ✅ Employ `reportDiagnostic` in a decorator, `$onValidate` or `$onEmit`
- ❌ Refrain from using `reportDiagnostic` in an accessor (a function intended to be used in another library or emitter). Refer to the [section on collecting diagnostics](#collect-diagnostics) for more information.
## Diagnostic specification
## Diagnostic requirements
- Each diagnostic MUST have a `code`. The full code is the the library name followed by the declared code. (`<lib-name>/<local-code>`)
- Each diagnostic MUST have a `severity`. It can be `error`, `warning`. Errors cannot be suppressed
- Each diagnostics MUST have at least one message. Using `default` as the `messageId` will allow it to be the default selected.
- Each diagnostics message MAY have parameters to interpolate information into the message
- Each diagnostic MUST have a `code`. The complete code is the library name followed by the declared code. (`<lib-name>/<local-code>`)
- Each diagnostic MUST have a `severity`. It can be `error` or `warning`. Errors cannot be suppressed.
- Each diagnostic MUST have at least one message. Using `default` as the `messageId` will make it the default selection.
- Each diagnostic message MAY have parameters to interpolate information into the message.
## Usage
## How to use
### Declare the diagnostics you are reporting
### Declare the diagnostics you plan to report
```ts
import { createTypeSpecLibrary } from "@typespec/compiler";
@ -56,11 +56,11 @@ export const $lib = createTypeSpecLibrary({
},
} as const);
// Rexport the helper functions to be able to just call them directly.
// Re-export the helper functions to be able to just call them directly.
export const { reportDiagnostic, createDiagnostic };
```
This will represent 3 different diagnostics with full name of
This will represent three different diagnostics with the full names of:
- `@typespec/my-lib/no-array`
- `@typespec/my-lib/duplicate-route`
@ -87,7 +87,7 @@ reportDiagnostic(program, {
// Multiple messages
reportDiagnostic(program, {
code: "duplicate-name",
messageId: "parmaeter",
messageId: "parameter",
format: {value: "$select"},
target: diagnosticTarget,
});
@ -95,8 +95,8 @@ reportDiagnostic(program, {
### Collect diagnostics
When trying to report diagnostic in an accessor a good pattern is not to report the diagnostic to the program directly but return a tuple to let the user decide what to do.
This prevent duplicate diagnostics emitter if the accessor is called multiple times.
When attempting to report a diagnostic in an accessor, a good practice is not to report the diagnostic to the program directly, but return a tuple to let the user decide what to do.
This prevents duplicate diagnostics emitter if the accessor is called multiple times.
```ts
import { createDiagnosticCollector, Diagnostic } from "@typespec/compiler";

Просмотреть файл

@ -3,28 +3,32 @@ id: emitter-framework
title: Emitter framework
---
The emitter framework makes writing emitters from TypeSpec to other assets a fair bit easier than manually consuming the type graph. The framework gives you an easy way to handle all the types TypeSpec might throw at you and know when you're "feature complete". It also handles a lot of hard problems for you, such as how to construct references between types, how to handle circular references, or how to propagate the context of the types you're emitting based on their containers or where they're referenced from. Lastly, it provides a class-based inheritance model that makes it fairly painless to extend and customize existing emitters.
# Emitter framework
## Getting Started
The emitter framework simplifies the process of creating emitters from TypeSpec to other assets, compared to manually navigating the type graph. This framework provides a straightforward way to manage all the types that TypeSpec might present, and helps you determine when you've covered all features.
Make sure to read the getting started section under the [emitter basics](./emitters-basics.md) topic. To use the framework, you will need an emitter library and `$onEmit` function ready to go.
The also solves complex problems such as constructing references between types, handling circular references, and propagating the context of the types you're emitting based on their containers or where they're referenced from. Additionally, it offers a class-based inheritance model that simplifies the extension and customization of existing emitters.
## Implementing your emitter
## How to get started
Implementing an emitter using the emitter framework will use a variety of types from the framework. To give you a high level overview, these are:
Before you start, make sure to read the 'Getting Started' section under the [emitter basics](./emitters-basics.md) topic. To use the framework, you will need an emitter library and a `$onEmit` function.
- `AssetEmitter`: The asset emitter is the main type you will interact with in your `$onEmit` function. You can pass the asset emitter types to emit, and tell it to write types to disk or give you source files for you to process in other ways.
- `TypeEmitter`: The type emitter is the base class for most of your emit logic. Every TypeSpec type has a corresponding method on TypeEmitter. It also is where you will manage your emit context, making it easy to answer such questions as "is this type inside something I care about" or "was this type referenced from something".
- `CodeTypeEmitter`: A subclass of `TypeEmitter` that makes building source code easier.
- `StringBuilder`, `ObjectBuilder`, `ArrayBuilder`: when implementing your `TypeEmitter` you will likely use these classes to help you build strings and object graphs. These classes take care of handling the placeholders that result from circular references.
## Creating your own emitter
Let's walk through each of these types in turn.
When you create an emitter using the emitter framework, you will use various types from the framework. Here's a high-level overview of these types:
- `AssetEmitter`: This is the main type you will use in your `$onEmit` function. You can pass types to the asset emitter to emit, and instruct it to write types to disk or provide you with source files for further processing.
- `TypeEmitter`: This is the base class for most of your emit logic. Every TypeSpec type has a corresponding method on TypeEmitter. This is also where you will manage your emit context, making it easy to answer questions like "is this type inside something I care about" or "was this type referenced from something".
- `CodeTypeEmitter`: This is a subclass of `TypeEmitter` that simplifies the creation of source code.
- `StringBuilder`, `ObjectBuilder`, `ArrayBuilder`: These classes are likely to be used when implementing your `TypeEmitter` to help you build strings and object graphs. They handle the placeholders that result from circular references.
Let's explore each of these types in more detail.
### `AssetEmitter<T>`
The asset emitter is responsible for driving the emit process. It has methods for taking TypeSpec types to emit, and maintains the state of your current emit process including the declarations you've accumulated, current emit context, and converting your emitted content into files on disk.
The asset emitter drives the emit process. It has methods for taking TypeSpec types to emit, and maintains the state of your current emit process, including the declarations you've accumulated, current emit context, and converting your emitted content into files on disk.
To create your asset emitter, call `getAssetEmitter` on your emit context in `$onEmit`. It takes the TypeEmitter which is covered in the next section. Once created, you can call `emitProgram()` to emit every type in the TypeSpec graph. Otherwise, you can call `emitType(someType)` to emit specific types instead.
To create your asset emitter, call `getAssetEmitter` on your emit context in `$onEmit`. It takes the TypeEmitter which is covered in the next section. Once created, you can call `emitProgram()` to emit every type in the TypeSpec graph. Alternatively, you can call `emitType(someType)` to emit specific types.
```typescript
export async function $onEmit(context: EmitContext) {
@ -49,7 +53,7 @@ To support emitting all TypeSpec types, you should expect to implement all of th
The generic type parameter `T` is the type of emit output you are building. For example, if you're emitting source code, `T` will be `string`. If you're building an object graph like JSON, `T` will be `object`. If your `T` is `string`, i.e. you are building source code, you will probably want to use the `CodeTypeEmitter` subclass which is a bit more convenient, but `TypeEmitter<string>` will also work fine.
A simple emitter that doesn't do much yet might look like:
Here's a simple emitter that doesn't do much yet:
```typescript
class MyCodeEmitter extends CodeTypeEmitter {
@ -59,13 +63,13 @@ class MyCodeEmitter extends CodeTypeEmitter {
}
```
Passing this to `getAssetEmitter` and calling `assetEmitter.emitProgram()` will console.log all the models in the program.
If you pass this to `getAssetEmitter` and call `assetEmitter.emitProgram()`, it will log all the models in the program to the console.
#### EmitterOutput
Most methods of the `TypeEmitter` must either return `T` or an `EmitterOutput<T>`. There are four kinds of `EmitterOutput`:
- `Declaration<T>`: A declaration, which has a name and is declared in a scope, and so can be referenced by other emitted types (more on References later). Declarations are created by calling `this.emitter.result.declaration(name, value)` in your emitter methods. Scopes come from your current context, which is covered later in this document.
- `Declaration<T>`: A declaration, which has a name and is declared in a scope, and so can be referenced by other emitted types. Declarations are created by calling `this.emitter.result.declaration(name, value)` in your emitter methods. Scopes come from your current context, which is covered later in this document.
- `RawCode<T>`: Output that is in some way concatenated into the output but cannot be referenced (e.g. things like type literals). Raw code is created by calling `this.emitter.result.rawCode(value)` in your emitter methods.
- `NoEmit`: The type does not contribute any output. This is created by calling `this.emitter.result.none()` in your emitter methods.
- `CircularEmit`: Indicates that a circular reference was encountered, which is generally handled by the framework with Placeholders (see the next section). You do not need to create this result yourself, the framework will produce this when required.
@ -279,7 +283,7 @@ If you're using the `Builder`s that come with the framework, you will not need t
#### Context
A common need when emitting TypeSpec is to know what context you are emitting the type in. There is one piece of required context: `scope`, which tells the emitter framework where you want to place your declarations. But you might also want to easily answer questions like: am I emitting a model inside a particular namespace? Or am I emitting a model that is referenced from the return type of an operation? The emitter framework makes managing this context fairly trivial.
When emitting TypeSpec, it's often necessary to know the context in which you are emitting the type. One piece of required context is `scope`, which tells the emitter framework where you want to place your declarations. But you might also want to easily answer questions like: am I emitting a model inside a particular namespace? Or am I emitting a model that is referenced from the return type of an operation? The emitter framework makes managing this context fairly trivial.
Every method that results in an `EmitterOutput` has a corresponding method for setting lexical and reference context. We saw this above when we created `modelDeclarationContext` in order to put some models into a different namespace.
@ -331,7 +335,7 @@ We can now see how this results in the `Person` model being located in a nested
### Extending `TypeEmitter`
TypeEmitters are classes and explicitly support subclassing, so you can customize an existing emitter by extending it and overriding any methods you want to customize in your subclass. In fact, emitters you find out in the ecosystem are likely not to work without creating a subclass, because they only know how to emit types, but you need to provide the scope for any declarations it wants to create. For example, if we have a base `TypeScriptEmitter` that can convert TypeSpec into TypeScript, we might extend it to tell it to put all declarations in the same file:
TypeEmitters are classes and explicitly support subclassing, so you can customize an existing emitter by extending it and overriding any methods you want to customize in your subclass. In fact, emitters you find out in the ecosystem are likely not to work without creating a subclass, because they only know how to emit types, but you need to provide the scope for any declarations it wants to create. For example, if we have a base `TypeScript Emitter` that can convert TypeSpec into TypeScript, we might extend it to tell it to put all declarations in the same file:
```typescript
class MyTsEmitter extends TypeScriptEmitter {
@ -357,3 +361,5 @@ class MyTsEmitter extends TypeScriptEmitter {
// and similar for other declarations: Unions, Enums, Interfaces, and Operations.
}
```
This way, each model will be emitted in its own file.

Просмотреть файл

@ -1,38 +1,62 @@
---
id: emitter-metadata-handling
title: Handling metadata and visibility in emitters for REST API
title: Managing metadata and visibility in REST API emitters
---
# Handling metadata and visibility in emitters for REST API
# Managing metadata and visibility in REST API emitters
It's important that all emitters for REST API handle [automatic visibility](../libraries/http/operations.md#automatic-visibility) and [metadata](../libraries/http/operations.md#metadata) consistently. Make sure to read through the TypeSpec-author documentation of these features to understand how they work. This document will cover how to incorporate them correctly into your own emitter.
Ensuring consistent handling of [automatic visibility](../libraries/http/operations.md#automatic-visibility) and [metadata](../libraries/http/operations.md#metadata) by all REST API emitters is crucial. To understand how these features work, please refer to the TypeSpec-author documentation. This guide will help you integrate these features into your own emitter correctly.
The standard `@typespec/rest` library provides JavaScript API for emitters to interpret API written using its decorators. We'll look at the API that are particularly relevant to these features.
The standard `/rest` library offers a JavaScript API for emitters to interpret APIs written using its decorators. We will focus on the APIs that are specifically relevant to these features.
Note that when we say that emitters must handle things consistently, we mean that they must agree on how data is sent and received over the wire. After all, a TypeSpec _specification_ must be able to serve as a source-of-truth on these details. Nevertheless, emitters are still free to _abstract_ things above this level and to make different choices in doing so. For example, the OpenAPI emitter will sometimes split a single TypeSpec model into multiple suffixed schemas with names like `UserCreate` and `UserUpdate` while a client SDK emitter may choose to emit a single `User` class that that can be serialized to a request or deserialized from a response with different fields present in different cases. In fact, these features were designed specifically to allow a TypeSpec specification to be written in terms of logical entities that emitters could then preserve.
When we say that emitters should handle things consistently, we mean they should agree on how data is transmitted and received. A TypeSpec _specification_ should serve as a reliable reference for these details. However, emitters can still _abstract_ things above this level and make different choices. For instance, the OpenAPI emitter may split a single TypeSpec model into multiple suffixed schemas like `UserCreate` and `UserUpdate`, while a client SDK emitter might emit a single `User` class that can be serialized to a request or deserialized from a response with different fields present in different situations. These features were specifically designed to allow a TypeSpec specification to be written in terms of logical entities that emitters could then maintain.
## Getting started
If you haven't written an emitter before, start with [emitter basics](./emitters-basics.md).
If you're new to writing emitters, begin with the [emitter basics](./emitters-basics.md).
Then look at the [REST metadata emitter sample](https://github.com/microsoft/typespec/tree/main/packages/samples/rest-metadata-emitter). This emitter sample uses all of the API discussed below to write out a simple textual representation. It deliberately does not split types like the OpenAPI emitter in order to emphasize that this is not required. Instead, it adds contextual remarks to denote how data depends on context.
Next, examine the [REST metadata emitter sample](https://github.com/microsoft/typespec/tree/main/packages/samples/rest-metadata-emitter). This sample uses all of the APIs discussed below to create a simple textual representation. It intentionally avoids splitting types like the OpenAPI emitter to highlight that this is not mandatory. Instead, it includes contextual comments to indicate how data depends on context.
However, if your emitter does want to split types as OpenAPI does, then it will still use the same API. Cross-referencing with where the official [OpenAPI emitter] calls these API can also be instructive.
However, if you want your emitter to split types like OpenAPI, you can still use the same API. Cross-referencing with the official [OpenAPI emitter] where these APIs are called can also be helpful.
## Key API
These are the main API involved in handling these features. See the linked API reference documentation for more details.
Here are the main APIs involved in managing these features. For more details, refer to the linked API reference documentation.
- [`getRequestVisibility(HttpVerb): Visibility`](../libraries/http/reference/js-api/index.md#getrequestvisibility) - Use this to determine the visibility implied for data in the request parameters or body. Also note that [`Visibility.Read`](../libraries/http/reference/js-api/enumerations/Visibility.md#item) is always applied for response data and therefore there is no corresponding API for the response.
- [`getRequestVisibility(HttpVerb): Visibility`](../libraries/http/reference/js-api/functions/getRequestVisibility.md) - Use this to determine the visibility implied for data in the request parameters or body. Note that [`Visibility.Read`](../libraries/http/reference/js-api/enumerations/Visibility.md) is always applied for response data, so there is no corresponding API for the response.
- [`MetadataInfo`](../libraries/http/reference/js-api/interfaces/MetadataInfo.md) - Create this once for each program using [`createMetadataInfo(Program, MetadataInfoOptions)`](../libraries/http/reference/js-api/index.md#createmetadatainfo) then use it to reason about metadata and visibility implications with the API below.
- [`MetadataInfo`](../libraries/http/reference/js-api/interfaces/MetadataInfo.md) - Create this once for each program using [`createMetadataInfo(Program, MetadataInfoOptions)`](../libraries/http/reference/js-api/functions/createMetadataInfo.md). Then use it to understand metadata and visibility implications with the APIs below.
- [`MetadataInfo.getEffectivePayloadType(Type, Visibility): Type`](../libraries/http/reference/js-api/interfaces/MetadataInfo.md#geteffectivepayloadtype) - Use this recursively on every type that is referenced. When given an anonymous model sourced entirely from a single named model after metadata is moved elsewhere or invisible properties are removed, it will recover the named model. This handles the commonly discussed case of seeing that `op something(...Thing)` receives a `Thing` in its request body, but also many other cases..
- [`MetadataInfo.getEffectivePayloadType(Type, Visibility): Type`](../libraries/http/reference/js-api/interfaces/MetadataInfo.md#geteffectivepayloadtype) - Use this recursively on every referenced type. When given an anonymous model sourced entirely from a single named model after metadata is moved elsewhere or invisible properties are removed, it will recover the named model. This handles the commonly discussed case of seeing that `op something(...Thing)` receives a `Thing` in its request body, among other cases.
- [`MetadataInfo.isTransformed(Model, Visibility)`](../libraries/http/reference/js-api/interfaces/MetadataInfo.md#istransformed) - Use this to check if a type undergoes any changes in shape due to visibility or metadata. If not, this can allow for simplifications in emit.
- [`MetadataInfo.isTransformed(Model, Visibility)`](../libraries/http/reference/js-api/interfaces/MetadataInfo.md#istransformed) - Use this to check if a type undergoes any shape changes due to visibility or metadata. If not, this can allow for simplifications in emit.
- [`MetadataInfo.isPayloadProperty(ModelProperty, Visibility): boolean`](../libraries/http/reference/js-api/interfaces/MetadataInfo.md#ispayloadproperty) - Use this to check if a property is transmitted as an object property in the payload and is not invisible or metadata sent elsewhere.
- [`MetadataInfo.isOptional(ModelProperty, Visibility): boolean`](../libraries/http/reference/js-api/interfaces/MetadataInfo.md#isoptional) - Use this to determine if a property is optional for the given visibility. This will differ from `ModelProperty.isOptional` when the Visibility is Update in which case the property is always considered optional.
- [`MetadataInfo.isOptional(ModelProperty, Visibility): boolean`](../libraries/http/reference/js-api/interfaces/MetadataInfo.md#isoptional) - Use this to determine if a property is optional for the given visibility. This will differ from `ModelProperty.isOptional` when the Visibility is Update, in which case the property is always considered optional.
- [`Visibility.Item`](../libraries/http/reference/js-api/enumerations/Visibility.md#item) - Add this flag when recursing into an array. This moves all metadata into the payload, which can be useful in scenarios like batching API calls.
- [`Visibility.Item`](../libraries/http/reference/js-api/enumerations/Visibility.md) - Add this flag when recursing into an array. This moves all metadata into the payload, which can be useful in scenarios like batching API calls.
## Working with metadata and visibility
When working with metadata and visibility, it's important to understand how they interact with each other and with the data being transmitted. Here are some key points to consider:
- Metadata is data about the data being transmitted. It can include information like the data's origin, when it was created or last updated, who created or updated it, and so on. Metadata is not usually visible to the end user, but it can be crucial for the system processing the data.
- Visibility determines what data is visible to the end user or the system at any given time. For example, some data might be visible only to the system, some only to the end user, and some to both. Visibility can change depending on the context, such as whether the data is being created, read, updated, or deleted.
- The `getRequestVisibility(HttpVerb): Visibility` API is used to determine the visibility implied for data in the request parameters or body. Note that `Visibility.Read` is always applied for response data, so there is no corresponding API for the response.
- The `MetadataInfo` API is used to create a metadata object for each program, which can then be used to reason about metadata and visibility implications.
- The `MetadataInfo.getEffectivePayloadType(Type, Visibility): Type` API is used recursively on every type that is referenced. It recovers the named model when given an anonymous model sourced entirely from a single named model after metadata is moved elsewhere or invisible properties are removed.
- The `MetadataInfo.isTransformed(Model, Visibility)` API is used to check if a type undergoes any changes in shape due to visibility or metadata. If not, this can allow for simplifications in emit.
- The `MetadataInfo.isPayloadProperty(ModelProperty, Visibility): boolean` API is used to check if a property is transmitted as an object property in the payload and is not invisible or metadata sent elsewhere.
- The `MetadataInfo.isOptional(ModelProperty, Visibility): boolean` API is used to determine if a property is optional for the given visibility. This will differ from `ModelProperty.isOptional` when the Visibility is Update, in which case the property is always considered optional.
- The `Visibility.Item` flag is added when recursing into an array. This moves all metadata into the payload, which can be useful in scenarios like batching API calls.
By understanding and correctly using these APIs and concepts, you can ensure that your emitter handles metadata and visibility consistently and effectively.

Просмотреть файл

@ -3,30 +3,30 @@ id: emitters
title: Emitters
---
# Writing emitters
# Creating emitters
TypeSpec emitters are libraries that use various TypeSpec compiler APIs to reflect on the TypeSpec compilation and produce generated artifacts. The TypeSpec standard library includes an emitter for OpenAPI version 3.0, but odds are good you will want to emit TypeSpec to another output format. In fact, one of TypeSpec's main benefits is how easy it is to use TypeSpec as a source of truth for all data shapes, and the ease of writing an emitter is a big part of that story.
TypeSpec emitters are libraries that utilize various TypeSpec compiler APIs to reflect on the TypeSpec compilation process and generate artifacts. The TypeSpec standard library includes an emitter for OpenAPI version 3.0. However, you might want to emit TypeSpec to a different output format. One of the main advantages of TypeSpec is its ease of use as a single source of truth for all data shapes, and the simplicity of creating an emitter contributes significantly to this.
## Getting started
## Starting out
TypeSpec emitters are a special kind of TypeSpec library and so have the same getting started instructions.
TypeSpec emitters are a unique type of TypeSpec library, so they follow the same initial setup instructions.
Setup the boilerplate for an emitter using our template:
Set up the boilerplate for an emitter using our template:
```bash
tsp init --template emitter-ts
```
or follow [these steps](./basics.md) to initialize a TypeSpec library.
Alternatively, follow [these steps](./basics.md) to initialize a TypeSpec library.
## $onEmit
A TypeSpec emitter exports a function named `$onEmit` from its main entrypoint. It receives two arguments:
A TypeSpec emitter exports a function named `$onEmit` from its main entry point. It takes two arguments:
- _context_: The current context including the current progfam being compiled
- _context_: The current context, including the current program being compiled
- _options_: Custom configuration options selected for this emitter
For example, the following will write a text file to the output directory:
For instance, the following code will write a text file to the output directory:
```typescript
import { EmitContext, emitFile, resolvePath } from "@typespec/compiler";
@ -41,13 +41,13 @@ export async function $onEmit(context: EmitContext) {
}
```
You can now compile a TypeSpec program passing your library name to --emit, or add it to your `tspconfig.yaml`.
You can now compile a TypeSpec program by passing your library name to --emit, or add it to your `tspconfig.yaml`.
### Custom configuration options
To pass your emitter custom options, the options must be registered with the compiler by setting `emitter.options` in your library definition to the JSON schema for the options you want to take. The compiler has a helper to make this easier:
To provide custom options to your emitter, you need to register the options with the compiler by setting `emitter.options` in your library definition to the JSON schema for the options you want to use. The compiler provides a helper to simplify this:
- _JSONSchemaType_: Takes a TypeScript interface and returns a type that helps you fill in the JSON schema for that type.
- _JSONSchemaType_: Takes a TypeScript interface and returns a type that assists you in filling in the JSON schema for that type.
The following example extends the hello world emitter to be configured with a name:
@ -128,19 +128,19 @@ The general guideline is to use a decorator when the customization is intrinsic
## Emitting TypeSpec types to assets on disk
One of the main tasks of an emitter is finding types to emit. There are three main approaches:
One of the main tasks of an emitter is to identify types to emit. There are three primary methods:
1. The [emitter framework](./emitter-framework.md), which makes it relatively easy to emit all your TypeSpec types (or a subset, if you wish).
1. The Semantic Walker, which lets you easily run code for every type in the program
1. Custom traversal, which gives you a lot more flexibility than either of the previous approaches at the cost of some complexity.
1. The [emitter framework](./emitter-framework.md), which simplifies the process of emitting all your TypeSpec types (or a subset, if you prefer).
2. The Semantic Walker, which allows you to easily execute code for every type in the program.
3. Custom traversal, which offers more flexibility than the previous two methods, albeit with some added complexity.
### Emitter Framework
The emitter framework provides handles a lot of hard problems for you while providing an easy-to-use API to convert your TypeSpec into source code or other object graphs. Visit the [emitter framework](./emitter-framework.md) page to learn more.
The emitter framework handles many complex issues for you while offering an easy-to-use API to convert your TypeSpec into source code or other object graphs. Visit the [emitter framework](./emitter-framework.md) page for more information.
### Semantic Walker
The Semantic Walker will visit every type in the TypeSpec program and call any callbacks you provide for that type. To use, import `navigateProgram` from `@typespec/compiler`. Starting a walk needs two parameters - the program to walk, and an object with callbacks for each type. For example, if we want to do something for every model in the program, we could do the following in our `$onEmit` function:
The Semantic Walker visits every type in the TypeSpec program and calls any callbacks you provide for that type. To use it, import `navigateProgram` from `@typespec/compiler`. Starting a walk requires two parameters - the program to walk, and an object with callbacks for each type. For instance, if we want to do something for every model in the program, we could do the following in our `$onEmit` function:
```typescript
navigateProgram(program, {
@ -150,15 +150,15 @@ navigateProgram(program, {
});
```
You can provide a callback for every kind of TypeSpec type. The walker will call your callback pre-order, i.e. as soon as it sees a type for the first time it will invoke your callback. You can invoke callback post-order instead by prefixing the type name with `exit`, for example `exitModel(m)`.
You can provide a callback for every kind of TypeSpec type. The walker will call your callback pre-order, i.e., it will invoke your callback as soon as it encounters a type for the first time. You can invoke a callback post-order instead by prefixing the type name with `exit`, for example, `exitModel(m)`.
Note that the semantic walker will visit all types in the program including built-in TypeSpec types and TypeSpec types defined by any libraries you're using. Care must be taken to filter out any types you do not intend to emit. Sometimes this is quite difficult, so a custom traversal may be easier.
Note that the semantic walker will visit all types in the program, including built-in TypeSpec types and TypeSpec types defined by any libraries you're using. You must filter out any types you do not intend to emit. Sometimes this can be quite challenging, so a custom traversal may be easier.
### Custom traversal
Often times you will want to emit specific types, for example types that have a particular decorator or are in a particular namespace. In such cases it is often easier to write a custom traversal to find and emit those types. Once you have a type, you can access its [various fields](#todo) to and emit those types as well if needed.
Often, you'll want to emit specific types, such as types that have a particular decorator or are in a specific namespace. In such cases, it's often easier to write a custom traversal to find and emit those types. Once you have a type, you can access its various fields and emit those types as well if needed.
For example, let's say we want to emit a text file of model names but only if it has an `@emitThis` decorator. We could filter out such models in the Semantic Walker `model` callback, but it is more efficient to implement `@emitThis` such that it keeps a list of all the types its attached to and iterate that list. We can then traverse into types it references if needed.
For instance, let's say we want to emit a text file of model names but only if it has an `@emitThis` decorator. We could filter out such models in the Semantic Walker `model` callback, but it's more efficient to implement `@emitThis` such that it keeps a list of all the types it's attached to and iterate that list. We can then traverse into types it references if needed.
The following example will emit models with the `@emitThis` decorator and also any models referenced by that model.
@ -192,7 +192,7 @@ function emitModel(model: Model) {
### Resolving a TypeSpec type
Sometimes you might want to get access to a known TypeSpec type in the type graph, for example a model that you have defined in your library.
Sometimes you might want to access a known TypeSpec type in the type graph, for example, a model that you have defined in your library.
A helper is provided on the program to do that.
@ -220,22 +220,22 @@ program.resolveTypeReference("model Foo {}"); // Resolve `[undefined, diagnostic
Since an emitter is a Node library, you could use standard `fs` APIs to write files. However, this approach has a drawback - your emitter will not work in the browser, and will not work with the test framework that depends on storing emitted files in an in-memory file system.
Instead, use the compiler [`host` interface](#todo) to access the file system. The API is equivalent to the Node API but works in a wider range of scenarios.
Instead, use the compiler's `host` interface to access the file system. This API is equivalent to the Node API but works in a broader range of scenarios.
In order to know where to emit files, the emitter context has a `emitterOutputDir` property that is automatically resolved using the `emitter-output-dir` built-in emitter options. This is set to `{cwd}/tsp-output/{emitter-name}` by default, but can be overridden by the user. Do not use the `compilerOptions.outputDir`
To know where to emit files, the emitter context has an `emitterOutputDir` property that is automatically resolved using the `emitter-output-dir` built-in emitter options. By default, this is set to `{cwd}/tsp-output/{emitter-name}`, but it can be overridden by the user. Do not use the `compilerOptions.outputDir`.
## Handling scalars
## Dealing with scalars
Scalars are types in TypeSpec that most likely have a primitive or built-in datastructure representing those in the target language.
Scalars are types in TypeSpec that are most likely represented by a primitive or built-in data structure in the target language.
Recommended logic for emitting scalar is to:
The recommended logic for emitting a scalar is as follows:
1. If scalar is a known scalar (e.g. `int32`), emit the known mapping.
2. Otherwise check scalar `baseScalar` and go back to `1.`
2.1 After resolving which scalar apply any decorators
1. If the scalar is a known scalar (e.g., `int32`), emit the known mapping.
2. Otherwise, check the scalar `baseScalar` and go back to step 1.
2.1 After resolving which scalar to use, apply any decorators.
:::note
If the scalar is generic and doesn't have a mapping (e.g. integer), we recommend substituting it with the next closest mapping (e.g. integer->int64) and emitting a warning.
If the scalar is generic and doesn't have a mapping (e.g., integer), we recommend substituting it with the next closest mapping (e.g., integer->int64) and emitting a warning.
:::
### Examples
@ -255,7 +255,7 @@ scalar specializedInt32 extends myInt32;
| `specializedInt32` | `int32` | Emitter doesn't know what specializedInt32 is. Check baseScalar, finds myInt32 knows that it is an int32 now and applies minValue override. |
| `float` | `float64` | Emitter knows float but doesn't have a mapping. Emit `float64` and a warning. |
## Handling Default Values
## Managing Default Values
Several TypeSpec types have a `default` property that can be used to specify a default value. For example, the following model has a default value of `true` for the `isActive` property:
@ -272,4 +272,4 @@ const modelProp: ModelProperty = ...; // the isActive ModelProperty type
const defaultValue = modelProp.default; // value: true
```
It is important that emitters handle default values in a consistent way. Default values SHOULD NOT be used as client-side default values. Instead, they should be used as a way to specify a default value for the server-side implementation. For example, if a model property has a default value of `true`, the server-side implementation should use that value if the client does not provide a value. Default values SHOULD be expressed in documentation to properly communicate the service-side default.
It's important that emitters handle default values consistently. Default values SHOULD NOT be used as client-side default values. Instead, they should be used to specify a default value for the server-side implementation. For example, if a model property has a default value of `true`, the server-side implementation should use that value if the client does not provide a value. Default values SHOULD be expressed in documentation to properly communicate the server-side default.

Просмотреть файл

@ -1,22 +1,21 @@
---
id: linters
title: Linters
title: Understanding linters
---
# Linters
# Understanding linters
## Linter vs `onValidate`
## Linter versus `onValidate`
TypeSpec library can probide a `$onValidate` hook which can be used to validate the TypeSpec program is valid in the eye of your library.
A TypeSpec library can provide an `$onValidate` hook, which can be used to validate whether the TypeSpec program is valid according to your library's rules.
A linter on the other hand might be a validation that is optional, the program is correct but there could be some improvements. For example requiring documentation on every type. This is not something that is needed to represent the TypeSpec program but without it the end user experience might suffer.
Linters need to be explicitly enabled. `$onValidate` will be run automatically if that library is imported.
On the other hand, a linter might provide optional validation. The program could be correct, but there might be room for improvements. For instance, a linter might require documentation on every type. While this isn't necessary to represent the TypeSpec program, it could enhance the end user experience. Linters need to be explicitly enabled, whereas `$onValidate` will run automatically if that library is imported.
## Writing a linter
## Creating a linter
See examples in `packages/best-practices`.
You can find examples in `packages/best-practices`.
### 1. Define a rules
### 1. Define rules
```ts
import { createLinterRule } from "@typespec/compiler";
@ -90,7 +89,7 @@ context.reportDiagnostic({
});
```
#### Don'ts
#### Things to avoid
- ❌ Do not call `program.reportDiagnostic` or your library `reportDiagnostic` helper directly in a linter rule
@ -149,14 +148,13 @@ export const $linter = defineLinter({
});
```
When referencing a rule or ruleset(in `enable`, `extends`, `disable`) the rule or rule set id must be used which in this format: `<libraryName>/<ruleName>`
When referencing a rule or ruleset (in `enable`, `extends`, `disable`), you must use the rule or rule set id, which is in this format: `<libraryName>/<ruleName>`.
## Testing a linter
To test linter rule an rule tester is provided letting you test a specific rule without enabling the others.
To test a linter rule, a rule tester is provided, allowing you to test a specific rule without enabling the others.
First you'll want to create an instance of the rule tester using `createLinterRuleTester` passing it the rule that is being tested.
You can then provide different test checking the rule pass or fails.
First, you'll want to create an instance of the rule tester using `createLinterRuleTester`, passing it the rule that is being tested. You can then provide different tests to check whether the rule passes or fails.
```ts
import { RuleTester, createLinterRuleTester, createTestRunner } from "@typespec/compiler/testing";
@ -187,11 +185,10 @@ describe("required-doc rule", () => {
The linter rule tester provides an API to easily test a codefix. This is a different approach from the standalone codefix tester, which is more targeted at testing codefixes in isolation.
This can be done with calling `applyCodeFix` with the fix id. It will expect a single diagnostic to be emitted with a codefix with the given id.
Then calling `toEqual` with the expected code after the codefix is applied.
This can be done by calling `applyCodeFix` with the fix id. It will expect a single diagnostic to be emitted with a codefix with the given id. Then, call `toEqual` with the expected code after the codefix is applied.
:::note
When using multi-line strings (with `\``) in typescript there is no de-indenting done so you will need to make sure the input and expected result are aligned to the left.
When using multi-line strings (with `\``) in TypeScript, there is no de-indenting done, so you will need to make sure the input and expected result are aligned to the left.
:::
```ts

Просмотреть файл

@ -2,19 +2,19 @@
title: Scaffolding templates
---
# Writting a scaffolding template
# Creating a scaffolding template
typespec provides scaffolding functionality via the `tsp init` command.
TypeSpec offers a scaffolding feature through the `tsp init` command.
```bash
tsp init <templateUrl>
```
## Specifying a minimum TypeSpec version
## Setting a minimum TypeSpec version
If your template needs a functionality that was only added in a newer version of TypeSpec you might want to specify that in the template. This will warn the user that the template might not work as expected and prompt them to confirm that they want to continue.
If your template requires a feature that was introduced in a later version of TypeSpec, you can specify this in the template. This will alert the user that the template may not function as expected and ask them to confirm if they wish to proceed.
To do so you can set `compilerVersion` in the each template configuration. The value is the minimum semver version required.
You can set the `compilerVersion` in each template configuration. The value should be the minimum semver version required.
```json
{
@ -22,18 +22,17 @@ To do so you can set `compilerVersion` in the each template configuration. The v
}
```
## Basic
## Basics of a scaffolding template
A scaffolding template is a `json` document that can be hosted locally or online.
The root of the document is a dictionary allowing multiple templates to be hosted at the same location.
A scaffolding template is a `json` document that can be hosted either locally or online. The document's root is a dictionary, allowing for multiple templates to be hosted in the same location.
Each template needs at the minimum:
Each template must include:
- key: Key of the template
- title: Human readable name of the template
- description: Extended description of the template.
- key: The template's key
- title: A user-friendly name for the template
- description: A detailed description of the template.
Example:
Here's an example:
```json
{
@ -48,27 +47,26 @@ Example:
}
```
## Adding libraries
## Including libraries
You can add a list of TypeSpec libraries to include. This will automatically add those libraries to the `package.json` and imported in `main.tsp`.
You can include a list of TypeSpec libraries. These will be automatically added to the `package.json` and imported in `main.tsp`.
```json
{
"rest": {
"title": "REST API",
"description": "Create a new project representing a REST API",
"libraries": ["@typespec/rest", "@typespec/openapi3"]
"libraries": ["/rest", "@typespec/openapi3"]
}
}
```
## Adding new files
Additional files.typespec or other types) can be generated by the initializer. The template takes a list of the files to copy and interpolate values.
Each file need the following properties:
The initializer can generate additional files (either .typespec or other types). The template includes a list of files to copy and interpolate values. Each file requires the following properties:
- `path`: Absolute or relative path(to the template file) to the file
- `destination`: Relative path of the file relative to the project root.
- `path`: The absolute or relative path (relative to the template file) to the file
- `destination`: The file's relative path, relative to the project root.
```json
{
@ -90,32 +88,30 @@ model {{parameters.ModelName}} {
### Interpolating values
The template can interpolate values in the files. The values available are anything available in the template configuration referenced as it is.
Examples:
The template can interpolate values in the files. The available values are anything in the template configuration, referenced as is. For example:
- Reference a parameter `{{parameters.ModelName}}`
- Reference a the template title `{{title}}`
- To reference a parameter, use `{{parameters.ModelName}}`
- To reference the template title, use `{{title}}`
Additionally the following values and functions are available:
Additionally, the following values and functions are available:
| Name | Description |
| ------------------------------------- | --------------------------------------------------------------- |
| `directory` | Directory full path where the project should be initialized. |
| `folderName` | Folder name where the project should be initialized. |
| `name` | Name of the project. |
| `libraries` | List of libraries to include |
| `templateUri` | Path where this template was loaded from. |
| Functions | |
| `toLowerCase(value: string)` | Convert string to lower case |
| `normalizePackageName(value: string)` | Normalize package name. It replaces `.` with`-` and toLowerCase |
| `casing.pascalCase(value: string)` | Convert string to PascalCase |
| `casing.camelCase(value: string)` | Convert string to camelCase |
| `casing.kebabCase(value: string)` | Convert string to kebab-case |
| Name | Description |
| ------------------------------------- | --------------------------------------------------------------------------------- |
| `directory` | The full directory path where the project should be initialized. |
| `folderName` | The name of the folder where the project should be initialized. |
| `name` | The name of the project. |
| `libraries` | The list of libraries to include. |
| `templateUri` | The path from where this template was loaded. |
| Functions | |
| `toLowerCase(value: string)` | Converts a string to lower case. |
| `normalizePackageName(value: string)` | Normalizes the package name. It replaces `.` with `-` and converts to lower case. |
| `casing.pascalCase(value: string)` | Converts a string to PascalCase. |
| `casing.camelCase(value: string)` | Converts a string to camelCase. |
| `casing.kebabCase(value: string)` | Converts a string to kebab-case. |
## Demanding additional input from the user
## Requesting additional user input
When generating files there might be a need for additional inputs to be retrieved from the user. For example the model name.
The template takes in a map of inputs that will get prompted to the user during initialization.
When generating files, you may need additional input from the user, such as the model name. The template includes a map of inputs that will be prompted to the user during initialization.
```json
{
@ -132,6 +128,6 @@ The template takes in a map of inputs that will get prompted to the user during
}
```
Types of input supported:
Supported input types:
- `text`: Ask for a raw text value.
- `text`: Requests a raw text value.

Просмотреть файл

@ -3,6 +3,6 @@ id: getting-started
title: Getting Started
---
# Getting Started with TypeSpec
# Getting started with TypeSpec
- [Get started with HTTP in TypeSpec](./getting-started-http.md)

Просмотреть файл

@ -2,9 +2,9 @@
title: Configuration
---
# Compiler and Libraries configurations
# Compiler and library configurations
The TypeSpec compiler and libraries can be configured either via a [configuration file](#configuration-file) or [command line flags](#command-line-flags).
The TypeSpec compiler and libraries can be configured either via a [configuration file](#configuration-file) or command line flags.
## Configuration file
@ -46,13 +46,13 @@ model LinterConfig {
}
```
### Extending project files
### Extending Project Files
There is cases where you might want to build different folders with different options (for example different emitters) but want to share some configuration for both as well.
There may be instances where you want to build different folders with varying options (such as different emitters), but still want to share some common configurations.
For that you can use the `extends` property of the configuration file
In such cases, you can use the `extends` property in the configuration file.
in `<my-pkg>/tspconfig.yaml`
For instance, in `<my-pkg>/tspconfig.yaml`:
```yaml
options:
@ -72,7 +72,7 @@ emit:
### Variable interpolation
The TypeSpec project file provide variable interpolation using:
The TypeSpec project file provides variable interpolation using:
- built-in variables
- environment variables
@ -86,14 +86,14 @@ Examples:
- `{output-dir}/my-path`
- `{env.SHARED_PATH}/my-path`
### Emitter path config interpolation
### Interpolation of Emitter Path Config
Some config of emitters can be interpolated using a special rule that will collapse a path.
Certain emitter configurations can be interpolated using a specific rule designed to collapse a path.
If a variable is followed by a `/` or `.` and the emitter interpolating the config doesn't provide that variable it will then omit the path segment.
If a variable is succeeded by a `/` or `.` and the emitter responsible for interpolating the config doesn't supply that variable, the path segment will be omitted.
For example given the following config value: `{service-name}/output.{version}.json`
The following would get produced
For instance, consider the following config value: `{service-name}/output.{version}.json`
Here's what would be produced:
| Service name value | Version value | Result |
| ------------------ | ------------- | ------------------------- |
@ -111,15 +111,15 @@ The following would get produced
| `output-dir` | emitter options | Common `output-dir` See [output-dir](#output-dir---configure-the-default-output-dir) |
| `emitter-name` | emitter options | Name of the emitter |
#### Project parameters
#### Project Parameters
A TypeSpec project file can specify some parameters that can then be specified via the CLI.
A TypeSpec project file can define certain parameters that can subsequently be specified through the CLI.
`{cwd}` and `{project-root}` variables can be used in the default value of those parmeters.
The `{cwd}` and `{project-root}` variables can be utilized in the default value of these parameters.
The parameters can then be referenced by their name in a variable interpolation expression.
These parameters can then be referred to by their name in a variable interpolation expression.
Parameters must have a default value.
All parameters must have a default value.
**Example:**
```yaml
@ -130,7 +130,7 @@ parameters:
output-dir: {base-dir}/output
```
The parameter can then be specified with `--arg` in this format `--arg "<parameter-name>=<value>"`
The parameter can then be specified via `--arg` in this format `--arg "<parameter-name>=<value>"`
```bash
tsp compile . --arg "base-dir=/path/to/base"
@ -138,13 +138,13 @@ tsp compile . --arg "base-dir=/path/to/base"
#### Environment variables
A TypeSpec project file can define which environment variables it can interpolate.
A TypeSpec project file can specify which environment variables it can interpolate.
`{cwd}` and `{project-root}` variables can be used in the default value of the environment variables.
The `{cwd}` and `{project-root}` variables can be used in the default value of these environment variables.
The environment variables can then be referenced by their name in a variable interpolation expression with the `env.` prefix.
These environment variables can then be referred to by their name in a variable interpolation expression, using the `env.` prefix.
Environment variables must have a default value.
All environment variables must have a default value.
**Example:**
@ -156,11 +156,11 @@ environment-variables:
output-dir: {env.BASE_DIR}/output
```
#### Emitter options
#### Emitter Options
Emitter options can reference each other using the other option name as the variable expresion.
Emitter options can refer to each other by using the other option's name as the variable expression.
Can only interpolate emitter options from the same emitter.
Interpolation is only possible among emitter options from the same emitter.
```yaml
options:
@ -185,7 +185,7 @@ options:
### `output-dir` - Configure the default output dir
Specify the common output-dir for all emitters. See [this](#output-directory-configuration) to configure per emitter.
Specify the common output-dir for all emitters. See [this](#configuring-output-directory) to configure per emitter.
```yaml
output-dir: {cwd}/typespec-build
@ -199,7 +199,7 @@ tsp compile . --output-dir "./typespec-build"
Output dir must be an absolute path in the config. Use `{cwd}` or `{project-root}` to explicitly specify what it should be relative to.
See [output directory configuration for mode details](#output-directory-configuration)
See [output directory configuration for mode details](#configuring-output-directory)
### `trace` - Configure what to trace
@ -221,11 +221,11 @@ Trace can be provided using the `--trace` cli flag
tsp compile . --trace import-resolution --trace projection
```
### `warn-as-error` - Treat warning as error
### `warn-as-error` - Treating Warnings as Errors
All warnings will be emitted as error. Result in a non zero exit code in case of warning.
All warnings will be treated and emitted as errors, resulting in a non-zero exit code in the event of a warning.
**This is recommended to use in CI to prevent warning from being unadressed.**
**It is recommended to use this feature in Continuous Integration (CI) to ensure all warnings are addressed.**
```yaml
warn-as-error: true
@ -307,11 +307,11 @@ Represent the path where the emitter should be outputing the generated files.
Default: `{output-dir}/{emitter-name}`
See [output directory configuration for mode details](#output-directory-configuration)
See [output directory configuration for mode details](#configuring-output-directory)
### `linter` - Configuring linters
### `linter` - Setting Up Linters
Configure which linter rules should be enabled in this repository. Referencing to a rule or ruleset must be using their id which is in this format `<libraryName>:<ruleName>`
This allows you to configure the linter rules to be enabled in this repository. When referencing a rule or ruleset, use their ID, which follows the format `<libraryName>:<ruleName>`.
```yaml
linter:
@ -325,13 +325,13 @@ linter:
"@typespec/best-practices/no-y": "This rule cannot be applied in this project because X"
```
## Emitter control cli flags
## CLI Flags for Emitter Control
### `--no-emit`
Disable emitting. If emitters are still specified it will still run the emitter but emitters shouldn't be writing anything to disk.
This flag disables emitting. If emitters are still specified, the emitter will run but it should not write anything to the disk.
Can also be used to hide the "There is no emitters warning".
This flag can also be used to suppress the "There are no emitters" warning.
```yaml
tsp compile . --no-emit
@ -385,22 +385,20 @@ Enable/Disable pretty logging (colors, diagnostic preview, etc.).
tsp compile . --pretty=false
```
## Output directory configuration
## Configuring Output Directory
Typespec compiler will provide a unique output directory for each emitter that is being run to reduce conflicts.
By default the output-dir of an emitter is set to this value:
The TypeSpec compiler assigns a unique output directory to each emitter that runs, in order to minimize conflicts. By default, the output directory of an emitter is set to:
```
{output-dir}/{emitter-name}
```
where
where:
- `output-dir` is the compiler common `output-dir` that can be configured via `--output-dir`
- `emitter-name` is the name of the emitter package (for example `@typespec/openapi3`)
- `output-dir` is the common output directory for the compiler, which can be configured via `--output-dir`.
- `emitter-name` is the name of the emitter package (for example, `/openapi3`).
Example:
Given the following emitters: `@typespec/openapi3` and `@typespec/jsonschema`, the default output folder structure would be
For instance, if the emitters `@typespec/openapi3` and `@typespec/jsonschema` are given, the default output folder structure would be:
```
{project-root}/tsp-output:
@ -411,7 +409,7 @@ Given the following emitters: `@typespec/openapi3` and `@typespec/jsonschema`, t
... json schema files ...
```
Changing the compiler `output-dir` with `--output-dir` or setting that value in the tspconfig.yaml would result in the following structure
You can change the compiler's `output-dir` with `--output-dir` or by setting that value in the tspconfig.yaml, which would result in the following structure:
```
--output-dir={cwd}/my-custom-output-dir
@ -422,10 +420,9 @@ Changing the compiler `output-dir` with `--output-dir` or setting that value in
... openapi3 files ...
jsonschema
... json schema files ...
```
Changing a specific emitter output-dir can be done by setting that emitter `emitter-output-dir` option
To change a specific emitter's output directory, you can set the `emitter-output-dir` option for that emitter:
```
--option "@typespec/openapi3.output-dir={projectroot}/openapispec"

Просмотреть файл

@ -4,23 +4,23 @@ title: Tracing
# Tracing
By default the TypeSpec Compiler will build without any debug information. The standard output will be minimal and limited to any `warning` or `error` diagnostics emitted during compilation.
The TypeSpec Compiler, by default, builds without any debug information. The standard output is minimal, only including any `warning` or `error` diagnostics that occur during the compilation process.
Some additional information is however being collected and can be revealed using the `--trace` cli flag.
However, the compiler does collect additional information that can be accessed using the `--trace` command-line interface (CLI) flag.
```bash
tsp compile . --trace import-resolution
```
You can use the `--trace` option multiple times if there is multiple areas that should be logged from.
If you want to log multiple areas, you can use the `--trace` option more than once.
```bash
tsp compile . --trace import-resolution --trace projection
```
Using `--trace *` will log everything. This might be a bit overwhelming but you can [pick and choose which trace area to include](#trace-selection)
To log everything, use `--trace *`. This might produce a lot of output, but you can [select specific trace areas to include](#selecting-trace-areas)
It can also be provided via the `tspconfig.yaml` file:
You can also specify the trace areas in the `tspconfig.yaml` file:
```yaml
trace: *
@ -30,55 +30,54 @@ trace:
- projection
```
## Trace selection
## Selecting trace areas
The tracing system in the tsp compiler works by having each trace under an area. The area name is a dot `.` separated string of area segments.
The tracing system in the tsp compiler organizes each trace under a specific area. The area name is a dot `.` separated string of area segments.
When filtering which area to select you can use this area path to select which area is going to be revealed.
The filter follow the same naming style, except the last segment could be a wildcard `*`. This is however the same result as omitting the last segment all together. In other words, those filter have the exact same behavior:
To filter the areas you want to trace, you can use this area path. The filter follows the same naming style, but the last segment can be a wildcard `*`. However, this produces the same result as leaving out the last segment. In other words, these filters behave identically:
- `foo` and `foo.*`
- `one.two` and `one.two.*`
For example, assuming we'd have those 3 areas
For instance, if we have these three areas:
- `one.two.three`
- `one.foo`
- `bar.info`
Using:
You can use:
- `*` will log everything
- `one` will log everything under `one`(`one.two.three`, `one.foo`)
- `bar` will log everything under `bar`(`bar.info`)
- `one.foo` will log everything under `one.foo`(`one.foo`)
- `other` will log everything under `other` which is nothing here.
- `*` to log everything
- `one` to log everything under `one`(`one.two.three`, `one.foo`)
- `bar` to log everything under `bar`(`bar.info`)
- `one.foo` to log everything under `one.foo`(`one.foo`)
- `other` to log everything under `other`, which is nothing in this case.
## Compiler Trace Areas
## Trace areas in the compiler
This is a list of the trace area used in the compiler
Here is a list of the trace areas used in the compiler:
| Area | Description |
| ------------------------------ | -------------------------------------------------------------------- |
| `compiler.options` | Log the resolved compiler options |
| `import-resolution.library` | Information related to the resolution of import libraries |
| `projection.log` | Debug information logged by the `log()` function used in projections |
| `bind.js` | Information when binding JS files |
| `linter.register-library` | Information that a library rules will be loaded |
| `linter.register-library.rule` | Information about a rule that is being registered |
| `linter.extend-rule-set.start` | Information about a ruleset it is about to extend |
| `linter.extend-rule-set.end` | Information about rules enabled after extending a ruleset |
| `linter.lint` | Start the lint process and show information of all the rules enabled |
| Area | Description |
| ------------------------------ | ---------------------------------------------------------------------- |
| `compiler.options` | Logs the resolved compiler options |
| `import-resolution.library` | Logs information related to the resolution of import libraries |
| `projection.log` | Logs debug information from the `log()` function used in projections |
| `bind.js` | Logs information when binding JS files |
| `linter.register-library` | Logs information when a library's rules are being loaded |
| `linter.register-library.rule` | Logs information about a rule that is being registered |
| `linter.extend-rule-set.start` | Logs information about a ruleset that is about to be extended |
| `linter.extend-rule-set.end` | Logs information about rules enabled after extending a ruleset |
| `linter.lint` | Starts the lint process and shows information of all the rules enabled |
## Tracing in TypeSpec library
## Tracing in TypeSpec libraries
TypeSpec libraries can emit their own tracing that can be collected using the same mechanism. It is recommended that a library scope their tracing area under the library name to prevent collision. This can be achieved by calling the `sub(subArea: string)` method on the tracer.
TypeSpec libraries can also emit their own traces that can be collected using the same mechanism. To avoid naming conflicts, it's recommended that a library prefixes their tracing area with the library name. This can be done by calling the `sub(subArea: string)` method on the tracer.
```ts
const tracer = program.tracer.sub("my-library");
```
the tracer is then available for trace collection
The tracer can then be used for trace collection:
```ts
tracer.trace("emitting-ts", "Emitting ts interface");

Просмотреть файл

@ -3,14 +3,13 @@ id: faq
title: FAQ
---
# Frequently Asked Questions
# Frequently Asked Questions (FAQ)
## `Cannot find package 'x' imported from 'y'` but not using this package
## I'm getting the error `Cannot find package 'x' imported from 'y'`, but I'm not using this package. Why?
This is most likely due to package y having a `peerDependency` on package `x` and package x wasn't installed.
Verify the version of npm you are using. Before version 7 peerDependencies would not get installed automatically and you would have to manually install them.
This issue typically arises when package 'y' has a `peerDependency` on package 'x', and package 'x' hasn't been installed. This can occur if you're using a version of npm that's older than version 7, as these older versions don't automatically install peerDependencies. You would need to install them manually.
### Solutions
### How can I fix this?
- Update npm `npm install -g npm`
- If you cannot update npm, then adding the dependencies to your project dependency should resolve the issue `npm install x`
- You can update npm using the command `npm install -g npm`.
- If you're unable to update npm, you can add the dependencies to your project dependency. This should resolve the issue. Use the command `npm install x`.

Просмотреть файл

@ -9,7 +9,7 @@ TypeSpec comes with a built-in formatter. The formatter can be used in different
- [Via the cli](#via-the-cli)
- Via the vscode/vs extension
- As a prettier plugin
- As a `prettier` plugin
## Via the cli
@ -19,7 +19,7 @@ Format all TypeSpec files:
tsp format "**/*.tsp"
```
Validate that the files are formatted but don't format them. Useful for enforcing in CI.
Check file formatting without modifying them, useful for CI enforcement.
```bash
tsp format --check "**/*.tsp"
@ -27,15 +27,15 @@ tsp format --check "**/*.tsp"
## Via the VSCode or VS extension
When using the VS Code or Visual Studio extensions, the tsp formatter is automatically available.
When you use the extensions for VS Code or Visual Studio, the tsp formatter becomes automatically accessible.
Using the keyboard shortcut for formatting the document (`alt+shift+F` by default) when inside a TypeSpec file will format the document.
If you're working within a TypeSpec file, you can format the document using the default keyboard shortcut for formatting, `alt+shift+F`.
## Via prettier
Underneath the tsp formatter is a prettier plugin. If you already have a prettier configuration for formatting other languages it can be convenient to just have TypeSpec plug in into this existing pipeline.
The tsp formatter is essentially a `prettier` plugin. If you already have a `prettier` configuration set up for other languages, it can be quite handy to simply integrate TypeSpec into this existing pipeline.
In your prettier config file, add:
In your `prettier` config file, add:
```yaml
plugins:

Просмотреть файл

@ -5,13 +5,13 @@ title: Releases
# Releases
## Package versioning strategy
## Versioning strategy for packages
TypeSpec is not stable yet, all packages are released with `0.` major version. Each minor version might have some breaking changes to the TypeSpec language, library API or both. Those are documented [here](../release-notes).
TypeSpec is currently in its development phase, and all packages are released with a `0.` major version. Each minor version may introduce some breaking changes to the TypeSpec language, library API, or both. These changes are documented [here](../release-notes).
Every change to the `main` branch is automatically published under the npm `@next` tag.
Any modification to the `main` branch is automatically published under the npm `@next` tag.
## Current packages
## Available packages
| Name | Changelog | Latest | Next |
| -------------------------------------------------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
@ -49,9 +49,9 @@ Every change to the `main` branch is automatically published under the npm `@nex
## Release cadence
We release changes from all packages the first week of every month.
We roll out updates for all packages during the first week of each month.
You can look at the millestones https://github.com/microsoft/typespec/milestones to see upcoming changes. Millestones are named after the target release month (i.e `[2022] October` is the sprint running in september targeting a release in the first week of October.)
To preview upcoming changes, you can check the milestones at https://github.com/microsoft/typespec/milestones. Milestones are labeled according to the target release month.
## Breaking changes migration guides

Просмотреть файл

@ -2,13 +2,13 @@
title: Reproducibility
---
A key point to service definition is the ability to reliably reproduce the exact same output over time. In cases like:
A crucial aspect of service definition is ensuring consistent output over time. This is important in scenarios such as:
1. A dependency or dependency of dependency was updated with an unintended breaking change
2. Changes to a new version of a service shouldn't affect the older versions
3. A change to the TypeSpec spec
- An update to a dependency or sub-dependency introduces an unexpected breaking change.
- Updates to a new version of a service should not impact older versions.
- Modifications are made to the TypeSpec specification.
This can be mitigated with a few steps:
These issues can be mitigated with a few precautionary measures:
## 1. Defend against dependency changes

Просмотреть файл

@ -2,9 +2,9 @@
title: Style guide
---
# TypeSpec Language Style Guide
# TypeSpec language style guide
This is a guide providing a recommended set of naming convention to use when writing a TypeSpec spec.
This guide offers a recommended set of naming conventions to follow when drafting a TypeSpec specification.
:::info
The guidelines in this article are used in TypeSpec Core libraries. You can use them, or adapt them to your needs. The primary objectives are consistency and readability within your project, team, organization, or company source code.
@ -167,7 +167,7 @@ alias foo = [1, 2, 3];
### Model layout
- Properties should hug each other unless it has decorators or comments
- Properties should hug each other unless they have decorators or comments
<!-- prettier-ignore -->
```tsp
@ -188,7 +188,7 @@ model Foo {
}
```
- Wrap properties in new lines if it has leading comments or decorators
- Wrap properties in new lines if they have leading comments or decorators
<!-- prettier-ignore -->
```tsp

Просмотреть файл

@ -17,7 +17,7 @@ Install the extension via the Visual Studio extension manager from the [TypeSpec
}
```
TypeSpec wil interpolate a few variables using this pattern: `${<name>}`. For example: `${workspaceFolder}`.
TypeSpec will interpolate a few variables using this pattern: `${<name>}`. For example: `${workspaceFolder}`.
Available variables:

Просмотреть файл

@ -8,11 +8,11 @@ Install the extension via the Visual Studio Code extension manager https://marke
## Configure
TypeSpec wil interpolate a few variables using this pattern: `${<name>}`. For example: `${workspaceFolder}`.
TypeSpec will interpolate a few variables using this pattern: `${<name>}`. For example: `${workspaceFolder}`.
Available variables:
- `workspaceFolder`: Corespond to the root of your Visual Studio workspace.
- `workspaceFolder`: Corresponds to the root of your Visual Studio workspace.
### `typespec.tsp-server.path`: Configure the server path

Просмотреть файл

@ -3,13 +3,11 @@ id: aliases
title: Aliases
---
# Alias
# Aliases
Aliases can be defined for types. This can be helpful to reuse a complex expression.
Aliases are a convenient way to define shorthand for types, especially when dealing with complex expressions. They simplify the syntax but don't have a representation in the type graph. As a result, you can't decorate aliases. If you need to give an alternate name to a model, use [`model is`](./models.md).
Alias is only a syntax helper, and it has no representation in the type graph. This means that aliases cannot be decorated. Use [`model is`](./models.md) to provide an alternate name for a model.
Alias can be defined using the `alias` keyword
You can define an alias using the `alias` keyword.
```typespec
alias Options = "one" | "two";

Просмотреть файл

@ -3,7 +3,7 @@ id: built-in-types
title: Built-in types
---
# Built-in Types
# Built-in types
TypeSpec Standard Library provide some built-in types that can be used to build more complex types.

Просмотреть файл

@ -5,17 +5,17 @@ title: Decorators
# Decorators
Decorators enable a developer to attach metadata to types in a TypeSpec program. They can also be used to calculate types based on their inputs. Decorators are the backbone of TypeSpec's extensibility and give it the flexibility to describe many different kinds of APIs and associated metadata like documentation, constraints, samples, and the like.
Decorators in TypeSpec allow developers to attach metadata to types within a TypeSpec program. They can also be used to compute types based on their inputs. Decorators form the core of TypeSpec's extensibility, providing the flexibility to describe a wide variety of APIs and associated metadata such as documentation, constraints, samples, and more.
Many TypeSpec constructs can be decorated, including [namespaces](./namespaces.md), [operations](./operations.md) and their parameters, and [models](./models.md) and their members.
A range of TypeSpec constructs can be decorated, including [namespaces](./namespaces.md), [operations](./operations.md) and their parameters, and [models](./models.md) and their members.
Decorators are defined using JavaScript functions that are exported from a standard ECMAScript module. When you import a JavaScript file, TypeSpec will look for any exported functions prefixed with `$`, and make them available as decorators inside the TypeSpec syntax. When a decorated declaration is evaluated by TypeSpec, it will invoke the decorator function, passing along a reference to the current compilation, an object representing the type it is attached to, and any arguments the user provided to the decorator.
Decorators are defined using JavaScript functions that are exported from a standard ECMAScript module. When a JavaScript file is imported, TypeSpec will look for any exported functions prefixed with `$`, and make them available as decorators within the TypeSpec syntax. When a decorated declaration is evaluated by TypeSpec, the decorator function is invoked, passing along a reference to the current compilation, an object representing the type it is attached to, and any arguments the user provided to the decorator.
## Using decorators
## Applying decorators
Decorators are referenced using the `@` prefix and must be specified before the entity they are decorating. Arguments can be provided by using parentheses in a manner similar to many programming languages, e.g. `@myDec1, "hi", { a: string })`.
Decorators are referenced using the `@` prefix and must be placed before the entity they are decorating. Arguments can be provided by using parentheses, similar to function calls in many programming languages, e.g., `@myDec1("hi", { a: string })`.
The following shows an example of declaring and then using a decorator:
Here's an example of declaring and then using a decorator:
```typespec
@tag("Sample")
@ -25,16 +25,16 @@ model Dog {
}
```
The parentheses can be omitted when no arguments are provided.
If no arguments are provided, the parentheses can be omitted.
```typespec
@mark
model Dog {}
```
## Augment decorators
## Augmenting decorators
Decorators can also be used from a different location by referring to the type being decorated. For this you can declare an augment decorator using the `@@` prefix. The first argument of an augment decorator is the type reference that should be decorated. As the augment decorator is a statement, it must end with a semicolon (`;`).
Decorators can also be applied from a different location by referring to the type being decorated. For this, you can declare an augment decorator using the `@@` prefix. The first argument of an augment decorator is the type reference that should be decorated. As the augment decorator is a statement, it must end with a semicolon (`;`).
```typespec
model Dog {}
@ -42,14 +42,14 @@ model Dog {}
@@tag(Dog, "Sample");
```
Which is equivalent to
This is equivalent to:
```typespec
@tag("Sample")
model Dog {}
```
Example: Decorate a model property
Example: decorating a model property
```typespec
model Dog {
@ -59,6 +59,6 @@ model Dog {
@@readOnly(Dog.name);
```
## Writing decorator
## Creating decorators
[See creating decorator documentation](../extending-typespec/create-decorators.md)
_For more information on creating decorators, see the [Creating Decorators Documentation](../extending-typespec/create-decorators.md)._

Просмотреть файл

@ -5,20 +5,20 @@ title: Documentation
# Documentation
Documentation is crucial to any API. TypeSpec provides a number of ways to document your API using doc comments and decorators.
Documentation is a vital aspect of any API. TypeSpec offers several ways to document your API, including doc comments and decorators.
# Documenting APIs
# Approaches to documenting APIs
There are 2 ways to document your API using TypeSpec:
TypeSpec provides two primary methods for documenting your API:
- `@doc` decorator
- `/** */` Doc comments
The later has the advantage of being less intrusive to the spec.
The latter is less intrusive to the specification and is often preferred.
## `@doc` Decorator
## The `@doc` decorator
The `@doc` decorator can be used to attach documentation to most TypeSpec declarations. It most-commonly accepts a string argument that will be used as the documentation for the declaration.
The `@doc` decorator can be used to attach documentation to most TypeSpec declarations. It typically accepts a string argument that serves as the documentation for the declaration.
```typespec
@doc("This is a sample model")
@ -28,22 +28,22 @@ model Dog {
}
```
The `@doc` decorator can also accept a source object which can be used, for example, to provide templated documentation for a generic type.
The `@doc` decorator can also accept a source object, which can be used to provide templated documentation for a generic type, for example.
```typespec
@doc("Templated {name}", Type)
model Template<Type extends {}> {
}
// doc will read "Templated A"
// The documentation will read "Templated A"
model A is Template<A>
```
## Doc Comments
## Doc comments
You can annotate objects in your TypeSpec spec with doc comments. These comments will be considered the same as if they were attached using the `@doc` decorator and can be used to generate external documentation.
You can annotate objects in your TypeSpec specification with doc comments. These comments are treated as if they were attached using the `@doc` decorator and can be used to generate external documentation.
Doc comments starts with `/**` and continue until the closing `*/` is encountered. [Tags](#doc-comment-tags) can be used to provide additional documentation context.
Doc comments start with `/**` and continue until the closing `*/` is encountered. [Tags](#doc-comment-tags) can be used to provide additional documentation context.
```typespec
/**
@ -64,11 +64,11 @@ op read(
): Widget | Error;
```
The benefit to using doc comment syntax is that it keeps all of the documentation for a declaration in one place, making it easier to read and maintain. Additionally, it allows the generation of documentation using tools like TypeDoc without having to write a custom emitter to examine the `@doc` metadata.
The advantage of using doc comment syntax is that it keeps all of the documentation for a declaration in one place, making it easier to read and maintain. Additionally, it allows the generation of documentation using tools like TypeDoc without having to write a custom emitter to examine the `@doc` metadata.
### Doc comment tags
As shown in the previous example doc comments can use certain tags to document additional elements or provide different documentation context.
As shown in the previous example, doc comments can use certain tags to document additional elements or provide different documentation context.
| Tag | Description | Example |
| ----------------------- | --------------------------------- | --------------------------------------------------- |
@ -90,4 +90,4 @@ model Dog {
}
```
Comments are ignored by the compiler and are not included in the generated output. They are intended to be used to document your spec internally and are not suitable for generating external documentation.
Comments are ignored by the compiler and do not appear in the generated output. They are intended for internal documentation of your spec and are not suitable for generating external documentation.

Просмотреть файл

@ -5,12 +5,11 @@ title: Enums
# Enums
Enums allow a developer to define a set of named constants. Using enums can make it easier to document intent, or create a set of distinct cases. Enums can either be numeric or string-based. For other types, look into [union](./unions.md)
Enums, short for enumerations, provide a way for developers to define a collection of named constants. They are useful for documenting the purpose of the code or for establishing a set of distinct scenarios. Enums can be either numeric or string-based. For other data types, consider using [unions](./unions.md).
## Basics
## The basics
Enums are declared using the `enum` keyword.
The enums members are comma `,` separated and can be TypeSpec `identifier`s or `string literal`s.
You can declare enums using the `enum` keyword. The members of an enum are separated by commas `,` and can be either `identifier` TypeSpecs or `string literal`s.
```typespec
enum Direction {
@ -21,11 +20,11 @@ enum Direction {
}
```
In this case, we haven't specified how the constants will be represented. Different scenarios might handle the enums differently.
In the above example, we haven't defined the representation of the constants. Depending on the context, enums might be handled differently.
## Values
## Assigning values to enums
Enums members can have a custom value that can be assigned using the `:` operator.
You can assign custom values to enum members using the `:` operator.
```typespec
enum Direction {
@ -36,7 +35,7 @@ enum Direction {
}
```
Values can also be integers.
These values can also be integers.
```typespec
enum Foo {
@ -47,7 +46,7 @@ enum Foo {
}
```
or float
Or even floating-point numbers.
```typespec
enum Hour {
@ -58,9 +57,9 @@ enum Hour {
}
```
## Composing enums
## Combining enums
Enums can be reused using the spread `...` pattern. All the members of the source enums will be copied in the target enum but it doesn't create any reference between the source and target enums.
You can combine enums using the spread `...` pattern. This copies all the members from the source enum to the target enum, but it doesn't establish any reference between the source and target enums.
```typespec
enum DirectionExt {
@ -72,9 +71,9 @@ enum DirectionExt {
}
```
## Referencing enum members
## How to reference enum members
Enum members can be referenced using the `.` operator for identifiers.
You can reference enum members using the `.` operator for identifiers.
```typespec
alias North = Direction.North;

Просмотреть файл

@ -3,30 +3,30 @@ id: imports
title: Imports
---
# Import
# Importing files and libraries in TypeSpec
Imports add files or libraries to your TypeSpec program. When you compile a TypeSpec file, you provide a path to your root TypeSpec file, by convention called "main.tsp". From there, any files you import are added to your program. If you import a directory, TypeSpec will look for a `main.tsp` file inside that directory.
Imports are used to include files or libraries into your TypeSpec program. When compiling a TypeSpec file, you specify the path to your root TypeSpec file, typically named "main.tsp". From this root file, any imported files are added to your program. If a directory is imported, TypeSpec will search for a `main.tsp` file within that directory.
The path you import must either begin with `"./"` or `"../"` or otherwise be an absolute path. The path must either refer to a directory, or else have an extension of either ".tsp" or ".js". The following demonstrates how to use imports to assemble a TypeSpec program from multiple files:
The path specified in the import must either start with `"./"` or `"../"`, or be an absolute path. The path should either point to a directory, or have an extension of either ".tsp" or ".js". The examples below illustrate how to use imports to assemble a TypeSpec program from multiple files:
## Import TypeSpec file
## Importing a TypeSpec file
```typespec
import "./models/foo.tsp";
```
## Import Js file
## Importing a JavaScript file
```typespec
import "./decorators.js";
```
## Import a library
## Importing a library
The import value can be name one of the package dependencies. In that case TypeSpec will lookup for the `package.json` file and check the `tspMain` entry (or default to `main` if absent) to decide what is the library entrypoint to load.
The import value can be the name of one of the package dependencies. In this case, TypeSpec will look for the `package.json` file and check the `tspMain` entry (defaulting to `main` if `tspMain` is absent) to determine the library entrypoint to load.
```typespec
import "@typespec/rest";
import "/rest";
```
```json
@ -36,14 +36,14 @@ import "@typespec/rest";
}
```
which result in `./node_modules/@typespec/rest/lib/main.tsp` to be imported
This results in `./node_modules/@typespec/rest/lib/main.tsp` being imported.
## Import a directory
## Importing a directory
If the import value is a directory it will lookup if that directory is a Node package and follow the npm package [lookup logic](#import-a-library) or if the directory contains a `main.tsp`.
If the import value is a directory, TypeSpec will check if that directory is a Node package and follow the npm package [lookup logic](#importing-a-library), or if the directory contains a `main.tsp` file.
```typespec
import "./models"; // same as `import "./models/main.tsp";
import "./models"; // equivalent to `import "./models/main.tsp";
```
```typespec

Просмотреть файл

@ -5,9 +5,9 @@ title: Interfaces
# Interfaces
Interfaces can be used to group and reuse [operations](./operations.md).
Interfaces are useful for grouping and reusing [operations](./operations.md).
Interfaces are declared using the `interface` keyword.
You can declare interfaces using the `interface` keyword.
```typespec
interface SampleInterface {
@ -18,9 +18,9 @@ interface SampleInterface {
## Composing interfaces
The keyword `extends` can be used to compose operations from other interfaces into a new interface.
You can use the `extends` keyword to incorporate operations from other interfaces into a new interface.
Given the following interfaces
Consider the following interfaces:
```typespec
interface A {
@ -32,7 +32,7 @@ interface B {
}
```
a new interface `C` can be created including all operations from `A` and `B`
You can create a new interface `C` that includes all operations from `A` and `B`:
```typespec
interface C extends A, B {
@ -40,7 +40,7 @@ interface C extends A, B {
}
```
which is equivalent to
This is equivalent to:
```typespec
interface C {
@ -50,9 +50,9 @@ interface C {
}
```
## Interface template
## Interface templates
Interfaces can be templated, [see templates](./templates.md) for details on templates.
Interfaces can be templated. For more details on templates, [see templates](./templates.md).
```typespec
interface ReadWrite<T> {
@ -61,9 +61,9 @@ interface ReadWrite<T> {
}
```
## Interface operation templates
## Templating interface operations
Operations defined inside of an interface can also be templated. ([see templates](./templates.md) for details on templates.)
Operations defined within an interface can also be templated. For more details on templates, [see templates](./templates.md).
```typespec
interface ReadWrite<T> {
@ -77,7 +77,7 @@ op myWrite is MyReadWrite.write<int32>;
```
:::caution
Any uninstantiated, templated operation defined in an interface will be excluded from the list of service operations.
Any templated operation defined in an interface that is not instantiated will be omitted from the list of service operations.
This also applies when using `extends` on an interface that contains templated operations with unfilled template arguments.
@ -87,10 +87,10 @@ interface ReadWrite<T> {
write<R>(t: T): R;
}
interface MyReadWrite extends ReadWrite<string> {} // Here the `read()` operation is fully instantiated and will be included in a service definition. `write()` however isn't.
interface MyReadWrite extends ReadWrite<string> {} // Here, the `read()` operation is fully instantiated and will be included in a service definition. However, `write()` is not.
```
When working with building block interface like this use alias to create your interface building block instead of `interface extends`. This way the instantiated interface and its member will not be resolved in the service definition.
When working with building block interfaces like this, use an alias to create your interface building block instead of `interface extends`. This way, the instantiated interface and its members will not be resolved in the service definition.
```typespec
alias MyReadWrite = ReadWrite<string>;

Просмотреть файл

@ -5,13 +5,13 @@ title: Intersections
# Intersections
Intersections describe a type that must include all the intersection's constituents. Declare an intersection with the `&` operator.
Intersections in programming define a type that must encompass all the constituents of the intersection. You can declare an intersection using the `&` operator.
```typespec
alias Dog = Animal & Pet;
```
An intersection is equivalent to [spreading](./models.md#spread) both types.
An intersection is functionally equivalent to [spreading](./models.md#spread) both types.
```typespec
alias Dog = {

Просмотреть файл

@ -5,22 +5,22 @@ title: Models
# Models
TypeSpec models are used to describe data shapes or schemas.
Models in TypeSpec are utilized to define the structure or schema of data.
## Model kinds
## Types of models
Models can be used to represent 2 types:
Models can be categorized into two main types:
- [Record](#record)
- [Array](#array)
### Record
Record models are structure with named fields called properties.
A Record model is a structure that consists of named fields, referred to as properties.
- name can be an `identifier` or `string literal`.
- type can be any type reference
- properties are ordered. See [ordering of properties](#ordering-of-properties)
- The name can be an `identifier` or `string literal`.
- The type can be any type reference.
- Properties are arranged in a specific order. Refer to [property ordering](#property-ordering) for more details.
```typespec
model Dog {
@ -31,7 +31,7 @@ model Dog {
#### Optional properties
Properties can be marked as optional using the `?` punctuation.
Properties can be designated as optional by using the `?` symbol.
```typespec
model Dog {
@ -41,7 +41,7 @@ model Dog {
#### Default values
[Optional properties](#optional-properties) can be provided with a default value using `=` operator.
[Optional properties](#optional-properties) can be assigned a default value using the `=` operator.
```typespec
model Dog {
@ -49,9 +49,9 @@ model Dog {
}
```
#### Ordering of properties
#### Property ordering
Properties are ordered in the order that they appear in source. Properties obtained via `model is` appear before properties defined in the model body. Properties obtained via `...` are inserted where the spread appears in source.
Properties are arranged in the order they are defined in the source. Properties acquired via `model is` are placed before properties defined in the model body. Properties obtained via `...` are inserted at the point where the spread appears in the source.
Example:
@ -71,7 +71,7 @@ model Cat is Pet {
furColor: string;
}
// Resulting property order for cat:
// The resulting property order for Cat is:
// name, age, meow, address, furColor
```
@ -79,28 +79,27 @@ model Cat is Pet {
The `Record<T>` model can be used to define a model with an arbitrary number of properties of type T. It can be combined with a named model to provide some known properties.
There is 3 ways this can be done which all have slightly different semantics:
There are three ways to achieve this, each with slightly different semantics:
- Using the `...` operator
- Using `is` operator
- Using `extends` operator
- Using the `is` operator
- Using the `extends` operator
#### Using `...` operator
#### Using the `...` operator
Spreading a Record into your model means that your model has all the properties you have explicitly defined plus any additional properties defined by the Record.
This means that the property in the model could be of a different and incompatible type with the Record value type.
Spreading a Record into your model implies that your model includes all the properties you have explicitly defined, plus any additional properties defined by the Record. This means that a property in the model could be of a different and incompatible type with the Record value type.
```tsp
// Here we are saying the Person model has a property `age` that is an int32 but has some other properties that are all string.
// In this example, the Person model has a property `age` that is an int32, but also has other properties that are all strings.
model Person {
age: int32;
...Record<string>;
}
```
#### Using `is` operator
#### Using the `is` operator
When using `is Record<T>` it is now saying that all properties of this model are of type T. This means that each property explicitly defined in the model must be also be of type T.
When using `is Record<T>`, it indicates that all properties of this model are of type T. This means that each property explicitly defined in the model must also be of type T.
The example above would be invalid
@ -119,11 +118,9 @@ model Person is Record<string> {
}
```
#### Using `extends` operator
#### Using the `extends` operator
`extends` is going to have similar semantics to `is` but is going to define the relationship between the 2 models.
In many languages this would probably result in the same emitted code as `is` and is recommended to just use `is Record<T>` instead.
The `extends` operator has similar semantics to `is`, but it defines the relationship between the two models. In many languages, this would probably result in the same emitted code as `is` and it is recommended to use `is Record<T>` instead.
```tsp
model Person extends Record<string> {
@ -135,7 +132,7 @@ model Person extends Record<string> {
#### `never`
A model property can be declared as having the type never. This can be interpreted as the model not having that property.
A model property can be declared as having the type `never`. This can be interpreted as the model not having that property.
This can be useful in a model template to omit a property.
@ -150,18 +147,18 @@ model UKAddress is Address<never>;
```
:::note
It is up to the emitter to remove `never` properties. The TypeSpec compiler will not automatically omit them.
The responsibility of removing `never` properties lies with the emitter. The TypeSpec compiler will not automatically omit them.
:::
### Array
Array are models created using the `[]` syntax which is just a syntactic sugar for using the `Array<T>` model type.
Arrays are models created using the `[]` syntax, which is a shorthand for using the `Array<T>` model type.
## Model composition
### Spread
The spread operator takes the members of a source model and copies them into a target model. Spread doesn't create any nominal relationship between source and target, and so it's useful when you want to reuse common properties without reasoning about or generating complex inheritance relationships.
The spread operator (`...`) copies the members of a source model into a target model. This operation doesn't create any nominal relationship between the source and target, making it useful when you want to reuse common properties without generating complex inheritance relationships.
```typespec
model Animal {
@ -177,7 +174,7 @@ model Dog {
...Pet;
}
// Dog is equivalent to the following declaration:
// The Dog model is equivalent to the following declaration:
model Dog {
species: string;
name: string;
@ -186,7 +183,7 @@ model Dog {
### Extends
Sometimes you want to create an explicit relationship between two models, for example when you want to emit class definitions in languages which support inheritance. The `extends` keyword can be used to establish such a relationship.
There are times when you want to create an explicit relationship between two models, such as when you're generating class definitions in languages that support inheritance. The `extends` keyword can be used to establish this relationship.
```typespec
model Animal {
@ -198,7 +195,7 @@ model Dog extends Animal {}
### Is
Sometimes you want to create a new type that is an exact copy of an existing type but with some additional properties or metadata without creating a nominal inheritance relationship. The `is` keyword can be used for this purpose. It copies all the properties (like spread), but copies [decorators](./decorators.md) as well. One common use case is to give a better name to a [template](#Templates) instantiation:
There are instances when you want to create a new type that is an exact copy of an existing type but with additional properties or metadata, without creating a nominal inheritance relationship. The `is` keyword can be used for this purpose. It copies all the properties (like spread), but also copies [decorators](./decorators.md) as well. A common use case is to provide a better name to a [template](#model-templates) instantiation:
```typespec
@decorator
@ -208,7 +205,7 @@ model Thing<T> {
model StringThing is Thing<string>;
// StringThing declaration is equivalent to the following declaration:
// The StringThing declaration is equivalent to the following declaration:
@decorator
model StringThing {
property: string;
@ -217,7 +214,7 @@ model StringThing {
## Model templates
[See templates](./templates.md) for details on templates
Refer to [templates](./templates.md) for more details on templates.
```typespec
model Page<Item> {
@ -232,7 +229,7 @@ model DogPage {
## Meta type references
Some model property meta types can be referenced using `::`
Some model property meta types can be referenced using `::`.
| Name | Example | Description |
| ---- | ---------------- | ---------------------------------------- |

Просмотреть файл

@ -5,11 +5,11 @@ title: Namespaces
# Namespaces
Namespaces let you group related types together into namespaces. This helps organize your types, making them easier to find and prevents name conflicts. Namespaces are merged across files, so you can reference any type anywhere in your TypeSpec program via its namespace.
Namespaces in TypeSpec allow you to group related types together. This organization makes your types easier to locate and helps avoid naming conflicts. Namespaces are merged across files, enabling you to reference any type from anywhere in your TypeSpec program using its namespace.
## Basics
Create a namespace with the `namespace` keyword.
You can create a namespace using the `namespace` keyword.
```typespec
namespace SampleNamespace {
@ -17,9 +17,9 @@ namespace SampleNamespace {
}
```
_The name of a namespace must be a valid TypeSpec identifier._
_Note: The namespace name must be a valid TypeSpec identifier._
The `SampleNamespace` can then be used from other places:
You can then use `SampleNamespace` from other locations:
```typespec
model Foo {
@ -27,9 +27,9 @@ model Foo {
}
```
## Nested namespace
## Nested namespaces
Namespaces can contain sub namespaces providing additional granularity
Namespaces can contain sub-namespaces, offering additional layers of organization.
```typespec
namespace Foo {
@ -41,7 +41,7 @@ namespace Foo {
}
```
or this can be simplified using `.` notation
Alternatively, you can simplify this using `.` notation:
```typespec
namespace Foo.Bar.Baz {
@ -49,7 +49,7 @@ namespace Foo.Bar.Baz {
}
```
The sub-namespace can then be used from other places using the fully qualified name.
You can then use the sub-namespace from other locations using the fully qualified name.
```typespec
model A {
@ -57,9 +57,9 @@ model A {
}
```
## File namespace
## File-level namespaces
A namespace for all declarations contained in a file can be provided at the top (After the `import` statements) using a blockless namespace statement
You can define a namespace for all declarations within a file at the top of the file (after any `import` statements) using a blockless namespace statement:
```typespec
namespace SampleNamespace;
@ -67,11 +67,11 @@ namespace SampleNamespace;
model SampleModel {}
```
A file can only have a single blockless namespace.
A file can only have one blockless namespace.
## Using a namespace
## Using namespaces
The content of a namespace can be exposed to the current scope using the `using` keyword.
You can expose the contents of a namespace to the current scope using the `using` keyword.
```typespec
using SampleNamespace;
@ -81,7 +81,7 @@ model Foo {
}
```
The bindings introduced by a `using` statement are local to the namespace they are declared in. They do not become part of the namespace themselves.
The bindings introduced by a `using` statement are local to the namespace in which they are declared. They do not become part of the namespace themselves.
```typespec
namespace One {
@ -90,9 +90,9 @@ namespace One {
namespace Two {
using One;
alias B = A; // ok
alias B = A; // This is valid
}
alias C = One.A; // not ok
alias C = Two.B; // ok
alias C = One.A; // This is not valid
alias C = Two.B; // This is valid
```

Просмотреть файл

@ -5,9 +5,9 @@ title: Operations
# Operations
Operations describe service endpoints and consist of an operation name, parameters, and return type.
Operations are essentially service endpoints, characterized by an operation name, parameters, and a return type.
Operations are declared using the `op` keyword:
You can declare operations using the `op` keyword:
```typespec
op ping(): void;
@ -15,7 +15,7 @@ op ping(): void;
## Parameters
The operation's parameters describe a model, so anything you can do in a model you can do in a parameter list as well, including using the spread operator:
The parameters of an operation represent a model. Therefore, you can perform any action with parameters that you can with a model, including the use of the spread operator:
```typespec
op feedDog(...CommonParams, name: string): void;
@ -23,7 +23,7 @@ op feedDog(...CommonParams, name: string): void;
## Return type
Often an endpoint returns one of any number of models. For example, there might be a return type for when an item is found, and a return type for when an item isn't found. Unions are used to describe this pattern:
Frequently, an endpoint may return one of several possible models. For instance, there could be a return type for when an item is located, and another for when it isn't. Unions are employed to express this scenario:
```typespec
model DogNotFound {
@ -33,33 +33,33 @@ model DogNotFound {
op getDog(name: string): Dog | DogNotFound;
```
## Reuse operations
## Reusing operations
Operation signatures can be reused using the `is` keyword. Given an operation
You can reuse operation signatures with the `is` keyword. For example, given an operation
```typespec
op Delete(id: string): void;
```
its signature can be reused like this:
You can reuse its signature like so:
```typespec
op deletePet is Delete;
```
This means that `deletePet` will have the same parameters, return type and decorators as the `Delete` operation.
This implies that `deletePet` will inherit the same parameters, return type, and decorators as the `Delete` operation.
This pattern is most commonly used in combination with [operation templates](#operations-templates)
This practice is typically used in conjunction with [operation templates](#operation-templates)
## Operations templates
## Operation templates
[See templates](./templates.md) for details on templates.
For more information on templates, [see templates](./templates.md).
```typespec
op ReadResource<T>(id: string): T;
```
The operation template can then be referenced via `is`:
You can reference the operation template using `is`:
```typespec
op readPet is ReadResource<Pet>;
@ -67,7 +67,7 @@ op readPet is ReadResource<Pet>;
## Referencing model properties
Model properties can be referenced using the `.` operator for identifiers.
You can reference model properties using the `.` operator for identifiers.
```tsp
alias PetName = Pet.name;
@ -75,9 +75,9 @@ alias PetName = Pet.name;
## Meta type references
Some operation meta types can be referenced using `::`
Certain operation meta types can be referenced using `::`
| Name | Example | Description |
| ---------- | --------------------- | ----------------------------------------- |
| parameters | `readPet::parameters` | Reference the parameters model expression |
| returnType | `readPet::returnType` | Reference the operation return type |
| Name | Example | Description |
| ---------- | --------------------- | ------------------------------------------ |
| parameters | `readPet::parameters` | References the parameters model expression |
| returnType | `readPet::returnType` | References the operation return type |

Просмотреть файл

@ -5,20 +5,20 @@ title: Overview
# Language Overview
This is an overview of the language concept in TypeSpec. It doesn't go in detail but can be used as a cheat sheet.
This document provides a concise overview of the language concepts in TypeSpec. It serves as a quick reference guide rather than an in-depth tutorial.
## Declarations
- Declaration names must be unique across types within the same scope. For example this is not allowed
- Names of declarations must be unique across different types within the same scope. For instance, the following is not permissible:
<!-- prettier-ignore -->
```typespec
```typespec
model Dog {}
namespace Dog {}
```
## Imports
_Details: [Imports](./imports.md)_
_For more details, see: [Imports](./imports.md)_
| Feature | Example |
| -------------------- | ------------------------- |
@ -28,7 +28,7 @@ _Details: [Imports](./imports.md)_
## Namespaces
_Details: [Namespaces](./namespaces.md)_
_For more details, see: [Namespaces](./namespaces.md)_
| Feature | Example |
| ----------------- | ---------------------------- |
@ -39,7 +39,7 @@ _Details: [Namespaces](./namespaces.md)_
## Decorators
_Details: [Decorators](./decorators.md)_
_For more details, see: [Decorators](./decorators.md)_
| Feature | Example |
| ---------------------------- | ----------------------------------------------------------------------------------- |
@ -51,7 +51,7 @@ _Details: [Decorators](./decorators.md)_
## Scalars
_Details: [Scalars](./scalars.md)_
_For more details, see: [Scalars](./scalars.md)_
| Feature | Example |
| ------------------ | ------------------------------------------- |
@ -61,7 +61,7 @@ _Details: [Scalars](./scalars.md)_
## Models
_Details: [Models](./models.md)_
_For more details, see: [Models](./models.md)_
| Feature | Example |
| ------------------------------ | ------------------------------------- |
@ -76,7 +76,7 @@ _Details: [Models](./models.md)_
## Operations
_Details: [Operations](./operations.md)_
_For more details, see: [Operations](./operations.md)_
| Feature | Example |
| ----------------------------- | ------------------------------------------------ |
@ -89,7 +89,7 @@ _Details: [Operations](./operations.md)_
## Interfaces
_Details: [Interfaces](./interfaces.md)_
_For more details, see: [Interfaces](./interfaces.md)_
| Feature | Example |
| --------------------- | -------------------------------------- |
@ -99,7 +99,7 @@ _Details: [Interfaces](./interfaces.md)_
## Templates
_Details: [Templates](./templates.md)_
_For more details, see: [Templates](./templates.md)_
| Feature | Example |
| --------------------------------- | --------------------------------------------------- |
@ -111,7 +111,7 @@ _Details: [Templates](./templates.md)_
## Enums
_Details: [Enums](./enums.md)_
_For more details, see: [Enums](./enums.md)_
| Feature | Example |
| ------------------ | ---------------------------------------------- |
@ -123,7 +123,7 @@ _Details: [Enums](./enums.md)_
## Unions
_Details: [Unions](./unions.md)_
_For more details, see: [Unions](./unions.md)_
| Feature | Example |
| ----------------------- | -------------------------------- |
@ -132,7 +132,7 @@ _Details: [Unions](./unions.md)_
## Intersections
_Details: [Intersections](./intersections.md)_
_For more details, see: [Intersections](./intersections.md)_
| Feature | Example |
| ------------------------ | -------------- |
@ -140,7 +140,7 @@ _Details: [Intersections](./intersections.md)_
## Type literals
_Details: [Type literals](./type-literals.md)_
_For more details, see: [Type literals](./type-literals.md)_
| Feature | Example |
| ----------------- | -------------------------------------------------------- |
@ -152,7 +152,7 @@ _Details: [Type literals](./type-literals.md)_
## Aliases
_Details: [Aliases](./alias.md)_
_For more details, see: [Aliases](./alias.md)_
| Feature | Example |
| ----------------- | --------------------------------- |

Просмотреть файл

@ -4,25 +4,25 @@ title: Scalars
# Scalars
These are types without any fields (for example `string`, `int32`, `boolean`, etc.)
Scalars are simple types that don't have any fields. Examples of these include `string`, `int32`, `boolean`, and so on.
Scalar can be declared using the `scalar` keyword
You can declare a scalar by using the `scalar` keyword.
```typespec
scalar ternary;
```
## Extend another scalar
## Extending a scalar
Scalar can be extended using the `extends` keyword.
You can create a new scalar that extends an existing one by using the `extends` keyword.
```typespec
scalar Password extends string;
```
## Template scalar
## Scalars with template parameters
Scalar support template parameters. Note: the only use for those template are decorators.
Scalars can also support template parameters. However, it's important to note that these templates are primarily used for decorators.
```typespec
@doc(Type)

Просмотреть файл

@ -5,9 +5,9 @@ title: Templates
# Templates
It is often useful to let the users of a model fill in certain details. Templates enable this pattern. Similar to generics found in other languages, model templates declare template parameters that users provide when referencing the type.
Templates are a powerful tool that allow users to customize certain aspects of a type. Similar to generics in other programming languages, templates define template parameters that users can specify when referencing the type.
Templates can be used on:
Templates can be applied to:
- [aliases](./alias.md)
- [models](./models.md)
@ -27,7 +27,7 @@ model DogPage {
## Default values
A template parameter can be given a default argument value with `= <value>`.
You can assign a default value to a template parameter using `= <value>`.
```typespec
model Page<Item = string> {
@ -38,13 +38,13 @@ model Page<Item = string> {
## Parameter constraints
Template parameters can specify a constraint using the `extends` keyword. See the [type relations](./type-relations.md) documentation for details on how validation works.
You can impose constraints on template parameters using the `extends` keyword. For details on how validation works, refer to the [type relations](./type-relations.md) documentation.
```typespec
alias Foo<Type extends string> = Type;
```
Now, instantiating Foo with an argument that does not satisfy the constraint `string` will result in an error:
If you try to instantiate Foo with an argument that does not meet the `string` constraint, you will encounter an error:
```typespec
alias Bar = Foo<123>;
@ -58,7 +58,7 @@ A template parameter constraint can also be a model expression:
alias Foo<Type extends {name: string}> = Type;
```
Template parameter defaults also need to respect the constraint:
Default values for template parameters must also adhere to the constraint:
```typespec
alias Foo<Type extends string = "Abc"> = Type
@ -67,7 +67,7 @@ alias Bar<Type extends string = 123> = Type
^ Type '123' is not assignable to type 'TypeSpec.string'
```
Furthermore, all optional arguments must come at the end of the template. A required argument cannot follow an optional argument:
Also, all optional arguments must be placed at the end of the template. A required argument cannot follow an optional argument:
```typespec
// Invalid
@ -77,7 +77,7 @@ alias Foo<T extends string = "Abc", U> = ...;
## Named template arguments
Template arguments may also be specified by name. In that case, they can be specified out of order and optional arguments may be omitted. This can be useful when dealing with templates that have many defaultable arguments:
Template arguments can also be specified by name. This allows you to specify them out of order and omit optional arguments. This can be particularly useful when dealing with templates that have many arguments with defaults:
```typespec
alias Test<T, U extends numeric = int32, V extends string = "example"> = ...;
@ -105,6 +105,6 @@ alias Example3 = Test<
>;
```
Since template arguments may be specified by name, the names of template parameters are part of the public API of a template. **Changing the name of a template parameter may break existing specifications that use the template.**
Since template arguments can be specified by name, the names of template parameters are part of the template's public API. **Renaming a template parameter may break existing specifications that use the template.**
**Note**: Template arguments are evaluated in the order the parameters are defined in the template _definition_, not the order in which they are written in the template _instance_. Most of the time, this should not matter, but may be important in some cases where evaluating a template argument may invoke decorators with side effects.
**Note**: Template arguments are evaluated in the order the parameters are defined in the template _definition_, not the order in which they are written in the template _instance_. While this is usually inconsequential, it may be important in some cases where evaluating a template argument may trigger decorators with side effects.

Просмотреть файл

@ -1,23 +1,23 @@
---
id: type-literals
title: Type literals
title: Type Literals
---
# Type literals
API authors often need to describe API shapes in terms of specific literal values. For example, this operation returns this specific integer status code, or this model member can be one of a few specific string values. It is also often useful to pass specific literal values to decorators. TypeSpec supports string, number, and boolean literal values to support these cases.
When designing APIs, it's common to define the structure of the API in terms of specific literal values. For instance, an operation might return a specific integer status code, or a model member might be one of a few specific string values. It's also useful to pass specific literal values to decorators. TypeSpec supports string, number, and boolean literal values to cater to these needs.
## String literals
String literals can be represented using double quotes `"`
String literals are represented using double quotes `"`.
```typespec
alias Str = "Hello World!";
```
## Multi line string literals
## Multi-line string literals
A multi string literal is represented using a set of 3 double quotes `"""`.
Multi-line string literals are denoted using three double quotes `"""`.
```typespec
alias Str = """
@ -27,14 +27,14 @@ This is a multi line string
""";
```
- Opening `"""` must be followed by a new line.
- Closing `"""` must be preceded by a new line.
- The opening `"""` must be followed by a new line.
- The closing `"""` must be preceded by a new line.
### Multi line string indentation trimming
### Trimming indentation in multi-line strings
Multi lines automatically remove leading whitespaces of each line aligned with the closing `"""`. This is particularly useful to keep multi line string indented with the code and not have to worry about unwanted indentation.
Multi-line strings automatically trim leading whitespaces on each line to align with the closing `"""`. This feature is handy for maintaining the indentation of multi-line strings within the code without worrying about undesired indentation.
All those options will produce the exact same string value `"one\ntwo"`
All the following options will yield the same string value `"one\ntwo"`.
```typespec
model MultiLineContainer {
@ -64,7 +64,7 @@ two
## String template literal
Single or multi line string literal can be interpolated using `${}`
Both single and multi-line string literals can be interpolated using `${}`.
```typespec
alias hello = "bonjour";
@ -76,11 +76,11 @@ alias Multi = """
""";
```
Any valid expression can be used in the interpolation but only other literals will result in the template literal being assignable to a `valueof string`. Any other value will be dependent on the decorator/emitter receiving it to handle.
Any valid expression can be used in the interpolation, but only other literals will result in the template literal being assignable to a `valueof string`. Any other value will depend on the decorator/emitter receiving it for handling.
## Numeric literal
Numeric literals can be declared by using the raw number
Numeric literals are declared by using the raw number.
```typespec
alias Kilo = 1000;
@ -89,7 +89,7 @@ alias PI = 3.14;
## Boolean literal
Boolean literals can be declare by using `true` or `false` keywords
Boolean literals are declared by using the `true` or `false` keywords.
```typespec
alias InTypeSpec = true;

Просмотреть файл

@ -1,9 +1,9 @@
---
id: type-relations
title: Type relations
title: Type Relations
---
# Types Relations
# Type relations
## Type hierarchy
@ -43,9 +43,9 @@ graph RL
## Model with properties
When checking if type `S` can be assigned to type `T`, if `T` is a model with properties, it will look for all those properties to be present inside of `S` and their type be assignable to the type of the property is T.
When determining if type `S` can be assigned to type `T`, if `T` is a model with properties, it checks whether all those properties are present in `S` and if their types can be assigned to the type of the corresponding property in `T`.
For example
For instance,
```typespec
model T {
@ -85,7 +85,7 @@ model S {
## `Record<T>`
A record is a model indexed with a string with value of T. This means that it represents a model where all properties (string keys) are assignable to the type T. You can assign a model expression where all the properties are of type T or another model that `is` also a `Record<T>`
A record is a model indexed with a string with a value of T. It represents a model where all properties (string keys) are assignable to the type T. You can assign a model expression where all the properties are of type T or another model that `is` also a `Record<T>`.
```typespec
// Represent an object where all the values are int32.
@ -120,9 +120,9 @@ model S {
}
```
#### Why is the last case not assignable to `Record<int32>`?
#### Why isn't the last case assignable to `Record<int32>`?
In this scenario
In this scenario,
```typespec
alias T = Record<int32>;
@ -132,9 +132,9 @@ model S {
}
```
The reason is `model S` here is not assignable but the model expression `{ foo: 123; bar: 456; }` is, is that model S could be extended with additional properties that could then not be compatible.
The reason why `model S` is not assignable, but the model expression `{ foo: 123; bar: 456; }` is, is because model S could be extended with additional properties that might not be compatible.
If you for example now add a new model
For instance, if you add a new model,
```typespec
model Foo is S {
@ -142,4 +142,4 @@ model Foo is S {
}
```
Now here `Foo` is assignable to `S` following the [model with property logic](#model-with-properties) and if `S` was assignable to `Record<int32>`, `Foo` would be able to be passed through as well but this is now invalid as `otherProp` is not an `int32` property.
Here, `Foo` is assignable to `S` following the [model with property logic](#model-with-properties), and if `S` was assignable to `Record<int32>`, `Foo` would also be passable. However, this is now invalid as `otherProp` is not an `int32` property.

Просмотреть файл

@ -5,24 +5,24 @@ title: Unions
# Unions
Unions describe a type that must be exactly one of the union's constituents. There is 2 types of unions:
Unions define a type that must be exactly one of several possible variants. There are two types of unions:
- union expressions
- named unions
- Union expressions
- Named unions
## Union expressions
Unnamed unions can be declared by joining the variants using the `|` operator
Unnamed unions, or union expressions, can be declared by combining the variants using the `|` operator.
```typespec
alias Breed = Beagle | GermanShepherd | GoldenRetriever;
```
Here it says that `Breed` can accept either a `Beagle`, a `GermanShepherd` or a `GoldenRetriever`.
In this example, `Breed` can be either a `Beagle`, a `GermanShepherd`, or a `GoldenRetriever`.
## Named unions
Named unions provide a way to specify a name for the union as well as explicit variant reference. Named unions are in a way similar to [enums](./enums.md) but instead of having `string` or `numeric` values it is a [record models](./models.md)
Named unions allow you to assign a name to the union and provide explicit variant references. Named unions are somewhat similar to [enums](./enums.md), but instead of having `string` or `numeric` values, they use [record models](./models.md).
```typespec
union Breed {
@ -32,4 +32,4 @@ union Breed {
}
```
The above example is equivalent to the `Breed` alias above, except that emitters can actually see `Breed` as a named entity and also see the `beagle`, `shepherd`, and `retriever` names for the options. It also becomes possible to apply [decorators](./decorators.md) to each of the options when using this form.
The above example is equivalent to the `Breed` alias mentioned earlier, with the difference that emitters can recognize `Breed` as a named entity and also identify the `beagle`, `shepherd`, and `retriever` names for the options. This format also allows the application of [decorators](./decorators.md) to each of the options.

Просмотреть файл

@ -278,7 +278,7 @@ namespace Pets {
## Automatic visibility
The `@typespec/rest` library understands the following well-known [visibilities](../../standard-library/built-in-decorators.md#visibility-decorators) and provides functionality for emitters to apply them based on whether on request vs. response and HTTP method usage as detailed in the table below.
The `@typespec/rest` library understands the following well-known [visibilities](../../standard-library/built-in-decorators.md) and provides functionality for emitters to apply them based on whether on request vs. response and HTTP method usage as detailed in the table below.
See [handling visibility and metadata](../../extending-typespec/emitter-metadata-handling.md) for how to incorporate this into
@ -329,7 +329,7 @@ Metadata is determined to be applicable or inapplicable based on the context tha
Additionally metadata that appears in an array element type always inapplicable.
When metadata is deemed "inapplicable", for example, if a `@path` property is seen in a response, it becomes part of the payload instead unless the [@includeInapplicableMetadataInPayload](./reference/decorators.md#@TypeSpec.Rest.includeinapplicablemetadatainpayload) decorator is used and given a value of `false`.
When metadata is deemed "inapplicable", for example, if a `@path` property is seen in a response, it becomes part of the payload instead unless the [@includeInapplicableMetadataInPayload](./reference/decorators.md#@TypeSpec.Http.includeInapplicableMetadataInPayload) decorator is used and given a value of `false`.
The handling of metadata applicability furthers the goal of keeping a single logical model in TypeSpec. For example, this defines a logical `User` entity that has a name, ID and password, but further annotates that the ID is sent in the HTTP path and the HTTP body in responses. Also, using automatically visibility as before, we further indicate that the password is only present in create requests.

Просмотреть файл

@ -20,7 +20,7 @@ linter:
Available ruleSets:
- [`@typespec/http/all`](#@typespec/http/all)
- `@typespec/http/all`
## Rules

Просмотреть файл

@ -12,7 +12,7 @@ The OpenAPI emitter may produce any of the following diagnostic messages.
This diagnostic is issued when a response header is defined more than once for a response of a specific status code.
How to fix ???
**work in progress**
## duplicate-type-name
@ -23,11 +23,11 @@ To fix this issue, change the name or friendly-name of one of the models or para
## inline-cycle
???
**work in progress**
## invalid-default
???
**work in progress**
## invalid-extension-key
@ -38,7 +38,7 @@ To fix this issue, change the extension name to start with "x-".
## invalid-schema
???
**work in progress**
## invalid-server-variable

Просмотреть файл

@ -2,132 +2,106 @@
title: Emitter operation
---
# How the OpenAPI emitter works
# Exploring the Functionality of the OpenAPI Emitter
The OpenAPI emitter converts TypeSpec language elements into their natural OpenAPI expression as described below.
The OpenAPI emitter is designed to translate TypeSpec language elements into their corresponding OpenAPI expressions. Here's how it works:
## Servers
## Server Details
If the TypeSpec file contains an [(Http) `@server` decorator](../rest/reference/decorators.md#@TypeSpec.Http.server)
the OpenAPI emitter will generate a `servers` object with the server URL, description, and variables specified in the decorator.
If the TypeSpec file includes an [(Http) `@server` decorator](../http/reference/decorators.md#@TypeSpec.Http.server), the OpenAPI emitter will create a `servers` object. This object will contain the server URL, description, and variables as defined in the decorator.
You can specify multiple `@server` decorators to obtain multiple entries in the `servers` object.
You can use multiple `@server` decorators to generate multiple entries in the `servers` object.
## Operations
Each TypeSpec operation becomes an OpenAPI operation.
Every TypeSpec operation is converted into an OpenAPI operation by the emitter.
The HTTP method for the operation is either explicitly specified with an [(Http) `@get`, `@post`, `@put`, `@patch`, or `@delete` decorator][http-verb-decorators] on the operation or it is inferred from the operation name and signature.
The HTTP method for the operation can be explicitly defined using an [(Http) `@get`, `@post`, `@put`, `@patch`, or `@delete` decorator][http-verb-decorators] on the operation. If not explicitly defined, the HTTP method is inferred from the operation name and signature.
The path for the operation comes from the [(Http) `@route` decorator][http-route-decorator] on the operation.
The `@route` decorator can also be specified on a namespace and/or an interface (group of operations).
When specified, the route for the enclosing namespace(s) and interface are prefixed to the operation route.
The operation's path is derived from the [(Http) `@route` decorator][http-route-decorator] on the operation. The `@route` decorator can also be applied to a namespace and/or an interface (a group of operations). If specified, the route for the enclosing namespace(s) and interface are prefixed to the operation route.
[http-verb-decorators]: ../rest/reference/decorators.md
[http-route-decorator]: ../rest/reference/decorators.md#@TypeSpec.Http.route
[http-verb-decorators]: ../http/reference/decorators.md
[http-route-decorator]: ../http/reference/decorators.md#@TypeSpec.Http.route
The fields of the [OpenAPI Operation object][] are set as described below.
The [OpenAPI Operation object][] fields are set as described below.
[openapi operation object]: https://github.com/OAI/OpenAPI-Specification/blob/3.0.3/versions/3.0.3.md#operationObject
### description
### Description
The description field is set from the [(built-in) `@doc` decorator][doc-decorator] on the TypeSpec operation, and omitted when `@doc` is not present.
The description field is populated from the [(built-in) `@doc` decorator][doc-decorator] on the TypeSpec operation. If `@doc` is not present, the description field is omitted.
[doc-decorator]: ../../standard-library/built-in-decorators.md#doc
[doc-decorator]: ../../standard-library/built-in-decorators.md#@doc
### summary
### Summary
The summary field is set from the [(built-in) `@summary` decorator][summary-decorator] on the TypeSpec operation, and omitted when `@summary` is not present.
The summary field is populated from the [(built-in) `@summary` decorator][summary-decorator] on the TypeSpec operation. If `@summary` is not present, the summary field is omitted.
[summary-decorator]: ../../standard-library/built-in-decorators.md#summary
[summary-decorator]: ../../standard-library/built-in-decorators.md#@summary
### operationId
### Operation ID
The operationId can be explicitly specified with the [(OpenAPI) `@operationId` decorator][openapi-operationid-decorator],
and otherwise is simple the operation name, prefixed with "<interface*name>*" when the operation is within an interface.
The operation ID can be explicitly defined using the (OpenAPI) `@operationId` decorator. If not explicitly defined, the operation ID is simply the operation name, prefixed with "<interface*name>*" when the operation is within an interface.
[openapi-operationid-decorator]: ../../standard-library/built-in-decorators.md#operationId
### Parameters and Request Body
### parameters and requestBody
The parameters of the TypeSpec operation are translated into the parameter list and request body for the OpenAPI operation.
The parameters of the TypeSpec operation are translated into the parameter list and requestBody for the OpenAPI operation.
The `in` field of a parameter is defined using an [(Http) `@query`, `@header`, or `@path` decorator][http-parameter-decorators]. A parameter without one of these decorators is assumed to be passed in the request body.
The `in` field of a parameter is specified with an [(Http) `@query`, `@header`, or `@path` decorator][http-parameter-decorators].
A parameter without one of these decorators is assumed to be passed in the request body.
The request body parameter can also be explicitly defined with an [(Http) `@body` decorator][http-body-decorator]. If `@body` is not explicitly defined, the set of parameters that are not marked `@header`, `@query`, or `@path` form the request body, which is defined as required. If the request body should be optional, it must be declared as an optional property with the `@body` decorator.
The request body parameter can also be explicitly decorated with an [(Http) `@body` decorator][http-body-decorator].
In the absence of explicit `@body`, the set of parameters that are not marked `@header`, `@query`, or `@path` form the request body
and this request body is defined as required. If the request body should be optional, the body must be declared as
optional property with the `@body` decorator.
[http-parameter-decorators]: ../rest/reference/decorators.md#data-types
[http-body-decorator]: ../rest/reference/decorators.md#@TypeSpec.Http.body
[http-parameter-decorators]: ../http/reference/decorators.md
[http-body-decorator]: ../http/reference/decorators.md#@TypeSpec.Http.body
The content of a (built-in) `@doc` decorator on a parameter will be set in the description.
The TypeSpec parameter type will be translated into an appropriate OpenAPI schema for the parameter.
Likewise, the type of the body parameter(s) will be translated into an appropriate OpenAPI schema for the requestBody.
The request body will use the "application/json" media type unless the body model includes an explicit `content-type`
header.
Similarly, the type of the body parameter(s) will be translated into an appropriate OpenAPI schema for the request body. The request body will use the "application/json" media type unless the body model includes an explicit `content-type` header.
See also [metadata](../http/operations.md#metadata) for more advanced details.
For more advanced details, see [metadata](../http/operations.md#metadata).
### responses
### Responses
The return type(s) of the TypeSpec operation are translated into responses for the OpenAPI operation.
The status code for a response can be specified as a property in the return type model with the [(Http) `@statusCode` decorator][http-statuscode-decorator] (the property name is ignored).
If the [(built-in) `@error` decorator][error-decorator] is specified on a return type, this return type becomes the "default" response for the operation.
The media type for a response will be "application/json" unless the return type model includes an explicit `content-type`
header.
Models with different status codes and/or media types can be unioned together to describe complex response designs.
The return type(s) of the TypeSpec operation are translated into responses for the OpenAPI operation. The status code for a response can be defined as a property in the return type model with the [(Http) `@statusCode` decorator][http-statuscode-decorator] (the property name is ignored). If the [(built-in) `@error` decorator][error-decorator] is specified on a return type, this return type becomes the "default" response for the operation. The media type for a response will be "application/json" unless the return type model includes an explicit `content-type` header. Models with different status codes and/or media types can be combined to describe complex response designs.
When a return type model has a property explicitly decorated with an [(Http) `@body` decorator][http-body-decorator], this
is taken as the response body.
In the absence of explicit `@body`, the properties that are not marked `@statusCode` or `@header` form the response body.
When a return type model has a property explicitly decorated with an [(Http) `@body` decorator][http-body-decorator], this is considered as the response body. In the absence of an explicit `@body`, the properties that are not marked `@statusCode` or `@header` form the response body.
[http-statuscode-decorator]: ../rest/reference/decorators.md#@TypeSpec.Http.statuscode
[error-decorator]: ../../standard-library/built-in-decorators.md#error
[http-statuscode-decorator]: ../http/reference/decorators.md#@TypeSpec.Http.statusCode
[error-decorator]: ../../standard-library/built-in-decorators.md#@error
See also [metadata](../http/operations.md#metadata) for more advanced details.
For more advanced details, see [metadata](../http/operations.md#metadata).
### tags
### Tags
Any tags specified with the [(built-in) `@tag` decorator][tag-decorator] on the operation, interface, or
enclosing namespace(s) are included in the OpenAPI operation's tags array.
Any tags specified with the [(built-in) `@tag` decorator][tag-decorator] on the operation, interface, or enclosing namespace(s) are included in the OpenAPI operation's tags array.
[tag-decorator]: ../../standard-library/built-in-decorators.md#tag
[tag-decorator]: ../../standard-library/built-in-decorators.md#@tag
### deprecated
### Deprecated
If the [(built-in) `#deprecated` directive][deprecated-decorator] is specified on the operation, then the operation's
deprecated field is set to true.
If the [(built-in) `#deprecated` directive][deprecated-decorator] is specified on the operation, then the operation's deprecated field is set to true.
[deprecated-decorator]: ../../standard-library/built-in-decorators.md#deprecated
[deprecated-decorator]: ../../standard-library/built-in-decorators.md#@deprecated
### externalDocs
### External Documentation
If the TypeSpec operation has an [(OpenAPI) `@externalDocs` decorator](../openapi/reference/decorators.md#@OpenAPI.externaldocs) this will produce
an externalDocs field in the OpenAPI operation.
If the TypeSpec operation has an [(OpenAPI) `@externalDocs` decorator](../openapi/reference/decorators.md#@TypeSpec.OpenAPI.externalDocs), this will generate an externalDocs field in the OpenAPI operation.
### Specification extensions
### Specification Extensions
Any extensions specified on the TypeSpec operation with the [(OpenAPI) `@extension` decorator](../openapi/reference/decorators.md#OpenAPI.extension)
are included in the emitted OpenAPI operation.
Any extensions specified on the TypeSpec operation with the [(OpenAPI) `@extension` decorator](../openapi/reference/decorators.md#@TypeSpec.OpenAPI.extension) are included in the emitted OpenAPI operation.
## Models and enums
## Models and Enums
Models and enums are converted into schemas in the generated OpenAPI definition. Intrinsic types in TypeSpec are represented
with a JSON Schema type that most closely matches the semantics of the TypeSpec type.
Models and enums are converted into schemas in the generated OpenAPI definition. Intrinsic types in TypeSpec are represented with a JSON Schema type that closely matches the semantics of the TypeSpec type.
Models defined inline will result in an inline schema. Explicitly declared models will be defined in the `components/schemas`
section with the TypeSpec name qualified by any enclosing namespaces.
Inline defined models will result in an inline schema. Explicitly declared models will be defined in the `components/schemas` section with the TypeSpec name qualified by any enclosing namespaces.
A special case is an instantiation of a model template, it is treated as an inline model unless the model template has
a [(built-in) `@friendlyName` decorator][friendlyname], in which case the schema is defined in `components/schemas` with the friendly-name.
A special case is an instantiation of a model template, it is treated as an inline model unless the model template has a [(built-in) `@friendlyName` decorator][friendlyname], in which case the schema is defined in `components/schemas` with the friendly-name.
[friendlyname]: ../../standard-library/built-in-decorators.md#friendlyname
[friendlyname]: ../../standard-library/built-in-decorators.md#@friendlyName
The following table shows how TypeSpec types are translated to JSON Schema types:
@ -145,9 +119,9 @@ The following table shows how TypeSpec types are translated to JSON Schema types
| `utcDateTime` | `type: string, format: date-time` | RFC 3339 date in coordinated universal time (UTC) |
| `offsetDateTime` | `type: string, format: date-time` | RFC 3339 date with timezone offset |
[See encoding and format](#encoding-and-formats) for other way to encode those types.
[See encoding and format](#encoding-and-formats) for other ways to encode these types.
There are a variety of decorators that can modify or add metadata to the definitions produced in the generated OpenAPI.
There are several decorators that can modify or add metadata to the definitions produced in the generated OpenAPI.
For a numeric element (integer or float):
@ -158,13 +132,13 @@ For a numeric element (integer or float):
For any element defined as a `string` or a type that extends from `string`:
| Decorator | Library | OpenAPI/JSON Schema keyword | Notes |
| ------------------- | -------- | --------------------------- | ---------------------------------------------------------- |
| `@format(name)` | built-in | `format: name` | When format is not determined by type or another decorator |
| `@minLength(value)` | built-in | `minLength: value` | |
| `@maxLength(value)` | built-in | `maxLength: value` | |
| `@pattern(regex)` | built-in | `pattern: regex` | |
| `@secret` | built-in | `format: password` | |
| Decorator | Library | OpenAPI/JSON Schema keyword | Notes |
| ------------------- | -------- | --------------------------- | --------------------------------------------------------------- |
| `@format(name)` | built-in | `format: name` | Used when format is not determined by type or another decorator |
| `@minLength(value)` | built-in | `minLength: value` | |
| `@maxLength(value)` | built-in | `maxLength: value` | |
| `@pattern(regex)` | built-in | `pattern: regex` | |
| `@secret` | built-in | `format: password` | |
For an array type:
@ -173,8 +147,7 @@ For an array type:
| `@minItems(value)` | built-in | `minItems: value` | |
| `@maxItems(value)` | built-in | `maxItems: value` | |
The OpenAPI emitter provides an [`@useRef` decorator](../openapi/reference/decorators.md#@OpenAPI.useref) which will replace the TypeSpec model type in emitter output
with a reference to a pre-existing named OpenAPI schema. This can be useful for "common" schemas.
The OpenAPI emitter provides an [`@useRef` decorator](./reference/decorators.md#@TypeSpec.OpenAPI.useRef) which will replace the TypeSpec model type in emitter output with a reference to a pre-existing named OpenAPI schema. This can be useful for "common" schemas.
Example:
@ -203,51 +176,41 @@ status: "Running" | "Stopped" | "Failed"
The OpenAPI emitter converts both of these into a schema definition containing an "enum" with the list of defined values.
### Model composition
### Model Composition
TypeSpec has several mechanisms for model composition and extension. The following describes how these are handled in the OpenAPI emitter.
TypeSpec provides several mechanisms for model composition and extension. The following describes how these are handled in the OpenAPI emitter.
#### Spread
The spread operator does not convey any semantic relationship between the source and target models so the OpenAPI emitter
treats this as if the properties of the source model were explicitly included in the target model at the position where the
spread appears.
The spread operator does not convey any semantic relationship between the source and target models, so the OpenAPI emitter treats this as if the properties of the source model were explicitly included in the target model at the position where the spread appears.
#### Extends
When one model extends another model, this is intended to convey and inheritance relationship. While OpenAPI has no formal
construct for inheritance, the OpenAPI emitter represents this form of composition with an `allOf` in the schema of the child model
that references the schema for the parent model.
When one model extends another model, this is intended to convey an inheritance relationship. While OpenAPI has no formal construct for inheritance, the OpenAPI emitter represents this form of composition with an `allOf` in the schema of the child model that references the schema for the parent model.
##### Extends with discriminator
##### Extends with Discriminator
The OpenAPI emitter supports the `@discriminator(propertyName)` decorator on a `model`. This will produce a `discriminator` object
with the named property in the schema for this model.
The OpenAPI emitter supports the `@discriminator(propertyName)` decorator on a `model`. This will produce a `discriminator` object with the named property in the schema for this model.
Models that extend this model must define this property with a literal string value, and these values must be distinct across all the
models that extend this model. These values are used to construct a `mapping` for the discriminator.
Models that extend this model must define this property with a literal string value, and these values must be distinct across all the models that extend this model. These values are used to construct a `mapping` for the discriminator.
The `@discriminator` decorator can be used to create multi-level discriminated inheritance but must use a different discriminated property at each level.
#### Is
The `is` keyword provides a form of composition similar to the spread operator, where no semantic relationship is conveyed between
the source and target models. The OpenAPI emitter represents this form of composition with an independent schema that contains
all the same properties as the model named by the `is` keyword, plus any properties defined directly on the model.
The `is` keyword provides a form of composition similar to the spread operator, where no semantic relationship is conveyed between the source and target models. The OpenAPI emitter represents this form of composition with an independent schema that contains all the same properties as the model named by the `is` keyword, plus any properties defined directly on the model.
#### Union
Unions are another form of model composition.
Unions can be defined in two different ways in TypeSpec. One way is with
[the union type operator](../../language-basics/unions.md#union-expressions), `|`:
Unions can be defined in two different ways in TypeSpec. One way is with [the union type operator](../../language-basics/unions.md#union-expressions), `|`:
```typespec
alias GoodBreed = Beagle | GermanShepherd | GoldenRetriever;
```
The second way is with [the `union` statement](../../language-basics/unions.md#named-unions)
which not only declares the variant models but also assigns a name for each.
The second way is with [the `union` statement](../../language-basics/unions.md#named-unions) which not only declares the variant models but also assigns a name for each.
```typespec
union GoodBreed {
@ -257,27 +220,25 @@ union GoodBreed {
}
```
The OpenAPI emitter represents either form of union with an `anyOf` with an element for each option of the union.
The OpenAPI emitter ignores the "names" for variants in named unions.
The OpenAPI emitter represents either form of union with an `anyOf` with an element for each option of the union. The OpenAPI emitter ignores the "names" for variants in named unions.
The OpenAPI emitter also defines the[`@oneOf` decorator](../openapi/reference/decorators.md#OpenAPI.oneof) which can be specified on a `union` statement to indicate
that a union should be emitted as a `oneOf` rather than `anyOf`.
The OpenAPI emitter also defines the[`@oneOf` decorator](./reference/decorators.md#@TypeSpec.OpenAPI.oneOf) which can be specified on a `union` statement to indicate that a union should be emitted as a `oneOf` rather than `anyOf`.
## Encoding and formats
## Encoding and Formats
When working with the `@encode` decorator the rule is as follow. Given the 3 values `encoding`, `encodeAs` and `realType` where `@encode(encoding, encodeAs) _: realType`:
When working with the `@encode` decorator, the rule is as follows. Given the 3 values `encoding`, `encodeAs`, and `realType` where `@encode(encoding, encodeAs) _: realType`:
1. If `realType` is `utcDateTime` or `offsetDateTime`:
- `encoding` of `rfc3339` will produce `type: string, format: date-time`
- `encoding` of `rfc7231` will produce `type: string, format: http-date`
1. If `realType` is `utcDateTime` and `encoding` is `unixTimestamp`:
2. If `realType` is `utcDateTime` and `encoding` is `unixTimestamp`:
- `encodeAs` of any integer type will produce `type: integer, format: unixtime`
1. If the schema of encodeAs produces a `format` use it (e.g. encoding as `int32` will produce an `type: integer, format: integer` )
1. Otherwise use the `encoding` as the format
3. If the schema of `encodeAs` produces a `format`, use it (e.g., encoding as `int32` will produce `type: integer, format: integer`)
4. Otherwise, use the `encoding` as the format
**Summary table**
**Summary Table**
| encoding | Openapi3 | Swagger 2.0 (autorest) |
| encoding | OpenAPI 3 | Swagger 2.0 (autorest) |
| ------------------------------------------------ | --------------------------------- | --------------------------------- |
| `@encode("seconds", int32) _: duration` | `type: integer, format: int32` | `type: integer, format: int32` |
| `@encode("seconds", float32) _: duration` | `type: number, format: float32` | `type: number, format: float32` |
@ -290,7 +251,7 @@ When working with the `@encode` decorator the rule is as follow. Given the 3 val
## Security Definitions
The OpenAPI emitter takes the [(http) `@useAuth` decorator](../rest/reference/decorators.md#@TypeSpec.Http.useauth)
The OpenAPI emitter uses the [(http) `@useAuth` decorator](../http/reference/decorators.md#@TypeSpec.Http.useAuth) to handle security definitions.
### Examples

Просмотреть файл

@ -2,21 +2,21 @@
title: Guide
---
# The Protobuf Emitter guide
# Understanding the Protobuf Emitter
TypeSpec provides an emitter (`@typespec/protobuf`) that generates Protocol Buffers specifications from TypeSpec sources as part of its standard library. The resulting Protobuf files may be used as inputs for creating gRPC services or any other tools compatible with Protocol Buffers.
TypeSpec includes a built-in emitter (`/protobuf`) that can generate Protocol Buffers specifications from TypeSpec sources. The Protobuf files generated can then be used to create gRPC services or any other tools that are compatible with Protocol Buffers.
**Note**: The Protobuf emitter uses Protocol Buffers 3 (proto3) syntax. Your workflow (`protoc` version, etc.) must support proto3 to utilize this emitter.
**Please note**: The Protobuf emitter is designed to work with Protocol Buffers 3 (proto3) syntax. Ensure that your workflow (including `protoc` version) supports proto3 to make full use of this emitter.
## Core concepts
## Fundamental Concepts
The Protobuf emitter enables you to write TypeSpec and convert it into equivalent Protocol Buffers for use with Protobuf-enabled systems (such as gRPC). Your TypeSpec models and interfaces must adhere to certain requirements and restrictions in order for the emitter to convert them to Protobuf.
The Protobuf emitter allows you to write TypeSpec and transform it into corresponding Protocol Buffers for use with systems that support Protobuf (like gRPC). To successfully convert your TypeSpec models and interfaces to Protobuf, they must comply with certain rules and limitations.
### Packages
A protobuf package is defined by the [`TypeSpec.Protobuf.package` decorator][protobuf-package], which applies to a TypeSpec namespace. A package essentially defines a `.proto` file, and everything within the decorated namespace will be emitted to a single file.
A protobuf package is established by the [`TypeSpec.Protobuf.package` decorator][protobuf-package], which is applied to a TypeSpec namespace. Essentially, a package defines a `.proto` file, and all contents within the decorated namespace are emitted into a single file.
The following TypeSpec namespace results in a Protobuf file named `main.proto` that contains the contents of the `Test` namespace converted into Protobuf.
Consider the following TypeSpec namespace, which results in a Protobuf file named `main.proto` containing the contents of the `Test` namespace, converted into Protobuf.
```typespec
@package
@ -26,7 +26,7 @@ namespace Test {
}
```
Package names may be provided using the optional `PackageDetails` argument to the `@package` decorator. The following TypeSpec namespace will result in a file `com/example/test.proto` that has the line `package com.example.test;` within it:
You can specify package names using the optional `PackageDetails` argument with the `@package` decorator. The following TypeSpec namespace will create a file `com/example/test.proto` that includes the line `package com.example.test;`:
```typespec
@package({
@ -38,11 +38,11 @@ namespace Test {
}
```
TypeSpec objects (models, enums, etc.) are converted to Protobuf declarations within their nearest ancestor that has a package annotation. As a result, unlike in Protobuf, TypeSpec declarations of packages may be nested arbitrarily.p
TypeSpec entities (like models, enums, etc.) are transformed into Protobuf declarations within their closest ancestor that has a package annotation. This means that, unlike in Protobuf, TypeSpec package declarations can be nested as needed.
### Messages
TypeSpec models are converted into Protobuf messages. The following TypeSpec model:
TypeSpec models are translated into Protobuf messages. For instance, the following TypeSpec model:
```typespec
model TestMessage {
@ -50,7 +50,7 @@ model TestMessage {
}
```
will be converted into the following Protobuf message:
will be transformed into the Protobuf message below:
```proto3
message TestMessage {
@ -58,15 +58,15 @@ message TestMessage {
}
```
Models are converted into messages and included in the Protobuf file if any of the following conditions are met:
Models are converted into messages and included in the Protobuf file if they meet any of the following conditions:
- The model is explicitly annotated with the [`TypeSpec.Protobuf.message` decorator][protobuf-message].
- The model is referenced by any service operation (see [Services](#services) below).
- The model is a direct child of a [package namespace](#packages) and has _every_ field annotated with the [`TypeSpec.Protobuf.field` decorator][protobuf-field].
- The model is referenced by any service operation (refer to [Services](#services) below).
- The model is a direct child of a [package namespace](#packages) and every field is annotated with the [`TypeSpec.Protobuf.field` decorator][protobuf-field].
#### Field indices
#### Field Indices
Protobuf requires that the offset of each field within a Protobuf message be manually specified. In TypeSpec, the field indices are specified using the [`TypeSpec.Protobuf.field` decorator][protobuf-field]. All fields within a model must have an attached `@field` decorator to be converted into a Protobuf message.
Protobuf requires manual specification of the offset for each field within a Protobuf message. In TypeSpec, these field indices are specified using the [`TypeSpec.Protobuf.field` decorator][protobuf-field]. To be converted into a Protobuf message, all fields within a model must have an attached `@field` decorator.
The following TypeSpec model:
@ -76,7 +76,7 @@ model TestMessage {
}
```
will be converted into the following Protobuf message:
will be transformed into the Protobuf message below:
```proto3
message TestMessage {
@ -86,9 +86,9 @@ message TestMessage {
### Services
TypeSpec has a concept of a "service" defined by the [`TypeSpec.service` decorator][native-service], but the Protobuf concept of a "service" is different and is indicated by the [`TypeSpec.Protobuf.service` decorator][protobuf-service].
TypeSpec defines a "service" using the [`TypeSpec.service` decorator][native-service], but the Protobuf "service" concept is different and is denoted by the [`TypeSpec.Protobuf.service` decorator][protobuf-service].
When using the Protobuf emitter, a Protobuf service designation is applied to an _interface_ within a package. For example, the following TypeSpec:
When using the Protobuf emitter, a Protobuf service designation is applied to an interface within a package. For example, the following TypeSpec:
```typespec
@package
@ -100,7 +100,7 @@ namespace Example {
}
```
will yield the following Protobuf file (named `example.proto`):
will generate the following Protobuf file (named `example.proto`):
```proto3
syntax = "proto3";
@ -114,7 +114,7 @@ service Test {
#### Operations
Within a [service interface](#services), TypeSpec operations represent Protobuf service methods. Each operation in the service interface is converted into an equivalent Protobuf method declaration. For example, the following specification:
Within a [service interface](#services), TypeSpec operations are represented as Protobuf service methods. Each operation in the service interface is converted into an equivalent Protobuf method declaration. For instance, the following specification:
```typespec
model Input {
@ -149,25 +149,25 @@ service Example {
#### Streams
The Protobuf emitter supports declaring the streaming mode of an operation using the [`TypeSpec.Protobuf.stream` decorator][protobuf-stream]. The streaming mode is specified using the [`StreamMode`][protobuf-stream-mode] enum. An operation can have one of four streaming modes:
The Protobuf emitter supports the declaration of an operation's streaming mode using the [`TypeSpec.Protobuf.stream` decorator][protobuf-stream]. The streaming mode is defined using the [`StreamMode`][protobuf-stream-mode] enum. An operation can have one of four streaming modes:
- `None`: this is the default mode and indicates that neither the request nor response are streamed.
- `None`: This is the default mode, indicating that neither the request nor the response are streamed.
Example: `rpc Example(In) returns (Out);`
- `In`: indicates that the request is streamed, but the response is received synchronously.
- `In`: This mode indicates that the request is streamed, but the response is received synchronously.
Example: `rpc Example(stream In) returns (Out);`
- `Out`: indicates that the request is sent synchronously, but the response is streamed.
- `Out`: This mode indicates that the request is sent synchronously, but the response is streamed.
Example: `rpc Example(In) returns (stream Out);`
- `Duplex`: indicates that both the request and response are streamed.
- `Duplex`: This mode indicates that both the request and response are streamed.
Example: `rpc Example(stream In) returns (stream Out);`
[native-service]: ../../standard-library/built-in-decorators#service
[native-service]: ../../standard-library/built-in-decorators#@service
[protobuf-service]: reference/decorators#@TypeSpec.Protobuf.service
[protobuf-package]: reference/decorators#@TypeSpec.Protobuf.package
[protobuf-field]: reference/decorators#@TypeSpec.Protobuf.field

Просмотреть файл

@ -1,10 +1,10 @@
---
title: Guide
title: Tutorial
---
## Creating Versioned APIs
## Implementing versioned APIs
The primary purpose of the TypeSpec.Versioning library is to provide a way to version APIs. Let's start with an unversioned API.
The primary role of the TypeSpec.Versioning library is to enable API versioning. Let's start with an API that lacks versioning.
```typespec
@service({
@ -13,7 +13,7 @@ The primary purpose of the TypeSpec.Versioning library is to provide a way to ve
namespace Contoso.WidgetManager;
```
To make this API versioned, we need to add the `@versioned` decorator to the namespace and declare an enum that identifies the supported versions.
To introduce versioning to this API, we need to use the `@versioned` decorator on the namespace and define an enum that outlines the supported versions.
```typespec
@service({
@ -27,11 +27,11 @@ enum Versions {
}
```
## Declaring Versioned Dependencies
## Declaring versioned dependencies
Both versioned and unversioned services can declare their dependency on versioned TypeSpec libraries. To declare a dependency on a versioned library, use the `@useDependency` decorator. For unversioned services, this will be declared on the namespace. For versioned services, this will be declared on the versioned enum.
Both versioned and unversioned services can declare their dependencies on versioned TypeSpec libraries. This is achieved using the `@useDependency` decorator. For unversioned services, this is declared on the namespace. For versioned services, it's declared on the versioned enum.
For example, if our unversioned WidgetManager service has a dependency on the Azure.Core library, we would declare it like this:
For example, if our unversioned WidgetManager service depends on the Azure.Core library, we would declare it like this:
```typespec
@service({
@ -41,7 +41,7 @@ For example, if our unversioned WidgetManager service has a dependency on the Az
namespace Contoso.WidgetManager.Unversioned;
```
If our versioned WidgetManager service has a dependency on the Azure.Core library, we would declare it like this:
If our versioned WidgetManager service depends on the Azure.Core library, we would declare it like this:
```typespec
@service({
@ -56,7 +56,7 @@ enum Versions {
}
```
Let's assume we add a new version to our service, and it takes advantage of features in a newer version of the Azure.Core library. We can declare that dependency like this:
Let's say we introduce a new version to our service, and it uses features from a newer version of the Azure.Core library. We can declare that dependency like this:
```typespec
@service({
@ -76,7 +76,7 @@ enum Versions {
## Versioning APIs
The versioning library makes it easy to version APIs. Let's start with a simple example. Let's say we have a service that has a single API that returns a list of widgets. We can declare that API like this:
The versioning library simplifies the process of versioning APIs. Let's start with a basic example. Assume we have a service with a single API that returns a list of widgets. We can define that API like this:
```typespec
using TypeSpec.Versioning;
@ -109,7 +109,7 @@ model Widget {
op list(): Widget[] | Error;
```
Now let's say that in version 2 of the service, we add a get operation to retrieve a single widget. We can add that like this:
Now, let's suppose that in version 2 of the service, we add a get operation to retrieve a single widget. We can add that like this:
```typespec
enum Versions {
@ -132,8 +132,7 @@ op list(): Widget[] | Error;
op get(...Resource.KeysOf<Widget>): Widget | Error;
```
Now let's say that in version 3 of the service, we realize that `name` is inaccurate and that this field should be called `description`. Also, you
don't always have a description, so you decide it should be optional, not required. We can make these changes like this:
Now, let's suppose that in version 3 of the service, we realize that `name` is not accurate and that this field should be called `description`. Also, we decide that the description should be optional, not mandatory. We can implement these changes like this:
```typespec
model Widget {
@ -146,11 +145,9 @@ model Widget {
}
```
You can see that we made the change to the actual model property so that it now reflects the correct name and optional nature of the property. Both the
`@renamedFrom` and `@madeOptional` decorators identify the version in which the change was made, and the `@renamedFrom` decorator also identifies the
previous name of the property. This allows us to generate code that is aware of the change and can handle it appropriately.
We made the change to the actual model property so that it now reflects the correct name and optional nature of the property. Both the `@renamedFrom` and `@madeOptional` decorators indicate the version in which the change was made, and the `@renamedFrom` decorator also identifies the previous name of the property. This allows us to generate code that is aware of the change and can handle it appropriately.
The OpenAPI defintion of `Widget` for version 3 reflects the change:
The OpenAPI definition of `Widget` for version 3 reflects the change:
```yaml
Widget:
@ -164,7 +161,7 @@ Widget:
- id
```
But the OpenAPI definition for versions 1 and 2 still reflect the original name and required nature of the property:
However, the OpenAPI definition for versions 1 and 2 still reflect the original name and the mandatory nature of the property:
```yaml
Widget:
@ -179,6 +176,4 @@ Widget:
- name
```
This is the common pattern with the versioning decorators. The TypeSpec should reflect the _current state_ of the API. The decorators identify the
version at which this definition became true and, depending on the decorator, the other parameters reflect the preview values in order to preserve
that information.
This is a common pattern with the versioning decorators. The TypeSpec should represent the _current state_ of the API. The decorators indicate the version at which this definition became accurate and, depending on the decorator, the other parameters reflect the previous values to retain that information.

Просмотреть файл

@ -96,8 +96,8 @@ Resolving operation routes now follows the following logic:
### Action if applicable
- If a typespec spec used a service namespace without `@serviceTitle` add the `@serviceTitle` decorator to the service namespace, otherwise no routes will be emitted.
- If a typespec spec contains service namespaces that are not child namespaces of the service namespace, move these namespaces under the service namespace.
- If a TypeSpec spec used a service namespace without `@serviceTitle` add the `@serviceTitle` decorator to the service namespace, otherwise no routes will be emitted.
- If a TypeSpec spec contains service namespaces that are not child namespaces of the service namespace, move these namespaces under the service namespace.
### Cases

Просмотреть файл

@ -41,11 +41,11 @@ options:
## Breaking changes
### `TypeSpec` `Api` `Compiler` Moved typespec intrinsic types to a new Type `scalar`
### `TypeSpec` `Api` `Compiler` Moved TypeSpec intrinsic types to a new Type `scalar`
A new type has been introduced to typespec in order to differentiate scalar from structured models types. Those new types can be declared using `scalar` and can extend another scalar using `extends`.
A new type has been introduced to TypeSpec in order to differentiate scalar from structured models types. Those new types can be declared using `scalar` and can extend another scalar using `extends`.
#### Change to typespec intrinsic models
#### Change to TypeSpec intrinsic models
All of TypeSpec intrinsic models have been updated to be `scalar` types instead of `model`. In most cases this shouldn't affect a spec but if you are defining a custom model that `is` one of the intrinsic types you'll have to update it

Просмотреть файл

@ -49,7 +49,7 @@ This release contains **breaking changes**
### `@typespec/compiler` and core language bug fixes
- Add support for `UTF-8 with bom` for other files loaded by typespec compiler. `.tsp` files already had support, this make it more available for any library/emitter using the compiler api to load a file.
- Add support for `UTF-8 with bom` for other files loaded by TypeSpec compiler. `.tsp` files already had support, this make it more available for any library/emitter using the compiler api to load a file.
- Fix signature help after comment with no closing parenthesis or angle bracket
- Doc comment `/** */` should override base type doc in `model is` or `op is`
- Formatter incorrectly formatting `::` to `.`

Просмотреть файл

@ -67,7 +67,7 @@ This can be resolved by changing the following in `tsconfig.json`:
### Dropped Support for Node version 16
- All typespec packages now specify node 18 as the minimum version. Consuming packages should no longer use node 16 and update the node specification in the `engines` section of their package.json file appropriately.
- All TypeSpec packages now specify node 18 as the minimum version. Consuming packages should no longer use node 16 and update the node specification in the `engines` section of their package.json file appropriately.
### `@typespec/playground` Breaking Changes

Просмотреть файл

@ -4,7 +4,7 @@ title: March 2024
# Release Notes March 2024 (2024-03-05)
:::warn
:::warning
This release contains breaking changes and deprecations
:::

Просмотреть файл

@ -1,14 +1,14 @@
---
title: Discriminated types
title: Discriminated Types
---
TypeSpec can express unions and inheritance. However, when sending types over the wire many languages need a way to discriminate between the various union variants or models in an inheritance hierarchy.
TypeSpec allows for the expression of unions and inheritance. However, when transmitting types over the network, many languages require a mechanism to distinguish between different union variants or models within an inheritance hierarchy.
TypeSpec provide the [`@discriminator` decorator](./built-in-decorators#@discriminator) to be able to help with this pattern.
To facilitate this, TypeSpec offers the [`@discriminator` decorator](./built-in-decorators#@discriminator).
### Using polymorphism
### Implementing Polymorphism
#### `string` discriminator
#### `string` Discriminator
```typespec
@discriminator("kind")
@ -26,7 +26,7 @@ model Dog extends Pet {
}
```
#### `enum` discriminator
#### `enum` Discriminator
```typespec
enum PetKind {
@ -50,7 +50,7 @@ model Dog extends Pet {
}
```
#### Nested discriminator
#### Nested Discriminator
```tsp
@discriminator("kind")
@ -83,7 +83,7 @@ model Dog extends Pet {
}
```
### Using unions
### Implementing Unions
```typespec
@discriminator("kind")

Просмотреть файл

@ -1,56 +1,56 @@
---
id: projected-names
title: Projected names
title: Projected Names
---
# Projected Names
:::warning
Projected names is a legacy feature and will be phased out. Please use [Encoded Names](encoded-names.md) instead for changing the name over the wire.
The feature "Projected Names" is considered legacy and is planned for deprecation. We recommend using [Encoded Names](encoded-names.md) as a more modern alternative for modifying the name over the network.
:::
There is some cases where the name you have in TypeSpec might differ from the name over the wire or for a certain language.
In certain scenarios, the name you use in TypeSpec may need to be different from the name used over the network or in a specific programming language.
## Known targets
## Recognized Targets
List of known targets.
Here is a list of recognized targets.
- Wire:
- `json`: Configure JSON representation of data
- `xml`: Configure XML representation of data
- `json`: Set up JSON data representation
- `xml`: Set up XML data representation
- Language:
- `csharp`: Configure C# code generation
- `java`: Configure Java code generation
- `python`: Configure Python code generation
- `javascript`: Configure JavaScript code generation
- `swift` : Configure Swift code generation
- `c` : Configure C code generation
- `csharp`: Set up C# code generation
- `java`: Set up Java code generation
- `python`: Set up Python code generation
- `javascript`: Set up JavaScript code generation
- `swift` : Set up Swift code generation
- `c` : Set up C code generation
- Type:
- `client`: Configure output for the client
- `server`: Configure output for the server
- `client`: Set up client-side output
- `server`: Set up server-side output
## Update name for a given target
## How to Modify a Name for a Specific Target
### With decorator
### Using Decorator
To update the name of a TypeSpec entity you can use the `@projectedName` decorator. This decorator takes 2 parameters:
To change the name of a TypeSpec entity, you can use the `@projectedName` decorator. This decorator requires 2 parameters:
- `string` target name. See [known targets](#known-targets)
- `string` projected name. Whatever the name should be in the given target.
- `string` target name. Refer to the [recognized targets](#recognized-targets)
- `string` projected name. This is the name to be used in the specified target.
Example:
```typespec
model Foo {
// Specify that when serializing to JSON `expireAt` property should be named `exp`
// Indicate that the `expireAt` property should be named `exp` when serialized to JSON
@projectedName("json", "exp")
expireAt: string;
}
```
### With projection
### Using Projection
The decorator is just a syntax sugar for the `target` projection behind the scenes. In more complex cases you might want to just implement the projection manually.
The decorator is essentially a shorthand for the `target` projection. For more complex scenarios, you might prefer to implement the projection manually.
```typespec
model Foo {
@ -93,9 +93,9 @@ model CertificateAttributes {
</tr>
</thead>
<tr>
<td>When serialized to Json property use the json projected name</td>
<td>Typescript didn't provide any projected name so it keep the model as it is.</td>
<td>Model uses the `csharp` projected names and keeps the reference to the JSON name in JsonProperty</td>
<td>When serialized to Json, the property uses the json projected name</td>
<td>Typescript doesn't specify any projected name, so it retains the model as is.</td>
<td>The model uses the `csharp` projected names and maintains the reference to the JSON name in JsonProperty</td>
</tr>
<tr>
<td>

Просмотреть файл

@ -1,3 +1,3 @@
# TypeSpec Raw Svg Icons
Those are the raw typespec icons with text still has text. It can be used to easily convert the text to path with different font and different positions.
Those are the raw TypeSpec icons with text still has text. It can be used to easily convert the text to path with different font and different positions.

Просмотреть файл

@ -481,7 +481,7 @@ Wed, 07 Dec 2022 17:21:52 GMT
- Add new helper `validateDecoratorUniqueOnNode` that validate the decorator is not being used twice on the same node
- Add variable interpolation functionality in the cadl-project.yaml
- Add built-in `emitter-output-dir` options for all emitter.
- **Api Breaking change** $onEmit signature was updated to take an EmitContext object as only parmaeter.
- **Api Breaking change** $onEmit signature was updated to take an EmitContext object as only parameter.
- Fix typing and export format command
- **Api Breaking** Multiple `@service` per specs are now allowed.
- Add new `program.resolveTypeReference` helper to resolve a type in the cadl program using fully qualified name

Просмотреть файл

@ -497,7 +497,7 @@ const diagnostics = {
shadow: {
severity: "warning",
messages: {
default: paramMessage`Shadowing parent template parmaeter with the same name "${"name"}"`,
default: paramMessage`Shadowing parent template parameter with the same name "${"name"}"`,
},
},
"invalid-deprecation-argument": {

Просмотреть файл

@ -419,7 +419,7 @@ describe("compiler: interfaces", () => {
expectDiagnostics(diagnostics, {
code: "shadow",
message: `Shadowing parent template parmaeter with the same name "A"`,
message: `Shadowing parent template parameter with the same name "A"`,
});
});

Просмотреть файл

@ -178,7 +178,7 @@ describe("cli", () => {
expect(file.toString()).toEqual("foo");
});
it("set config parmaeter with --arg", async () => {
it("set config parameter with --arg", async () => {
await cleanOutputDir("with-config");
const { stdout } = await execCliSuccess(

Просмотреть файл

@ -24,7 +24,7 @@ linter:
Available ruleSets:
- [`@typespec/http/all`](#@typespec/http/all)
- `@typespec/http/all`
### Rules

Просмотреть файл

@ -337,7 +337,7 @@ describe("openapi3: parameters", () => {
});
});
it("query named contentType doesn't get resolved as the content type parmaeter.", async () => {
it("query named contentType doesn't get resolved as the content type parameter.", async () => {
const res = await openApiFor(
`
op test(

Просмотреть файл

@ -360,7 +360,10 @@ export class MarkdownRenderer {
return section("Linter", [
section("Usage", ["Add the following in `tspconfig.yaml`:", codeblock(setupExample, "yaml")]),
refDoc.linter.ruleSets
? section("RuleSets", ["Available ruleSets:", this.toc(refDoc.linter.ruleSets)])
? section("RuleSets", [
"Available ruleSets:",
refDoc.linter.ruleSets.map((item) => ` - ${inlinecode(item.name)}`),
])
: [],
section("Rules", this.linterRuleToc(refDoc.linter.rules)),
]);

Просмотреть файл

@ -55,7 +55,7 @@ const config: Config = {
url: "https://typespec.io",
baseUrl,
onBrokenLinks: "throw",
onBrokenMarkdownLinks: "warn",
onBrokenMarkdownLinks: "throw",
favicon: "img/favicon.svg",
// GitHub pages deployment config.
@ -235,10 +235,6 @@ const config: Config = {
label: "Community",
to: "/community",
},
{
label: "Getting started",
to: "/docs",
},
{
type: "docsVersionDropdown",
position: "right",

Просмотреть файл

@ -41,12 +41,12 @@ const DataValidationContent = () => {
layout="text-right"
header="Output"
title="Produce JSON Schema"
description="Benefit from the JSON Schema ecosystem to validate your data while writing a much more concise and readable code."
description="Benefit from the JSON Schema ecosystem to validate your data while writing more concise and readable code."
illustration={<MultiFileIllustration />}
>
<LearnMoreCard
title="Configure the JSON schema emitter"
description="Change how the JSON schema is emitted: specify a bundleId to combine all schemas into a single file or use json instead of yaml."
description="Change how the JSON schema is emitted: specify a bundleId to combine all schemas into a single file or use JSON instead of yaml."
image="shield-settings"
link={Links.libraryReferences.jsonSchema.index}
/>
@ -55,7 +55,7 @@ const DataValidationContent = () => {
<Section
header="Customize"
title="JSON Schema Decorators"
description="The json schema library provide decorators to customize the output with json schema specific concept."
description="The JSON schema library provides decorators to customize the output with JSON schema specific concepts."
illustration={<JsonSchemaExtensionsIllustration />}
>
<LearnMoreCard

Просмотреть файл

@ -43,13 +43,13 @@ const OpenApiContent = () => {
<Section
layout="text-right"
header="Ecosystem"
title="Abstract common patterns"
description="Codify API patterns into reusable components, improving up quality and consistency across your API surface."
title="Abstract recurring patterns"
description="Transform API patterns into reusable elements to enhance both the quality and uniformity of your API interface."
illustration={<OpenAPI3AbstractCode />}
>
<LearnMoreCard
title="Example: TypeSpec Azure Library"
description="Azure library for TypeSpec allows a multitude of teams to reuse approved patterns."
description="The TypeSpec library for Azure allows multiple teams to reuse pre-approved patterns."
image="document-cloud"
link={Links.typespecAzure}
/>

Просмотреть файл

@ -17,7 +17,7 @@ const DataValidationContent = () => {
return (
<div>
<UseCaseOverview
title="Syntax highlighting, autocomplete, formatter and more"
title="Syntax highlighting, autocomplete, formatting, and more"
subtitle="TypeSpec is supported by a variety of readily available tools, designed to boost your productivity right from the start."
link={Links.editor.home}
illustration={<LightDarkImg src="illustrations/ide-hero" />}
@ -26,7 +26,7 @@ const DataValidationContent = () => {
<Section
header="Style consistency"
title="Built-in formatter"
description="TypeSpec provide an opinionated formatter that enables you to enforce a consistent style in your codebase."
description="TypeSpec provides an opinionated formatter that enables you to enforce a consistent style in your codebase."
illustration={<FormatterIllustration />}
>
<LearnMoreCard
@ -53,14 +53,14 @@ const DataValidationContent = () => {
<Section
header="Intellisense"
title="Autocomplete and more"
description="IntelliSense shows you intelligent code completion, hover information, and signature help so that you can write code more quickly and correctly."
description="IntelliSense shows you intelligent code completion, hover information, and signature help so that you can quickly and efficiency write correct code."
illustration={<LightDarkImg src="illustrations/autocomplete" />}
/>
<Section
layout="text-right"
header="Refactor"
title="Bulk renaming"
description="One of the simplest refactoring is to rename a reference. You can rename a identifier and see all its reference across your TypeSpec project update."
description="One of the simplest forms of refactoring is renaming a reference. Rename an identifier and see all of its references updated across your TypeSpec project."
illustration={
<video
src={useBaseUrl("/img/illustrations/refactor.mp4")}

Просмотреть файл

@ -5,17 +5,17 @@ title: Creating a TypeSpec Library
# Creating a TypeSpec library
TypeSpec libraries are packages that contain TypeSpec types, decorators, emitters, linters, and other bits of reusable code. TypeSpec libraries are [npm packages](https://docs.npmjs.com/packages-and-modules/contributing-packages-to-the-registry) with some additional typespec-specific metadata and conventions. The following will show how to establish a new TypeSpec library, add some types to it, and distribute it on the public npm registry. Later sections will cover more details on how to write [decorators](create-decorators.md), [emitters](./emitters-basics.md) and [linters](./linters.md).
A TypeSpec library is a package that includes TypeSpec types, decorators, emitters or linters. These libraries are [npm packages](https://docs.npmjs.com/packages-and-modules/contributing-packages-to-the-registry) with some additional TypeSpec-specific metadata and conventions. This guide will walk you through the process of creating a new TypeSpec library, adding types to it, and distributing it on the public npm registry. Further sections will delve into the specifics of creating [decorators](create-decorators.md), [emitters](./emitters-basics.md) and [linters](./linters.md).
This document assumes you will be using [TypeScript](https://typescriptlang.org) to develop your library, but you should feel free to skip the TypeScript steps if you want to use plain JavaScript.
While this guide assumes that you'll be using [TypeScript](https://typescriptlang.org) to develop your library, you can skip the TypeScript-related steps if you prefer to use plain JavaScript.
## Prerequisites
You will need both Node and npm installed. Additionally, if you intend to develop multiple libraries together, you will likely want to establish a monorepo as this will make developing the libraries in tandem much easier. TypeSpec itself uses [pnpm](https://pnpm.io/).
You'll need to have both Node and npm installed. If you're planning to develop multiple libraries simultaneously, it's recommended to set up a monorepo to simplify the development process. TypeSpec itself uses [pnpm](https://pnpm.io/).
## Setup with templates
## Setting up with templates
Available templates:
You can use the following templates:
```bash
# Create a TypeSpec library (Decorators & Linters) with TypeScript enabled.
@ -25,21 +25,21 @@ tsp init --template library-ts
tsp init --template emitter-ts
```
## Canonical package structure
## Standard package structure
The following is a high level overview of the contents of a TypeSpec package. These files are explained in more detail in the subsequent sections.
Here's a high-level overview of what a TypeSpec package typically contains. Each of these files will be explained in more detail in the following sections.
- **dist/index.js** - the main file for your Node library
- **lib/main.tsp** - the main file for your TypeSpec types (optional)
- **src/index.ts** - the main file for your Node library in TypeScript
- **src/lib.ts** - the TypeSpec library definition file
- **package.json** - metadata about your TypeSpec package
- **dist/index.js** - The main file for your Node library
- **lib/main.tsp** - The main file for your TypeSpec types (optional)
- **src/index.ts** - The main file for your Node library in TypeScript
- **src/lib.ts** - The file that defines your TypeSpec library
- **package.json** - Metadata about your TypeSpec package
## 1 - Initial setup
## Step 1: Initial setup
You can skip this if you used one of the templates above.
You can skip this step if you've used one of the templates above.
### a. Initialize your package directory &amp; package.json
### a. Initialize your package directory & package.json
Run the following commands:
@ -49,9 +49,9 @@ Run the following commands:
> npm init
```
After filling out the wizard, you will have a package.json file that defines your TypeSpec library.
After completing the wizard, you'll have a package.json file that defines your TypeSpec library.
Unlike Node libraries which support CommonJS (cjs), TypeSpec libraries must be ECMAScript Modules. Open your `package.json` and add the following top-level configuration key:
Unlike Node libraries which support CommonJS (cjs), TypeSpec libraries must be ECMAScript Modules. To specify this, open your `package.json` and add the following top-level configuration key:
```jsonc
"type": "module"
@ -65,13 +65,13 @@ Run the following command:
npm install --save-peer @typespec/compiler
```
You may have need of other dependencies in the TypeSpec standard library depending on what you are doing (e.g. if you want to use the metadata found in `@typespec/openapi` you will need to install that as well).
You might need to install other dependencies from the TypeSpec standard library. For example, if you want to use the metadata found in `@typespec/openapi`, you'll need to install that as well.
See [dependency section](#defining-dependencies) for information on how to define your dependencies.
Refer to the [dependency section](#step-3-defining-dependencies) for more information on defining your dependencies.
### c. Define your main files
Your package.json needs to refer to two main files: your Node module main file, and your TypeSpec main. The Node module main file is the `"main"` key in your package.json file, and defines the entrypoint for your library when consumed as a Node library, and must reference a JS file. The TypeSpec main defines the entrypoint for your library when consumed from a TypeSpec program, and may reference either a JS file (when your library doesn't contain any TypeSpec types) or a TypeSpec file.
Your package.json needs to refer to two main files: your Node module main file, and your TypeSpec main. The Node module main file is specified by the `"main"` key in your package.json file, and it defines the entry point for your library when it's used as a Node library. This must reference a JS file. The TypeSpec main defines the entry point for your library when it's used from a TypeSpec program, and it can reference either a JS file (when your library doesn't contain any TypeSpec types) or a TypeSpec file.
```jsonc
"main": "dist/src/index.js",
@ -87,7 +87,7 @@ npm install -D typescript
npx tsc --init --strict
```
This will create `tsconfig.json`. But we need to make a couple changes to this. Open `tsconfig.json` and set the following settings:
This will create a `tsconfig.json` file. You'll need to make a few changes to this file. Open `tsconfig.json` and set the following settings:
```jsonc
"module": "Node16", // This and next setting tells TypeScript to use the new ESM import system to resolve types.
@ -103,10 +103,10 @@ This will create `tsconfig.json`. But we need to make a couple changes to this.
Open `./src/lib.ts` and create your library definition that registers your library with the TypeSpec compiler and defines any diagnostics your library will emit. Make sure to export the library definition as `$lib`.
:::warning
If `$lib` is not accessible from your library package (`import {$lib} from "my-library";`) some functionality will be unavailable like validation of emitter options, linter rules, etc.
If `$lib` is not accessible from your library package (for example, `import {$lib} from "my-library";`), some features such as linting and emitter option validation will not be available.
:::
The following shows an example:
Here's an example:
```typescript
import { createTypeSpecLibrary } from "@typespec/compiler";
@ -116,24 +116,24 @@ export const $lib = createTypeSpecLibrary({
diagnostics: {},
} as const);
// Optional but convenient, those are meant to be used locally in your library.
// Optional but convenient, these are meant to be used locally in your library.
export const { reportDiagnostic, createDiagnostic } = $lib;
```
Diagnostics are used for linters and decorators which are covered in subsequent topics.
Diagnostics are used for linters and decorators, which are covered in subsequent topics.
### f. Create `index.ts`
Open `./src/index.ts` and import your library definition:
```typescript
// Re-export $lib to the compiler can get access to it and register your library correctly.
// Re-export $lib so the compiler can access it and register your library correctly.
export { $lib } from "./lib.js";
```
### g. Build TypeScript
TypeSpec can only import JavaScript files, so any time changes are made to TypeScript sources, they need to be compiled before they are visible to TypeSpec. To do so, run `npx tsc -p .` in your library's root directory. You can also run `npx tsc -p . --watch` if you would like to re-run the TypeScript compiler whenever files are changed.
TypeSpec can only import JavaScript files, so any changes made to TypeScript sources need to be compiled before they are visible to TypeSpec. To do this, run `npx tsc -p .` in your library's root directory. If you want to re-run the TypeScript compiler whenever files are changed, you can run `npx tsc -p . --watch`.
Alternatively, you can add these as scripts in your `package.json` to make them easier to invoke. Consider adding the following:
@ -150,15 +150,15 @@ You can then run `npm run build` or `npm run watch` to build or watch your libra
### h. Add your main TypeSpec file
Open `./lib/main.tsp` and import your JS entrypoint. This ensures that when TypeSpec imports your library, the code to define the library is run. In later topics when we add decorators, this import will ensure those get exposed as well.
Open `./lib/main.tsp` and import your JS entrypoint. This ensures that when TypeSpec imports your library, the code to define the library is run. When we add decorators in later topics, this import will ensure those get exposed as well.
```typespec
import "../dist/index.js";
```
## 2. Adding TypeSpec types to your library
## Step 2: Adding TypeSpec types to your library
Open `./lib/main.tsp` and add any types you want to be available when users import this library. It is also strongly recommended you put these types in a namespace that corresponds with the library name. For example, your `./lib/main.tsp` file might look like:
Open `./lib/main.tsp` and add any types you want to be available when users import this library. It's strongly recommended to put these types in a namespace that corresponds with the library name. For example, your `./lib/main.tsp` file might look like:
```typespec
import "../dist/index.js";
@ -170,15 +170,15 @@ model Person {
}
```
## 3. Defining Dependencies
## Step 3: Defining dependencies
Defining dependencies in a TypeSpec library should be following these rules:
When defining dependencies in a TypeSpec library, follow these rules:
- use `peerDependencies` for all TypeSpec libraries (+ compiler) that you use in your own library/emitter
- use `devDependencies` for the other TypeSpec libraries used only in tests
- use `dependencies`/`devDependencies` for any other packages depending if using in library code or in test/dev scripts
- Use `peerDependencies` for all TypeSpec libraries (and the compiler) that you use in your own library or emitter.
- Use `devDependencies` for other TypeSpec libraries that are only used in tests.
- Use `dependencies` or `devDependencies` for any other packages, depending on whether they're used in library code or in test/dev scripts.
TypeSpec libraries are defined using `peerDependencies` so we don't end-up with multiple versions of the compiler/library running at the same time.
TypeSpec libraries are defined using `peerDependencies` to avoid having multiple versions of the compiler or library running at the same time.
**Example**
@ -188,7 +188,7 @@ TypeSpec libraries are defined using `peerDependencies` so we don't end-up with
"yaml": "~2.3.1", // This is a regular package this library/emitter will use
},
"peerDependencies": {
// Those are all TypeSpec libraries this library/emitter depend on
// These are all TypeSpec libraries this library/emitter depends on
"@typespec/compiler": "~0.43.0",
"@typespec/http": "~0.43.1",
"@typespec/openapi": "~0.43.0",
@ -196,19 +196,19 @@ TypeSpec libraries are defined using `peerDependencies` so we don't end-up with
"devDependencies": {
// This TypeSpec library is only used in the tests but is not required to use this library.
"@typespec/versioning": "~0.43.0",
// Typescript is only used during development
// TypeScript is only used during development
"typescript": "~5.0.2",
},
}
```
## 4. Testing your TypeSpec library
## Step 4: Testing your TypeSpec library
TypeSpec provides a testing framework to help testing libraries. Examples here are shown using Node.js's built-in test framework (available in Node 20+) but any other JS test framework can be used that will provide more advanced features like vitest which is used in this project.
TypeSpec provides a testing framework to assist in testing libraries. The examples here are shown using Node.js's built-in test framework (available in Node 20+), but any other JS test framework can be used that will provide more advanced features like vitest, which is used in this project.
### a. Add devDependencies
Verify that you have the following in your `package.json`:
Ensure that you have the following in your `package.json`:
```json
"devDependencies": {
@ -233,7 +233,7 @@ export default defineConfig({
### b. Define the testing library
The first step is to define how your library can be loaded from the test framework. This will let your library to be reused by other library tests.
The first step is to define how your library can be loaded from the test framework. This will allow your library to be reused by other library tests.
1. Create a new file `./src/testing/index.ts` with the following content
@ -272,14 +272,14 @@ export const MyTestLibrary = createTestLibrary({
Define some of the test framework base pieces that will be used in the tests. There are 2 functions:
- `createTestHost`: This is a lower level API that provides a virtual file system.
- `createTestHost`: This is a lower-level API that provides a virtual file system.
- `createTestRunner`: This is a wrapper on top of the test host that will automatically add a `main.tsp` file and automatically import libraries.
Create a new file `test/test-host.js` (change `test` to be your test folder)
```ts
import { createTestHost, createTestWrapper } from "@typespec/compiler/testing";
import { RestTestLibrary } from "@typespec/rest/testing";
import { RestTestLibrary } from "/rest/testing";
import { MyTestLibrary } from "../src/testing/index.js";
export async function createMyTestHost() {
@ -340,7 +340,7 @@ describe("my library", () => {
#### e. `@test` decorator
The `@test` decorator is a decorator loaded in the test environment. It can be used to collect any decorable type.
When using the `compile` method it will return a `Record<string, Type>` which is a map of all the types annoted with the `@test` decorator.
When using the `compile` method it will return a `Record<string, Type>` which is a map of all the types annotated with the `@test` decorator.
```ts
const { Foo, CustomName } = await runner.compile(`
@ -357,23 +357,23 @@ CustomName; // type of : Bar.name
#### f. Install vscode extension for the test framework
If you are using VSCode, you can install the [Node test runner](https://marketplace.visualstudio.com/items?itemName=connor4312.nodejs-testing) to run your tests from the editor. This will also allow you easily debug into your tests.
If you are using VSCode, you can install the [Node test runner](https://marketplace.visualstudio.com/items?itemName=connor4312.nodejs-testing) to run your tests from the editor. This will also allow you to easily debug your tests.
You should now be able to discover, run and debug into your tests from the test explorer.
After installing the extension, you should be able to discover, run, and debug your tests from the test explorer.
## 5. Publishing your TypeSpec library
## Step 5: Publishing your TypeSpec library
To publish to the public npm registry, follow [their documentation](https://docs.npmjs.com/creating-and-publishing-unscoped-public-packages).
To publish your library to the public npm registry, follow the instructions in the [npm documentation](https://docs.npmjs.com/creating-and-publishing-unscoped-public-packages).
## 6. Importing your TypeSpec library
## Step 6: Importing your TypeSpec library
Once your TypeSpec library is published, your users can install and use it just like any of the TypeSpec standard libraries. First, they have to install it:
Once your TypeSpec library is published, users can install and use it just like any of the standard TypeSpec libraries. First, they need to install it:
```bash
npm install $packageName
```
Next, they import it into their TypeSpec program and use the namespace (if desired):
Next, they can import it into their TypeSpec program and use the namespace (if desired):
```typespec
import "MyLibrary";
@ -384,6 +384,6 @@ model Employee extends Person {
}
```
## 7. Next steps
## Step 7: Next steps
TypeSpec libraries can contain more than just types. Read the subsequent topics for more details on how to write [decorators](./create-decorators.md), [emitters](./emitters-basics.md) and [linters](./linters.md).
TypeSpec libraries can contain more than just types. For more details on how to write [decorators](./create-decorators.md), [emitters](./emitters-basics.md) and [linters](./linters.md), refer to the subsequent topics.

Просмотреть файл

@ -41,7 +41,7 @@ reportDiagnostic({
## Test a diagnostic
[See here for testing a codefix inside a linter rule](./linters.md#test-a-codefix)
[See here for testing a codefix inside a linter rule](./linters.md#testing-linter-with-codefixes)
Testing a codefix is done by using the `expectCodeFixOnAst` function from the `@typespec/compiler/testing` package. It takes in the source code and a function that returns the codefix to apply.
It takes the input source code with a cursor defined by `┆` which will be used to resolve the node where the codefix should be applied. The callback function will receive that resolved node and is expected to return the codefix to test.

Просмотреть файл

@ -1,33 +1,33 @@
---
id: create-decorators
title: Creating TypeSpec Decorators
title: How to create TypeSpec decorators
---
# Creating TypeSpec decorators
# How to create TypeSpec decorators
TypeSpec decorator are implemented as JavaScript function. Declarating a decorator can be done in 1 or 2 part:
TypeSpec decorators are implemented as JavaScript functions. The process of creating a decorator can be divided into two parts:
1. [(Optional) Declare the decorator signature in typespec](#declaring-a-decorator-signature)
2. [Implement the decorator in Javascript](#implement-the-decorator-in-js)
1. [(Optional) Declare the decorator signature in TypeSpec](#declare-the-decorator-signature)
2. [Implement the decorator in JavaScript](#implement-the-decorator-in-javascript)
## Declaring a decorator signature
## Declare the decorator signature
This part is optional but provides great value:
While this step is optional, it offers significant benefits:
- Type checking for the parameters
- IDE IntelliSense
- It enables type checking for the parameters
- It provides IDE IntelliSense
A decorator signature can be declared using the `dec` keyword. As we are implementing the decorator in JS (only choice right now), we must apply the `extern` modifier as well.
You can declare a decorator signature using the `dec` keyword. Since we're implementing the decorator in JavaScript (the only option currently), we need to use the `extern` modifier as well.
```typespec
extern dec logType(target: unknown, name: string);
```
## Decorator target
## Specifying the decorator target
The first parameter of the decorator represents the TypeSpec type(s) that the decorator can be applied on.
The first parameter of the decorator represents the TypeSpec type(s) that the decorator can be applied to.
You can specify multiple potential target type using an `union expression`
You can specify multiple potential target types using a `union expression`.
```typespec
using TypeSpec.Reflection;
@ -37,31 +37,30 @@ extern dec track(target: Model | Enum);
### Optional parameters
A decorator parameter can be marked optional using `?`
You can mark a decorator parameter as optional using `?`.
```typespec
extern dec track(target: Model | Enum, name?: valueof string);
```
### REST parameters
### Rest parameters
A decorator's last parameter can be prefixed with `...` to collect all the remaining arguments. The type of that parameter must be an `array expression`
You can prefix the last parameter of a decorator with `...` to collect all the remaining arguments. The type of this parameter must be an `array expression`.
```typespec
extern dec track(target: Model | Enum, ...names: valueof string[]);
```
## Ask for a value type
## Requesting a value type
It is common that decorators parameter will expect a value(e.g. a string or a number). However just using `: string` as the type will also allow a user of the decorator to pass `string` itself or a custom scalar extending string as well as union of strings.
Instead the decorator can use `valueof <T>` to specify that it is expecting a value of that kind.
It's common for decorator parameters to expect a value (e.g., a string or a number). However, using `: string` as the type would also allow a user of the decorator to pass `string` itself or a custom scalar extending string, as well as a union of strings. Instead, the decorator can use `valueof <T>` to specify that it expects a value of that kind.
| Example | Description |
| ----------------- | ---------------- |
| `valueof string` | Expect a string |
| `valueof float64` | Expect a float |
| `valueof int32` | Expect a number |
| `valueof boolean` | Expect a boolean |
| Example | Description |
| ----------------- | ----------------- |
| `valueof string` | Expects a string |
| `valueof float64` | Expects a float |
| `valueof int32` | Expects a number |
| `valueof boolean` | Expects a boolean |
```tsp
extern dec tag(target: unknown, value: valueof string);
@ -73,7 +72,7 @@ extern dec tag(target: unknown, value: valueof string);
@tag("This is the tag name")
```
## Implement the decorator in JS
## Implement the decorator in JavaScript
Decorators can be implemented in JavaScript by prefixing the function name with `$`. A decorator function must have the following parameters:
@ -90,7 +89,7 @@ export function $logType(context: DecoratorContext, target: Type, name: valueof
}
```
or in pure JS
Or in pure JavaScript:
```ts
// model.js
@ -99,7 +98,7 @@ export function $logType(context, target, name) {
}
```
The decorator can then be consumed this way
The decorator can then be used like this:
```typespec
// main.tsp
@ -114,7 +113,7 @@ model Dog {
### Decorator parameter marshalling
For certain TypeSpec types (Literal types) the decorator do not receive the actual type but a marshalled value if the decorator parmaeter type is a `valueof`. This is to simplify the most common cases.
For certain TypeSpec types (Literal types), the decorator does not receive the actual type but a marshalled value if the decorator parameter type is a `valueof`. This simplifies the most common cases.
| TypeSpec Type | Marshalled value in JS |
| ----------------- | ---------------------- |
@ -122,7 +121,7 @@ For certain TypeSpec types (Literal types) the decorator do not receive the actu
| `valueof numeric` | `number` |
| `valueof boolean` | `boolean` |
for all the other types they are not transformed.
For all other types, they are not transformed.
Example:
@ -130,7 +129,7 @@ Example:
export function $tag(
context: DecoratorContext,
target: Type,
stringArg: string, // Here instead of receiving a `StringLiteral` the string value is being sent.
stringArg: string, // Here instead of receiving a `StringLiteral`, the string value is being sent.
modelArg: Model // Model has no special handling so we receive the Model type
) {}
```
@ -142,17 +141,13 @@ The TypeSpec type system will already validate the string template can be serial
```tsp
extern dec doc(target: unknown, name: valueof string);
alias world = "world!";
@doc("Hello ${world} ") // receive: "Hello world!"
@doc("Hello ${123} ") // receive: "Hello 123"
@doc("Hello ${true} ") // receive: "Hello true"
model Bar {}
@doc("Hello ${Bar} ") // not called error
^ String template cannot be serialized as a string.
```
#### Typescript type Reference
@ -168,7 +163,7 @@ model Bar {}
### Adding metadata with decorators
Decorators can be used to register some metadata. For this you can use the `context.program.stateMap` or `context.program.stateSet` to insert data that will be tied to the current execution.
Decorators can be used to register some metadata. For this, you can use the `context.program.stateMap` or `context.program.stateSet` to insert data that will be tied to the current execution.
❌ Do not save the data in a global variable.
@ -200,7 +195,7 @@ export const StateKeys = $lib.stateKeys;
### Reporting diagnostic on decorator or arguments
Decorator context provide the `decoratorTarget` and `getArgumentTarget` helpers
The decorator context provides the `decoratorTarget` and `getArgumentTarget` helpers.
```ts
import type { DecoratorContext, Type } from "@typespec/compiler";
@ -218,9 +213,9 @@ export function $customName(context: DecoratorContext, target: Type, name: strin
}
```
## Declaration - implementation link
## Linking declaration and implementation
Decorator signatures are linked to the implementation of the same name in the same namespace
Decorator signatures are linked to the implementation of the same name in the same namespace.
```typespec
import "./lib.js";
@ -231,7 +226,7 @@ namespace MyLib {
}
```
is linked the the following in `lib.js`
This is linked to the following in `lib.js`:
```ts
export function $customName(context: DecoratorContext, name: string) {}
@ -242,12 +237,12 @@ setTypeSpecNamespace("MyLib", $tableName);
## Troubleshooting
### Extern declation must have an implementation in JS file
### Extern declaration must have an implementation in JS file
Potential issues:
- JS function is not prefixed with `$`. For a decorator called `@decorate` the JS function must be called `$decoratate`
- JS function is not in the same namespace as the the `extern dec`
- Error is only showing in the IDE? Restart the TypeSpec server or the IDE.
- The JS function is not prefixed with `$`. For a decorator called `@decorate`, the JS function must be called `$decorate`.
- The JS function is not in the same namespace as the `extern dec`.
- Is the error only showing in the IDE? Try restarting the TypeSpec server or the IDE.
You can use `--trace bind.js.decorator` to log debug information about decorator loading in JS file that should help pinning down which of those is the issue.
You can use `--trace bind.js.decorator` to log debug information about decorator loading in the JS file, which should help identify the issue.

Просмотреть файл

@ -2,25 +2,25 @@
title: Diagnostics
---
The TypeSpec compiler reports errors and warnings in the spec using the diagnostic API.
The TypeSpec compiler uses the diagnostic API to report errors and warnings in the specification.
## Best practices
- ❌ Do not use `throw` to report errors. Any exception thrown like this will be presented as a bug in your library to the user.
- ✅ Use diagnostic API to report expected errors and warnings.
- ✅ Use `reportDiagnostic` in a decorator, `$onValidate` or `$onEmit`
- ❌ Do not use `reportDiagnostic` in an accessor (A function meant to be consumed in another library or emitter). See [collect diagnostics section](#collect-diagnostics)
- ❌ Avoid using `throw` to report errors. Any exceptions thrown in this manner will be perceived as bugs in your library by the user.
- ✅ Utilize the diagnostic API to report anticipated errors and warnings.
- ✅ Employ `reportDiagnostic` in a decorator, `$onValidate` or `$onEmit`
- ❌ Refrain from using `reportDiagnostic` in an accessor (a function intended to be used in another library or emitter). Refer to the [section on collecting diagnostics](#collect-diagnostics) for more information.
## Diagnostic specification
## Diagnostic requirements
- Each diagnostic MUST have a `code`. The full code is the the library name followed by the declared code. (`<lib-name>/<local-code>`)
- Each diagnostic MUST have a `severity`. It can be `error`, `warning`. Errors cannot be suppressed
- Each diagnostics MUST have at least one message. Using `default` as the `messageId` will allow it to be the default selected.
- Each diagnostics message MAY have parameters to interpolate information into the message
- Each diagnostic MUST have a `code`. The complete code is the library name followed by the declared code. (`<lib-name>/<local-code>`)
- Each diagnostic MUST have a `severity`. It can be `error` or `warning`. Errors cannot be suppressed.
- Each diagnostic MUST have at least one message. Using `default` as the `messageId` will make it the default selection.
- Each diagnostic message MAY have parameters to interpolate information into the message.
## Usage
## How to use
### Declare the diagnostics you are reporting
### Declare the diagnostics you plan to report
```ts
import { createTypeSpecLibrary } from "@typespec/compiler";
@ -56,11 +56,11 @@ export const $lib = createTypeSpecLibrary({
},
} as const);
// Rexport the helper functions to be able to just call them directly.
// Re-export the helper functions to be able to just call them directly.
export const { reportDiagnostic, createDiagnostic };
```
This will represent 3 different diagnostics with full name of
This will represent three different diagnostics with the full names of:
- `@typespec/my-lib/no-array`
- `@typespec/my-lib/duplicate-route`
@ -87,7 +87,7 @@ reportDiagnostic(program, {
// Multiple messages
reportDiagnostic(program, {
code: "duplicate-name",
messageId: "parmaeter",
messageId: "parameter",
format: {value: "$select"},
target: diagnosticTarget,
});
@ -95,8 +95,8 @@ reportDiagnostic(program, {
### Collect diagnostics
When trying to report diagnostic in an accessor a good pattern is not to report the diagnostic to the program directly but return a tuple to let the user decide what to do.
This prevent duplicate diagnostics emitter if the accessor is called multiple times.
When attempting to report a diagnostic in an accessor, a good practice is not to report the diagnostic to the program directly, but return a tuple to let the user decide what to do.
This prevents duplicate diagnostics emitter if the accessor is called multiple times.
```ts
import { createDiagnosticCollector, Diagnostic } from "@typespec/compiler";

Просмотреть файл

@ -3,28 +3,32 @@ id: emitter-framework
title: Emitter framework
---
The emitter framework makes writing emitters from TypeSpec to other assets a fair bit easier than manually consuming the type graph. The framework gives you an easy way to handle all the types TypeSpec might throw at you and know when you're "feature complete". It also handles a lot of hard problems for you, such as how to construct references between types, how to handle circular references, or how to propagate the context of the types you're emitting based on their containers or where they're referenced from. Lastly, it provides a class-based inheritance model that makes it fairly painless to extend and customize existing emitters.
# Emitter framework
## Getting Started
The emitter framework simplifies the process of creating emitters from TypeSpec to other assets, compared to manually navigating the type graph. This framework provides a straightforward way to manage all the types that TypeSpec might present, and helps you determine when you've covered all features.
Make sure to read the getting started section under the [emitter basics](./emitters-basics.md) topic. To use the framework, you will need an emitter library and `$onEmit` function ready to go.
The also solves complex problems such as constructing references between types, handling circular references, and propagating the context of the types you're emitting based on their containers or where they're referenced from. Additionally, it offers a class-based inheritance model that simplifies the extension and customization of existing emitters.
## Implementing your emitter
## How to get started
Implementing an emitter using the emitter framework will use a variety of types from the framework. To give you a high level overview, these are:
Before you start, make sure to read the 'Getting Started' section under the [emitter basics](./emitters-basics.md) topic. To use the framework, you will need an emitter library and a `$onEmit` function.
- `AssetEmitter`: The asset emitter is the main type you will interact with in your `$onEmit` function. You can pass the asset emitter types to emit, and tell it to write types to disk or give you source files for you to process in other ways.
- `TypeEmitter`: The type emitter is the base class for most of your emit logic. Every TypeSpec type has a corresponding method on TypeEmitter. It also is where you will manage your emit context, making it easy to answer such questions as "is this type inside something I care about" or "was this type referenced from something".
- `CodeTypeEmitter`: A subclass of `TypeEmitter` that makes building source code easier.
- `StringBuilder`, `ObjectBuilder`, `ArrayBuilder`: when implementing your `TypeEmitter` you will likely use these classes to help you build strings and object graphs. These classes take care of handling the placeholders that result from circular references.
## Creating your own emitter
Let's walk through each of these types in turn.
When you create an emitter using the emitter framework, you will use various types from the framework. Here's a high-level overview of these types:
- `AssetEmitter`: This is the main type you will use in your `$onEmit` function. You can pass types to the asset emitter to emit, and instruct it to write types to disk or provide you with source files for further processing.
- `TypeEmitter`: This is the base class for most of your emit logic. Every TypeSpec type has a corresponding method on TypeEmitter. This is also where you will manage your emit context, making it easy to answer questions like "is this type inside something I care about" or "was this type referenced from something".
- `CodeTypeEmitter`: This is a subclass of `TypeEmitter` that simplifies the creation of source code.
- `StringBuilder`, `ObjectBuilder`, `ArrayBuilder`: These classes are likely to be used when implementing your `TypeEmitter` to help you build strings and object graphs. They handle the placeholders that result from circular references.
Let's explore each of these types in more detail.
### `AssetEmitter<T>`
The asset emitter is responsible for driving the emit process. It has methods for taking TypeSpec types to emit, and maintains the state of your current emit process including the declarations you've accumulated, current emit context, and converting your emitted content into files on disk.
The asset emitter drives the emit process. It has methods for taking TypeSpec types to emit, and maintains the state of your current emit process, including the declarations you've accumulated, current emit context, and converting your emitted content into files on disk.
To create your asset emitter, call `getAssetEmitter` on your emit context in `$onEmit`. It takes the TypeEmitter which is covered in the next section. Once created, you can call `emitProgram()` to emit every type in the TypeSpec graph. Otherwise, you can call `emitType(someType)` to emit specific types instead.
To create your asset emitter, call `getAssetEmitter` on your emit context in `$onEmit`. It takes the TypeEmitter which is covered in the next section. Once created, you can call `emitProgram()` to emit every type in the TypeSpec graph. Alternatively, you can call `emitType(someType)` to emit specific types.
```typescript
export async function $onEmit(context: EmitContext) {
@ -49,7 +53,7 @@ To support emitting all TypeSpec types, you should expect to implement all of th
The generic type parameter `T` is the type of emit output you are building. For example, if you're emitting source code, `T` will be `string`. If you're building an object graph like JSON, `T` will be `object`. If your `T` is `string`, i.e. you are building source code, you will probably want to use the `CodeTypeEmitter` subclass which is a bit more convenient, but `TypeEmitter<string>` will also work fine.
A simple emitter that doesn't do much yet might look like:
Here's a simple emitter that doesn't do much yet:
```typescript
class MyCodeEmitter extends CodeTypeEmitter {
@ -59,13 +63,13 @@ class MyCodeEmitter extends CodeTypeEmitter {
}
```
Passing this to `getAssetEmitter` and calling `assetEmitter.emitProgram()` will console.log all the models in the program.
If you pass this to `getAssetEmitter` and call `assetEmitter.emitProgram()`, it will log all the models in the program to the console.
#### EmitterOutput
Most methods of the `TypeEmitter` must either return `T` or an `EmitterOutput<T>`. There are four kinds of `EmitterOutput`:
- `Declaration<T>`: A declaration, which has a name and is declared in a scope, and so can be referenced by other emitted types (more on References later). Declarations are created by calling `this.emitter.result.declaration(name, value)` in your emitter methods. Scopes come from your current context, which is covered later in this document.
- `Declaration<T>`: A declaration, which has a name and is declared in a scope, and so can be referenced by other emitted types. Declarations are created by calling `this.emitter.result.declaration(name, value)` in your emitter methods. Scopes come from your current context, which is covered later in this document.
- `RawCode<T>`: Output that is in some way concatenated into the output but cannot be referenced (e.g. things like type literals). Raw code is created by calling `this.emitter.result.rawCode(value)` in your emitter methods.
- `NoEmit`: The type does not contribute any output. This is created by calling `this.emitter.result.none()` in your emitter methods.
- `CircularEmit`: Indicates that a circular reference was encountered, which is generally handled by the framework with Placeholders (see the next section). You do not need to create this result yourself, the framework will produce this when required.
@ -279,7 +283,7 @@ If you're using the `Builder`s that come with the framework, you will not need t
#### Context
A common need when emitting TypeSpec is to know what context you are emitting the type in. There is one piece of required context: `scope`, which tells the emitter framework where you want to place your declarations. But you might also want to easily answer questions like: am I emitting a model inside a particular namespace? Or am I emitting a model that is referenced from the return type of an operation? The emitter framework makes managing this context fairly trivial.
When emitting TypeSpec, it's often necessary to know the context in which you are emitting the type. One piece of required context is `scope`, which tells the emitter framework where you want to place your declarations. But you might also want to easily answer questions like: am I emitting a model inside a particular namespace? Or am I emitting a model that is referenced from the return type of an operation? The emitter framework makes managing this context fairly trivial.
Every method that results in an `EmitterOutput` has a corresponding method for setting lexical and reference context. We saw this above when we created `modelDeclarationContext` in order to put some models into a different namespace.
@ -331,7 +335,7 @@ We can now see how this results in the `Person` model being located in a nested
### Extending `TypeEmitter`
TypeEmitters are classes and explicitly support subclassing, so you can customize an existing emitter by extending it and overriding any methods you want to customize in your subclass. In fact, emitters you find out in the ecosystem are likely not to work without creating a subclass, because they only know how to emit types, but you need to provide the scope for any declarations it wants to create. For example, if we have a base `TypeScriptEmitter` that can convert TypeSpec into TypeScript, we might extend it to tell it to put all declarations in the same file:
TypeEmitters are classes and explicitly support subclassing, so you can customize an existing emitter by extending it and overriding any methods you want to customize in your subclass. In fact, emitters you find out in the ecosystem are likely not to work without creating a subclass, because they only know how to emit types, but you need to provide the scope for any declarations it wants to create. For example, if we have a base `TypeScript Emitter` that can convert TypeSpec into TypeScript, we might extend it to tell it to put all declarations in the same file:
```typescript
class MyTsEmitter extends TypeScriptEmitter {
@ -357,3 +361,5 @@ class MyTsEmitter extends TypeScriptEmitter {
// and similar for other declarations: Unions, Enums, Interfaces, and Operations.
}
```
This way, each model will be emitted in its own file.

Просмотреть файл

@ -1,38 +1,62 @@
---
id: emitter-metadata-handling
title: Handling metadata and visibility in emitters for REST API
title: Managing metadata and visibility in REST API emitters
---
# Handling metadata and visibility in emitters for REST API
# Managing metadata and visibility in REST API emitters
It's important that all emitters for REST API handle [automatic visibility](../libraries/http/operations.md#automatic-visibility) and [metadata](../libraries/http/operations.md#metadata) consistently. Make sure to read through the TypeSpec-author documentation of these features to understand how they work. This document will cover how to incorporate them correctly into your own emitter.
Ensuring consistent handling of [automatic visibility](../libraries/http/operations.md#automatic-visibility) and [metadata](../libraries/http/operations.md#metadata) by all REST API emitters is crucial. To understand how these features work, please refer to the TypeSpec-author documentation. This guide will help you integrate these features into your own emitter correctly.
The standard `@typespec/rest` library provides JavaScript API for emitters to interpret API written using its decorators. We'll look at the API that are particularly relevant to these features.
The standard `/rest` library offers a JavaScript API for emitters to interpret APIs written using its decorators. We will focus on the APIs that are specifically relevant to these features.
Note that when we say that emitters must handle things consistently, we mean that they must agree on how data is sent and received over the wire. After all, a TypeSpec _specification_ must be able to serve as a source-of-truth on these details. Nevertheless, emitters are still free to _abstract_ things above this level and to make different choices in doing so. For example, the OpenAPI emitter will sometimes split a single TypeSpec model into multiple suffixed schemas with names like `UserCreate` and `UserUpdate` while a client SDK emitter may choose to emit a single `User` class that that can be serialized to a request or deserialized from a response with different fields present in different cases. In fact, these features were designed specifically to allow a TypeSpec specification to be written in terms of logical entities that emitters could then preserve.
When we say that emitters should handle things consistently, we mean they should agree on how data is transmitted and received. A TypeSpec _specification_ should serve as a reliable reference for these details. However, emitters can still _abstract_ things above this level and make different choices. For instance, the OpenAPI emitter may split a single TypeSpec model into multiple suffixed schemas like `UserCreate` and `UserUpdate`, while a client SDK emitter might emit a single `User` class that can be serialized to a request or deserialized from a response with different fields present in different situations. These features were specifically designed to allow a TypeSpec specification to be written in terms of logical entities that emitters could then maintain.
## Getting started
If you haven't written an emitter before, start with [emitter basics](./emitters-basics.md).
If you're new to writing emitters, begin with the [emitter basics](./emitters-basics.md).
Then look at the [REST metadata emitter sample](https://github.com/microsoft/typespec/tree/main/packages/samples/rest-metadata-emitter). This emitter sample uses all of the API discussed below to write out a simple textual representation. It deliberately does not split types like the OpenAPI emitter in order to emphasize that this is not required. Instead, it adds contextual remarks to denote how data depends on context.
Next, examine the [REST metadata emitter sample](https://github.com/microsoft/typespec/tree/main/packages/samples/rest-metadata-emitter). This sample uses all of the APIs discussed below to create a simple textual representation. It intentionally avoids splitting types like the OpenAPI emitter to highlight that this is not mandatory. Instead, it includes contextual comments to indicate how data depends on context.
However, if your emitter does want to split types as OpenAPI does, then it will still use the same API. Cross-referencing with where the official [OpenAPI emitter] calls these API can also be instructive.
However, if you want your emitter to split types like OpenAPI, you can still use the same API. Cross-referencing with the official [OpenAPI emitter] where these APIs are called can also be helpful.
## Key API
These are the main API involved in handling these features. See the linked API reference documentation for more details.
Here are the main APIs involved in managing these features. For more details, refer to the linked API reference documentation.
- [`getRequestVisibility(HttpVerb): Visibility`](../libraries/http/reference/js-api/index.md#getrequestvisibility) - Use this to determine the visibility implied for data in the request parameters or body. Also note that [`Visibility.Read`](../libraries/http/reference/js-api/enumerations/Visibility.md#item) is always applied for response data and therefore there is no corresponding API for the response.
- [`getRequestVisibility(HttpVerb): Visibility`](../libraries/http/reference/js-api/functions/getRequestVisibility.md) - Use this to determine the visibility implied for data in the request parameters or body. Note that [`Visibility.Read`](../libraries/http/reference/js-api/enumerations/Visibility.md) is always applied for response data, so there is no corresponding API for the response.
- [`MetadataInfo`](../libraries/http/reference/js-api/interfaces/MetadataInfo.md) - Create this once for each program using [`createMetadataInfo(Program, MetadataInfoOptions)`](../libraries/http/reference/js-api/index.md#createmetadatainfo) then use it to reason about metadata and visibility implications with the API below.
- [`MetadataInfo`](../libraries/http/reference/js-api/interfaces/MetadataInfo.md) - Create this once for each program using [`createMetadataInfo(Program, MetadataInfoOptions)`](../libraries/http/reference/js-api/functions/createMetadataInfo.md). Then use it to understand metadata and visibility implications with the APIs below.
- [`MetadataInfo.getEffectivePayloadType(Type, Visibility): Type`](../libraries/http/reference/js-api/interfaces/MetadataInfo.md#geteffectivepayloadtype) - Use this recursively on every type that is referenced. When given an anonymous model sourced entirely from a single named model after metadata is moved elsewhere or invisible properties are removed, it will recover the named model. This handles the commonly discussed case of seeing that `op something(...Thing)` receives a `Thing` in its request body, but also many other cases..
- [`MetadataInfo.getEffectivePayloadType(Type, Visibility): Type`](../libraries/http/reference/js-api/interfaces/MetadataInfo.md#geteffectivepayloadtype) - Use this recursively on every referenced type. When given an anonymous model sourced entirely from a single named model after metadata is moved elsewhere or invisible properties are removed, it will recover the named model. This handles the commonly discussed case of seeing that `op something(...Thing)` receives a `Thing` in its request body, among other cases.
- [`MetadataInfo.isTransformed(Model, Visibility)`](../libraries/http/reference/js-api/interfaces/MetadataInfo.md#istransformed) - Use this to check if a type undergoes any changes in shape due to visibility or metadata. If not, this can allow for simplifications in emit.
- [`MetadataInfo.isTransformed(Model, Visibility)`](../libraries/http/reference/js-api/interfaces/MetadataInfo.md#istransformed) - Use this to check if a type undergoes any shape changes due to visibility or metadata. If not, this can allow for simplifications in emit.
- [`MetadataInfo.isPayloadProperty(ModelProperty, Visibility): boolean`](../libraries/http/reference/js-api/interfaces/MetadataInfo.md#ispayloadproperty) - Use this to check if a property is transmitted as an object property in the payload and is not invisible or metadata sent elsewhere.
- [`MetadataInfo.isOptional(ModelProperty, Visibility): boolean`](../libraries/http/reference/js-api/interfaces/MetadataInfo.md#isoptional) - Use this to determine if a property is optional for the given visibility. This will differ from `ModelProperty.isOptional` when the Visibility is Update in which case the property is always considered optional.
- [`MetadataInfo.isOptional(ModelProperty, Visibility): boolean`](../libraries/http/reference/js-api/interfaces/MetadataInfo.md#isoptional) - Use this to determine if a property is optional for the given visibility. This will differ from `ModelProperty.isOptional` when the Visibility is Update, in which case the property is always considered optional.
- [`Visibility.Item`](../libraries/http/reference/js-api/enumerations/Visibility.md#item) - Add this flag when recursing into an array. This moves all metadata into the payload, which can be useful in scenarios like batching API calls.
- [`Visibility.Item`](../libraries/http/reference/js-api/enumerations/Visibility.md) - Add this flag when recursing into an array. This moves all metadata into the payload, which can be useful in scenarios like batching API calls.
## Working with metadata and visibility
When working with metadata and visibility, it's important to understand how they interact with each other and with the data being transmitted. Here are some key points to consider:
- Metadata is data about the data being transmitted. It can include information like the data's origin, when it was created or last updated, who created or updated it, and so on. Metadata is not usually visible to the end user, but it can be crucial for the system processing the data.
- Visibility determines what data is visible to the end user or the system at any given time. For example, some data might be visible only to the system, some only to the end user, and some to both. Visibility can change depending on the context, such as whether the data is being created, read, updated, or deleted.
- The `getRequestVisibility(HttpVerb): Visibility` API is used to determine the visibility implied for data in the request parameters or body. Note that `Visibility.Read` is always applied for response data, so there is no corresponding API for the response.
- The `MetadataInfo` API is used to create a metadata object for each program, which can then be used to reason about metadata and visibility implications.
- The `MetadataInfo.getEffectivePayloadType(Type, Visibility): Type` API is used recursively on every type that is referenced. It recovers the named model when given an anonymous model sourced entirely from a single named model after metadata is moved elsewhere or invisible properties are removed.
- The `MetadataInfo.isTransformed(Model, Visibility)` API is used to check if a type undergoes any changes in shape due to visibility or metadata. If not, this can allow for simplifications in emit.
- The `MetadataInfo.isPayloadProperty(ModelProperty, Visibility): boolean` API is used to check if a property is transmitted as an object property in the payload and is not invisible or metadata sent elsewhere.
- The `MetadataInfo.isOptional(ModelProperty, Visibility): boolean` API is used to determine if a property is optional for the given visibility. This will differ from `ModelProperty.isOptional` when the Visibility is Update, in which case the property is always considered optional.
- The `Visibility.Item` flag is added when recursing into an array. This moves all metadata into the payload, which can be useful in scenarios like batching API calls.
By understanding and correctly using these APIs and concepts, you can ensure that your emitter handles metadata and visibility consistently and effectively.

Просмотреть файл

@ -3,30 +3,30 @@ id: emitters
title: Emitters
---
# Writing emitters
# Creating emitters
TypeSpec emitters are libraries that use various TypeSpec compiler APIs to reflect on the TypeSpec compilation and produce generated artifacts. The TypeSpec standard library includes an emitter for OpenAPI version 3.0, but odds are good you will want to emit TypeSpec to another output format. In fact, one of TypeSpec's main benefits is how easy it is to use TypeSpec as a source of truth for all data shapes, and the ease of writing an emitter is a big part of that story.
TypeSpec emitters are libraries that utilize various TypeSpec compiler APIs to reflect on the TypeSpec compilation process and generate artifacts. The TypeSpec standard library includes an emitter for OpenAPI version 3.0. However, you might want to emit TypeSpec to a different output format. One of the main advantages of TypeSpec is its ease of use as a single source of truth for all data shapes, and the simplicity of creating an emitter contributes significantly to this.
## Getting started
## Starting out
TypeSpec emitters are a special kind of TypeSpec library and so have the same getting started instructions.
TypeSpec emitters are a unique type of TypeSpec library, so they follow the same initial setup instructions.
Setup the boilerplate for an emitter using our template:
Set up the boilerplate for an emitter using our template:
```bash
tsp init --template emitter-ts
```
or follow [these steps](./basics.md) to initialize a TypeSpec library.
Alternatively, follow [these steps](./basics.md) to initialize a TypeSpec library.
## $onEmit
A TypeSpec emitter exports a function named `$onEmit` from its main entrypoint. It receives two arguments:
A TypeSpec emitter exports a function named `$onEmit` from its main entry point. It takes two arguments:
- _context_: The current context including the current progfam being compiled
- _context_: The current context, including the current program being compiled
- _options_: Custom configuration options selected for this emitter
For example, the following will write a text file to the output directory:
For instance, the following code will write a text file to the output directory:
```typescript
import { EmitContext, emitFile, resolvePath } from "@typespec/compiler";
@ -41,13 +41,13 @@ export async function $onEmit(context: EmitContext) {
}
```
You can now compile a TypeSpec program passing your library name to --emit, or add it to your `tspconfig.yaml`.
You can now compile a TypeSpec program by passing your library name to --emit, or add it to your `tspconfig.yaml`.
### Custom configuration options
To pass your emitter custom options, the options must be registered with the compiler by setting `emitter.options` in your library definition to the JSON schema for the options you want to take. The compiler has a helper to make this easier:
To provide custom options to your emitter, you need to register the options with the compiler by setting `emitter.options` in your library definition to the JSON schema for the options you want to use. The compiler provides a helper to simplify this:
- _JSONSchemaType_: Takes a TypeScript interface and returns a type that helps you fill in the JSON schema for that type.
- _JSONSchemaType_: Takes a TypeScript interface and returns a type that assists you in filling in the JSON schema for that type.
The following example extends the hello world emitter to be configured with a name:
@ -128,19 +128,19 @@ The general guideline is to use a decorator when the customization is intrinsic
## Emitting TypeSpec types to assets on disk
One of the main tasks of an emitter is finding types to emit. There are three main approaches:
One of the main tasks of an emitter is to identify types to emit. There are three primary methods:
1. The [emitter framework](./emitter-framework.md), which makes it relatively easy to emit all your TypeSpec types (or a subset, if you wish).
1. The Semantic Walker, which lets you easily run code for every type in the program
1. Custom traversal, which gives you a lot more flexibility than either of the previous approaches at the cost of some complexity.
1. The [emitter framework](./emitter-framework.md), which simplifies the process of emitting all your TypeSpec types (or a subset, if you prefer).
2. The Semantic Walker, which allows you to easily execute code for every type in the program.
3. Custom traversal, which offers more flexibility than the previous two methods, albeit with some added complexity.
### Emitter Framework
The emitter framework provides handles a lot of hard problems for you while providing an easy-to-use API to convert your TypeSpec into source code or other object graphs. Visit the [emitter framework](./emitter-framework.md) page to learn more.
The emitter framework handles many complex issues for you while offering an easy-to-use API to convert your TypeSpec into source code or other object graphs. Visit the [emitter framework](./emitter-framework.md) page for more information.
### Semantic Walker
The Semantic Walker will visit every type in the TypeSpec program and call any callbacks you provide for that type. To use, import `navigateProgram` from `@typespec/compiler`. Starting a walk needs two parameters - the program to walk, and an object with callbacks for each type. For example, if we want to do something for every model in the program, we could do the following in our `$onEmit` function:
The Semantic Walker visits every type in the TypeSpec program and calls any callbacks you provide for that type. To use it, import `navigateProgram` from `@typespec/compiler`. Starting a walk requires two parameters - the program to walk, and an object with callbacks for each type. For instance, if we want to do something for every model in the program, we could do the following in our `$onEmit` function:
```typescript
navigateProgram(program, {
@ -150,15 +150,15 @@ navigateProgram(program, {
});
```
You can provide a callback for every kind of TypeSpec type. The walker will call your callback pre-order, i.e. as soon as it sees a type for the first time it will invoke your callback. You can invoke callback post-order instead by prefixing the type name with `exit`, for example `exitModel(m)`.
You can provide a callback for every kind of TypeSpec type. The walker will call your callback pre-order, i.e., it will invoke your callback as soon as it encounters a type for the first time. You can invoke a callback post-order instead by prefixing the type name with `exit`, for example, `exitModel(m)`.
Note that the semantic walker will visit all types in the program including built-in TypeSpec types and TypeSpec types defined by any libraries you're using. Care must be taken to filter out any types you do not intend to emit. Sometimes this is quite difficult, so a custom traversal may be easier.
Note that the semantic walker will visit all types in the program, including built-in TypeSpec types and TypeSpec types defined by any libraries you're using. You must filter out any types you do not intend to emit. Sometimes this can be quite challenging, so a custom traversal may be easier.
### Custom traversal
Often times you will want to emit specific types, for example types that have a particular decorator or are in a particular namespace. In such cases it is often easier to write a custom traversal to find and emit those types. Once you have a type, you can access its [various fields](#todo) to and emit those types as well if needed.
Often, you'll want to emit specific types, such as types that have a particular decorator or are in a specific namespace. In such cases, it's often easier to write a custom traversal to find and emit those types. Once you have a type, you can access its various fields and emit those types as well if needed.
For example, let's say we want to emit a text file of model names but only if it has an `@emitThis` decorator. We could filter out such models in the Semantic Walker `model` callback, but it is more efficient to implement `@emitThis` such that it keeps a list of all the types its attached to and iterate that list. We can then traverse into types it references if needed.
For instance, let's say we want to emit a text file of model names but only if it has an `@emitThis` decorator. We could filter out such models in the Semantic Walker `model` callback, but it's more efficient to implement `@emitThis` such that it keeps a list of all the types it's attached to and iterate that list. We can then traverse into types it references if needed.
The following example will emit models with the `@emitThis` decorator and also any models referenced by that model.
@ -192,7 +192,7 @@ function emitModel(model: Model) {
### Resolving a TypeSpec type
Sometimes you might want to get access to a known TypeSpec type in the type graph, for example a model that you have defined in your library.
Sometimes you might want to access a known TypeSpec type in the type graph, for example, a model that you have defined in your library.
A helper is provided on the program to do that.
@ -220,22 +220,22 @@ program.resolveTypeReference("model Foo {}"); // Resolve `[undefined, diagnostic
Since an emitter is a Node library, you could use standard `fs` APIs to write files. However, this approach has a drawback - your emitter will not work in the browser, and will not work with the test framework that depends on storing emitted files in an in-memory file system.
Instead, use the compiler [`host` interface](#todo) to access the file system. The API is equivalent to the Node API but works in a wider range of scenarios.
Instead, use the compiler's `host` interface to access the file system. This API is equivalent to the Node API but works in a broader range of scenarios.
In order to know where to emit files, the emitter context has a `emitterOutputDir` property that is automatically resolved using the `emitter-output-dir` built-in emitter options. This is set to `{cwd}/tsp-output/{emitter-name}` by default, but can be overridden by the user. Do not use the `compilerOptions.outputDir`
To know where to emit files, the emitter context has an `emitterOutputDir` property that is automatically resolved using the `emitter-output-dir` built-in emitter options. By default, this is set to `{cwd}/tsp-output/{emitter-name}`, but it can be overridden by the user. Do not use the `compilerOptions.outputDir`.
## Handling scalars
## Dealing with scalars
Scalars are types in TypeSpec that most likely have a primitive or built-in datastructure representing those in the target language.
Scalars are types in TypeSpec that are most likely represented by a primitive or built-in data structure in the target language.
Recommended logic for emitting scalar is to:
The recommended logic for emitting a scalar is as follows:
1. If scalar is a known scalar (e.g. `int32`), emit the known mapping.
2. Otherwise check scalar `baseScalar` and go back to `1.`
2.1 After resolving which scalar apply any decorators
1. If the scalar is a known scalar (e.g., `int32`), emit the known mapping.
2. Otherwise, check the scalar `baseScalar` and go back to step 1.
2.1 After resolving which scalar to use, apply any decorators.
:::note
If the scalar is generic and doesn't have a mapping (e.g. integer), we recommend substituting it with the next closest mapping (e.g. integer->int64) and emitting a warning.
If the scalar is generic and doesn't have a mapping (e.g., integer), we recommend substituting it with the next closest mapping (e.g., integer->int64) and emitting a warning.
:::
### Examples
@ -255,7 +255,7 @@ scalar specializedInt32 extends myInt32;
| `specializedInt32` | `int32` | Emitter doesn't know what specializedInt32 is. Check baseScalar, finds myInt32 knows that it is an int32 now and applies minValue override. |
| `float` | `float64` | Emitter knows float but doesn't have a mapping. Emit `float64` and a warning. |
## Handling Default Values
## Managing Default Values
Several TypeSpec types have a `default` property that can be used to specify a default value. For example, the following model has a default value of `true` for the `isActive` property:
@ -272,4 +272,4 @@ const modelProp: ModelProperty = ...; // the isActive ModelProperty type
const defaultValue = modelProp.default; // value: true
```
It is important that emitters handle default values in a consistent way. Default values SHOULD NOT be used as client-side default values. Instead, they should be used as a way to specify a default value for the server-side implementation. For example, if a model property has a default value of `true`, the server-side implementation should use that value if the client does not provide a value. Default values SHOULD be expressed in documentation to properly communicate the service-side default.
It's important that emitters handle default values consistently. Default values SHOULD NOT be used as client-side default values. Instead, they should be used to specify a default value for the server-side implementation. For example, if a model property has a default value of `true`, the server-side implementation should use that value if the client does not provide a value. Default values SHOULD be expressed in documentation to properly communicate the server-side default.

Просмотреть файл

@ -1,22 +1,21 @@
---
id: linters
title: Linters
title: Understanding linters
---
# Linters
# Understanding linters
## Linter vs `onValidate`
## Linter versus `onValidate`
TypeSpec library can probide a `$onValidate` hook which can be used to validate the TypeSpec program is valid in the eye of your library.
A TypeSpec library can provide an `$onValidate` hook, which can be used to validate whether the TypeSpec program is valid according to your library's rules.
A linter on the other hand might be a validation that is optional, the program is correct but there could be some improvements. For example requiring documentation on every type. This is not something that is needed to represent the TypeSpec program but without it the end user experience might suffer.
Linters need to be explicitly enabled. `$onValidate` will be run automatically if that library is imported.
On the other hand, a linter might provide optional validation. The program could be correct, but there might be room for improvements. For instance, a linter might require documentation on every type. While this isn't necessary to represent the TypeSpec program, it could enhance the end user experience. Linters need to be explicitly enabled, whereas `$onValidate` will run automatically if that library is imported.
## Writing a linter
## Creating a linter
See examples in `packages/best-practices`.
You can find examples in `packages/best-practices`.
### 1. Define a rules
### 1. Define rules
```ts
import { createLinterRule } from "@typespec/compiler";
@ -90,7 +89,7 @@ context.reportDiagnostic({
});
```
#### Don'ts
#### Things to avoid
- ❌ Do not call `program.reportDiagnostic` or your library `reportDiagnostic` helper directly in a linter rule
@ -149,14 +148,13 @@ export const $linter = defineLinter({
});
```
When referencing a rule or ruleset(in `enable`, `extends`, `disable`) the rule or rule set id must be used which in this format: `<libraryName>/<ruleName>`
When referencing a rule or ruleset (in `enable`, `extends`, `disable`), you must use the rule or rule set id, which is in this format: `<libraryName>/<ruleName>`.
## Testing a linter
To test linter rule an rule tester is provided letting you test a specific rule without enabling the others.
To test a linter rule, a rule tester is provided, allowing you to test a specific rule without enabling the others.
First you'll want to create an instance of the rule tester using `createLinterRuleTester` passing it the rule that is being tested.
You can then provide different test checking the rule pass or fails.
First, you'll want to create an instance of the rule tester using `createLinterRuleTester`, passing it the rule that is being tested. You can then provide different tests to check whether the rule passes or fails.
```ts
import { RuleTester, createLinterRuleTester, createTestRunner } from "@typespec/compiler/testing";
@ -187,11 +185,10 @@ describe("required-doc rule", () => {
The linter rule tester provides an API to easily test a codefix. This is a different approach from the standalone codefix tester, which is more targeted at testing codefixes in isolation.
This can be done with calling `applyCodeFix` with the fix id. It will expect a single diagnostic to be emitted with a codefix with the given id.
Then calling `toEqual` with the expected code after the codefix is applied.
This can be done by calling `applyCodeFix` with the fix id. It will expect a single diagnostic to be emitted with a codefix with the given id. Then, call `toEqual` with the expected code after the codefix is applied.
:::note
When using multi-line strings (with `\``) in typescript there is no de-indenting done so you will need to make sure the input and expected result are aligned to the left.
When using multi-line strings (with `\``) in TypeScript, there is no de-indenting done, so you will need to make sure the input and expected result are aligned to the left.
:::
```ts

Просмотреть файл

@ -2,19 +2,19 @@
title: Scaffolding templates
---
# Writting a scaffolding template
# Creating a scaffolding template
typespec provides scaffolding functionality via the `tsp init` command.
TypeSpec offers a scaffolding feature through the `tsp init` command.
```bash
tsp init <templateUrl>
```
## Specifying a minimum TypeSpec version
## Setting a minimum TypeSpec version
If your template needs a functionality that was only added in a newer version of TypeSpec you might want to specify that in the template. This will warn the user that the template might not work as expected and prompt them to confirm that they want to continue.
If your template requires a feature that was introduced in a later version of TypeSpec, you can specify this in the template. This will alert the user that the template may not function as expected and ask them to confirm if they wish to proceed.
To do so you can set `compilerVersion` in the each template configuration. The value is the minimum semver version required.
You can set the `compilerVersion` in each template configuration. The value should be the minimum semver version required.
```json
{
@ -22,18 +22,17 @@ To do so you can set `compilerVersion` in the each template configuration. The v
}
```
## Basic
## Basics of a scaffolding template
A scaffolding template is a `json` document that can be hosted locally or online.
The root of the document is a dictionary allowing multiple templates to be hosted at the same location.
A scaffolding template is a `json` document that can be hosted either locally or online. The document's root is a dictionary, allowing for multiple templates to be hosted in the same location.
Each template needs at the minimum:
Each template must include:
- key: Key of the template
- title: Human readable name of the template
- description: Extended description of the template.
- key: The template's key
- title: A user-friendly name for the template
- description: A detailed description of the template.
Example:
Here's an example:
```json
{
@ -48,27 +47,26 @@ Example:
}
```
## Adding libraries
## Including libraries
You can add a list of TypeSpec libraries to include. This will automatically add those libraries to the `package.json` and imported in `main.tsp`.
You can include a list of TypeSpec libraries. These will be automatically added to the `package.json` and imported in `main.tsp`.
```json
{
"rest": {
"title": "REST API",
"description": "Create a new project representing a REST API",
"libraries": ["@typespec/rest", "@typespec/openapi3"]
"libraries": ["/rest", "@typespec/openapi3"]
}
}
```
## Adding new files
Additional files.typespec or other types) can be generated by the initializer. The template takes a list of the files to copy and interpolate values.
Each file need the following properties:
The initializer can generate additional files (either .typespec or other types). The template includes a list of files to copy and interpolate values. Each file requires the following properties:
- `path`: Absolute or relative path(to the template file) to the file
- `destination`: Relative path of the file relative to the project root.
- `path`: The absolute or relative path (relative to the template file) to the file
- `destination`: The file's relative path, relative to the project root.
```json
{
@ -90,32 +88,30 @@ model {{parameters.ModelName}} {
### Interpolating values
The template can interpolate values in the files. The values available are anything available in the template configuration referenced as it is.
Examples:
The template can interpolate values in the files. The available values are anything in the template configuration, referenced as is. For example:
- Reference a parameter `{{parameters.ModelName}}`
- Reference a the template title `{{title}}`
- To reference a parameter, use `{{parameters.ModelName}}`
- To reference the template title, use `{{title}}`
Additionally the following values and functions are available:
Additionally, the following values and functions are available:
| Name | Description |
| ------------------------------------- | --------------------------------------------------------------- |
| `directory` | Directory full path where the project should be initialized. |
| `folderName` | Folder name where the project should be initialized. |
| `name` | Name of the project. |
| `libraries` | List of libraries to include |
| `templateUri` | Path where this template was loaded from. |
| Functions | |
| `toLowerCase(value: string)` | Convert string to lower case |
| `normalizePackageName(value: string)` | Normalize package name. It replaces `.` with`-` and toLowerCase |
| `casing.pascalCase(value: string)` | Convert string to PascalCase |
| `casing.camelCase(value: string)` | Convert string to camelCase |
| `casing.kebabCase(value: string)` | Convert string to kebab-case |
| Name | Description |
| ------------------------------------- | --------------------------------------------------------------------------------- |
| `directory` | The full directory path where the project should be initialized. |
| `folderName` | The name of the folder where the project should be initialized. |
| `name` | The name of the project. |
| `libraries` | The list of libraries to include. |
| `templateUri` | The path from where this template was loaded. |
| Functions | |
| `toLowerCase(value: string)` | Converts a string to lower case. |
| `normalizePackageName(value: string)` | Normalizes the package name. It replaces `.` with `-` and converts to lower case. |
| `casing.pascalCase(value: string)` | Converts a string to PascalCase. |
| `casing.camelCase(value: string)` | Converts a string to camelCase. |
| `casing.kebabCase(value: string)` | Converts a string to kebab-case. |
## Demanding additional input from the user
## Requesting additional user input
When generating files there might be a need for additional inputs to be retrieved from the user. For example the model name.
The template takes in a map of inputs that will get prompted to the user during initialization.
When generating files, you may need additional input from the user, such as the model name. The template includes a map of inputs that will be prompted to the user during initialization.
```json
{
@ -132,6 +128,6 @@ The template takes in a map of inputs that will get prompted to the user during
}
```
Types of input supported:
Supported input types:
- `text`: Ask for a raw text value.
- `text`: Requests a raw text value.

Просмотреть файл

@ -3,6 +3,6 @@ id: getting-started
title: Getting Started
---
# Getting Started with TypeSpec
# Getting started with TypeSpec
- [Get started with HTTP in TypeSpec](./getting-started-http.md)

Просмотреть файл

@ -2,9 +2,9 @@
title: Configuration
---
# Compiler and Libraries configurations
# Compiler and library configurations
The TypeSpec compiler and libraries can be configured either via a [configuration file](#configuration-file) or [command line flags](#command-line-flags).
The TypeSpec compiler and libraries can be configured either via a [configuration file](#configuration-file) or command line flags.
## Configuration file
@ -46,13 +46,13 @@ model LinterConfig {
}
```
### Extending project files
### Extending Project Files
There is cases where you might want to build different folders with different options (for example different emitters) but want to share some configuration for both as well.
There may be instances where you want to build different folders with varying options (such as different emitters), but still want to share some common configurations.
For that you can use the `extends` property of the configuration file
In such cases, you can use the `extends` property in the configuration file.
in `<my-pkg>/tspconfig.yaml`
For instance, in `<my-pkg>/tspconfig.yaml`:
```yaml
options:
@ -72,7 +72,7 @@ emit:
### Variable interpolation
The TypeSpec project file provide variable interpolation using:
The TypeSpec project file provides variable interpolation using:
- built-in variables
- environment variables
@ -86,14 +86,14 @@ Examples:
- `{output-dir}/my-path`
- `{env.SHARED_PATH}/my-path`
### Emitter path config interpolation
### Interpolation of Emitter Path Config
Some config of emitters can be interpolated using a special rule that will collapse a path.
Certain emitter configurations can be interpolated using a specific rule designed to collapse a path.
If a variable is followed by a `/` or `.` and the emitter interpolating the config doesn't provide that variable it will then omit the path segment.
If a variable is succeeded by a `/` or `.` and the emitter responsible for interpolating the config doesn't supply that variable, the path segment will be omitted.
For example given the following config value: `{service-name}/output.{version}.json`
The following would get produced
For instance, consider the following config value: `{service-name}/output.{version}.json`
Here's what would be produced:
| Service name value | Version value | Result |
| ------------------ | ------------- | ------------------------- |
@ -111,15 +111,15 @@ The following would get produced
| `output-dir` | emitter options | Common `output-dir` See [output-dir](#output-dir---configure-the-default-output-dir) |
| `emitter-name` | emitter options | Name of the emitter |
#### Project parameters
#### Project Parameters
A TypeSpec project file can specify some parameters that can then be specified via the CLI.
A TypeSpec project file can define certain parameters that can subsequently be specified through the CLI.
`{cwd}` and `{project-root}` variables can be used in the default value of those parmeters.
The `{cwd}` and `{project-root}` variables can be utilized in the default value of these parameters.
The parameters can then be referenced by their name in a variable interpolation expression.
These parameters can then be referred to by their name in a variable interpolation expression.
Parameters must have a default value.
All parameters must have a default value.
**Example:**
```yaml
@ -130,7 +130,7 @@ parameters:
output-dir: {base-dir}/output
```
The parameter can then be specified with `--arg` in this format `--arg "<parameter-name>=<value>"`
The parameter can then be specified via `--arg` in this format `--arg "<parameter-name>=<value>"`
```bash
tsp compile . --arg "base-dir=/path/to/base"
@ -138,13 +138,13 @@ tsp compile . --arg "base-dir=/path/to/base"
#### Environment variables
A TypeSpec project file can define which environment variables it can interpolate.
A TypeSpec project file can specify which environment variables it can interpolate.
`{cwd}` and `{project-root}` variables can be used in the default value of the environment variables.
The `{cwd}` and `{project-root}` variables can be used in the default value of these environment variables.
The environment variables can then be referenced by their name in a variable interpolation expression with the `env.` prefix.
These environment variables can then be referred to by their name in a variable interpolation expression, using the `env.` prefix.
Environment variables must have a default value.
All environment variables must have a default value.
**Example:**
@ -156,11 +156,11 @@ environment-variables:
output-dir: {env.BASE_DIR}/output
```
#### Emitter options
#### Emitter Options
Emitter options can reference each other using the other option name as the variable expresion.
Emitter options can refer to each other by using the other option's name as the variable expression.
Can only interpolate emitter options from the same emitter.
Interpolation is only possible among emitter options from the same emitter.
```yaml
options:
@ -185,7 +185,7 @@ options:
### `output-dir` - Configure the default output dir
Specify the common output-dir for all emitters. See [this](#output-directory-configuration) to configure per emitter.
Specify the common output-dir for all emitters. See [this](#configuring-output-directory) to configure per emitter.
```yaml
output-dir: {cwd}/typespec-build
@ -199,7 +199,7 @@ tsp compile . --output-dir "./typespec-build"
Output dir must be an absolute path in the config. Use `{cwd}` or `{project-root}` to explicitly specify what it should be relative to.
See [output directory configuration for mode details](#output-directory-configuration)
See [output directory configuration for mode details](#configuring-output-directory)
### `trace` - Configure what to trace
@ -221,11 +221,11 @@ Trace can be provided using the `--trace` cli flag
tsp compile . --trace import-resolution --trace projection
```
### `warn-as-error` - Treat warning as error
### `warn-as-error` - Treating Warnings as Errors
All warnings will be emitted as error. Result in a non zero exit code in case of warning.
All warnings will be treated and emitted as errors, resulting in a non-zero exit code in the event of a warning.
**This is recommended to use in CI to prevent warning from being unadressed.**
**It is recommended to use this feature in Continuous Integration (CI) to ensure all warnings are addressed.**
```yaml
warn-as-error: true
@ -307,11 +307,11 @@ Represent the path where the emitter should be outputing the generated files.
Default: `{output-dir}/{emitter-name}`
See [output directory configuration for mode details](#output-directory-configuration)
See [output directory configuration for mode details](#configuring-output-directory)
### `linter` - Configuring linters
### `linter` - Setting Up Linters
Configure which linter rules should be enabled in this repository. Referencing to a rule or ruleset must be using their id which is in this format `<libraryName>:<ruleName>`
This allows you to configure the linter rules to be enabled in this repository. When referencing a rule or ruleset, use their ID, which follows the format `<libraryName>:<ruleName>`.
```yaml
linter:
@ -325,13 +325,13 @@ linter:
"@typespec/best-practices/no-y": "This rule cannot be applied in this project because X"
```
## Emitter control cli flags
## CLI Flags for Emitter Control
### `--no-emit`
Disable emitting. If emitters are still specified it will still run the emitter but emitters shouldn't be writing anything to disk.
This flag disables emitting. If emitters are still specified, the emitter will run but it should not write anything to the disk.
Can also be used to hide the "There is no emitters warning".
This flag can also be used to suppress the "There are no emitters" warning.
```yaml
tsp compile . --no-emit
@ -385,22 +385,20 @@ Enable/Disable pretty logging (colors, diagnostic preview, etc.).
tsp compile . --pretty=false
```
## Output directory configuration
## Configuring Output Directory
Typespec compiler will provide a unique output directory for each emitter that is being run to reduce conflicts.
By default the output-dir of an emitter is set to this value:
The TypeSpec compiler assigns a unique output directory to each emitter that runs, in order to minimize conflicts. By default, the output directory of an emitter is set to:
```
{output-dir}/{emitter-name}
```
where
where:
- `output-dir` is the compiler common `output-dir` that can be configured via `--output-dir`
- `emitter-name` is the name of the emitter package (for example `@typespec/openapi3`)
- `output-dir` is the common output directory for the compiler, which can be configured via `--output-dir`.
- `emitter-name` is the name of the emitter package (for example, `/openapi3`).
Example:
Given the following emitters: `@typespec/openapi3` and `@typespec/jsonschema`, the default output folder structure would be
For instance, if the emitters `@typespec/openapi3` and `@typespec/jsonschema` are given, the default output folder structure would be:
```
{project-root}/tsp-output:
@ -411,7 +409,7 @@ Given the following emitters: `@typespec/openapi3` and `@typespec/jsonschema`, t
... json schema files ...
```
Changing the compiler `output-dir` with `--output-dir` or setting that value in the tspconfig.yaml would result in the following structure
You can change the compiler's `output-dir` with `--output-dir` or by setting that value in the tspconfig.yaml, which would result in the following structure:
```
--output-dir={cwd}/my-custom-output-dir
@ -422,10 +420,9 @@ Changing the compiler `output-dir` with `--output-dir` or setting that value in
... openapi3 files ...
jsonschema
... json schema files ...
```
Changing a specific emitter output-dir can be done by setting that emitter `emitter-output-dir` option
To change a specific emitter's output directory, you can set the `emitter-output-dir` option for that emitter:
```
--option "@typespec/openapi3.output-dir={projectroot}/openapispec"

Просмотреть файл

@ -4,23 +4,23 @@ title: Tracing
# Tracing
By default the TypeSpec Compiler will build without any debug information. The standard output will be minimal and limited to any `warning` or `error` diagnostics emitted during compilation.
The TypeSpec Compiler, by default, builds without any debug information. The standard output is minimal, only including any `warning` or `error` diagnostics that occur during the compilation process.
Some additional information is however being collected and can be revealed using the `--trace` cli flag.
However, the compiler does collect additional information that can be accessed using the `--trace` command-line interface (CLI) flag.
```bash
tsp compile . --trace import-resolution
```
You can use the `--trace` option multiple times if there is multiple areas that should be logged from.
If you want to log multiple areas, you can use the `--trace` option more than once.
```bash
tsp compile . --trace import-resolution --trace projection
```
Using `--trace *` will log everything. This might be a bit overwhelming but you can [pick and choose which trace area to include](#trace-selection)
To log everything, use `--trace *`. This might produce a lot of output, but you can [select specific trace areas to include](#selecting-trace-areas)
It can also be provided via the `tspconfig.yaml` file:
You can also specify the trace areas in the `tspconfig.yaml` file:
```yaml
trace: *
@ -30,55 +30,54 @@ trace:
- projection
```
## Trace selection
## Selecting trace areas
The tracing system in the tsp compiler works by having each trace under an area. The area name is a dot `.` separated string of area segments.
The tracing system in the tsp compiler organizes each trace under a specific area. The area name is a dot `.` separated string of area segments.
When filtering which area to select you can use this area path to select which area is going to be revealed.
The filter follow the same naming style, except the last segment could be a wildcard `*`. This is however the same result as omitting the last segment all together. In other words, those filter have the exact same behavior:
To filter the areas you want to trace, you can use this area path. The filter follows the same naming style, but the last segment can be a wildcard `*`. However, this produces the same result as leaving out the last segment. In other words, these filters behave identically:
- `foo` and `foo.*`
- `one.two` and `one.two.*`
For example, assuming we'd have those 3 areas
For instance, if we have these three areas:
- `one.two.three`
- `one.foo`
- `bar.info`
Using:
You can use:
- `*` will log everything
- `one` will log everything under `one`(`one.two.three`, `one.foo`)
- `bar` will log everything under `bar`(`bar.info`)
- `one.foo` will log everything under `one.foo`(`one.foo`)
- `other` will log everything under `other` which is nothing here.
- `*` to log everything
- `one` to log everything under `one`(`one.two.three`, `one.foo`)
- `bar` to log everything under `bar`(`bar.info`)
- `one.foo` to log everything under `one.foo`(`one.foo`)
- `other` to log everything under `other`, which is nothing in this case.
## Compiler Trace Areas
## Trace areas in the compiler
This is a list of the trace area used in the compiler
Here is a list of the trace areas used in the compiler:
| Area | Description |
| ------------------------------ | -------------------------------------------------------------------- |
| `compiler.options` | Log the resolved compiler options |
| `import-resolution.library` | Information related to the resolution of import libraries |
| `projection.log` | Debug information logged by the `log()` function used in projections |
| `bind.js` | Information when binding JS files |
| `linter.register-library` | Information that a library rules will be loaded |
| `linter.register-library.rule` | Information about a rule that is being registered |
| `linter.extend-rule-set.start` | Information about a ruleset it is about to extend |
| `linter.extend-rule-set.end` | Information about rules enabled after extending a ruleset |
| `linter.lint` | Start the lint process and show information of all the rules enabled |
| Area | Description |
| ------------------------------ | ---------------------------------------------------------------------- |
| `compiler.options` | Logs the resolved compiler options |
| `import-resolution.library` | Logs information related to the resolution of import libraries |
| `projection.log` | Logs debug information from the `log()` function used in projections |
| `bind.js` | Logs information when binding JS files |
| `linter.register-library` | Logs information when a library's rules are being loaded |
| `linter.register-library.rule` | Logs information about a rule that is being registered |
| `linter.extend-rule-set.start` | Logs information about a ruleset that is about to be extended |
| `linter.extend-rule-set.end` | Logs information about rules enabled after extending a ruleset |
| `linter.lint` | Starts the lint process and shows information of all the rules enabled |
## Tracing in TypeSpec library
## Tracing in TypeSpec libraries
TypeSpec libraries can emit their own tracing that can be collected using the same mechanism. It is recommended that a library scope their tracing area under the library name to prevent collision. This can be achieved by calling the `sub(subArea: string)` method on the tracer.
TypeSpec libraries can also emit their own traces that can be collected using the same mechanism. To avoid naming conflicts, it's recommended that a library prefixes their tracing area with the library name. This can be done by calling the `sub(subArea: string)` method on the tracer.
```ts
const tracer = program.tracer.sub("my-library");
```
the tracer is then available for trace collection
The tracer can then be used for trace collection:
```ts
tracer.trace("emitting-ts", "Emitting ts interface");

Просмотреть файл

@ -3,14 +3,13 @@ id: faq
title: FAQ
---
# Frequently Asked Questions
# Frequently Asked Questions (FAQ)
## `Cannot find package 'x' imported from 'y'` but not using this package
## I'm getting the error `Cannot find package 'x' imported from 'y'`, but I'm not using this package. Why?
This is most likely due to package y having a `peerDependency` on package `x` and package x wasn't installed.
Verify the version of npm you are using. Before version 7 peerDependencies would not get installed automatically and you would have to manually install them.
This issue typically arises when package 'y' has a `peerDependency` on package 'x', and package 'x' hasn't been installed. This can occur if you're using a version of npm that's older than version 7, as these older versions don't automatically install peerDependencies. You would need to install them manually.
### Solutions
### How can I fix this?
- Update npm `npm install -g npm`
- If you cannot update npm, then adding the dependencies to your project dependency should resolve the issue `npm install x`
- You can update npm using the command `npm install -g npm`.
- If you're unable to update npm, you can add the dependencies to your project dependency. This should resolve the issue. Use the command `npm install x`.

Просмотреть файл

@ -9,7 +9,7 @@ TypeSpec comes with a built-in formatter. The formatter can be used in different
- [Via the cli](#via-the-cli)
- Via the vscode/vs extension
- As a prettier plugin
- As a `prettier` plugin
## Via the cli
@ -19,7 +19,7 @@ Format all TypeSpec files:
tsp format "**/*.tsp"
```
Validate that the files are formatted but don't format them. Useful for enforcing in CI.
Check file formatting without modifying them, useful for CI enforcement.
```bash
tsp format --check "**/*.tsp"
@ -27,15 +27,15 @@ tsp format --check "**/*.tsp"
## Via the VSCode or VS extension
When using the VS Code or Visual Studio extensions, the tsp formatter is automatically available.
When you use the extensions for VS Code or Visual Studio, the tsp formatter becomes automatically accessible.
Using the keyboard shortcut for formatting the document (`alt+shift+F` by default) when inside a TypeSpec file will format the document.
If you're working within a TypeSpec file, you can format the document using the default keyboard shortcut for formatting, `alt+shift+F`.
## Via prettier
Underneath the tsp formatter is a prettier plugin. If you already have a prettier configuration for formatting other languages it can be convenient to just have TypeSpec plug in into this existing pipeline.
The tsp formatter is essentially a `prettier` plugin. If you already have a `prettier` configuration set up for other languages, it can be quite handy to simply integrate TypeSpec into this existing pipeline.
In your prettier config file, add:
In your `prettier` config file, add:
```yaml
plugins:

Просмотреть файл

@ -5,13 +5,13 @@ title: Releases
# Releases
## Package versioning strategy
## Versioning strategy for packages
TypeSpec is not stable yet, all packages are released with `0.` major version. Each minor version might have some breaking changes to the TypeSpec language, library API or both. Those are documented [here](../release-notes).
TypeSpec is currently in its development phase, and all packages are released with a `0.` major version. Each minor version may introduce some breaking changes to the TypeSpec language, library API, or both. These changes are documented [here](../release-notes).
Every change to the `main` branch is automatically published under the npm `@next` tag.
Any modification to the `main` branch is automatically published under the npm `@next` tag.
## Current packages
## Available packages
| Name | Changelog | Latest | Next |
| -------------------------------------------------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
@ -49,9 +49,9 @@ Every change to the `main` branch is automatically published under the npm `@nex
## Release cadence
We release changes from all packages the first week of every month.
We roll out updates for all packages during the first week of each month.
You can look at the millestones https://github.com/microsoft/typespec/milestones to see upcoming changes. Millestones are named after the target release month (i.e `[2022] October` is the sprint running in september targeting a release in the first week of October.)
To preview upcoming changes, you can check the milestones at https://github.com/microsoft/typespec/milestones. Milestones are labeled according to the target release month.
## Breaking changes migration guides

Просмотреть файл

@ -2,13 +2,13 @@
title: Reproducibility
---
A key point to service definition is the ability to reliably reproduce the exact same output over time. In cases like:
A crucial aspect of service definition is ensuring consistent output over time. This is important in scenarios such as:
1. A dependency or dependency of dependency was updated with an unintended breaking change
2. Changes to a new version of a service shouldn't affect the older versions
3. A change to the TypeSpec spec
- An update to a dependency or sub-dependency introduces an unexpected breaking change.
- Updates to a new version of a service should not impact older versions.
- Modifications are made to the TypeSpec specification.
This can be mitigated with a few steps:
These issues can be mitigated with a few precautionary measures:
## 1. Defend against dependency changes

Просмотреть файл

@ -2,9 +2,9 @@
title: Style guide
---
# TypeSpec Language Style Guide
# TypeSpec language style guide
This is a guide providing a recommended set of naming convention to use when writing a TypeSpec spec.
This guide offers a recommended set of naming conventions to follow when drafting a TypeSpec specification.
:::info
The guidelines in this article are used in TypeSpec Core libraries. You can use them, or adapt them to your needs. The primary objectives are consistency and readability within your project, team, organization, or company source code.
@ -167,7 +167,7 @@ alias foo = [1, 2, 3];
### Model layout
- Properties should hug each other unless it has decorators or comments
- Properties should hug each other unless they have decorators or comments
<!-- prettier-ignore -->
```tsp
@ -188,7 +188,7 @@ model Foo {
}
```
- Wrap properties in new lines if it has leading comments or decorators
- Wrap properties in new lines if they have leading comments or decorators
<!-- prettier-ignore -->
```tsp

Просмотреть файл

@ -4,12 +4,12 @@ title: TypeSpec Visual Studio Extension
## Installation
Install the extension via the Visual Studio extension manager. [TypeSpec for Visual Studio - Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=typespec.typespecvs)
Install the extension via the Visual Studio extension manager from the [TypeSpec for Visual Studio - Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=typespec.typespec-vs).
## Configure
1. Create a file `.vs/VSWorkspaceSettings.json` at the root of the project.
2. Add configuration as key value pair in this file. Example:
1. Create a file named `.vs/VSWorkspaceSettings.json` at the root of the project.
2. Add the configuration as a key-value pair in this file. For example:
```json
{
@ -17,15 +17,15 @@ Install the extension via the Visual Studio extension manager. [TypeSpec for Vis
}
```
TypeSpec wil interpolate a few variables using this pattern `${<name>}`. For example `${workspaceFolder}`.
TypeSpec will interpolate a few variables using this pattern: `${<name>}`. For example: `${workspaceFolder}`.
Available variables:
- `workspaceFolder`: Correspond to the root of your Visual Studio workspace.
- `workspaceFolder`: Corresponds to the root of your Visual Studio workspace.
## Uninstall
Uninstalling can be done via the Visual Studio extension manager or via the command line:
You can uninstall the extension via the Visual Studio extension manager or through the command line:
```bash
tsp vs uninstall

Просмотреть файл

@ -8,16 +8,16 @@ Install the extension via the Visual Studio Code extension manager https://marke
## Configure
TypeSpec wil interpolate a few variables using this pattern `${<name>}`. For example `${workspaceFolder}`.
TypeSpec will interpolate a few variables using this pattern: `${<name>}`. For example: `${workspaceFolder}`.
Available variables:
- `workspaceFolder`: Corespond to the root of your Visual Studio workspace.
- `workspaceFolder`: Corresponds to the root of your Visual Studio workspace.
### `typespec.tsp-server.path`: Configure the server path
There are cases where the TypeSpec project is located in a subfolder. In such cases, the TypeSpec extension is not able to find the tsp compiler automatically and needs a little guidance.
This setting provides the ability to configure where the tsp compiler is located.
This setting allows you to configure where the tsp compiler is located:
```json
{
@ -27,7 +27,7 @@ This setting provides the ability to configure where the tsp compiler is located
## Uninstall
Uninstalling can be done via the Visual Studio Code extension manager or via the command line:
You can uninstall the extension via the Visual Studio Code extension manager or through the command line:
```bash
tsp code uninstall

Просмотреть файл

@ -8,17 +8,17 @@ slug: /
## Requirements
Install [Node.js 20 LTS](https://nodejs.org/en/download/) and ensure you are able to run the `npm` command in a command prompt:
Install [Node.js 20 LTS](https://nodejs.org/en/download/) and ensure you can run the `npm` command in a command prompt:
```bash
npm --version
```
It is recommended to have npm 7+. To update npm run `npm install -g npm`
We recommend using npm 7+. To update npm, run `npm install -g npm`
## Install tsp
First step is to install `tsp`, the TypeSpec compiler/CLI
The first step is to install `tsp`, the TypeSpec compiler/CLI.
```bash
npm install -g @typespec/compiler
@ -31,23 +31,23 @@ TypeSpec provides extension for the following editors:
- [Visual Studio Code](./editor/vscode.md)
- [Visual Studio](./editor/vs.md)
## Create first TypeSpec Project
## Create a new TypeSpec project
To get your first TypeSpec project started run in a fresh directory
Run the following command in a clean directory to create a new TypeSpec project.
```bash
tsp init
```
This will prompt you with a few questions, pick the `Generic REST API` template, your project name, and select the `@typespec/openapi3` library.
This will prompt you with a few questions. Pick the `Generic REST API` template, your project name, and select the `@typespec/openapi3` library.
Next, you can install the dependencies
Next, you can install the dependencies.
```bash
tsp install
```
You should now have a basic TypeSpec project setup with a structure looking like
You should now have a basic TypeSpec project setup with a structure looking like this:
```bash
package.json # Package manifest defining your TypeSpec project as a Node package.

Просмотреть файл

@ -3,13 +3,11 @@ id: aliases
title: Aliases
---
# Alias
# Aliases
Aliases can be defined for types. This can be helpful to reuse a complex expression.
Aliases are a convenient way to define shorthand for types, especially when dealing with complex expressions. They simplify the syntax but don't have a representation in the type graph. As a result, you can't decorate aliases. If you need to give an alternate name to a model, use [`model is`](./models.md).
Alias is only a syntax helper, and it has no representation in the type graph. This means that aliases cannot be decorated. Use [`model is`](./models.md) to provide an alternate name for a model.
Alias can be defined using the `alias` keyword
You can define an alias using the `alias` keyword.
```typespec
alias Options = "one" | "two";

Просмотреть файл

@ -3,7 +3,7 @@ id: built-in-types
title: Built-in types
---
# Built-in Types
# Built-in types
TypeSpec Standard Library provide some built-in types that can be used to build more complex types.

Просмотреть файл

@ -5,17 +5,17 @@ title: Decorators
# Decorators
Decorators enable a developer to attach metadata to types in a TypeSpec program. They can also be used to calculate types based on their inputs. Decorators are the backbone of TypeSpec's extensibility and give it the flexibility to describe many different kinds of APIs and associated metadata like documentation, constraints, samples, and the like.
Decorators in TypeSpec allow developers to attach metadata to types within a TypeSpec program. They can also be used to compute types based on their inputs. Decorators form the core of TypeSpec's extensibility, providing the flexibility to describe a wide variety of APIs and associated metadata such as documentation, constraints, samples, and more.
Many TypeSpec constructs can be decorated, including [namespaces](./namespaces.md), [operations](./operations.md) and their parameters, and [models](./models.md) and their members.
A range of TypeSpec constructs can be decorated, including [namespaces](./namespaces.md), [operations](./operations.md) and their parameters, and [models](./models.md) and their members.
Decorators are defined using JavaScript functions that are exported from a standard ECMAScript module. When you import a JavaScript file, TypeSpec will look for any exported functions prefixed with `$`, and make them available as decorators inside the TypeSpec syntax. When a decorated declaration is evaluated by TypeSpec, it will invoke the decorator function, passing along a reference to the current compilation, an object representing the type it is attached to, and any arguments the user provided to the decorator.
Decorators are defined using JavaScript functions that are exported from a standard ECMAScript module. When a JavaScript file is imported, TypeSpec will look for any exported functions prefixed with `$`, and make them available as decorators within the TypeSpec syntax. When a decorated declaration is evaluated by TypeSpec, the decorator function is invoked, passing along a reference to the current compilation, an object representing the type it is attached to, and any arguments the user provided to the decorator.
## Using decorators
## Applying decorators
Decorators are referenced using the `@` prefix and must be specified before the entity they are decorating. Arguments can be provided by using parentheses in a manner similar to many programming languages, e.g. `@myDec1, "hi", { a: string })`.
Decorators are referenced using the `@` prefix and must be placed before the entity they are decorating. Arguments can be provided by using parentheses, similar to function calls in many programming languages, e.g., `@myDec1("hi", { a: string })`.
The following shows an example of declaring and then using a decorator:
Here's an example of declaring and then using a decorator:
```typespec
@tag("Sample")
@ -25,16 +25,16 @@ model Dog {
}
```
The parentheses can be omitted when no arguments are provided.
If no arguments are provided, the parentheses can be omitted.
```typespec
@mark
model Dog {}
```
## Augment decorators
## Augmenting decorators
Decorators can also be used from a different location by referring to the type being decorated. For this you can declare an augment decorator using the `@@` prefix. The first argument of an augment decorator is the type reference that should be decorated. As the augment decorator is a statement, it must end with a semicolon (`;`).
Decorators can also be applied from a different location by referring to the type being decorated. For this, you can declare an augment decorator using the `@@` prefix. The first argument of an augment decorator is the type reference that should be decorated. As the augment decorator is a statement, it must end with a semicolon (`;`).
```typespec
model Dog {}
@ -42,14 +42,14 @@ model Dog {}
@@tag(Dog, "Sample");
```
Which is equivalent to
This is equivalent to:
```typespec
@tag("Sample")
model Dog {}
```
Example: Decorate a model property
Example: decorating a model property
```typespec
model Dog {
@ -59,6 +59,6 @@ model Dog {
@@readOnly(Dog.name);
```
## Writing decorator
## Creating decorators
[See creating decorator documentation](../extending-typespec/create-decorators.md)
_For more information on creating decorators, see the [Creating Decorators Documentation](../extending-typespec/create-decorators.md)._

Просмотреть файл

@ -5,20 +5,20 @@ title: Documentation
# Documentation
Documentation is crucial to any API. TypeSpec provides a number of ways to document your API using doc comments and decorators.
Documentation is a vital aspect of any API. TypeSpec offers several ways to document your API, including doc comments and decorators.
# Documenting APIs
# Approaches to documenting APIs
There are 2 ways to document your API using TypeSpec:
TypeSpec provides two primary methods for documenting your API:
- `@doc` decorator
- `/** */` Doc comments
The later has the advantage of being less intrusive to the spec.
The latter is less intrusive to the specification and is often preferred.
## `@doc` Decorator
## The `@doc` decorator
The `@doc` decorator can be used to attach documentation to most TypeSpec declarations. It most-commonly accepts a string argument that will be used as the documentation for the declaration.
The `@doc` decorator can be used to attach documentation to most TypeSpec declarations. It typically accepts a string argument that serves as the documentation for the declaration.
```typespec
@doc("This is a sample model")
@ -28,22 +28,22 @@ model Dog {
}
```
The `@doc` decorator can also accept a source object which can be used, for example, to provide templated documentation for a generic type.
The `@doc` decorator can also accept a source object, which can be used to provide templated documentation for a generic type, for example.
```typespec
@doc("Templated {name}", Type)
model Template<Type extends {}> {
}
// doc will read "Templated A"
// The documentation will read "Templated A"
model A is Template<A>
```
## Doc Comments
## Doc comments
You can annotate objects in your TypeSpec spec with doc comments. These comments will be considered the same as if they were attached using the `@doc` decorator and can be used to generate external documentation.
You can annotate objects in your TypeSpec specification with doc comments. These comments are treated as if they were attached using the `@doc` decorator and can be used to generate external documentation.
Doc comments starts with `/**` and continue until the closing `*/` is encountered. [Tags](#doc-comment-tags) can be used to provide additional documentation context.
Doc comments start with `/**` and continue until the closing `*/` is encountered. [Tags](#doc-comment-tags) can be used to provide additional documentation context.
```typespec
/**
@ -64,11 +64,11 @@ op read(
): Widget | Error;
```
The benefit to using doc comment syntax is that it keeps all of the documentation for a declaration in one place, making it easier to read and maintain. Additionally, it allows the generation of documentation using tools like TypeDoc without having to write a custom emitter to examine the `@doc` metadata.
The advantage of using doc comment syntax is that it keeps all of the documentation for a declaration in one place, making it easier to read and maintain. Additionally, it allows the generation of documentation using tools like TypeDoc without having to write a custom emitter to examine the `@doc` metadata.
### Doc comment tags
As shown in the previous example doc comments can use certain tags to document additional elements or provide different documentation context.
As shown in the previous example, doc comments can use certain tags to document additional elements or provide different documentation context.
| Tag | Description | Example |
| ----------------------- | --------------------------------- | --------------------------------------------------- |
@ -90,4 +90,4 @@ model Dog {
}
```
Comments are ignored by the compiler and are not included in the generated output. They are intended to be used to document your spec internally and are not suitable for generating external documentation.
Comments are ignored by the compiler and do not appear in the generated output. They are intended for internal documentation of your spec and are not suitable for generating external documentation.

Просмотреть файл

@ -5,12 +5,11 @@ title: Enums
# Enums
Enums allow a developer to define a set of named constants. Using enums can make it easier to document intent, or create a set of distinct cases. Enums can either be numeric or string-based. For other types, look into [union](./unions.md)
Enums, short for enumerations, provide a way for developers to define a collection of named constants. They are useful for documenting the purpose of the code or for establishing a set of distinct scenarios. Enums can be either numeric or string-based. For other data types, consider using [unions](./unions.md).
## Basics
## The basics
Enums are declared using the `enum` keyword.
The enums members are comma `,` separated and can be TypeSpec `identifier`s or `string literal`s.
You can declare enums using the `enum` keyword. The members of an enum are separated by commas `,` and can be either `identifier` TypeSpecs or `string literal`s.
```typespec
enum Direction {
@ -21,11 +20,11 @@ enum Direction {
}
```
In this case, we haven't specified how the constants will be represented. Different scenarios might handle the enums differently.
In the above example, we haven't defined the representation of the constants. Depending on the context, enums might be handled differently.
## Values
## Assigning values to enums
Enums members can have a custom value that can be assigned using the `:` operator.
You can assign custom values to enum members using the `:` operator.
```typespec
enum Direction {
@ -36,7 +35,7 @@ enum Direction {
}
```
Values can also be integers.
These values can also be integers.
```typespec
enum Foo {
@ -47,7 +46,7 @@ enum Foo {
}
```
or float
Or even floating-point numbers.
```typespec
enum Hour {
@ -58,9 +57,9 @@ enum Hour {
}
```
## Composing enums
## Combining enums
Enums can be reused using the spread `...` pattern. All the members of the source enums will be copied in the target enum but it doesn't create any reference between the source and target enums.
You can combine enums using the spread `...` pattern. This copies all the members from the source enum to the target enum, but it doesn't establish any reference between the source and target enums.
```typespec
enum DirectionExt {
@ -72,9 +71,9 @@ enum DirectionExt {
}
```
## Referencing enum members
## How to reference enum members
Enum members can be referenced using the `.` operator for identifiers.
You can reference enum members using the `.` operator for identifiers.
```typespec
alias North = Direction.North;

Просмотреть файл

@ -3,30 +3,30 @@ id: imports
title: Imports
---
# Import
# Importing files and libraries in TypeSpec
Imports add files or libraries to your TypeSpec program. When you compile a TypeSpec file, you provide a path to your root TypeSpec file, by convention called "main.tsp". From there, any files you import are added to your program. If you import a directory, TypeSpec will look for a `main.tsp` file inside that directory.
Imports are used to include files or libraries into your TypeSpec program. When compiling a TypeSpec file, you specify the path to your root TypeSpec file, typically named "main.tsp". From this root file, any imported files are added to your program. If a directory is imported, TypeSpec will search for a `main.tsp` file within that directory.
The path you import must either begin with `"./"` or `"../"` or otherwise be an absolute path. The path must either refer to a directory, or else have an extension of either ".tsp" or ".js". The following demonstrates how to use imports to assemble a TypeSpec program from multiple files:
The path specified in the import must either start with `"./"` or `"../"`, or be an absolute path. The path should either point to a directory, or have an extension of either ".tsp" or ".js". The examples below illustrate how to use imports to assemble a TypeSpec program from multiple files:
## Import TypeSpec file
## Importing a TypeSpec file
```typespec
import "./models/foo.tsp";
```
## Import Js file
## Importing a JavaScript file
```typespec
import "./decorators.js";
```
## Import a library
## Importing a library
The import value can be name one of the package dependencies. In that case TypeSpec will lookup for the `package.json` file and check the `tspMain` entry (or default to `main` if absent) to decide what is the library entrypoint to load.
The import value can be the name of one of the package dependencies. In this case, TypeSpec will look for the `package.json` file and check the `tspMain` entry (defaulting to `main` if `tspMain` is absent) to determine the library entrypoint to load.
```typespec
import "@typespec/rest";
import "/rest";
```
```json
@ -36,14 +36,14 @@ import "@typespec/rest";
}
```
which result in `./node_modules/@typespec/rest/lib/main.tsp` to be imported
This results in `./node_modules/@typespec/rest/lib/main.tsp` being imported.
## Import a directory
## Importing a directory
If the import value is a directory it will lookup if that directory is a Node package and follow the npm package [lookup logic](#import-a-library) or if the directory contains a `main.tsp`.
If the import value is a directory, TypeSpec will check if that directory is a Node package and follow the npm package [lookup logic](#importing-a-library), or if the directory contains a `main.tsp` file.
```typespec
import "./models"; // same as `import "./models/main.tsp";
import "./models"; // equivalent to `import "./models/main.tsp";
```
```typespec

Просмотреть файл

@ -5,9 +5,9 @@ title: Interfaces
# Interfaces
Interfaces can be used to group and reuse [operations](./operations.md).
Interfaces are useful for grouping and reusing [operations](./operations.md).
Interfaces are declared using the `interface` keyword.
You can declare interfaces using the `interface` keyword.
```typespec
interface SampleInterface {
@ -18,9 +18,9 @@ interface SampleInterface {
## Composing interfaces
The keyword `extends` can be used to compose operations from other interfaces into a new interface.
You can use the `extends` keyword to incorporate operations from other interfaces into a new interface.
Given the following interfaces
Consider the following interfaces:
```typespec
interface A {
@ -32,7 +32,7 @@ interface B {
}
```
a new interface `C` can be created including all operations from `A` and `B`
You can create a new interface `C` that includes all operations from `A` and `B`:
```typespec
interface C extends A, B {
@ -40,7 +40,7 @@ interface C extends A, B {
}
```
which is equivalent to
This is equivalent to:
```typespec
interface C {
@ -50,9 +50,9 @@ interface C {
}
```
## Interface template
## Interface templates
Interfaces can be templated, [see templates](./templates.md) for details on templates.
Interfaces can be templated. For more details on templates, [see templates](./templates.md).
```typespec
interface ReadWrite<T> {
@ -61,9 +61,9 @@ interface ReadWrite<T> {
}
```
## Interface operation templates
## Templating interface operations
Operations defined inside of an interface can also be templated. ([see templates](./templates.md) for details on templates.)
Operations defined within an interface can also be templated. For more details on templates, [see templates](./templates.md).
```typespec
interface ReadWrite<T> {
@ -77,7 +77,7 @@ op myWrite is MyReadWrite.write<int32>;
```
:::caution
Any uninstantiated, templated operation defined in an interface will be excluded from the list of service operations.
Any templated operation defined in an interface that is not instantiated will be omitted from the list of service operations.
This also applies when using `extends` on an interface that contains templated operations with unfilled template arguments.
@ -87,10 +87,10 @@ interface ReadWrite<T> {
write<R>(t: T): R;
}
interface MyReadWrite extends ReadWrite<string> {} // Here the `read()` operation is fully instantiated and will be included in a service definition. `write()` however isn't.
interface MyReadWrite extends ReadWrite<string> {} // Here, the `read()` operation is fully instantiated and will be included in a service definition. However, `write()` is not.
```
When working with building block interface like this use alias to create your interface building block instead of `interface extends`. This way the instantiated interface and its member will not be resolved in the service definition.
When working with building block interfaces like this, use an alias to create your interface building block instead of `interface extends`. This way, the instantiated interface and its members will not be resolved in the service definition.
```typespec
alias MyReadWrite = ReadWrite<string>;

Просмотреть файл

@ -5,13 +5,13 @@ title: Intersections
# Intersections
Intersections describe a type that must include all the intersection's constituents. Declare an intersection with the `&` operator.
Intersections in programming define a type that must encompass all the constituents of the intersection. You can declare an intersection using the `&` operator.
```typespec
alias Dog = Animal & Pet;
```
An intersection is equivalent to [spreading](./models.md#spread) both types.
An intersection is functionally equivalent to [spreading](./models.md#spread) both types.
```typespec
alias Dog = {

Просмотреть файл

@ -5,22 +5,22 @@ title: Models
# Models
TypeSpec models are used to describe data shapes or schemas.
Models in TypeSpec are utilized to define the structure or schema of data.
## Model kinds
## Types of models
Models can be used to represent 2 types:
Models can be categorized into two main types:
- [Record](#record)
- [Array](#array)
### Record
Record models are structure with named fields called properties.
A Record model is a structure that consists of named fields, referred to as properties.
- name can be an `identifier` or `string literal`.
- type can be any type reference
- properties are ordered. See [ordering of properties](#ordering-of-properties)
- The name can be an `identifier` or `string literal`.
- The type can be any type reference.
- Properties are arranged in a specific order. Refer to [property ordering](#property-ordering) for more details.
```typespec
model Dog {
@ -31,7 +31,7 @@ model Dog {
#### Optional properties
Properties can be marked as optional using the `?` punctuation.
Properties can be designated as optional by using the `?` symbol.
```typespec
model Dog {
@ -41,7 +41,7 @@ model Dog {
#### Default values
[Optional properties](#optional-properties) can be provided with a default value using `=` operator.
[Optional properties](#optional-properties) can be assigned a default value using the `=` operator.
```typespec
model Dog {
@ -49,9 +49,9 @@ model Dog {
}
```
#### Ordering of properties
#### Property ordering
Properties are ordered in the order that they appear in source. Properties obtained via `model is` appear before properties defined in the model body. Properties obtained via `...` are inserted where the spread appears in source.
Properties are arranged in the order they are defined in the source. Properties acquired via `model is` are placed before properties defined in the model body. Properties obtained via `...` are inserted at the point where the spread appears in the source.
Example:
@ -71,7 +71,7 @@ model Cat is Pet {
furColor: string;
}
// Resulting property order for cat:
// The resulting property order for Cat is:
// name, age, meow, address, furColor
```
@ -79,28 +79,27 @@ model Cat is Pet {
The `Record<T>` model can be used to define a model with an arbitrary number of properties of type T. It can be combined with a named model to provide some known properties.
There is 3 ways this can be done which all have slightly different semantics:
There are three ways to achieve this, each with slightly different semantics:
- Using the `...` operator
- Using `is` operator
- Using `extends` operator
- Using the `is` operator
- Using the `extends` operator
#### Using `...` operator
#### Using the `...` operator
Spreading a Record into your model means that your model has all the properties you have explicitly defined plus any additional properties defined by the Record.
This means that the property in the model could be of a different and incompatible type with the Record value type.
Spreading a Record into your model implies that your model includes all the properties you have explicitly defined, plus any additional properties defined by the Record. This means that a property in the model could be of a different and incompatible type with the Record value type.
```tsp
// Here we are saying the Person model has a property `age` that is an int32 but has some other properties that are all string.
// In this example, the Person model has a property `age` that is an int32, but also has other properties that are all strings.
model Person {
age: int32;
...Record<string>;
}
```
#### Using `is` operator
#### Using the `is` operator
When using `is Record<T>` it is now saying that all properties of this model are of type T. This means that each property explicitly defined in the model must be also be of type T.
When using `is Record<T>`, it indicates that all properties of this model are of type T. This means that each property explicitly defined in the model must also be of type T.
The example above would be invalid
@ -119,11 +118,9 @@ model Person is Record<string> {
}
```
#### Using `extends` operator
#### Using the `extends` operator
`extends` is going to have similar semantics to `is` but is going to define the relationship between the 2 models.
In many languages this would probably result in the same emitted code as `is` and is recommended to just use `is Record<T>` instead.
The `extends` operator has similar semantics to `is`, but it defines the relationship between the two models. In many languages, this would probably result in the same emitted code as `is` and it is recommended to use `is Record<T>` instead.
```tsp
model Person extends Record<string> {
@ -135,7 +132,7 @@ model Person extends Record<string> {
#### `never`
A model property can be declared as having the type never. This can be interpreted as the model not having that property.
A model property can be declared as having the type `never`. This can be interpreted as the model not having that property.
This can be useful in a model template to omit a property.
@ -150,18 +147,18 @@ model UKAddress is Address<never>;
```
:::note
It is up to the emitter to remove `never` properties. The TypeSpec compiler will not automatically omit them.
The responsibility of removing `never` properties lies with the emitter. The TypeSpec compiler will not automatically omit them.
:::
### Array
Array are models created using the `[]` syntax which is just a syntactic sugar for using the `Array<T>` model type.
Arrays are models created using the `[]` syntax, which is a shorthand for using the `Array<T>` model type.
## Model composition
### Spread
The spread operator takes the members of a source model and copies them into a target model. Spread doesn't create any nominal relationship between source and target, and so it's useful when you want to reuse common properties without reasoning about or generating complex inheritance relationships.
The spread operator (`...`) copies the members of a source model into a target model. This operation doesn't create any nominal relationship between the source and target, making it useful when you want to reuse common properties without generating complex inheritance relationships.
```typespec
model Animal {
@ -177,7 +174,7 @@ model Dog {
...Pet;
}
// Dog is equivalent to the following declaration:
// The Dog model is equivalent to the following declaration:
model Dog {
species: string;
name: string;
@ -186,7 +183,7 @@ model Dog {
### Extends
Sometimes you want to create an explicit relationship between two models, for example when you want to emit class definitions in languages which support inheritance. The `extends` keyword can be used to establish such a relationship.
There are times when you want to create an explicit relationship between two models, such as when you're generating class definitions in languages that support inheritance. The `extends` keyword can be used to establish this relationship.
```typespec
model Animal {
@ -198,7 +195,7 @@ model Dog extends Animal {}
### Is
Sometimes you want to create a new type that is an exact copy of an existing type but with some additional properties or metadata without creating a nominal inheritance relationship. The `is` keyword can be used for this purpose. It copies all the properties (like spread), but copies [decorators](./decorators.md) as well. One common use case is to give a better name to a [template](#Templates) instantiation:
There are instances when you want to create a new type that is an exact copy of an existing type but with additional properties or metadata, without creating a nominal inheritance relationship. The `is` keyword can be used for this purpose. It copies all the properties (like spread), but also copies [decorators](./decorators.md) as well. A common use case is to provide a better name to a [template](#model-templates) instantiation:
```typespec
@decorator
@ -208,7 +205,7 @@ model Thing<T> {
model StringThing is Thing<string>;
// StringThing declaration is equivalent to the following declaration:
// The StringThing declaration is equivalent to the following declaration:
@decorator
model StringThing {
property: string;
@ -217,7 +214,7 @@ model StringThing {
## Model templates
[See templates](./templates.md) for details on templates
Refer to [templates](./templates.md) for more details on templates.
```typespec
model Page<Item> {
@ -232,7 +229,7 @@ model DogPage {
## Meta type references
Some model property meta types can be referenced using `::`
Some model property meta types can be referenced using `::`.
| Name | Example | Description |
| ---- | ---------------- | ---------------------------------------- |

Просмотреть файл

@ -5,11 +5,11 @@ title: Namespaces
# Namespaces
Namespaces let you group related types together into namespaces. This helps organize your types, making them easier to find and prevents name conflicts. Namespaces are merged across files, so you can reference any type anywhere in your TypeSpec program via its namespace.
Namespaces in TypeSpec allow you to group related types together. This organization makes your types easier to locate and helps avoid naming conflicts. Namespaces are merged across files, enabling you to reference any type from anywhere in your TypeSpec program using its namespace.
## Basics
Create a namespace with the `namespace` keyword.
You can create a namespace using the `namespace` keyword.
```typespec
namespace SampleNamespace {
@ -17,9 +17,9 @@ namespace SampleNamespace {
}
```
_The name of a namespace must be a valid TypeSpec identifier._
_Note: The namespace name must be a valid TypeSpec identifier._
The `SampleNamespace` can then be used from other places:
You can then use `SampleNamespace` from other locations:
```typespec
model Foo {
@ -27,9 +27,9 @@ model Foo {
}
```
## Nested namespace
## Nested namespaces
Namespaces can contain sub namespaces providing additional granularity
Namespaces can contain sub-namespaces, offering additional layers of organization.
```typespec
namespace Foo {
@ -41,7 +41,7 @@ namespace Foo {
}
```
or this can be simplified using `.` notation
Alternatively, you can simplify this using `.` notation:
```typespec
namespace Foo.Bar.Baz {
@ -49,7 +49,7 @@ namespace Foo.Bar.Baz {
}
```
The sub-namespace can then be used from other places using the fully qualified name.
You can then use the sub-namespace from other locations using the fully qualified name.
```typespec
model A {
@ -57,9 +57,9 @@ model A {
}
```
## File namespace
## File-level namespaces
A namespace for all declarations contained in a file can be provided at the top (After the `import` statements) using a blockless namespace statement
You can define a namespace for all declarations within a file at the top of the file (after any `import` statements) using a blockless namespace statement:
```typespec
namespace SampleNamespace;
@ -67,11 +67,11 @@ namespace SampleNamespace;
model SampleModel {}
```
A file can only have a single blockless namespace.
A file can only have one blockless namespace.
## Using a namespace
## Using namespaces
The content of a namespace can be exposed to the current scope using the `using` keyword.
You can expose the contents of a namespace to the current scope using the `using` keyword.
```typespec
using SampleNamespace;
@ -81,7 +81,7 @@ model Foo {
}
```
The bindings introduced by a `using` statement are local to the namespace they are declared in. They do not become part of the namespace themselves.
The bindings introduced by a `using` statement are local to the namespace in which they are declared. They do not become part of the namespace themselves.
```typespec
namespace One {
@ -90,9 +90,9 @@ namespace One {
namespace Two {
using One;
alias B = A; // ok
alias B = A; // This is valid
}
alias C = One.A; // not ok
alias C = Two.B; // ok
alias C = One.A; // This is not valid
alias C = Two.B; // This is valid
```

Просмотреть файл

@ -5,9 +5,9 @@ title: Operations
# Operations
Operations describe service endpoints and consist of an operation name, parameters, and return type.
Operations are essentially service endpoints, characterized by an operation name, parameters, and a return type.
Operations are declared using the `op` keyword:
You can declare operations using the `op` keyword:
```typespec
op ping(): void;
@ -15,7 +15,7 @@ op ping(): void;
## Parameters
The operation's parameters describe a model, so anything you can do in a model you can do in a parameter list as well, including using the spread operator:
The parameters of an operation represent a model. Therefore, you can perform any action with parameters that you can with a model, including the use of the spread operator:
```typespec
op feedDog(...CommonParams, name: string): void;
@ -23,7 +23,7 @@ op feedDog(...CommonParams, name: string): void;
## Return type
Often an endpoint returns one of any number of models. For example, there might be a return type for when an item is found, and a return type for when an item isn't found. Unions are used to describe this pattern:
Frequently, an endpoint may return one of several possible models. For instance, there could be a return type for when an item is located, and another for when it isn't. Unions are employed to express this scenario:
```typespec
model DogNotFound {
@ -33,33 +33,33 @@ model DogNotFound {
op getDog(name: string): Dog | DogNotFound;
```
## Reuse operations
## Reusing operations
Operation signatures can be reused using the `is` keyword. Given an operation
You can reuse operation signatures with the `is` keyword. For example, given an operation
```typespec
op Delete(id: string): void;
```
its signature can be reused like this:
You can reuse its signature like so:
```typespec
op deletePet is Delete;
```
This means that `deletePet` will have the same parameters, return type and decorators as the `Delete` operation.
This implies that `deletePet` will inherit the same parameters, return type, and decorators as the `Delete` operation.
This pattern is most commonly used in combination with [operation templates](#operations-templates)
This practice is typically used in conjunction with [operation templates](#operation-templates)
## Operations templates
## Operation templates
[See templates](./templates.md) for details on templates.
For more information on templates, [see templates](./templates.md).
```typespec
op ReadResource<T>(id: string): T;
```
The operation template can then be referenced via `is`:
You can reference the operation template using `is`:
```typespec
op readPet is ReadResource<Pet>;
@ -67,7 +67,7 @@ op readPet is ReadResource<Pet>;
## Referencing model properties
Model properties can be referenced using the `.` operator for identifiers.
You can reference model properties using the `.` operator for identifiers.
```tsp
alias PetName = Pet.name;
@ -75,9 +75,9 @@ alias PetName = Pet.name;
## Meta type references
Some operation meta types can be referenced using `::`
Certain operation meta types can be referenced using `::`
| Name | Example | Description |
| ---------- | --------------------- | ----------------------------------------- |
| parameters | `readPet::parameters` | Reference the parameters model expression |
| returnType | `readPet::returnType` | Reference the operation return type |
| Name | Example | Description |
| ---------- | --------------------- | ------------------------------------------ |
| parameters | `readPet::parameters` | References the parameters model expression |
| returnType | `readPet::returnType` | References the operation return type |

Просмотреть файл

@ -5,20 +5,20 @@ title: Overview
# Language Overview
This is an overview of the language concept in TypeSpec. It doesn't go in detail but can be used as a cheat sheet.
This document provides a concise overview of the language concepts in TypeSpec. It serves as a quick reference guide rather than an in-depth tutorial.
## Declarations
- Declaration names must be unique across types within the same scope. For example this is not allowed
- Names of declarations must be unique across different types within the same scope. For instance, the following is not permissible:
<!-- prettier-ignore -->
```typespec
```typespec
model Dog {}
namespace Dog {}
```
## Imports
_Details: [Imports](./imports.md)_
_For more details, see: [Imports](./imports.md)_
| Feature | Example |
| -------------------- | ------------------------- |
@ -28,7 +28,7 @@ _Details: [Imports](./imports.md)_
## Namespaces
_Details: [Namespaces](./namespaces.md)_
_For more details, see: [Namespaces](./namespaces.md)_
| Feature | Example |
| ----------------- | ---------------------------- |
@ -39,7 +39,7 @@ _Details: [Namespaces](./namespaces.md)_
## Decorators
_Details: [Decorators](./decorators.md)_
_For more details, see: [Decorators](./decorators.md)_
| Feature | Example |
| ---------------------------- | ----------------------------------------------------------------------------------- |
@ -51,7 +51,7 @@ _Details: [Decorators](./decorators.md)_
## Scalars
_Details: [Scalars](./scalars.md)_
_For more details, see: [Scalars](./scalars.md)_
| Feature | Example |
| ------------------ | ------------------------------------------- |
@ -61,7 +61,7 @@ _Details: [Scalars](./scalars.md)_
## Models
_Details: [Models](./models.md)_
_For more details, see: [Models](./models.md)_
| Feature | Example |
| ------------------------------ | ------------------------------------- |
@ -76,7 +76,7 @@ _Details: [Models](./models.md)_
## Operations
_Details: [Operations](./operations.md)_
_For more details, see: [Operations](./operations.md)_
| Feature | Example |
| ----------------------------- | ------------------------------------------------ |
@ -89,7 +89,7 @@ _Details: [Operations](./operations.md)_
## Interfaces
_Details: [Interfaces](./interfaces.md)_
_For more details, see: [Interfaces](./interfaces.md)_
| Feature | Example |
| --------------------- | -------------------------------------- |
@ -99,7 +99,7 @@ _Details: [Interfaces](./interfaces.md)_
## Templates
_Details: [Templates](./templates.md)_
_For more details, see: [Templates](./templates.md)_
| Feature | Example |
| --------------------------------- | --------------------------------------------------- |
@ -111,7 +111,7 @@ _Details: [Templates](./templates.md)_
## Enums
_Details: [Enums](./enums.md)_
_For more details, see: [Enums](./enums.md)_
| Feature | Example |
| ------------------ | ---------------------------------------------- |
@ -123,7 +123,7 @@ _Details: [Enums](./enums.md)_
## Unions
_Details: [Unions](./unions.md)_
_For more details, see: [Unions](./unions.md)_
| Feature | Example |
| ----------------------- | -------------------------------- |
@ -132,7 +132,7 @@ _Details: [Unions](./unions.md)_
## Intersections
_Details: [Intersections](./intersections.md)_
_For more details, see: [Intersections](./intersections.md)_
| Feature | Example |
| ------------------------ | -------------- |
@ -140,7 +140,7 @@ _Details: [Intersections](./intersections.md)_
## Type literals
_Details: [Type literals](./type-literals.md)_
_For more details, see: [Type literals](./type-literals.md)_
| Feature | Example |
| ----------------- | -------------------------------------------------------- |
@ -152,7 +152,7 @@ _Details: [Type literals](./type-literals.md)_
## Aliases
_Details: [Aliases](./alias.md)_
_For more details, see: [Aliases](./alias.md)_
| Feature | Example |
| ----------------- | --------------------------------- |

Просмотреть файл

@ -4,25 +4,25 @@ title: Scalars
# Scalars
These are types without any fields (for example `string`, `int32`, `boolean`, etc.)
Scalars are simple types that don't have any fields. Examples of these include `string`, `int32`, `boolean`, and so on.
Scalar can be declared using the `scalar` keyword
You can declare a scalar by using the `scalar` keyword.
```typespec
scalar ternary;
```
## Extend another scalar
## Extending a scalar
Scalar can be extended using the `extends` keyword.
You can create a new scalar that extends an existing one by using the `extends` keyword.
```typespec
scalar Password extends string;
```
## Template scalar
## Scalars with template parameters
Scalar support template parameters. Note: the only use for those template are decorators.
Scalars can also support template parameters. However, it's important to note that these templates are primarily used for decorators.
```typespec
@doc(Type)

Просмотреть файл

@ -5,9 +5,9 @@ title: Templates
# Templates
It is often useful to let the users of a model fill in certain details. Templates enable this pattern. Similar to generics found in other languages, model templates declare template parameters that users provide when referencing the type.
Templates are a powerful tool that allow users to customize certain aspects of a type. Similar to generics in other programming languages, templates define template parameters that users can specify when referencing the type.
Templates can be used on:
Templates can be applied to:
- [aliases](./alias.md)
- [models](./models.md)
@ -27,7 +27,7 @@ model DogPage {
## Default values
A template parameter can be given a default argument value with `= <value>`.
You can assign a default value to a template parameter using `= <value>`.
```typespec
model Page<Item = string> {
@ -38,13 +38,13 @@ model Page<Item = string> {
## Parameter constraints
Template parameters can specify a constraint using the `extends` keyword. See the [type relations](./type-relations.md) documentation for details on how validation works.
You can impose constraints on template parameters using the `extends` keyword. For details on how validation works, refer to the [type relations](./type-relations.md) documentation.
```typespec
alias Foo<Type extends string> = Type;
```
Now, instantiating Foo with an argument that does not satisfy the constraint `string` will result in an error:
If you try to instantiate Foo with an argument that does not meet the `string` constraint, you will encounter an error:
```typespec
alias Bar = Foo<123>;
@ -58,7 +58,7 @@ A template parameter constraint can also be a model expression:
alias Foo<Type extends {name: string}> = Type;
```
Template parameter defaults also need to respect the constraint:
Default values for template parameters must also adhere to the constraint:
```typespec
alias Foo<Type extends string = "Abc"> = Type
@ -67,7 +67,7 @@ alias Bar<Type extends string = 123> = Type
^ Type '123' is not assignable to type 'TypeSpec.string'
```
Furthermore, all optional arguments must come at the end of the template. A required argument cannot follow an optional argument:
Also, all optional arguments must be placed at the end of the template. A required argument cannot follow an optional argument:
```typespec
// Invalid
@ -77,7 +77,7 @@ alias Foo<T extends string = "Abc", U> = ...;
## Named template arguments
Template arguments may also be specified by name. In that case, they can be specified out of order and optional arguments may be omitted. This can be useful when dealing with templates that have many defaultable arguments:
Template arguments can also be specified by name. This allows you to specify them out of order and omit optional arguments. This can be particularly useful when dealing with templates that have many arguments with defaults:
```typespec
alias Test<T, U extends numeric = int32, V extends string = "example"> = ...;
@ -105,6 +105,6 @@ alias Example3 = Test<
>;
```
Since template arguments may be specified by name, the names of template parameters are part of the public API of a template. **Changing the name of a template parameter may break existing specifications that use the template.**
Since template arguments can be specified by name, the names of template parameters are part of the template's public API. **Renaming a template parameter may break existing specifications that use the template.**
**Note**: Template arguments are evaluated in the order the parameters are defined in the template _definition_, not the order in which they are written in the template _instance_. Most of the time, this should not matter, but may be important in some cases where evaluating a template argument may invoke decorators with side effects.
**Note**: Template arguments are evaluated in the order the parameters are defined in the template _definition_, not the order in which they are written in the template _instance_. While this is usually inconsequential, it may be important in some cases where evaluating a template argument may trigger decorators with side effects.

Просмотреть файл

@ -1,23 +1,23 @@
---
id: type-literals
title: Type literals
title: Type Literals
---
# Type literals
API authors often need to describe API shapes in terms of specific literal values. For example, this operation returns this specific integer status code, or this model member can be one of a few specific string values. It is also often useful to pass specific literal values to decorators. TypeSpec supports string, number, and boolean literal values to support these cases.
When designing APIs, it's common to define the structure of the API in terms of specific literal values. For instance, an operation might return a specific integer status code, or a model member might be one of a few specific string values. It's also useful to pass specific literal values to decorators. TypeSpec supports string, number, and boolean literal values to cater to these needs.
## String literals
String literals can be represented using double quotes `"`
String literals are represented using double quotes `"`.
```typespec
alias Str = "Hello World!";
```
## Multi line string literals
## Multi-line string literals
A multi string literal is represented using a set of 3 double quotes `"""`.
Multi-line string literals are denoted using three double quotes `"""`.
```typespec
alias Str = """
@ -27,14 +27,14 @@ This is a multi line string
""";
```
- Opening `"""` must be followed by a new line.
- Closing `"""` must be preceded by a new line.
- The opening `"""` must be followed by a new line.
- The closing `"""` must be preceded by a new line.
### Multi line string indentation trimming
### Trimming indentation in multi-line strings
Multi lines automatically remove leading whitespaces of each line aligned with the closing `"""`. This is particularly useful to keep multi line string indented with the code and not have to worry about unwanted indentation.
Multi-line strings automatically trim leading whitespaces on each line to align with the closing `"""`. This feature is handy for maintaining the indentation of multi-line strings within the code without worrying about undesired indentation.
All those options will produce the exact same string value `"one\ntwo"`
All the following options will yield the same string value `"one\ntwo"`.
```typespec
model MultiLineContainer {
@ -64,7 +64,7 @@ two
## String template literal
Single or multi line string literal can be interpolated using `${}`
Both single and multi-line string literals can be interpolated using `${}`.
```typespec
alias hello = "bonjour";
@ -76,11 +76,11 @@ alias Multi = """
""";
```
Any valid expression can be used in the interpolation but only other literals will result in the template literal being assignable to a `valueof string`. Any other value will be dependent on the decorator/emitter receiving it to handle.
Any valid expression can be used in the interpolation, but only other literals will result in the template literal being assignable to a `valueof string`. Any other value will depend on the decorator/emitter receiving it for handling.
## Numeric literal
Numeric literals can be declared by using the raw number
Numeric literals are declared by using the raw number.
```typespec
alias Kilo = 1000;
@ -89,7 +89,7 @@ alias PI = 3.14;
## Boolean literal
Boolean literals can be declare by using `true` or `false` keywords
Boolean literals are declared by using the `true` or `false` keywords.
```typespec
alias InTypeSpec = true;

Просмотреть файл

@ -1,9 +1,9 @@
---
id: type-relations
title: Type relations
title: Type Relations
---
# Types Relations
# Type relations
## Type hierarchy
@ -43,9 +43,9 @@ graph RL
## Model with properties
When checking if type `S` can be assigned to type `T`, if `T` is a model with properties, it will look for all those properties to be present inside of `S` and their type be assignable to the type of the property is T.
When determining if type `S` can be assigned to type `T`, if `T` is a model with properties, it checks whether all those properties are present in `S` and if their types can be assigned to the type of the corresponding property in `T`.
For example
For instance,
```typespec
model T {
@ -85,7 +85,7 @@ model S {
## `Record<T>`
A record is a model indexed with a string with value of T. This means that it represents a model where all properties (string keys) are assignable to the type T. You can assign a model expression where all the properties are of type T or another model that `is` also a `Record<T>`
A record is a model indexed with a string with a value of T. It represents a model where all properties (string keys) are assignable to the type T. You can assign a model expression where all the properties are of type T or another model that `is` also a `Record<T>`.
```typespec
// Represent an object where all the values are int32.
@ -120,9 +120,9 @@ model S {
}
```
#### Why is the last case not assignable to `Record<int32>`?
#### Why isn't the last case assignable to `Record<int32>`?
In this scenario
In this scenario,
```typespec
alias T = Record<int32>;
@ -132,9 +132,9 @@ model S {
}
```
The reason is `model S` here is not assignable but the model expression `{ foo: 123; bar: 456; }` is, is that model S could be extended with additional properties that could then not be compatible.
The reason why `model S` is not assignable, but the model expression `{ foo: 123; bar: 456; }` is, is because model S could be extended with additional properties that might not be compatible.
If you for example now add a new model
For instance, if you add a new model,
```typespec
model Foo is S {
@ -142,4 +142,4 @@ model Foo is S {
}
```
Now here `Foo` is assignable to `S` following the [model with property logic](#model-with-properties) and if `S` was assignable to `Record<int32>`, `Foo` would be able to be passed through as well but this is now invalid as `otherProp` is not an `int32` property.
Here, `Foo` is assignable to `S` following the [model with property logic](#model-with-properties), and if `S` was assignable to `Record<int32>`, `Foo` would also be passable. However, this is now invalid as `otherProp` is not an `int32` property.

Просмотреть файл

@ -5,24 +5,24 @@ title: Unions
# Unions
Unions describe a type that must be exactly one of the union's constituents. There is 2 types of unions:
Unions define a type that must be exactly one of several possible variants. There are two types of unions:
- union expressions
- named unions
- Union expressions
- Named unions
## Union expressions
Unnamed unions can be declared by joining the variants using the `|` operator
Unnamed unions, or union expressions, can be declared by combining the variants using the `|` operator.
```typespec
alias Breed = Beagle | GermanShepherd | GoldenRetriever;
```
Here it says that `Breed` can accept either a `Beagle`, a `GermanShepherd` or a `GoldenRetriever`.
In this example, `Breed` can be either a `Beagle`, a `GermanShepherd`, or a `GoldenRetriever`.
## Named unions
Named unions provide a way to specify a name for the union as well as explicit variant reference. Named unions are in a way similar to [enums](./enums.md) but instead of having `string` or `numeric` values it is a [record models](./models.md)
Named unions allow you to assign a name to the union and provide explicit variant references. Named unions are somewhat similar to [enums](./enums.md), but instead of having `string` or `numeric` values, they use [record models](./models.md).
```typespec
union Breed {
@ -32,4 +32,4 @@ union Breed {
}
```
The above example is equivalent to the `Breed` alias above, except that emitters can actually see `Breed` as a named entity and also see the `beagle`, `shepherd`, and `retriever` names for the options. It also becomes possible to apply [decorators](./decorators.md) to each of the options when using this form.
The above example is equivalent to the `Breed` alias mentioned earlier, with the difference that emitters can recognize `Breed` as a named entity and also identify the `beagle`, `shepherd`, and `retriever` names for the options. This format also allows the application of [decorators](./decorators.md) to each of the options.

Просмотреть файл

@ -278,7 +278,7 @@ namespace Pets {
## Automatic visibility
The `@typespec/rest` library understands the following well-known [visibilities](../../standard-library/built-in-decorators.md#visibility-decorators) and provides functionality for emitters to apply them based on whether on request vs. response and HTTP method usage as detailed in the table below.
The `@typespec/rest` library understands the following well-known [visibilities](../../standard-library/built-in-decorators.md) and provides functionality for emitters to apply them based on whether on request vs. response and HTTP method usage as detailed in the table below.
See [handling visibility and metadata](../../extending-typespec/emitter-metadata-handling.md) for how to incorporate this into
@ -329,7 +329,7 @@ Metadata is determined to be applicable or inapplicable based on the context tha
Additionally metadata that appears in an array element type always inapplicable.
When metadata is deemed "inapplicable", for example, if a `@path` property is seen in a response, it becomes part of the payload instead unless the [@includeInapplicableMetadataInPayload](./reference/decorators.md#@TypeSpec.Rest.includeinapplicablemetadatainpayload) decorator is used and given a value of `false`.
When metadata is deemed "inapplicable", for example, if a `@path` property is seen in a response, it becomes part of the payload instead unless the [@includeInapplicableMetadataInPayload](./reference/decorators.md#@TypeSpec.Http.includeInapplicableMetadataInPayload) decorator is used and given a value of `false`.
The handling of metadata applicability furthers the goal of keeping a single logical model in TypeSpec. For example, this defines a logical `User` entity that has a name, ID and password, but further annotates that the ID is sent in the HTTP path and the HTTP body in responses. Also, using automatically visibility as before, we further indicate that the password is only present in create requests.

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше