Website & Docs Cleanup (#3002)
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:
Родитель
57f0d47bef
Коммит
d8576effd5
|
@ -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
|
# 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
|
## 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
|
```bash
|
||||||
# Create a TypeSpec library (Decorators & Linters) with TypeScript enabled.
|
# Create a TypeSpec library (Decorators & Linters) with TypeScript enabled.
|
||||||
|
@ -25,21 +25,21 @@ tsp init --template library-ts
|
||||||
tsp init --template emitter-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
|
- **dist/index.js** - The main file for your Node library
|
||||||
- **lib/main.tsp** - the main file for your TypeSpec types (optional)
|
- **lib/main.tsp** - The main file for your TypeSpec types (optional)
|
||||||
- **src/index.ts** - the main file for your Node library in TypeScript
|
- **src/index.ts** - The main file for your Node library in TypeScript
|
||||||
- **src/lib.ts** - the TypeSpec library definition file
|
- **src/lib.ts** - The file that defines your TypeSpec library
|
||||||
- **package.json** - metadata about your TypeSpec package
|
- **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 & package.json
|
### a. Initialize your package directory & package.json
|
||||||
|
|
||||||
Run the following commands:
|
Run the following commands:
|
||||||
|
|
||||||
|
@ -49,9 +49,9 @@ Run the following commands:
|
||||||
> npm init
|
> 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
|
```jsonc
|
||||||
"type": "module"
|
"type": "module"
|
||||||
|
@ -65,13 +65,13 @@ Run the following command:
|
||||||
npm install --save-peer @typespec/compiler
|
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
|
### 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
|
```jsonc
|
||||||
"main": "dist/src/index.js",
|
"main": "dist/src/index.js",
|
||||||
|
@ -87,7 +87,7 @@ npm install -D typescript
|
||||||
npx tsc --init --strict
|
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
|
```jsonc
|
||||||
"module": "Node16", // This and next setting tells TypeScript to use the new ESM import system to resolve types.
|
"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`.
|
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
|
:::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
|
```typescript
|
||||||
import { createTypeSpecLibrary } from "@typespec/compiler";
|
import { createTypeSpecLibrary } from "@typespec/compiler";
|
||||||
|
@ -116,24 +116,24 @@ export const $lib = createTypeSpecLibrary({
|
||||||
diagnostics: {},
|
diagnostics: {},
|
||||||
} as const);
|
} 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;
|
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`
|
### f. Create `index.ts`
|
||||||
|
|
||||||
Open `./src/index.ts` and import your library definition:
|
Open `./src/index.ts` and import your library definition:
|
||||||
|
|
||||||
```typescript
|
```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";
|
export { $lib } from "./lib.js";
|
||||||
```
|
```
|
||||||
|
|
||||||
### g. Build TypeScript
|
### 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:
|
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
|
### 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
|
```typespec
|
||||||
import "../dist/index.js";
|
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
|
```typespec
|
||||||
import "../dist/index.js";
|
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 `peerDependencies` for all TypeSpec libraries (and the compiler) that you use in your own library or emitter.
|
||||||
- use `devDependencies` for the other TypeSpec libraries used only in tests
|
- Use `devDependencies` for other TypeSpec libraries that are only used in tests.
|
||||||
- use `dependencies`/`devDependencies` for any other packages depending if using in library code or in test/dev scripts
|
- 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**
|
**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
|
"yaml": "~2.3.1", // This is a regular package this library/emitter will use
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"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/compiler": "~0.43.0",
|
||||||
"@typespec/http": "~0.43.1",
|
"@typespec/http": "~0.43.1",
|
||||||
"@typespec/openapi": "~0.43.0",
|
"@typespec/openapi": "~0.43.0",
|
||||||
|
@ -196,19 +196,19 @@ TypeSpec libraries are defined using `peerDependencies` so we don't end-up with
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
// This TypeSpec library is only used in the tests but is not required to use this library.
|
// This TypeSpec library is only used in the tests but is not required to use this library.
|
||||||
"@typespec/versioning": "~0.43.0",
|
"@typespec/versioning": "~0.43.0",
|
||||||
// Typescript is only used during development
|
// TypeScript is only used during development
|
||||||
"typescript": "~5.0.2",
|
"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
|
### a. Add devDependencies
|
||||||
|
|
||||||
Verify that you have the following in your `package.json`:
|
Ensure that you have the following in your `package.json`:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -233,7 +233,7 @@ export default defineConfig({
|
||||||
|
|
||||||
### b. Define the testing library
|
### 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
|
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:
|
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.
|
- `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)
|
Create a new file `test/test-host.js` (change `test` to be your test folder)
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { createTestHost, createTestWrapper } from "@typespec/compiler/testing";
|
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";
|
import { MyTestLibrary } from "../src/testing/index.js";
|
||||||
|
|
||||||
export async function createMyTestHost() {
|
export async function createMyTestHost() {
|
||||||
|
@ -340,7 +340,7 @@ describe("my library", () => {
|
||||||
#### e. `@test` decorator
|
#### e. `@test` decorator
|
||||||
|
|
||||||
The `@test` decorator is a decorator loaded in the test environment. It can be used to collect any decorable type.
|
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
|
```ts
|
||||||
const { Foo, CustomName } = await runner.compile(`
|
const { Foo, CustomName } = await runner.compile(`
|
||||||
|
@ -357,23 +357,23 @@ CustomName; // type of : Bar.name
|
||||||
|
|
||||||
#### f. Install vscode extension for the test framework
|
#### 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
|
```bash
|
||||||
npm install $packageName
|
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
|
```typespec
|
||||||
import "MyLibrary";
|
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
|
## 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.
|
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.
|
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
|
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)
|
1. [(Optional) Declare the decorator signature in TypeSpec](#declare-the-decorator-signature)
|
||||||
2. [Implement the decorator in Javascript](#implement-the-decorator-in-js)
|
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
|
- It enables type checking for the parameters
|
||||||
- IDE IntelliSense
|
- 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
|
```typespec
|
||||||
extern dec logType(target: unknown, name: string);
|
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
|
```typespec
|
||||||
using TypeSpec.Reflection;
|
using TypeSpec.Reflection;
|
||||||
|
@ -37,31 +37,30 @@ extern dec track(target: Model | Enum);
|
||||||
|
|
||||||
### Optional parameters
|
### Optional parameters
|
||||||
|
|
||||||
A decorator parameter can be marked optional using `?`
|
You can mark a decorator parameter as optional using `?`.
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
extern dec track(target: Model | Enum, name?: valueof string);
|
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
|
```typespec
|
||||||
extern dec track(target: Model | Enum, ...names: valueof string[]);
|
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.
|
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.
|
||||||
Instead the decorator can use `valueof <T>` to specify that it is expecting a value of that kind.
|
|
||||||
|
|
||||||
| Example | Description |
|
| Example | Description |
|
||||||
| ----------------- | ---------------- |
|
| ----------------- | ----------------- |
|
||||||
| `valueof string` | Expect a string |
|
| `valueof string` | Expects a string |
|
||||||
| `valueof float64` | Expect a float |
|
| `valueof float64` | Expects a float |
|
||||||
| `valueof int32` | Expect a number |
|
| `valueof int32` | Expects a number |
|
||||||
| `valueof boolean` | Expect a boolean |
|
| `valueof boolean` | Expects a boolean |
|
||||||
|
|
||||||
```tsp
|
```tsp
|
||||||
extern dec tag(target: unknown, value: valueof string);
|
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")
|
@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:
|
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
|
```ts
|
||||||
// model.js
|
// 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
|
```typespec
|
||||||
// main.tsp
|
// main.tsp
|
||||||
|
@ -114,7 +113,7 @@ model Dog {
|
||||||
|
|
||||||
### Decorator parameter marshalling
|
### 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 |
|
| 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 numeric` | `number` |
|
||||||
| `valueof boolean` | `boolean` |
|
| `valueof boolean` | `boolean` |
|
||||||
|
|
||||||
for all the other types they are not transformed.
|
For all other types, they are not transformed.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
@ -130,7 +129,7 @@ Example:
|
||||||
export function $tag(
|
export function $tag(
|
||||||
context: DecoratorContext,
|
context: DecoratorContext,
|
||||||
target: Type,
|
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
|
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
|
```tsp
|
||||||
extern dec doc(target: unknown, name: valueof string);
|
extern dec doc(target: unknown, name: valueof string);
|
||||||
|
|
||||||
|
|
||||||
alias world = "world!";
|
alias world = "world!";
|
||||||
@doc("Hello ${world} ") // receive: "Hello world!"
|
@doc("Hello ${world} ") // receive: "Hello world!"
|
||||||
@doc("Hello ${123} ") // receive: "Hello 123"
|
@doc("Hello ${123} ") // receive: "Hello 123"
|
||||||
@doc("Hello ${true} ") // receive: "Hello true"
|
@doc("Hello ${true} ") // receive: "Hello true"
|
||||||
|
|
||||||
model Bar {}
|
model Bar {}
|
||||||
@doc("Hello ${Bar} ") // not called error
|
@doc("Hello ${Bar} ") // not called error
|
||||||
^ String template cannot be serialized as a string.
|
^ String template cannot be serialized as a string.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Typescript type Reference
|
#### Typescript type Reference
|
||||||
|
@ -168,7 +163,7 @@ model Bar {}
|
||||||
|
|
||||||
### Adding metadata with decorators
|
### 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.
|
❌ Do not save the data in a global variable.
|
||||||
|
|
||||||
|
@ -200,7 +195,7 @@ export const StateKeys = $lib.stateKeys;
|
||||||
|
|
||||||
### Reporting diagnostic on decorator or arguments
|
### Reporting diagnostic on decorator or arguments
|
||||||
|
|
||||||
Decorator context provide the `decoratorTarget` and `getArgumentTarget` helpers
|
The decorator context provides the `decoratorTarget` and `getArgumentTarget` helpers.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import type { DecoratorContext, Type } from "@typespec/compiler";
|
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
|
```typespec
|
||||||
import "./lib.js";
|
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
|
```ts
|
||||||
export function $customName(context: DecoratorContext, name: string) {}
|
export function $customName(context: DecoratorContext, name: string) {}
|
||||||
|
@ -242,12 +237,12 @@ setTypeSpecNamespace("MyLib", $tableName);
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
### Extern declation must have an implementation in JS file
|
### Extern declaration must have an implementation in JS file
|
||||||
|
|
||||||
Potential issues:
|
Potential issues:
|
||||||
|
|
||||||
- JS function is not prefixed with `$`. For a decorator called `@decorate` the JS function must be called `$decoratate`
|
- The JS function is not prefixed with `$`. For a decorator called `@decorate`, the JS function must be called `$decorate`.
|
||||||
- JS function is not in the same namespace as the the `extern dec`
|
- The JS function is not in the same namespace as the `extern dec`.
|
||||||
- Error is only showing in the IDE? Restart the TypeSpec server or the IDE.
|
- 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
|
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
|
## 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.
|
- ❌ Avoid using `throw` to report errors. Any exceptions thrown in this manner will be perceived as bugs in your library by the user.
|
||||||
- ✅ Use diagnostic API to report expected errors and warnings.
|
- ✅ Utilize the diagnostic API to report anticipated errors and warnings.
|
||||||
- ✅ Use `reportDiagnostic` in a decorator, `$onValidate` or `$onEmit`
|
- ✅ Employ `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)
|
- ❌ 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 `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`, `warning`. Errors cannot be suppressed
|
- Each diagnostic MUST have a `severity`. It can be `error` or `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 diagnostic MUST have at least one message. Using `default` as the `messageId` will make it the default selection.
|
||||||
- Each diagnostics message MAY have parameters to interpolate information into the message
|
- 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
|
```ts
|
||||||
import { createTypeSpecLibrary } from "@typespec/compiler";
|
import { createTypeSpecLibrary } from "@typespec/compiler";
|
||||||
|
@ -56,11 +56,11 @@ export const $lib = createTypeSpecLibrary({
|
||||||
},
|
},
|
||||||
} as const);
|
} 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 };
|
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/no-array`
|
||||||
- `@typespec/my-lib/duplicate-route`
|
- `@typespec/my-lib/duplicate-route`
|
||||||
|
@ -87,7 +87,7 @@ reportDiagnostic(program, {
|
||||||
// Multiple messages
|
// Multiple messages
|
||||||
reportDiagnostic(program, {
|
reportDiagnostic(program, {
|
||||||
code: "duplicate-name",
|
code: "duplicate-name",
|
||||||
messageId: "parmaeter",
|
messageId: "parameter",
|
||||||
format: {value: "$select"},
|
format: {value: "$select"},
|
||||||
target: diagnosticTarget,
|
target: diagnosticTarget,
|
||||||
});
|
});
|
||||||
|
@ -95,8 +95,8 @@ reportDiagnostic(program, {
|
||||||
|
|
||||||
### Collect diagnostics
|
### 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.
|
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 prevent duplicate diagnostics emitter if the accessor is called multiple times.
|
This prevents duplicate diagnostics emitter if the accessor is called multiple times.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { createDiagnosticCollector, Diagnostic } from "@typespec/compiler";
|
import { createDiagnosticCollector, Diagnostic } from "@typespec/compiler";
|
||||||
|
|
|
@ -3,28 +3,32 @@ id: emitter-framework
|
||||||
title: 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.
|
## Creating your own emitter
|
||||||
- `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.
|
|
||||||
|
|
||||||
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>`
|
### `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
|
```typescript
|
||||||
export async function $onEmit(context: EmitContext) {
|
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.
|
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
|
```typescript
|
||||||
class MyCodeEmitter extends CodeTypeEmitter {
|
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
|
#### EmitterOutput
|
||||||
|
|
||||||
Most methods of the `TypeEmitter` must either return `T` or an `EmitterOutput<T>`. There are four kinds of `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.
|
- `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.
|
- `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.
|
- `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
|
#### 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.
|
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`
|
### 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
|
```typescript
|
||||||
class MyTsEmitter extends TypeScriptEmitter {
|
class MyTsEmitter extends TypeScriptEmitter {
|
||||||
|
@ -357,3 +361,5 @@ class MyTsEmitter extends TypeScriptEmitter {
|
||||||
// and similar for other declarations: Unions, Enums, Interfaces, and Operations.
|
// 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
|
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
|
## 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
|
## 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.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
|
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
|
```bash
|
||||||
tsp init --template emitter-ts
|
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
|
## $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
|
- _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
|
```typescript
|
||||||
import { EmitContext, emitFile, resolvePath } from "@typespec/compiler";
|
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
|
### 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:
|
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
|
## 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 [emitter framework](./emitter-framework.md), which simplifies the process of emitting all your TypeSpec types (or a subset, if you prefer).
|
||||||
1. The Semantic Walker, which lets you easily run code for every type in the program
|
2. The Semantic Walker, which allows you to easily execute 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.
|
3. Custom traversal, which offers more flexibility than the previous two methods, albeit with some added complexity.
|
||||||
|
|
||||||
### Emitter Framework
|
### 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
|
### 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
|
```typescript
|
||||||
navigateProgram(program, {
|
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
|
### 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.
|
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
|
### 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.
|
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.
|
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.
|
1. If the scalar is a known scalar (e.g., `int32`), emit the known mapping.
|
||||||
2. Otherwise check scalar `baseScalar` and go back to `1.`
|
2. Otherwise, check the scalar `baseScalar` and go back to step 1.
|
||||||
2.1 After resolving which scalar apply any decorators
|
2.1 After resolving which scalar to use, apply any decorators.
|
||||||
|
|
||||||
:::note
|
:::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
|
### 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. |
|
| `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. |
|
| `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:
|
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
|
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
|
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.
|
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.
|
||||||
Linters need to be explicitly enabled. `$onValidate` will be 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
|
```ts
|
||||||
import { createLinterRule } from "@typespec/compiler";
|
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
|
- ❌ 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
|
## 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.
|
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.
|
||||||
You can then provide different test checking the rule pass or fails.
|
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { RuleTester, createLinterRuleTester, createTestRunner } from "@typespec/compiler/testing";
|
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.
|
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.
|
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.
|
||||||
Then calling `toEqual` with the expected code after the codefix is applied.
|
|
||||||
|
|
||||||
:::note
|
:::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
|
```ts
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
title: Scaffolding templates
|
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
|
```bash
|
||||||
tsp init <templateUrl>
|
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
|
```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.
|
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.
|
||||||
The root of the document is a dictionary allowing multiple templates to be hosted at the same location.
|
|
||||||
|
|
||||||
Each template needs at the minimum:
|
Each template must include:
|
||||||
|
|
||||||
- key: Key of the template
|
- key: The template's key
|
||||||
- title: Human readable name of the template
|
- title: A user-friendly name for the template
|
||||||
- description: Extended description of the template.
|
- description: A detailed description of the template.
|
||||||
|
|
||||||
Example:
|
Here's an example:
|
||||||
|
|
||||||
```json
|
```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
|
```json
|
||||||
{
|
{
|
||||||
"rest": {
|
"rest": {
|
||||||
"title": "REST API",
|
"title": "REST API",
|
||||||
"description": "Create a new project representing a REST API",
|
"description": "Create a new project representing a REST API",
|
||||||
"libraries": ["@typespec/rest", "@typespec/openapi3"]
|
"libraries": ["/rest", "@typespec/openapi3"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Adding new files
|
## 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.
|
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:
|
||||||
Each file need the following properties:
|
|
||||||
|
|
||||||
- `path`: Absolute or relative path(to the template file) to the file
|
- `path`: The absolute or relative path (relative to the template file) to the file
|
||||||
- `destination`: Relative path of the file relative to the project root.
|
- `destination`: The file's relative path, relative to the project root.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
@ -90,32 +88,30 @@ model {{parameters.ModelName}} {
|
||||||
|
|
||||||
### Interpolating values
|
### Interpolating values
|
||||||
|
|
||||||
The template can interpolate values in the files. The values available are anything available in the template configuration referenced as it is.
|
The template can interpolate values in the files. The available values are anything in the template configuration, referenced as is. For example:
|
||||||
Examples:
|
|
||||||
|
|
||||||
- Reference a parameter `{{parameters.ModelName}}`
|
- To reference a parameter, use `{{parameters.ModelName}}`
|
||||||
- Reference a the template title `{{title}}`
|
- 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 |
|
| Name | Description |
|
||||||
| ------------------------------------- | --------------------------------------------------------------- |
|
| ------------------------------------- | --------------------------------------------------------------------------------- |
|
||||||
| `directory` | Directory full path where the project should be initialized. |
|
| `directory` | The full directory path where the project should be initialized. |
|
||||||
| `folderName` | Folder name where the project should be initialized. |
|
| `folderName` | The name of the folder where the project should be initialized. |
|
||||||
| `name` | Name of the project. |
|
| `name` | The name of the project. |
|
||||||
| `libraries` | List of libraries to include |
|
| `libraries` | The list of libraries to include. |
|
||||||
| `templateUri` | Path where this template was loaded from. |
|
| `templateUri` | The path from where this template was loaded. |
|
||||||
| Functions | |
|
| Functions | |
|
||||||
| `toLowerCase(value: string)` | Convert string to lower case |
|
| `toLowerCase(value: string)` | Converts a string to lower case. |
|
||||||
| `normalizePackageName(value: string)` | Normalize package name. It replaces `.` with`-` and toLowerCase |
|
| `normalizePackageName(value: string)` | Normalizes the package name. It replaces `.` with `-` and converts to lower case. |
|
||||||
| `casing.pascalCase(value: string)` | Convert string to PascalCase |
|
| `casing.pascalCase(value: string)` | Converts a string to PascalCase. |
|
||||||
| `casing.camelCase(value: string)` | Convert string to camelCase |
|
| `casing.camelCase(value: string)` | Converts a string to camelCase. |
|
||||||
| `casing.kebabCase(value: string)` | Convert string to kebab-case |
|
| `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.
|
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.
|
||||||
The template takes in a map of inputs that will get prompted to the user during initialization.
|
|
||||||
|
|
||||||
```json
|
```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
|
title: Getting Started
|
||||||
---
|
---
|
||||||
|
|
||||||
# Getting Started with TypeSpec
|
# Getting started with TypeSpec
|
||||||
|
|
||||||
- [Get started with HTTP in TypeSpec](./getting-started-http.md)
|
- [Get started with HTTP in TypeSpec](./getting-started-http.md)
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
title: Configuration
|
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
|
## 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
|
```yaml
|
||||||
options:
|
options:
|
||||||
|
@ -72,7 +72,7 @@ emit:
|
||||||
|
|
||||||
### Variable interpolation
|
### Variable interpolation
|
||||||
|
|
||||||
The TypeSpec project file provide variable interpolation using:
|
The TypeSpec project file provides variable interpolation using:
|
||||||
|
|
||||||
- built-in variables
|
- built-in variables
|
||||||
- environment variables
|
- environment variables
|
||||||
|
@ -86,14 +86,14 @@ Examples:
|
||||||
- `{output-dir}/my-path`
|
- `{output-dir}/my-path`
|
||||||
- `{env.SHARED_PATH}/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`
|
For instance, consider the following config value: `{service-name}/output.{version}.json`
|
||||||
The following would get produced
|
Here's what would be produced:
|
||||||
|
|
||||||
| Service name value | Version value | Result |
|
| 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) |
|
| `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 |
|
| `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:**
|
**Example:**
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -130,7 +130,7 @@ parameters:
|
||||||
output-dir: {base-dir}/output
|
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
|
```bash
|
||||||
tsp compile . --arg "base-dir=/path/to/base"
|
tsp compile . --arg "base-dir=/path/to/base"
|
||||||
|
@ -138,13 +138,13 @@ tsp compile . --arg "base-dir=/path/to/base"
|
||||||
|
|
||||||
#### Environment variables
|
#### 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:**
|
**Example:**
|
||||||
|
|
||||||
|
@ -156,11 +156,11 @@ environment-variables:
|
||||||
output-dir: {env.BASE_DIR}/output
|
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
|
```yaml
|
||||||
options:
|
options:
|
||||||
|
@ -185,7 +185,7 @@ options:
|
||||||
|
|
||||||
### `output-dir` - Configure the default output dir
|
### `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
|
```yaml
|
||||||
output-dir: {cwd}/typespec-build
|
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.
|
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
|
### `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
|
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
|
```yaml
|
||||||
warn-as-error: true
|
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}`
|
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
|
```yaml
|
||||||
linter:
|
linter:
|
||||||
|
@ -325,13 +325,13 @@ linter:
|
||||||
"@typespec/best-practices/no-y": "This rule cannot be applied in this project because X"
|
"@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`
|
### `--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
|
```yaml
|
||||||
tsp compile . --no-emit
|
tsp compile . --no-emit
|
||||||
|
@ -385,22 +385,20 @@ Enable/Disable pretty logging (colors, diagnostic preview, etc.).
|
||||||
tsp compile . --pretty=false
|
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.
|
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:
|
||||||
By default the output-dir of an emitter is set to this value:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
{output-dir}/{emitter-name}
|
{output-dir}/{emitter-name}
|
||||||
```
|
```
|
||||||
|
|
||||||
where
|
where:
|
||||||
|
|
||||||
- `output-dir` is the compiler common `output-dir` that can be configured via `--output-dir`
|
- `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 `@typespec/openapi3`)
|
- `emitter-name` is the name of the emitter package (for example, `/openapi3`).
|
||||||
|
|
||||||
Example:
|
For instance, if the emitters `@typespec/openapi3` and `@typespec/jsonschema` are given, the default output folder structure would be:
|
||||||
Given the following emitters: `@typespec/openapi3` and `@typespec/jsonschema`, the default output folder structure would be
|
|
||||||
|
|
||||||
```
|
```
|
||||||
{project-root}/tsp-output:
|
{project-root}/tsp-output:
|
||||||
|
@ -411,7 +409,7 @@ Given the following emitters: `@typespec/openapi3` and `@typespec/jsonschema`, t
|
||||||
... json schema files ...
|
... 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
|
--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 ...
|
... openapi3 files ...
|
||||||
jsonschema
|
jsonschema
|
||||||
... json schema files ...
|
... 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"
|
--option "@typespec/openapi3.output-dir={projectroot}/openapispec"
|
||||||
|
|
|
@ -4,23 +4,23 @@ title: Tracing
|
||||||
|
|
||||||
# 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
|
```bash
|
||||||
tsp compile . --trace import-resolution
|
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
|
```bash
|
||||||
tsp compile . --trace import-resolution --trace projection
|
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
|
```yaml
|
||||||
trace: *
|
trace: *
|
||||||
|
@ -30,55 +30,54 @@ trace:
|
||||||
- projection
|
- 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.
|
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:
|
||||||
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:
|
|
||||||
|
|
||||||
- `foo` and `foo.*`
|
- `foo` and `foo.*`
|
||||||
- `one.two` and `one.two.*`
|
- `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.two.three`
|
||||||
- `one.foo`
|
- `one.foo`
|
||||||
- `bar.info`
|
- `bar.info`
|
||||||
|
|
||||||
Using:
|
You can use:
|
||||||
|
|
||||||
- `*` will log everything
|
- `*` to log everything
|
||||||
- `one` will log everything under `one`(`one.two.three`, `one.foo`)
|
- `one` to log everything under `one`(`one.two.three`, `one.foo`)
|
||||||
- `bar` will log everything under `bar`(`bar.info`)
|
- `bar` to log everything under `bar`(`bar.info`)
|
||||||
- `one.foo` will log everything under `one.foo`(`one.foo`)
|
- `one.foo` to log everything under `one.foo`(`one.foo`)
|
||||||
- `other` will log everything under `other` which is nothing here.
|
- `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 |
|
| Area | Description |
|
||||||
| ------------------------------ | -------------------------------------------------------------------- |
|
| ------------------------------ | ---------------------------------------------------------------------- |
|
||||||
| `compiler.options` | Log the resolved compiler options |
|
| `compiler.options` | Logs the resolved compiler options |
|
||||||
| `import-resolution.library` | Information related to the resolution of import libraries |
|
| `import-resolution.library` | Logs information related to the resolution of import libraries |
|
||||||
| `projection.log` | Debug information logged by the `log()` function used in projections |
|
| `projection.log` | Logs debug information from the `log()` function used in projections |
|
||||||
| `bind.js` | Information when binding JS files |
|
| `bind.js` | Logs information when binding JS files |
|
||||||
| `linter.register-library` | Information that a library rules will be loaded |
|
| `linter.register-library` | Logs information when a library's rules are being loaded |
|
||||||
| `linter.register-library.rule` | Information about a rule that is being registered |
|
| `linter.register-library.rule` | Logs 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.start` | Logs information about a ruleset that is about to be extended |
|
||||||
| `linter.extend-rule-set.end` | Information about rules enabled after extending a ruleset |
|
| `linter.extend-rule-set.end` | Logs information about rules enabled after extending a ruleset |
|
||||||
| `linter.lint` | Start the lint process and show information of all the rules enabled |
|
| `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
|
```ts
|
||||||
const tracer = program.tracer.sub("my-library");
|
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
|
```ts
|
||||||
tracer.trace("emitting-ts", "Emitting ts interface");
|
tracer.trace("emitting-ts", "Emitting ts interface");
|
||||||
|
|
|
@ -3,14 +3,13 @@ id: faq
|
||||||
title: 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.
|
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.
|
||||||
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.
|
|
||||||
|
|
||||||
### Solutions
|
### How can I fix this?
|
||||||
|
|
||||||
- Update npm `npm install -g npm`
|
- You can update npm using the command `npm install -g npm`.
|
||||||
- If you cannot update npm, then adding the dependencies to your project dependency should resolve the issue `npm install x`
|
- 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 cli](#via-the-cli)
|
||||||
- Via the vscode/vs extension
|
- Via the vscode/vs extension
|
||||||
- As a prettier plugin
|
- As a `prettier` plugin
|
||||||
|
|
||||||
## Via the cli
|
## Via the cli
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ Format all TypeSpec files:
|
||||||
tsp format "**/*.tsp"
|
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
|
```bash
|
||||||
tsp format --check "**/*.tsp"
|
tsp format --check "**/*.tsp"
|
||||||
|
@ -27,15 +27,15 @@ tsp format --check "**/*.tsp"
|
||||||
|
|
||||||
## Via the VSCode or VS extension
|
## 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
|
## 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
|
```yaml
|
||||||
plugins:
|
plugins:
|
||||||
|
|
|
@ -5,13 +5,13 @@ title: Releases
|
||||||
|
|
||||||
# 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 |
|
| Name | Changelog | Latest | Next |
|
||||||
| -------------------------------------------------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
|
| -------------------------------------------------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
|
||||||
|
@ -49,9 +49,9 @@ Every change to the `main` branch is automatically published under the npm `@nex
|
||||||
|
|
||||||
## Release cadence
|
## 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
|
## Breaking changes migration guides
|
||||||
|
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
title: Reproducibility
|
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
|
- An update to a dependency or sub-dependency introduces an unexpected breaking change.
|
||||||
2. Changes to a new version of a service shouldn't affect the older versions
|
- Updates to a new version of a service should not impact older versions.
|
||||||
3. A change to the TypeSpec spec
|
- 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
|
## 1. Defend against dependency changes
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
title: Style guide
|
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
|
:::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.
|
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
|
### 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 -->
|
<!-- prettier-ignore -->
|
||||||
```tsp
|
```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 -->
|
<!-- prettier-ignore -->
|
||||||
```tsp
|
```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:
|
Available variables:
|
||||||
|
|
||||||
|
|
|
@ -8,11 +8,11 @@ Install the extension via the Visual Studio Code extension manager https://marke
|
||||||
|
|
||||||
## Configure
|
## 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:
|
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
|
### `typespec.tsp-server.path`: Configure the server path
|
||||||
|
|
||||||
|
|
|
@ -3,13 +3,11 @@ id: aliases
|
||||||
title: 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.
|
You can define an alias using the `alias` keyword.
|
||||||
|
|
||||||
Alias can be defined using the `alias` keyword
|
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
alias Options = "one" | "two";
|
alias Options = "one" | "two";
|
||||||
|
|
|
@ -3,7 +3,7 @@ id: built-in-types
|
||||||
title: 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.
|
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
|
||||||
|
|
||||||
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
|
```typespec
|
||||||
@tag("Sample")
|
@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
|
```typespec
|
||||||
@mark
|
@mark
|
||||||
model Dog {}
|
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
|
```typespec
|
||||||
model Dog {}
|
model Dog {}
|
||||||
|
@ -42,14 +42,14 @@ model Dog {}
|
||||||
@@tag(Dog, "Sample");
|
@@tag(Dog, "Sample");
|
||||||
```
|
```
|
||||||
|
|
||||||
Which is equivalent to
|
This is equivalent to:
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
@tag("Sample")
|
@tag("Sample")
|
||||||
model Dog {}
|
model Dog {}
|
||||||
```
|
```
|
||||||
|
|
||||||
Example: Decorate a model property
|
Example: decorating a model property
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
model Dog {
|
model Dog {
|
||||||
|
@ -59,6 +59,6 @@ model Dog {
|
||||||
@@readOnly(Dog.name);
|
@@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
|
||||||
|
|
||||||
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` decorator
|
||||||
- `/** */` Doc comments
|
- `/** */` 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
|
```typespec
|
||||||
@doc("This is a sample model")
|
@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
|
```typespec
|
||||||
@doc("Templated {name}", Type)
|
@doc("Templated {name}", Type)
|
||||||
model Template<Type extends {}> {
|
model Template<Type extends {}> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// doc will read "Templated A"
|
// The documentation will read "Templated A"
|
||||||
model A is Template<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
|
```typespec
|
||||||
/**
|
/**
|
||||||
|
@ -64,11 +64,11 @@ op read(
|
||||||
): Widget | Error;
|
): 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
|
### 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 |
|
| 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
|
||||||
|
|
||||||
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.
|
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.
|
||||||
The enums members are comma `,` separated and can be TypeSpec `identifier`s or `string literal`s.
|
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
enum Direction {
|
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
|
```typespec
|
||||||
enum Direction {
|
enum Direction {
|
||||||
|
@ -36,7 +35,7 @@ enum Direction {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Values can also be integers.
|
These values can also be integers.
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
enum Foo {
|
enum Foo {
|
||||||
|
@ -47,7 +46,7 @@ enum Foo {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
or float
|
Or even floating-point numbers.
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
enum Hour {
|
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
|
```typespec
|
||||||
enum DirectionExt {
|
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
|
```typespec
|
||||||
alias North = Direction.North;
|
alias North = Direction.North;
|
||||||
|
|
|
@ -3,30 +3,30 @@ id: imports
|
||||||
title: 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
|
```typespec
|
||||||
import "./models/foo.tsp";
|
import "./models/foo.tsp";
|
||||||
```
|
```
|
||||||
|
|
||||||
## Import Js file
|
## Importing a JavaScript file
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
import "./decorators.js";
|
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
|
```typespec
|
||||||
import "@typespec/rest";
|
import "/rest";
|
||||||
```
|
```
|
||||||
|
|
||||||
```json
|
```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
|
```typespec
|
||||||
import "./models"; // same as `import "./models/main.tsp";
|
import "./models"; // equivalent to `import "./models/main.tsp";
|
||||||
```
|
```
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
|
|
|
@ -5,9 +5,9 @@ title: Interfaces
|
||||||
|
|
||||||
# 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
|
```typespec
|
||||||
interface SampleInterface {
|
interface SampleInterface {
|
||||||
|
@ -18,9 +18,9 @@ interface SampleInterface {
|
||||||
|
|
||||||
## Composing interfaces
|
## 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
|
```typespec
|
||||||
interface A {
|
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
|
```typespec
|
||||||
interface C extends A, B {
|
interface C extends A, B {
|
||||||
|
@ -40,7 +40,7 @@ interface C extends A, B {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
which is equivalent to
|
This is equivalent to:
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
interface C {
|
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
|
```typespec
|
||||||
interface ReadWrite<T> {
|
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
|
```typespec
|
||||||
interface ReadWrite<T> {
|
interface ReadWrite<T> {
|
||||||
|
@ -77,7 +77,7 @@ op myWrite is MyReadWrite.write<int32>;
|
||||||
```
|
```
|
||||||
|
|
||||||
:::caution
|
:::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.
|
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;
|
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
|
```typespec
|
||||||
alias MyReadWrite = ReadWrite<string>;
|
alias MyReadWrite = ReadWrite<string>;
|
||||||
|
|
|
@ -5,13 +5,13 @@ title: Intersections
|
||||||
|
|
||||||
# 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
|
```typespec
|
||||||
alias Dog = Animal & Pet;
|
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
|
```typespec
|
||||||
alias Dog = {
|
alias Dog = {
|
||||||
|
|
|
@ -5,22 +5,22 @@ title: Models
|
||||||
|
|
||||||
# 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)
|
- [Record](#record)
|
||||||
- [Array](#array)
|
- [Array](#array)
|
||||||
|
|
||||||
### Record
|
### 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`.
|
- The name can be an `identifier` or `string literal`.
|
||||||
- type can be any type reference
|
- The type can be any type reference.
|
||||||
- properties are ordered. See [ordering of properties](#ordering-of-properties)
|
- Properties are arranged in a specific order. Refer to [property ordering](#property-ordering) for more details.
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
model Dog {
|
model Dog {
|
||||||
|
@ -31,7 +31,7 @@ model Dog {
|
||||||
|
|
||||||
#### Optional properties
|
#### Optional properties
|
||||||
|
|
||||||
Properties can be marked as optional using the `?` punctuation.
|
Properties can be designated as optional by using the `?` symbol.
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
model Dog {
|
model Dog {
|
||||||
|
@ -41,7 +41,7 @@ model Dog {
|
||||||
|
|
||||||
#### Default values
|
#### 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
|
```typespec
|
||||||
model Dog {
|
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:
|
Example:
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ model Cat is Pet {
|
||||||
furColor: string;
|
furColor: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resulting property order for cat:
|
// The resulting property order for Cat is:
|
||||||
// name, age, meow, address, furColor
|
// 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.
|
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 the `...` operator
|
||||||
- Using `is` operator
|
- Using the `is` operator
|
||||||
- Using `extends` 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.
|
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.
|
||||||
This means that the property in the model could be of a different and incompatible type with the Record value type.
|
|
||||||
|
|
||||||
```tsp
|
```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 {
|
model Person {
|
||||||
age: int32;
|
age: int32;
|
||||||
...Record<string>;
|
...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
|
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.
|
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.
|
||||||
|
|
||||||
In many languages this would probably result in the same emitted code as `is` and is recommended to just use `is Record<T>` instead.
|
|
||||||
|
|
||||||
```tsp
|
```tsp
|
||||||
model Person extends Record<string> {
|
model Person extends Record<string> {
|
||||||
|
@ -135,7 +132,7 @@ model Person extends Record<string> {
|
||||||
|
|
||||||
#### `never`
|
#### `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.
|
This can be useful in a model template to omit a property.
|
||||||
|
|
||||||
|
@ -150,18 +147,18 @@ model UKAddress is Address<never>;
|
||||||
```
|
```
|
||||||
|
|
||||||
:::note
|
:::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
|
||||||
|
|
||||||
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
|
## Model composition
|
||||||
|
|
||||||
### Spread
|
### 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
|
```typespec
|
||||||
model Animal {
|
model Animal {
|
||||||
|
@ -177,7 +174,7 @@ model Dog {
|
||||||
...Pet;
|
...Pet;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dog is equivalent to the following declaration:
|
// The Dog model is equivalent to the following declaration:
|
||||||
model Dog {
|
model Dog {
|
||||||
species: string;
|
species: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -186,7 +183,7 @@ model Dog {
|
||||||
|
|
||||||
### Extends
|
### 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
|
```typespec
|
||||||
model Animal {
|
model Animal {
|
||||||
|
@ -198,7 +195,7 @@ model Dog extends Animal {}
|
||||||
|
|
||||||
### Is
|
### 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
|
```typespec
|
||||||
@decorator
|
@decorator
|
||||||
|
@ -208,7 +205,7 @@ model Thing<T> {
|
||||||
|
|
||||||
model StringThing is Thing<string>;
|
model StringThing is Thing<string>;
|
||||||
|
|
||||||
// StringThing declaration is equivalent to the following declaration:
|
// The StringThing declaration is equivalent to the following declaration:
|
||||||
@decorator
|
@decorator
|
||||||
model StringThing {
|
model StringThing {
|
||||||
property: string;
|
property: string;
|
||||||
|
@ -217,7 +214,7 @@ model StringThing {
|
||||||
|
|
||||||
## Model templates
|
## Model templates
|
||||||
|
|
||||||
[See templates](./templates.md) for details on templates
|
Refer to [templates](./templates.md) for more details on templates.
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
model Page<Item> {
|
model Page<Item> {
|
||||||
|
@ -232,7 +229,7 @@ model DogPage {
|
||||||
|
|
||||||
## Meta type references
|
## Meta type references
|
||||||
|
|
||||||
Some model property meta types can be referenced using `::`
|
Some model property meta types can be referenced using `::`.
|
||||||
|
|
||||||
| Name | Example | Description |
|
| Name | Example | Description |
|
||||||
| ---- | ---------------- | ---------------------------------------- |
|
| ---- | ---------------- | ---------------------------------------- |
|
||||||
|
|
|
@ -5,11 +5,11 @@ title: Namespaces
|
||||||
|
|
||||||
# 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
|
## Basics
|
||||||
|
|
||||||
Create a namespace with the `namespace` keyword.
|
You can create a namespace using the `namespace` keyword.
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
namespace SampleNamespace {
|
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
|
```typespec
|
||||||
model Foo {
|
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
|
```typespec
|
||||||
namespace Foo {
|
namespace Foo {
|
||||||
|
@ -41,7 +41,7 @@ namespace Foo {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
or this can be simplified using `.` notation
|
Alternatively, you can simplify this using `.` notation:
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
namespace Foo.Bar.Baz {
|
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
|
```typespec
|
||||||
model A {
|
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
|
```typespec
|
||||||
namespace SampleNamespace;
|
namespace SampleNamespace;
|
||||||
|
@ -67,11 +67,11 @@ namespace SampleNamespace;
|
||||||
model SampleModel {}
|
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
|
```typespec
|
||||||
using SampleNamespace;
|
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
|
```typespec
|
||||||
namespace One {
|
namespace One {
|
||||||
|
@ -90,9 +90,9 @@ namespace One {
|
||||||
|
|
||||||
namespace Two {
|
namespace Two {
|
||||||
using One;
|
using One;
|
||||||
alias B = A; // ok
|
alias B = A; // This is valid
|
||||||
}
|
}
|
||||||
|
|
||||||
alias C = One.A; // not ok
|
alias C = One.A; // This is not valid
|
||||||
alias C = Two.B; // ok
|
alias C = Two.B; // This is valid
|
||||||
```
|
```
|
||||||
|
|
|
@ -5,9 +5,9 @@ title: Operations
|
||||||
|
|
||||||
# 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
|
```typespec
|
||||||
op ping(): void;
|
op ping(): void;
|
||||||
|
@ -15,7 +15,7 @@ op ping(): void;
|
||||||
|
|
||||||
## Parameters
|
## 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
|
```typespec
|
||||||
op feedDog(...CommonParams, name: string): void;
|
op feedDog(...CommonParams, name: string): void;
|
||||||
|
@ -23,7 +23,7 @@ op feedDog(...CommonParams, name: string): void;
|
||||||
|
|
||||||
## Return type
|
## 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
|
```typespec
|
||||||
model DogNotFound {
|
model DogNotFound {
|
||||||
|
@ -33,33 +33,33 @@ model DogNotFound {
|
||||||
op getDog(name: string): Dog | 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
|
```typespec
|
||||||
op Delete(id: string): void;
|
op Delete(id: string): void;
|
||||||
```
|
```
|
||||||
|
|
||||||
its signature can be reused like this:
|
You can reuse its signature like so:
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
op deletePet is Delete;
|
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
|
```typespec
|
||||||
op ReadResource<T>(id: string): T;
|
op ReadResource<T>(id: string): T;
|
||||||
```
|
```
|
||||||
|
|
||||||
The operation template can then be referenced via `is`:
|
You can reference the operation template using `is`:
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
op readPet is ReadResource<Pet>;
|
op readPet is ReadResource<Pet>;
|
||||||
|
@ -67,7 +67,7 @@ op readPet is ReadResource<Pet>;
|
||||||
|
|
||||||
## Referencing model properties
|
## Referencing model properties
|
||||||
|
|
||||||
Model properties can be referenced using the `.` operator for identifiers.
|
You can reference model properties using the `.` operator for identifiers.
|
||||||
|
|
||||||
```tsp
|
```tsp
|
||||||
alias PetName = Pet.name;
|
alias PetName = Pet.name;
|
||||||
|
@ -75,9 +75,9 @@ alias PetName = Pet.name;
|
||||||
|
|
||||||
## Meta type references
|
## Meta type references
|
||||||
|
|
||||||
Some operation meta types can be referenced using `::`
|
Certain operation meta types can be referenced using `::`
|
||||||
|
|
||||||
| Name | Example | Description |
|
| Name | Example | Description |
|
||||||
| ---------- | --------------------- | ----------------------------------------- |
|
| ---------- | --------------------- | ------------------------------------------ |
|
||||||
| parameters | `readPet::parameters` | Reference the parameters model expression |
|
| parameters | `readPet::parameters` | References the parameters model expression |
|
||||||
| returnType | `readPet::returnType` | Reference the operation return type |
|
| returnType | `readPet::returnType` | References the operation return type |
|
||||||
|
|
|
@ -5,20 +5,20 @@ title: Overview
|
||||||
|
|
||||||
# Language 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
|
## 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 -->
|
<!-- prettier-ignore -->
|
||||||
```typespec
|
```typespec
|
||||||
model Dog {}
|
model Dog {}
|
||||||
namespace Dog {}
|
namespace Dog {}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Imports
|
## Imports
|
||||||
|
|
||||||
_Details: [Imports](./imports.md)_
|
_For more details, see: [Imports](./imports.md)_
|
||||||
|
|
||||||
| Feature | Example |
|
| Feature | Example |
|
||||||
| -------------------- | ------------------------- |
|
| -------------------- | ------------------------- |
|
||||||
|
@ -28,7 +28,7 @@ _Details: [Imports](./imports.md)_
|
||||||
|
|
||||||
## Namespaces
|
## Namespaces
|
||||||
|
|
||||||
_Details: [Namespaces](./namespaces.md)_
|
_For more details, see: [Namespaces](./namespaces.md)_
|
||||||
|
|
||||||
| Feature | Example |
|
| Feature | Example |
|
||||||
| ----------------- | ---------------------------- |
|
| ----------------- | ---------------------------- |
|
||||||
|
@ -39,7 +39,7 @@ _Details: [Namespaces](./namespaces.md)_
|
||||||
|
|
||||||
## Decorators
|
## Decorators
|
||||||
|
|
||||||
_Details: [Decorators](./decorators.md)_
|
_For more details, see: [Decorators](./decorators.md)_
|
||||||
|
|
||||||
| Feature | Example |
|
| Feature | Example |
|
||||||
| ---------------------------- | ----------------------------------------------------------------------------------- |
|
| ---------------------------- | ----------------------------------------------------------------------------------- |
|
||||||
|
@ -51,7 +51,7 @@ _Details: [Decorators](./decorators.md)_
|
||||||
|
|
||||||
## Scalars
|
## Scalars
|
||||||
|
|
||||||
_Details: [Scalars](./scalars.md)_
|
_For more details, see: [Scalars](./scalars.md)_
|
||||||
|
|
||||||
| Feature | Example |
|
| Feature | Example |
|
||||||
| ------------------ | ------------------------------------------- |
|
| ------------------ | ------------------------------------------- |
|
||||||
|
@ -61,7 +61,7 @@ _Details: [Scalars](./scalars.md)_
|
||||||
|
|
||||||
## Models
|
## Models
|
||||||
|
|
||||||
_Details: [Models](./models.md)_
|
_For more details, see: [Models](./models.md)_
|
||||||
|
|
||||||
| Feature | Example |
|
| Feature | Example |
|
||||||
| ------------------------------ | ------------------------------------- |
|
| ------------------------------ | ------------------------------------- |
|
||||||
|
@ -76,7 +76,7 @@ _Details: [Models](./models.md)_
|
||||||
|
|
||||||
## Operations
|
## Operations
|
||||||
|
|
||||||
_Details: [Operations](./operations.md)_
|
_For more details, see: [Operations](./operations.md)_
|
||||||
|
|
||||||
| Feature | Example |
|
| Feature | Example |
|
||||||
| ----------------------------- | ------------------------------------------------ |
|
| ----------------------------- | ------------------------------------------------ |
|
||||||
|
@ -89,7 +89,7 @@ _Details: [Operations](./operations.md)_
|
||||||
|
|
||||||
## Interfaces
|
## Interfaces
|
||||||
|
|
||||||
_Details: [Interfaces](./interfaces.md)_
|
_For more details, see: [Interfaces](./interfaces.md)_
|
||||||
|
|
||||||
| Feature | Example |
|
| Feature | Example |
|
||||||
| --------------------- | -------------------------------------- |
|
| --------------------- | -------------------------------------- |
|
||||||
|
@ -99,7 +99,7 @@ _Details: [Interfaces](./interfaces.md)_
|
||||||
|
|
||||||
## Templates
|
## Templates
|
||||||
|
|
||||||
_Details: [Templates](./templates.md)_
|
_For more details, see: [Templates](./templates.md)_
|
||||||
|
|
||||||
| Feature | Example |
|
| Feature | Example |
|
||||||
| --------------------------------- | --------------------------------------------------- |
|
| --------------------------------- | --------------------------------------------------- |
|
||||||
|
@ -111,7 +111,7 @@ _Details: [Templates](./templates.md)_
|
||||||
|
|
||||||
## Enums
|
## Enums
|
||||||
|
|
||||||
_Details: [Enums](./enums.md)_
|
_For more details, see: [Enums](./enums.md)_
|
||||||
|
|
||||||
| Feature | Example |
|
| Feature | Example |
|
||||||
| ------------------ | ---------------------------------------------- |
|
| ------------------ | ---------------------------------------------- |
|
||||||
|
@ -123,7 +123,7 @@ _Details: [Enums](./enums.md)_
|
||||||
|
|
||||||
## Unions
|
## Unions
|
||||||
|
|
||||||
_Details: [Unions](./unions.md)_
|
_For more details, see: [Unions](./unions.md)_
|
||||||
|
|
||||||
| Feature | Example |
|
| Feature | Example |
|
||||||
| ----------------------- | -------------------------------- |
|
| ----------------------- | -------------------------------- |
|
||||||
|
@ -132,7 +132,7 @@ _Details: [Unions](./unions.md)_
|
||||||
|
|
||||||
## Intersections
|
## Intersections
|
||||||
|
|
||||||
_Details: [Intersections](./intersections.md)_
|
_For more details, see: [Intersections](./intersections.md)_
|
||||||
|
|
||||||
| Feature | Example |
|
| Feature | Example |
|
||||||
| ------------------------ | -------------- |
|
| ------------------------ | -------------- |
|
||||||
|
@ -140,7 +140,7 @@ _Details: [Intersections](./intersections.md)_
|
||||||
|
|
||||||
## Type literals
|
## Type literals
|
||||||
|
|
||||||
_Details: [Type literals](./type-literals.md)_
|
_For more details, see: [Type literals](./type-literals.md)_
|
||||||
|
|
||||||
| Feature | Example |
|
| Feature | Example |
|
||||||
| ----------------- | -------------------------------------------------------- |
|
| ----------------- | -------------------------------------------------------- |
|
||||||
|
@ -152,7 +152,7 @@ _Details: [Type literals](./type-literals.md)_
|
||||||
|
|
||||||
## Aliases
|
## Aliases
|
||||||
|
|
||||||
_Details: [Aliases](./alias.md)_
|
_For more details, see: [Aliases](./alias.md)_
|
||||||
|
|
||||||
| Feature | Example |
|
| Feature | Example |
|
||||||
| ----------------- | --------------------------------- |
|
| ----------------- | --------------------------------- |
|
||||||
|
|
|
@ -4,25 +4,25 @@ title: Scalars
|
||||||
|
|
||||||
# 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
|
```typespec
|
||||||
scalar ternary;
|
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
|
```typespec
|
||||||
scalar Password extends string;
|
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
|
```typespec
|
||||||
@doc(Type)
|
@doc(Type)
|
||||||
|
|
|
@ -5,9 +5,9 @@ title: Templates
|
||||||
|
|
||||||
# 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)
|
- [aliases](./alias.md)
|
||||||
- [models](./models.md)
|
- [models](./models.md)
|
||||||
|
@ -27,7 +27,7 @@ model DogPage {
|
||||||
|
|
||||||
## Default values
|
## 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
|
```typespec
|
||||||
model Page<Item = string> {
|
model Page<Item = string> {
|
||||||
|
@ -38,13 +38,13 @@ model Page<Item = string> {
|
||||||
|
|
||||||
## Parameter constraints
|
## 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
|
```typespec
|
||||||
alias Foo<Type extends string> = Type;
|
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
|
```typespec
|
||||||
alias Bar = Foo<123>;
|
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;
|
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
|
```typespec
|
||||||
alias Foo<Type extends string = "Abc"> = Type
|
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'
|
^ 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
|
```typespec
|
||||||
// Invalid
|
// Invalid
|
||||||
|
@ -77,7 +77,7 @@ alias Foo<T extends string = "Abc", U> = ...;
|
||||||
|
|
||||||
## Named template arguments
|
## 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
|
```typespec
|
||||||
alias Test<T, U extends numeric = int32, V extends string = "example"> = ...;
|
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
|
id: type-literals
|
||||||
title: Type literals
|
title: Type Literals
|
||||||
---
|
---
|
||||||
|
|
||||||
# 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
|
||||||
|
|
||||||
String literals can be represented using double quotes `"`
|
String literals are represented using double quotes `"`.
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
alias Str = "Hello World!";
|
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
|
```typespec
|
||||||
alias Str = """
|
alias Str = """
|
||||||
|
@ -27,14 +27,14 @@ This is a multi line string
|
||||||
""";
|
""";
|
||||||
```
|
```
|
||||||
|
|
||||||
- Opening `"""` must be followed by a new line.
|
- The opening `"""` must be followed by a new line.
|
||||||
- Closing `"""` must be preceded 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
|
```typespec
|
||||||
model MultiLineContainer {
|
model MultiLineContainer {
|
||||||
|
@ -64,7 +64,7 @@ two
|
||||||
|
|
||||||
## String template literal
|
## 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
|
```typespec
|
||||||
alias hello = "bonjour";
|
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 literal
|
||||||
|
|
||||||
Numeric literals can be declared by using the raw number
|
Numeric literals are declared by using the raw number.
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
alias Kilo = 1000;
|
alias Kilo = 1000;
|
||||||
|
@ -89,7 +89,7 @@ alias PI = 3.14;
|
||||||
|
|
||||||
## Boolean literal
|
## 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
|
```typespec
|
||||||
alias InTypeSpec = true;
|
alias InTypeSpec = true;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
---
|
---
|
||||||
id: type-relations
|
id: type-relations
|
||||||
title: Type relations
|
title: Type Relations
|
||||||
---
|
---
|
||||||
|
|
||||||
# Types Relations
|
# Type relations
|
||||||
|
|
||||||
## Type hierarchy
|
## Type hierarchy
|
||||||
|
|
||||||
|
@ -43,9 +43,9 @@ graph RL
|
||||||
|
|
||||||
## Model with properties
|
## 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
|
```typespec
|
||||||
model T {
|
model T {
|
||||||
|
@ -85,7 +85,7 @@ model S {
|
||||||
|
|
||||||
## `Record<T>`
|
## `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
|
```typespec
|
||||||
// Represent an object where all the values are int32.
|
// 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
|
```typespec
|
||||||
alias T = Record<int32>;
|
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
|
```typespec
|
||||||
model Foo is S {
|
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
|
||||||
|
|
||||||
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
|
- Union expressions
|
||||||
- named unions
|
- Named unions
|
||||||
|
|
||||||
## Union expressions
|
## 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
|
```typespec
|
||||||
alias Breed = Beagle | GermanShepherd | GoldenRetriever;
|
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
|
||||||
|
|
||||||
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
|
```typespec
|
||||||
union Breed {
|
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
|
## 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
|
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.
|
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.
|
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:
|
Available ruleSets:
|
||||||
|
|
||||||
- [`@typespec/http/all`](#@typespec/http/all)
|
- `@typespec/http/all`
|
||||||
|
|
||||||
## Rules
|
## 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.
|
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
|
## 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
|
## inline-cycle
|
||||||
|
|
||||||
???
|
**work in progress**
|
||||||
|
|
||||||
## invalid-default
|
## invalid-default
|
||||||
|
|
||||||
???
|
**work in progress**
|
||||||
|
|
||||||
## invalid-extension-key
|
## invalid-extension-key
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ To fix this issue, change the extension name to start with "x-".
|
||||||
|
|
||||||
## invalid-schema
|
## invalid-schema
|
||||||
|
|
||||||
???
|
**work in progress**
|
||||||
|
|
||||||
## invalid-server-variable
|
## invalid-server-variable
|
||||||
|
|
||||||
|
|
|
@ -2,132 +2,106 @@
|
||||||
title: Emitter operation
|
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)
|
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.
|
||||||
the OpenAPI emitter will generate a `servers` object with the server URL, description, and variables specified 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
|
## 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 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.
|
||||||
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.
|
|
||||||
|
|
||||||
[http-verb-decorators]: ../rest/reference/decorators.md
|
[http-verb-decorators]: ../http/reference/decorators.md
|
||||||
[http-route-decorator]: ../rest/reference/decorators.md#@TypeSpec.Http.route
|
[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
|
[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],
|
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.
|
||||||
and otherwise is simple 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].
|
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.
|
||||||
A parameter without one of these decorators is assumed to be passed in the request body.
|
|
||||||
|
|
||||||
The request body parameter can also be explicitly decorated with an [(Http) `@body` decorator][http-body-decorator].
|
[http-parameter-decorators]: ../http/reference/decorators.md
|
||||||
In the absence of explicit `@body`, the set of parameters that are not marked `@header`, `@query`, or `@path` form the request body
|
[http-body-decorator]: ../http/reference/decorators.md#@TypeSpec.Http.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
|
|
||||||
|
|
||||||
The content of a (built-in) `@doc` decorator on a parameter will be set in the description.
|
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.
|
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.
|
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.
|
||||||
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 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.
|
||||||
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.
|
|
||||||
|
|
||||||
When a return type model has a property explicitly decorated with an [(Http) `@body` decorator][http-body-decorator], this
|
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.
|
||||||
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.
|
|
||||||
|
|
||||||
[http-statuscode-decorator]: ../rest/reference/decorators.md#@TypeSpec.Http.statuscode
|
[http-statuscode-decorator]: ../http/reference/decorators.md#@TypeSpec.Http.statusCode
|
||||||
[error-decorator]: ../../standard-library/built-in-decorators.md#error
|
[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
|
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.
|
||||||
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
|
If the [(built-in) `#deprecated` directive][deprecated-decorator] is specified on the operation, then the operation's deprecated field is set to true.
|
||||||
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
|
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.
|
||||||
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)
|
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.
|
||||||
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
|
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.
|
||||||
with a JSON Schema type that most 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`
|
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.
|
||||||
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 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 [(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:
|
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) |
|
| `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 |
|
| `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):
|
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`:
|
For any element defined as a `string` or a type that extends from `string`:
|
||||||
|
|
||||||
| Decorator | Library | OpenAPI/JSON Schema keyword | Notes |
|
| Decorator | Library | OpenAPI/JSON Schema keyword | Notes |
|
||||||
| ------------------- | -------- | --------------------------- | ---------------------------------------------------------- |
|
| ------------------- | -------- | --------------------------- | --------------------------------------------------------------- |
|
||||||
| `@format(name)` | built-in | `format: name` | When format is not determined by type or another decorator |
|
| `@format(name)` | built-in | `format: name` | Used when format is not determined by type or another decorator |
|
||||||
| `@minLength(value)` | built-in | `minLength: value` | |
|
| `@minLength(value)` | built-in | `minLength: value` | |
|
||||||
| `@maxLength(value)` | built-in | `maxLength: value` | |
|
| `@maxLength(value)` | built-in | `maxLength: value` | |
|
||||||
| `@pattern(regex)` | built-in | `pattern: regex` | |
|
| `@pattern(regex)` | built-in | `pattern: regex` | |
|
||||||
| `@secret` | built-in | `format: password` | |
|
| `@secret` | built-in | `format: password` | |
|
||||||
|
|
||||||
For an array type:
|
For an array type:
|
||||||
|
|
||||||
|
@ -173,8 +147,7 @@ For an array type:
|
||||||
| `@minItems(value)` | built-in | `minItems: value` | |
|
| `@minItems(value)` | built-in | `minItems: value` | |
|
||||||
| `@maxItems(value)` | built-in | `maxItems: 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
|
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.
|
||||||
with a reference to a pre-existing named OpenAPI schema. This can be useful for "common" schemas.
|
|
||||||
|
|
||||||
Example:
|
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.
|
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
|
#### Spread
|
||||||
|
|
||||||
The spread operator does not convey any semantic relationship between the source and target models so the OpenAPI emitter
|
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.
|
||||||
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
|
#### Extends
|
||||||
|
|
||||||
When one model extends another model, this is intended to convey and inheritance relationship. While OpenAPI has no formal
|
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.
|
||||||
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
|
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.
|
||||||
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 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. 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.
|
The `@discriminator` decorator can be used to create multi-level discriminated inheritance but must use a different discriminated property at each level.
|
||||||
|
|
||||||
#### Is
|
#### Is
|
||||||
|
|
||||||
The `is` keyword provides a form of composition similar to the spread operator, where no semantic relationship is conveyed between
|
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 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
|
#### Union
|
||||||
|
|
||||||
Unions are another form of model composition.
|
Unions are another form of model composition.
|
||||||
|
|
||||||
Unions can be defined in two different ways in TypeSpec. One way is with
|
Unions can be defined in two different ways in TypeSpec. One way is with [the union type operator](../../language-basics/unions.md#union-expressions), `|`:
|
||||||
[the union type operator](../../language-basics/unions.md#union-expressions), `|`:
|
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
alias GoodBreed = Beagle | GermanShepherd | GoldenRetriever;
|
alias GoodBreed = Beagle | GermanShepherd | GoldenRetriever;
|
||||||
```
|
```
|
||||||
|
|
||||||
The second way is with [the `union` statement](../../language-basics/unions.md#named-unions)
|
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.
|
||||||
which not only declares the variant models but also assigns a name for each.
|
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
union GoodBreed {
|
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 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 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
|
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`.
|
||||||
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`:
|
1. If `realType` is `utcDateTime` or `offsetDateTime`:
|
||||||
- `encoding` of `rfc3339` will produce `type: string, format: date-time`
|
- `encoding` of `rfc3339` will produce `type: string, format: date-time`
|
||||||
- `encoding` of `rfc7231` will produce `type: string, format: http-date`
|
- `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`
|
- `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` )
|
3. If the schema of `encodeAs` produces a `format`, use it (e.g., encoding as `int32` will produce `type: integer, format: integer`)
|
||||||
1. Otherwise use the `encoding` as the format
|
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", int32) _: duration` | `type: integer, format: int32` | `type: integer, format: int32` |
|
||||||
| `@encode("seconds", float32) _: duration` | `type: number, format: float32` | `type: number, format: float32` |
|
| `@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
|
## 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
|
### Examples
|
||||||
|
|
||||||
|
|
|
@ -2,21 +2,21 @@
|
||||||
title: Guide
|
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
|
### 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
|
```typespec
|
||||||
@package
|
@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
|
```typespec
|
||||||
@package({
|
@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
|
### 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
|
```typespec
|
||||||
model TestMessage {
|
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
|
```proto3
|
||||||
message TestMessage {
|
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 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 referenced by any service operation (refer to [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 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:
|
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
|
```proto3
|
||||||
message TestMessage {
|
message TestMessage {
|
||||||
|
@ -86,9 +86,9 @@ message TestMessage {
|
||||||
|
|
||||||
### Services
|
### 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
|
```typespec
|
||||||
@package
|
@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
|
```proto3
|
||||||
syntax = "proto3";
|
syntax = "proto3";
|
||||||
|
@ -114,7 +114,7 @@ service Test {
|
||||||
|
|
||||||
#### Operations
|
#### 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
|
```typespec
|
||||||
model Input {
|
model Input {
|
||||||
|
@ -149,25 +149,25 @@ service Example {
|
||||||
|
|
||||||
#### Streams
|
#### 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);`
|
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);`
|
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);`
|
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);`
|
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-service]: reference/decorators#@TypeSpec.Protobuf.service
|
||||||
[protobuf-package]: reference/decorators#@TypeSpec.Protobuf.package
|
[protobuf-package]: reference/decorators#@TypeSpec.Protobuf.package
|
||||||
[protobuf-field]: reference/decorators#@TypeSpec.Protobuf.field
|
[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
|
```typespec
|
||||||
@service({
|
@service({
|
||||||
|
@ -13,7 +13,7 @@ The primary purpose of the TypeSpec.Versioning library is to provide a way to ve
|
||||||
namespace Contoso.WidgetManager;
|
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
|
```typespec
|
||||||
@service({
|
@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
|
```typespec
|
||||||
@service({
|
@service({
|
||||||
|
@ -41,7 +41,7 @@ For example, if our unversioned WidgetManager service has a dependency on the Az
|
||||||
namespace Contoso.WidgetManager.Unversioned;
|
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
|
```typespec
|
||||||
@service({
|
@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
|
```typespec
|
||||||
@service({
|
@service({
|
||||||
|
@ -76,7 +76,7 @@ enum Versions {
|
||||||
|
|
||||||
## Versioning APIs
|
## 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
|
```typespec
|
||||||
using TypeSpec.Versioning;
|
using TypeSpec.Versioning;
|
||||||
|
@ -109,7 +109,7 @@ model Widget {
|
||||||
op list(): Widget[] | Error;
|
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
|
```typespec
|
||||||
enum Versions {
|
enum Versions {
|
||||||
|
@ -132,8 +132,7 @@ op list(): Widget[] | Error;
|
||||||
op get(...Resource.KeysOf<Widget>): 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
|
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:
|
||||||
don't always have a description, so you decide it should be optional, not required. We can make these changes like this:
|
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
model Widget {
|
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
|
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.
|
||||||
`@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.
|
|
||||||
|
|
||||||
The OpenAPI defintion of `Widget` for version 3 reflects the change:
|
The OpenAPI definition of `Widget` for version 3 reflects the change:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
Widget:
|
Widget:
|
||||||
|
@ -164,7 +161,7 @@ Widget:
|
||||||
- id
|
- 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
|
```yaml
|
||||||
Widget:
|
Widget:
|
||||||
|
@ -179,6 +176,4 @@ Widget:
|
||||||
- name
|
- name
|
||||||
```
|
```
|
||||||
|
|
||||||
This is the common pattern with the versioning decorators. The TypeSpec should reflect the _current state_ of the API. The decorators identify the
|
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.
|
||||||
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.
|
|
||||||
|
|
|
@ -96,8 +96,8 @@ Resolving operation routes now follows the following logic:
|
||||||
|
|
||||||
### Action if applicable
|
### 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 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 contains service namespaces that are not child namespaces of the service namespace, move these namespaces under the service namespace.
|
||||||
|
|
||||||
### Cases
|
### Cases
|
||||||
|
|
||||||
|
|
|
@ -41,11 +41,11 @@ options:
|
||||||
|
|
||||||
## Breaking changes
|
## 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
|
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
|
### `@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
|
- 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`
|
- Doc comment `/** */` should override base type doc in `model is` or `op is`
|
||||||
- Formatter incorrectly formatting `::` to `.`
|
- 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
|
### 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
|
### `@typespec/playground` Breaking Changes
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ title: March 2024
|
||||||
|
|
||||||
# Release Notes March 2024 (2024-03-05)
|
# Release Notes March 2024 (2024-03-05)
|
||||||
|
|
||||||
:::warn
|
:::warning
|
||||||
This release contains breaking changes and deprecations
|
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
|
```typespec
|
||||||
@discriminator("kind")
|
@discriminator("kind")
|
||||||
|
@ -26,7 +26,7 @@ model Dog extends Pet {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `enum` discriminator
|
#### `enum` Discriminator
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
enum PetKind {
|
enum PetKind {
|
||||||
|
@ -50,7 +50,7 @@ model Dog extends Pet {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Nested discriminator
|
#### Nested Discriminator
|
||||||
|
|
||||||
```tsp
|
```tsp
|
||||||
@discriminator("kind")
|
@discriminator("kind")
|
||||||
|
@ -83,7 +83,7 @@ model Dog extends Pet {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Using unions
|
### Implementing Unions
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
@discriminator("kind")
|
@discriminator("kind")
|
||||||
|
|
|
@ -1,56 +1,56 @@
|
||||||
---
|
---
|
||||||
id: projected-names
|
id: projected-names
|
||||||
title: Projected names
|
title: Projected Names
|
||||||
---
|
---
|
||||||
|
|
||||||
# Projected Names
|
# Projected Names
|
||||||
|
|
||||||
:::warning
|
:::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:
|
- Wire:
|
||||||
- `json`: Configure JSON representation of data
|
- `json`: Set up JSON data representation
|
||||||
- `xml`: Configure XML representation of data
|
- `xml`: Set up XML data representation
|
||||||
- Language:
|
- Language:
|
||||||
- `csharp`: Configure C# code generation
|
- `csharp`: Set up C# code generation
|
||||||
- `java`: Configure Java code generation
|
- `java`: Set up Java code generation
|
||||||
- `python`: Configure Python code generation
|
- `python`: Set up Python code generation
|
||||||
- `javascript`: Configure JavaScript code generation
|
- `javascript`: Set up JavaScript code generation
|
||||||
- `swift` : Configure Swift code generation
|
- `swift` : Set up Swift code generation
|
||||||
- `c` : Configure C code generation
|
- `c` : Set up C code generation
|
||||||
- Type:
|
- Type:
|
||||||
- `client`: Configure output for the client
|
- `client`: Set up client-side output
|
||||||
- `server`: Configure output for the server
|
- `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` target name. Refer to the [recognized targets](#recognized-targets)
|
||||||
- `string` projected name. Whatever the name should be in the given target.
|
- `string` projected name. This is the name to be used in the specified target.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
model Foo {
|
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")
|
@projectedName("json", "exp")
|
||||||
expireAt: string;
|
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
|
```typespec
|
||||||
model Foo {
|
model Foo {
|
||||||
|
@ -93,9 +93,9 @@ model CertificateAttributes {
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tr>
|
<tr>
|
||||||
<td>When serialized to Json property use the json projected name</td>
|
<td>When serialized to Json, the property uses the json projected name</td>
|
||||||
<td>Typescript didn't provide any projected name so it keep the model as it is.</td>
|
<td>Typescript doesn't specify any projected name, so it retains the model as is.</td>
|
||||||
<td>Model uses the `csharp` projected names and keeps the reference to the JSON name in JsonProperty</td>
|
<td>The model uses the `csharp` projected names and maintains the reference to the JSON name in JsonProperty</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
# TypeSpec Raw Svg Icons
|
# 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 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 variable interpolation functionality in the cadl-project.yaml
|
||||||
- Add built-in `emitter-output-dir` options for all emitter.
|
- 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
|
- Fix typing and export format command
|
||||||
- **Api Breaking** Multiple `@service` per specs are now allowed.
|
- **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
|
- Add new `program.resolveTypeReference` helper to resolve a type in the cadl program using fully qualified name
|
||||||
|
|
|
@ -497,7 +497,7 @@ const diagnostics = {
|
||||||
shadow: {
|
shadow: {
|
||||||
severity: "warning",
|
severity: "warning",
|
||||||
messages: {
|
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": {
|
"invalid-deprecation-argument": {
|
||||||
|
|
|
@ -419,7 +419,7 @@ describe("compiler: interfaces", () => {
|
||||||
|
|
||||||
expectDiagnostics(diagnostics, {
|
expectDiagnostics(diagnostics, {
|
||||||
code: "shadow",
|
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");
|
expect(file.toString()).toEqual("foo");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("set config parmaeter with --arg", async () => {
|
it("set config parameter with --arg", async () => {
|
||||||
await cleanOutputDir("with-config");
|
await cleanOutputDir("with-config");
|
||||||
|
|
||||||
const { stdout } = await execCliSuccess(
|
const { stdout } = await execCliSuccess(
|
||||||
|
|
|
@ -24,7 +24,7 @@ linter:
|
||||||
|
|
||||||
Available ruleSets:
|
Available ruleSets:
|
||||||
|
|
||||||
- [`@typespec/http/all`](#@typespec/http/all)
|
- `@typespec/http/all`
|
||||||
|
|
||||||
### Rules
|
### 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(
|
const res = await openApiFor(
|
||||||
`
|
`
|
||||||
op test(
|
op test(
|
||||||
|
|
|
@ -360,7 +360,10 @@ export class MarkdownRenderer {
|
||||||
return section("Linter", [
|
return section("Linter", [
|
||||||
section("Usage", ["Add the following in `tspconfig.yaml`:", codeblock(setupExample, "yaml")]),
|
section("Usage", ["Add the following in `tspconfig.yaml`:", codeblock(setupExample, "yaml")]),
|
||||||
refDoc.linter.ruleSets
|
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)),
|
section("Rules", this.linterRuleToc(refDoc.linter.rules)),
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -55,7 +55,7 @@ const config: Config = {
|
||||||
url: "https://typespec.io",
|
url: "https://typespec.io",
|
||||||
baseUrl,
|
baseUrl,
|
||||||
onBrokenLinks: "throw",
|
onBrokenLinks: "throw",
|
||||||
onBrokenMarkdownLinks: "warn",
|
onBrokenMarkdownLinks: "throw",
|
||||||
favicon: "img/favicon.svg",
|
favicon: "img/favicon.svg",
|
||||||
|
|
||||||
// GitHub pages deployment config.
|
// GitHub pages deployment config.
|
||||||
|
@ -235,10 +235,6 @@ const config: Config = {
|
||||||
label: "Community",
|
label: "Community",
|
||||||
to: "/community",
|
to: "/community",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: "Getting started",
|
|
||||||
to: "/docs",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
type: "docsVersionDropdown",
|
type: "docsVersionDropdown",
|
||||||
position: "right",
|
position: "right",
|
||||||
|
|
|
@ -41,12 +41,12 @@ const DataValidationContent = () => {
|
||||||
layout="text-right"
|
layout="text-right"
|
||||||
header="Output"
|
header="Output"
|
||||||
title="Produce JSON Schema"
|
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 />}
|
illustration={<MultiFileIllustration />}
|
||||||
>
|
>
|
||||||
<LearnMoreCard
|
<LearnMoreCard
|
||||||
title="Configure the JSON schema emitter"
|
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"
|
image="shield-settings"
|
||||||
link={Links.libraryReferences.jsonSchema.index}
|
link={Links.libraryReferences.jsonSchema.index}
|
||||||
/>
|
/>
|
||||||
|
@ -55,7 +55,7 @@ const DataValidationContent = () => {
|
||||||
<Section
|
<Section
|
||||||
header="Customize"
|
header="Customize"
|
||||||
title="JSON Schema Decorators"
|
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 />}
|
illustration={<JsonSchemaExtensionsIllustration />}
|
||||||
>
|
>
|
||||||
<LearnMoreCard
|
<LearnMoreCard
|
||||||
|
|
|
@ -43,13 +43,13 @@ const OpenApiContent = () => {
|
||||||
<Section
|
<Section
|
||||||
layout="text-right"
|
layout="text-right"
|
||||||
header="Ecosystem"
|
header="Ecosystem"
|
||||||
title="Abstract common patterns"
|
title="Abstract recurring patterns"
|
||||||
description="Codify API patterns into reusable components, improving up quality and consistency across your API surface."
|
description="Transform API patterns into reusable elements to enhance both the quality and uniformity of your API interface."
|
||||||
illustration={<OpenAPI3AbstractCode />}
|
illustration={<OpenAPI3AbstractCode />}
|
||||||
>
|
>
|
||||||
<LearnMoreCard
|
<LearnMoreCard
|
||||||
title="Example: TypeSpec Azure Library"
|
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"
|
image="document-cloud"
|
||||||
link={Links.typespecAzure}
|
link={Links.typespecAzure}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -17,7 +17,7 @@ const DataValidationContent = () => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<UseCaseOverview
|
<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."
|
subtitle="TypeSpec is supported by a variety of readily available tools, designed to boost your productivity right from the start."
|
||||||
link={Links.editor.home}
|
link={Links.editor.home}
|
||||||
illustration={<LightDarkImg src="illustrations/ide-hero" />}
|
illustration={<LightDarkImg src="illustrations/ide-hero" />}
|
||||||
|
@ -26,7 +26,7 @@ const DataValidationContent = () => {
|
||||||
<Section
|
<Section
|
||||||
header="Style consistency"
|
header="Style consistency"
|
||||||
title="Built-in formatter"
|
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 />}
|
illustration={<FormatterIllustration />}
|
||||||
>
|
>
|
||||||
<LearnMoreCard
|
<LearnMoreCard
|
||||||
|
@ -53,14 +53,14 @@ const DataValidationContent = () => {
|
||||||
<Section
|
<Section
|
||||||
header="Intellisense"
|
header="Intellisense"
|
||||||
title="Autocomplete and more"
|
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" />}
|
illustration={<LightDarkImg src="illustrations/autocomplete" />}
|
||||||
/>
|
/>
|
||||||
<Section
|
<Section
|
||||||
layout="text-right"
|
layout="text-right"
|
||||||
header="Refactor"
|
header="Refactor"
|
||||||
title="Bulk renaming"
|
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={
|
illustration={
|
||||||
<video
|
<video
|
||||||
src={useBaseUrl("/img/illustrations/refactor.mp4")}
|
src={useBaseUrl("/img/illustrations/refactor.mp4")}
|
||||||
|
|
|
@ -5,17 +5,17 @@ title: Creating a TypeSpec Library
|
||||||
|
|
||||||
# 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
|
## 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
|
```bash
|
||||||
# Create a TypeSpec library (Decorators & Linters) with TypeScript enabled.
|
# Create a TypeSpec library (Decorators & Linters) with TypeScript enabled.
|
||||||
|
@ -25,21 +25,21 @@ tsp init --template library-ts
|
||||||
tsp init --template emitter-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
|
- **dist/index.js** - The main file for your Node library
|
||||||
- **lib/main.tsp** - the main file for your TypeSpec types (optional)
|
- **lib/main.tsp** - The main file for your TypeSpec types (optional)
|
||||||
- **src/index.ts** - the main file for your Node library in TypeScript
|
- **src/index.ts** - The main file for your Node library in TypeScript
|
||||||
- **src/lib.ts** - the TypeSpec library definition file
|
- **src/lib.ts** - The file that defines your TypeSpec library
|
||||||
- **package.json** - metadata about your TypeSpec package
|
- **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 & package.json
|
### a. Initialize your package directory & package.json
|
||||||
|
|
||||||
Run the following commands:
|
Run the following commands:
|
||||||
|
|
||||||
|
@ -49,9 +49,9 @@ Run the following commands:
|
||||||
> npm init
|
> 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
|
```jsonc
|
||||||
"type": "module"
|
"type": "module"
|
||||||
|
@ -65,13 +65,13 @@ Run the following command:
|
||||||
npm install --save-peer @typespec/compiler
|
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
|
### 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
|
```jsonc
|
||||||
"main": "dist/src/index.js",
|
"main": "dist/src/index.js",
|
||||||
|
@ -87,7 +87,7 @@ npm install -D typescript
|
||||||
npx tsc --init --strict
|
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
|
```jsonc
|
||||||
"module": "Node16", // This and next setting tells TypeScript to use the new ESM import system to resolve types.
|
"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`.
|
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
|
:::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
|
```typescript
|
||||||
import { createTypeSpecLibrary } from "@typespec/compiler";
|
import { createTypeSpecLibrary } from "@typespec/compiler";
|
||||||
|
@ -116,24 +116,24 @@ export const $lib = createTypeSpecLibrary({
|
||||||
diagnostics: {},
|
diagnostics: {},
|
||||||
} as const);
|
} 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;
|
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`
|
### f. Create `index.ts`
|
||||||
|
|
||||||
Open `./src/index.ts` and import your library definition:
|
Open `./src/index.ts` and import your library definition:
|
||||||
|
|
||||||
```typescript
|
```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";
|
export { $lib } from "./lib.js";
|
||||||
```
|
```
|
||||||
|
|
||||||
### g. Build TypeScript
|
### 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:
|
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
|
### 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
|
```typespec
|
||||||
import "../dist/index.js";
|
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
|
```typespec
|
||||||
import "../dist/index.js";
|
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 `peerDependencies` for all TypeSpec libraries (and the compiler) that you use in your own library or emitter.
|
||||||
- use `devDependencies` for the other TypeSpec libraries used only in tests
|
- Use `devDependencies` for other TypeSpec libraries that are only used in tests.
|
||||||
- use `dependencies`/`devDependencies` for any other packages depending if using in library code or in test/dev scripts
|
- 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**
|
**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
|
"yaml": "~2.3.1", // This is a regular package this library/emitter will use
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"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/compiler": "~0.43.0",
|
||||||
"@typespec/http": "~0.43.1",
|
"@typespec/http": "~0.43.1",
|
||||||
"@typespec/openapi": "~0.43.0",
|
"@typespec/openapi": "~0.43.0",
|
||||||
|
@ -196,19 +196,19 @@ TypeSpec libraries are defined using `peerDependencies` so we don't end-up with
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
// This TypeSpec library is only used in the tests but is not required to use this library.
|
// This TypeSpec library is only used in the tests but is not required to use this library.
|
||||||
"@typespec/versioning": "~0.43.0",
|
"@typespec/versioning": "~0.43.0",
|
||||||
// Typescript is only used during development
|
// TypeScript is only used during development
|
||||||
"typescript": "~5.0.2",
|
"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
|
### a. Add devDependencies
|
||||||
|
|
||||||
Verify that you have the following in your `package.json`:
|
Ensure that you have the following in your `package.json`:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -233,7 +233,7 @@ export default defineConfig({
|
||||||
|
|
||||||
### b. Define the testing library
|
### 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
|
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:
|
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.
|
- `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)
|
Create a new file `test/test-host.js` (change `test` to be your test folder)
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { createTestHost, createTestWrapper } from "@typespec/compiler/testing";
|
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";
|
import { MyTestLibrary } from "../src/testing/index.js";
|
||||||
|
|
||||||
export async function createMyTestHost() {
|
export async function createMyTestHost() {
|
||||||
|
@ -340,7 +340,7 @@ describe("my library", () => {
|
||||||
#### e. `@test` decorator
|
#### e. `@test` decorator
|
||||||
|
|
||||||
The `@test` decorator is a decorator loaded in the test environment. It can be used to collect any decorable type.
|
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
|
```ts
|
||||||
const { Foo, CustomName } = await runner.compile(`
|
const { Foo, CustomName } = await runner.compile(`
|
||||||
|
@ -357,23 +357,23 @@ CustomName; // type of : Bar.name
|
||||||
|
|
||||||
#### f. Install vscode extension for the test framework
|
#### 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
|
```bash
|
||||||
npm install $packageName
|
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
|
```typespec
|
||||||
import "MyLibrary";
|
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
|
## 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.
|
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.
|
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
|
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)
|
1. [(Optional) Declare the decorator signature in TypeSpec](#declare-the-decorator-signature)
|
||||||
2. [Implement the decorator in Javascript](#implement-the-decorator-in-js)
|
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
|
- It enables type checking for the parameters
|
||||||
- IDE IntelliSense
|
- 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
|
```typespec
|
||||||
extern dec logType(target: unknown, name: string);
|
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
|
```typespec
|
||||||
using TypeSpec.Reflection;
|
using TypeSpec.Reflection;
|
||||||
|
@ -37,31 +37,30 @@ extern dec track(target: Model | Enum);
|
||||||
|
|
||||||
### Optional parameters
|
### Optional parameters
|
||||||
|
|
||||||
A decorator parameter can be marked optional using `?`
|
You can mark a decorator parameter as optional using `?`.
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
extern dec track(target: Model | Enum, name?: valueof string);
|
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
|
```typespec
|
||||||
extern dec track(target: Model | Enum, ...names: valueof string[]);
|
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.
|
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.
|
||||||
Instead the decorator can use `valueof <T>` to specify that it is expecting a value of that kind.
|
|
||||||
|
|
||||||
| Example | Description |
|
| Example | Description |
|
||||||
| ----------------- | ---------------- |
|
| ----------------- | ----------------- |
|
||||||
| `valueof string` | Expect a string |
|
| `valueof string` | Expects a string |
|
||||||
| `valueof float64` | Expect a float |
|
| `valueof float64` | Expects a float |
|
||||||
| `valueof int32` | Expect a number |
|
| `valueof int32` | Expects a number |
|
||||||
| `valueof boolean` | Expect a boolean |
|
| `valueof boolean` | Expects a boolean |
|
||||||
|
|
||||||
```tsp
|
```tsp
|
||||||
extern dec tag(target: unknown, value: valueof string);
|
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")
|
@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:
|
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
|
```ts
|
||||||
// model.js
|
// 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
|
```typespec
|
||||||
// main.tsp
|
// main.tsp
|
||||||
|
@ -114,7 +113,7 @@ model Dog {
|
||||||
|
|
||||||
### Decorator parameter marshalling
|
### 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 |
|
| 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 numeric` | `number` |
|
||||||
| `valueof boolean` | `boolean` |
|
| `valueof boolean` | `boolean` |
|
||||||
|
|
||||||
for all the other types they are not transformed.
|
For all other types, they are not transformed.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
@ -130,7 +129,7 @@ Example:
|
||||||
export function $tag(
|
export function $tag(
|
||||||
context: DecoratorContext,
|
context: DecoratorContext,
|
||||||
target: Type,
|
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
|
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
|
```tsp
|
||||||
extern dec doc(target: unknown, name: valueof string);
|
extern dec doc(target: unknown, name: valueof string);
|
||||||
|
|
||||||
|
|
||||||
alias world = "world!";
|
alias world = "world!";
|
||||||
@doc("Hello ${world} ") // receive: "Hello world!"
|
@doc("Hello ${world} ") // receive: "Hello world!"
|
||||||
@doc("Hello ${123} ") // receive: "Hello 123"
|
@doc("Hello ${123} ") // receive: "Hello 123"
|
||||||
@doc("Hello ${true} ") // receive: "Hello true"
|
@doc("Hello ${true} ") // receive: "Hello true"
|
||||||
|
|
||||||
model Bar {}
|
model Bar {}
|
||||||
@doc("Hello ${Bar} ") // not called error
|
@doc("Hello ${Bar} ") // not called error
|
||||||
^ String template cannot be serialized as a string.
|
^ String template cannot be serialized as a string.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Typescript type Reference
|
#### Typescript type Reference
|
||||||
|
@ -168,7 +163,7 @@ model Bar {}
|
||||||
|
|
||||||
### Adding metadata with decorators
|
### 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.
|
❌ Do not save the data in a global variable.
|
||||||
|
|
||||||
|
@ -200,7 +195,7 @@ export const StateKeys = $lib.stateKeys;
|
||||||
|
|
||||||
### Reporting diagnostic on decorator or arguments
|
### Reporting diagnostic on decorator or arguments
|
||||||
|
|
||||||
Decorator context provide the `decoratorTarget` and `getArgumentTarget` helpers
|
The decorator context provides the `decoratorTarget` and `getArgumentTarget` helpers.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import type { DecoratorContext, Type } from "@typespec/compiler";
|
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
|
```typespec
|
||||||
import "./lib.js";
|
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
|
```ts
|
||||||
export function $customName(context: DecoratorContext, name: string) {}
|
export function $customName(context: DecoratorContext, name: string) {}
|
||||||
|
@ -242,12 +237,12 @@ setTypeSpecNamespace("MyLib", $tableName);
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
### Extern declation must have an implementation in JS file
|
### Extern declaration must have an implementation in JS file
|
||||||
|
|
||||||
Potential issues:
|
Potential issues:
|
||||||
|
|
||||||
- JS function is not prefixed with `$`. For a decorator called `@decorate` the JS function must be called `$decoratate`
|
- The JS function is not prefixed with `$`. For a decorator called `@decorate`, the JS function must be called `$decorate`.
|
||||||
- JS function is not in the same namespace as the the `extern dec`
|
- The JS function is not in the same namespace as the `extern dec`.
|
||||||
- Error is only showing in the IDE? Restart the TypeSpec server or the IDE.
|
- 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
|
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
|
## 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.
|
- ❌ Avoid using `throw` to report errors. Any exceptions thrown in this manner will be perceived as bugs in your library by the user.
|
||||||
- ✅ Use diagnostic API to report expected errors and warnings.
|
- ✅ Utilize the diagnostic API to report anticipated errors and warnings.
|
||||||
- ✅ Use `reportDiagnostic` in a decorator, `$onValidate` or `$onEmit`
|
- ✅ Employ `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)
|
- ❌ 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 `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`, `warning`. Errors cannot be suppressed
|
- Each diagnostic MUST have a `severity`. It can be `error` or `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 diagnostic MUST have at least one message. Using `default` as the `messageId` will make it the default selection.
|
||||||
- Each diagnostics message MAY have parameters to interpolate information into the message
|
- 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
|
```ts
|
||||||
import { createTypeSpecLibrary } from "@typespec/compiler";
|
import { createTypeSpecLibrary } from "@typespec/compiler";
|
||||||
|
@ -56,11 +56,11 @@ export const $lib = createTypeSpecLibrary({
|
||||||
},
|
},
|
||||||
} as const);
|
} 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 };
|
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/no-array`
|
||||||
- `@typespec/my-lib/duplicate-route`
|
- `@typespec/my-lib/duplicate-route`
|
||||||
|
@ -87,7 +87,7 @@ reportDiagnostic(program, {
|
||||||
// Multiple messages
|
// Multiple messages
|
||||||
reportDiagnostic(program, {
|
reportDiagnostic(program, {
|
||||||
code: "duplicate-name",
|
code: "duplicate-name",
|
||||||
messageId: "parmaeter",
|
messageId: "parameter",
|
||||||
format: {value: "$select"},
|
format: {value: "$select"},
|
||||||
target: diagnosticTarget,
|
target: diagnosticTarget,
|
||||||
});
|
});
|
||||||
|
@ -95,8 +95,8 @@ reportDiagnostic(program, {
|
||||||
|
|
||||||
### Collect diagnostics
|
### 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.
|
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 prevent duplicate diagnostics emitter if the accessor is called multiple times.
|
This prevents duplicate diagnostics emitter if the accessor is called multiple times.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { createDiagnosticCollector, Diagnostic } from "@typespec/compiler";
|
import { createDiagnosticCollector, Diagnostic } from "@typespec/compiler";
|
||||||
|
|
|
@ -3,28 +3,32 @@ id: emitter-framework
|
||||||
title: 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.
|
## Creating your own emitter
|
||||||
- `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.
|
|
||||||
|
|
||||||
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>`
|
### `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
|
```typescript
|
||||||
export async function $onEmit(context: EmitContext) {
|
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.
|
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
|
```typescript
|
||||||
class MyCodeEmitter extends CodeTypeEmitter {
|
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
|
#### EmitterOutput
|
||||||
|
|
||||||
Most methods of the `TypeEmitter` must either return `T` or an `EmitterOutput<T>`. There are four kinds of `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.
|
- `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.
|
- `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.
|
- `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
|
#### 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.
|
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`
|
### 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
|
```typescript
|
||||||
class MyTsEmitter extends TypeScriptEmitter {
|
class MyTsEmitter extends TypeScriptEmitter {
|
||||||
|
@ -357,3 +361,5 @@ class MyTsEmitter extends TypeScriptEmitter {
|
||||||
// and similar for other declarations: Unions, Enums, Interfaces, and Operations.
|
// 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
|
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
|
## 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
|
## 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.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
|
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
|
```bash
|
||||||
tsp init --template emitter-ts
|
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
|
## $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
|
- _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
|
```typescript
|
||||||
import { EmitContext, emitFile, resolvePath } from "@typespec/compiler";
|
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
|
### 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:
|
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
|
## 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 [emitter framework](./emitter-framework.md), which simplifies the process of emitting all your TypeSpec types (or a subset, if you prefer).
|
||||||
1. The Semantic Walker, which lets you easily run code for every type in the program
|
2. The Semantic Walker, which allows you to easily execute 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.
|
3. Custom traversal, which offers more flexibility than the previous two methods, albeit with some added complexity.
|
||||||
|
|
||||||
### Emitter Framework
|
### 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
|
### 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
|
```typescript
|
||||||
navigateProgram(program, {
|
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
|
### 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.
|
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
|
### 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.
|
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.
|
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.
|
1. If the scalar is a known scalar (e.g., `int32`), emit the known mapping.
|
||||||
2. Otherwise check scalar `baseScalar` and go back to `1.`
|
2. Otherwise, check the scalar `baseScalar` and go back to step 1.
|
||||||
2.1 After resolving which scalar apply any decorators
|
2.1 After resolving which scalar to use, apply any decorators.
|
||||||
|
|
||||||
:::note
|
:::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
|
### 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. |
|
| `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. |
|
| `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:
|
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
|
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
|
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.
|
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.
|
||||||
Linters need to be explicitly enabled. `$onValidate` will be 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
|
```ts
|
||||||
import { createLinterRule } from "@typespec/compiler";
|
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
|
- ❌ 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
|
## 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.
|
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.
|
||||||
You can then provide different test checking the rule pass or fails.
|
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { RuleTester, createLinterRuleTester, createTestRunner } from "@typespec/compiler/testing";
|
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.
|
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.
|
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.
|
||||||
Then calling `toEqual` with the expected code after the codefix is applied.
|
|
||||||
|
|
||||||
:::note
|
:::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
|
```ts
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
title: Scaffolding templates
|
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
|
```bash
|
||||||
tsp init <templateUrl>
|
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
|
```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.
|
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.
|
||||||
The root of the document is a dictionary allowing multiple templates to be hosted at the same location.
|
|
||||||
|
|
||||||
Each template needs at the minimum:
|
Each template must include:
|
||||||
|
|
||||||
- key: Key of the template
|
- key: The template's key
|
||||||
- title: Human readable name of the template
|
- title: A user-friendly name for the template
|
||||||
- description: Extended description of the template.
|
- description: A detailed description of the template.
|
||||||
|
|
||||||
Example:
|
Here's an example:
|
||||||
|
|
||||||
```json
|
```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
|
```json
|
||||||
{
|
{
|
||||||
"rest": {
|
"rest": {
|
||||||
"title": "REST API",
|
"title": "REST API",
|
||||||
"description": "Create a new project representing a REST API",
|
"description": "Create a new project representing a REST API",
|
||||||
"libraries": ["@typespec/rest", "@typespec/openapi3"]
|
"libraries": ["/rest", "@typespec/openapi3"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Adding new files
|
## 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.
|
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:
|
||||||
Each file need the following properties:
|
|
||||||
|
|
||||||
- `path`: Absolute or relative path(to the template file) to the file
|
- `path`: The absolute or relative path (relative to the template file) to the file
|
||||||
- `destination`: Relative path of the file relative to the project root.
|
- `destination`: The file's relative path, relative to the project root.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
@ -90,32 +88,30 @@ model {{parameters.ModelName}} {
|
||||||
|
|
||||||
### Interpolating values
|
### Interpolating values
|
||||||
|
|
||||||
The template can interpolate values in the files. The values available are anything available in the template configuration referenced as it is.
|
The template can interpolate values in the files. The available values are anything in the template configuration, referenced as is. For example:
|
||||||
Examples:
|
|
||||||
|
|
||||||
- Reference a parameter `{{parameters.ModelName}}`
|
- To reference a parameter, use `{{parameters.ModelName}}`
|
||||||
- Reference a the template title `{{title}}`
|
- 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 |
|
| Name | Description |
|
||||||
| ------------------------------------- | --------------------------------------------------------------- |
|
| ------------------------------------- | --------------------------------------------------------------------------------- |
|
||||||
| `directory` | Directory full path where the project should be initialized. |
|
| `directory` | The full directory path where the project should be initialized. |
|
||||||
| `folderName` | Folder name where the project should be initialized. |
|
| `folderName` | The name of the folder where the project should be initialized. |
|
||||||
| `name` | Name of the project. |
|
| `name` | The name of the project. |
|
||||||
| `libraries` | List of libraries to include |
|
| `libraries` | The list of libraries to include. |
|
||||||
| `templateUri` | Path where this template was loaded from. |
|
| `templateUri` | The path from where this template was loaded. |
|
||||||
| Functions | |
|
| Functions | |
|
||||||
| `toLowerCase(value: string)` | Convert string to lower case |
|
| `toLowerCase(value: string)` | Converts a string to lower case. |
|
||||||
| `normalizePackageName(value: string)` | Normalize package name. It replaces `.` with`-` and toLowerCase |
|
| `normalizePackageName(value: string)` | Normalizes the package name. It replaces `.` with `-` and converts to lower case. |
|
||||||
| `casing.pascalCase(value: string)` | Convert string to PascalCase |
|
| `casing.pascalCase(value: string)` | Converts a string to PascalCase. |
|
||||||
| `casing.camelCase(value: string)` | Convert string to camelCase |
|
| `casing.camelCase(value: string)` | Converts a string to camelCase. |
|
||||||
| `casing.kebabCase(value: string)` | Convert string to kebab-case |
|
| `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.
|
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.
|
||||||
The template takes in a map of inputs that will get prompted to the user during initialization.
|
|
||||||
|
|
||||||
```json
|
```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
|
title: Getting Started
|
||||||
---
|
---
|
||||||
|
|
||||||
# Getting Started with TypeSpec
|
# Getting started with TypeSpec
|
||||||
|
|
||||||
- [Get started with HTTP in TypeSpec](./getting-started-http.md)
|
- [Get started with HTTP in TypeSpec](./getting-started-http.md)
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
title: Configuration
|
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
|
## 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
|
```yaml
|
||||||
options:
|
options:
|
||||||
|
@ -72,7 +72,7 @@ emit:
|
||||||
|
|
||||||
### Variable interpolation
|
### Variable interpolation
|
||||||
|
|
||||||
The TypeSpec project file provide variable interpolation using:
|
The TypeSpec project file provides variable interpolation using:
|
||||||
|
|
||||||
- built-in variables
|
- built-in variables
|
||||||
- environment variables
|
- environment variables
|
||||||
|
@ -86,14 +86,14 @@ Examples:
|
||||||
- `{output-dir}/my-path`
|
- `{output-dir}/my-path`
|
||||||
- `{env.SHARED_PATH}/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`
|
For instance, consider the following config value: `{service-name}/output.{version}.json`
|
||||||
The following would get produced
|
Here's what would be produced:
|
||||||
|
|
||||||
| Service name value | Version value | Result |
|
| 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) |
|
| `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 |
|
| `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:**
|
**Example:**
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -130,7 +130,7 @@ parameters:
|
||||||
output-dir: {base-dir}/output
|
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
|
```bash
|
||||||
tsp compile . --arg "base-dir=/path/to/base"
|
tsp compile . --arg "base-dir=/path/to/base"
|
||||||
|
@ -138,13 +138,13 @@ tsp compile . --arg "base-dir=/path/to/base"
|
||||||
|
|
||||||
#### Environment variables
|
#### 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:**
|
**Example:**
|
||||||
|
|
||||||
|
@ -156,11 +156,11 @@ environment-variables:
|
||||||
output-dir: {env.BASE_DIR}/output
|
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
|
```yaml
|
||||||
options:
|
options:
|
||||||
|
@ -185,7 +185,7 @@ options:
|
||||||
|
|
||||||
### `output-dir` - Configure the default output dir
|
### `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
|
```yaml
|
||||||
output-dir: {cwd}/typespec-build
|
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.
|
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
|
### `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
|
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
|
```yaml
|
||||||
warn-as-error: true
|
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}`
|
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
|
```yaml
|
||||||
linter:
|
linter:
|
||||||
|
@ -325,13 +325,13 @@ linter:
|
||||||
"@typespec/best-practices/no-y": "This rule cannot be applied in this project because X"
|
"@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`
|
### `--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
|
```yaml
|
||||||
tsp compile . --no-emit
|
tsp compile . --no-emit
|
||||||
|
@ -385,22 +385,20 @@ Enable/Disable pretty logging (colors, diagnostic preview, etc.).
|
||||||
tsp compile . --pretty=false
|
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.
|
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:
|
||||||
By default the output-dir of an emitter is set to this value:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
{output-dir}/{emitter-name}
|
{output-dir}/{emitter-name}
|
||||||
```
|
```
|
||||||
|
|
||||||
where
|
where:
|
||||||
|
|
||||||
- `output-dir` is the compiler common `output-dir` that can be configured via `--output-dir`
|
- `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 `@typespec/openapi3`)
|
- `emitter-name` is the name of the emitter package (for example, `/openapi3`).
|
||||||
|
|
||||||
Example:
|
For instance, if the emitters `@typespec/openapi3` and `@typespec/jsonschema` are given, the default output folder structure would be:
|
||||||
Given the following emitters: `@typespec/openapi3` and `@typespec/jsonschema`, the default output folder structure would be
|
|
||||||
|
|
||||||
```
|
```
|
||||||
{project-root}/tsp-output:
|
{project-root}/tsp-output:
|
||||||
|
@ -411,7 +409,7 @@ Given the following emitters: `@typespec/openapi3` and `@typespec/jsonschema`, t
|
||||||
... json schema files ...
|
... 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
|
--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 ...
|
... openapi3 files ...
|
||||||
jsonschema
|
jsonschema
|
||||||
... json schema files ...
|
... 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"
|
--option "@typespec/openapi3.output-dir={projectroot}/openapispec"
|
||||||
|
|
|
@ -4,23 +4,23 @@ title: Tracing
|
||||||
|
|
||||||
# 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
|
```bash
|
||||||
tsp compile . --trace import-resolution
|
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
|
```bash
|
||||||
tsp compile . --trace import-resolution --trace projection
|
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
|
```yaml
|
||||||
trace: *
|
trace: *
|
||||||
|
@ -30,55 +30,54 @@ trace:
|
||||||
- projection
|
- 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.
|
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:
|
||||||
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:
|
|
||||||
|
|
||||||
- `foo` and `foo.*`
|
- `foo` and `foo.*`
|
||||||
- `one.two` and `one.two.*`
|
- `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.two.three`
|
||||||
- `one.foo`
|
- `one.foo`
|
||||||
- `bar.info`
|
- `bar.info`
|
||||||
|
|
||||||
Using:
|
You can use:
|
||||||
|
|
||||||
- `*` will log everything
|
- `*` to log everything
|
||||||
- `one` will log everything under `one`(`one.two.three`, `one.foo`)
|
- `one` to log everything under `one`(`one.two.three`, `one.foo`)
|
||||||
- `bar` will log everything under `bar`(`bar.info`)
|
- `bar` to log everything under `bar`(`bar.info`)
|
||||||
- `one.foo` will log everything under `one.foo`(`one.foo`)
|
- `one.foo` to log everything under `one.foo`(`one.foo`)
|
||||||
- `other` will log everything under `other` which is nothing here.
|
- `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 |
|
| Area | Description |
|
||||||
| ------------------------------ | -------------------------------------------------------------------- |
|
| ------------------------------ | ---------------------------------------------------------------------- |
|
||||||
| `compiler.options` | Log the resolved compiler options |
|
| `compiler.options` | Logs the resolved compiler options |
|
||||||
| `import-resolution.library` | Information related to the resolution of import libraries |
|
| `import-resolution.library` | Logs information related to the resolution of import libraries |
|
||||||
| `projection.log` | Debug information logged by the `log()` function used in projections |
|
| `projection.log` | Logs debug information from the `log()` function used in projections |
|
||||||
| `bind.js` | Information when binding JS files |
|
| `bind.js` | Logs information when binding JS files |
|
||||||
| `linter.register-library` | Information that a library rules will be loaded |
|
| `linter.register-library` | Logs information when a library's rules are being loaded |
|
||||||
| `linter.register-library.rule` | Information about a rule that is being registered |
|
| `linter.register-library.rule` | Logs 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.start` | Logs information about a ruleset that is about to be extended |
|
||||||
| `linter.extend-rule-set.end` | Information about rules enabled after extending a ruleset |
|
| `linter.extend-rule-set.end` | Logs information about rules enabled after extending a ruleset |
|
||||||
| `linter.lint` | Start the lint process and show information of all the rules enabled |
|
| `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
|
```ts
|
||||||
const tracer = program.tracer.sub("my-library");
|
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
|
```ts
|
||||||
tracer.trace("emitting-ts", "Emitting ts interface");
|
tracer.trace("emitting-ts", "Emitting ts interface");
|
||||||
|
|
|
@ -3,14 +3,13 @@ id: faq
|
||||||
title: 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.
|
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.
|
||||||
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.
|
|
||||||
|
|
||||||
### Solutions
|
### How can I fix this?
|
||||||
|
|
||||||
- Update npm `npm install -g npm`
|
- You can update npm using the command `npm install -g npm`.
|
||||||
- If you cannot update npm, then adding the dependencies to your project dependency should resolve the issue `npm install x`
|
- 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 cli](#via-the-cli)
|
||||||
- Via the vscode/vs extension
|
- Via the vscode/vs extension
|
||||||
- As a prettier plugin
|
- As a `prettier` plugin
|
||||||
|
|
||||||
## Via the cli
|
## Via the cli
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ Format all TypeSpec files:
|
||||||
tsp format "**/*.tsp"
|
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
|
```bash
|
||||||
tsp format --check "**/*.tsp"
|
tsp format --check "**/*.tsp"
|
||||||
|
@ -27,15 +27,15 @@ tsp format --check "**/*.tsp"
|
||||||
|
|
||||||
## Via the VSCode or VS extension
|
## 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
|
## 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
|
```yaml
|
||||||
plugins:
|
plugins:
|
||||||
|
|
|
@ -5,13 +5,13 @@ title: Releases
|
||||||
|
|
||||||
# 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 |
|
| Name | Changelog | Latest | Next |
|
||||||
| -------------------------------------------------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
|
| -------------------------------------------------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
|
||||||
|
@ -49,9 +49,9 @@ Every change to the `main` branch is automatically published under the npm `@nex
|
||||||
|
|
||||||
## Release cadence
|
## 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
|
## Breaking changes migration guides
|
||||||
|
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
title: Reproducibility
|
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
|
- An update to a dependency or sub-dependency introduces an unexpected breaking change.
|
||||||
2. Changes to a new version of a service shouldn't affect the older versions
|
- Updates to a new version of a service should not impact older versions.
|
||||||
3. A change to the TypeSpec spec
|
- 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
|
## 1. Defend against dependency changes
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
title: Style guide
|
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
|
:::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.
|
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
|
### 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 -->
|
<!-- prettier-ignore -->
|
||||||
```tsp
|
```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 -->
|
<!-- prettier-ignore -->
|
||||||
```tsp
|
```tsp
|
||||||
|
|
|
@ -4,12 +4,12 @@ title: TypeSpec Visual Studio Extension
|
||||||
|
|
||||||
## Installation
|
## 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
|
## Configure
|
||||||
|
|
||||||
1. Create a file `.vs/VSWorkspaceSettings.json` at the root of the project.
|
1. Create a file named `.vs/VSWorkspaceSettings.json` at the root of the project.
|
||||||
2. Add configuration as key value pair in this file. Example:
|
2. Add the configuration as a key-value pair in this file. For example:
|
||||||
|
|
||||||
```json
|
```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:
|
Available variables:
|
||||||
|
|
||||||
- `workspaceFolder`: Correspond to the root of your Visual Studio workspace.
|
- `workspaceFolder`: Corresponds to the root of your Visual Studio workspace.
|
||||||
|
|
||||||
## Uninstall
|
## 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
|
```bash
|
||||||
tsp vs uninstall
|
tsp vs uninstall
|
||||||
|
|
|
@ -8,16 +8,16 @@ Install the extension via the Visual Studio Code extension manager https://marke
|
||||||
|
|
||||||
## Configure
|
## 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:
|
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
|
### `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.
|
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
|
```json
|
||||||
{
|
{
|
||||||
|
@ -27,7 +27,7 @@ This setting provides the ability to configure where the tsp compiler is located
|
||||||
|
|
||||||
## Uninstall
|
## 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
|
```bash
|
||||||
tsp code uninstall
|
tsp code uninstall
|
||||||
|
|
|
@ -8,17 +8,17 @@ slug: /
|
||||||
|
|
||||||
## Requirements
|
## 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
|
```bash
|
||||||
npm --version
|
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
|
## Install tsp
|
||||||
|
|
||||||
First step is to install `tsp`, the TypeSpec compiler/CLI
|
The first step is to install `tsp`, the TypeSpec compiler/CLI.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install -g @typespec/compiler
|
npm install -g @typespec/compiler
|
||||||
|
@ -31,23 +31,23 @@ TypeSpec provides extension for the following editors:
|
||||||
- [Visual Studio Code](./editor/vscode.md)
|
- [Visual Studio Code](./editor/vscode.md)
|
||||||
- [Visual Studio](./editor/vs.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
|
```bash
|
||||||
tsp init
|
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
|
```bash
|
||||||
tsp install
|
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
|
```bash
|
||||||
package.json # Package manifest defining your TypeSpec project as a Node package.
|
package.json # Package manifest defining your TypeSpec project as a Node package.
|
||||||
|
|
|
@ -3,13 +3,11 @@ id: aliases
|
||||||
title: 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.
|
You can define an alias using the `alias` keyword.
|
||||||
|
|
||||||
Alias can be defined using the `alias` keyword
|
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
alias Options = "one" | "two";
|
alias Options = "one" | "two";
|
||||||
|
|
|
@ -3,7 +3,7 @@ id: built-in-types
|
||||||
title: 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.
|
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
|
||||||
|
|
||||||
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
|
```typespec
|
||||||
@tag("Sample")
|
@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
|
```typespec
|
||||||
@mark
|
@mark
|
||||||
model Dog {}
|
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
|
```typespec
|
||||||
model Dog {}
|
model Dog {}
|
||||||
|
@ -42,14 +42,14 @@ model Dog {}
|
||||||
@@tag(Dog, "Sample");
|
@@tag(Dog, "Sample");
|
||||||
```
|
```
|
||||||
|
|
||||||
Which is equivalent to
|
This is equivalent to:
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
@tag("Sample")
|
@tag("Sample")
|
||||||
model Dog {}
|
model Dog {}
|
||||||
```
|
```
|
||||||
|
|
||||||
Example: Decorate a model property
|
Example: decorating a model property
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
model Dog {
|
model Dog {
|
||||||
|
@ -59,6 +59,6 @@ model Dog {
|
||||||
@@readOnly(Dog.name);
|
@@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
|
||||||
|
|
||||||
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` decorator
|
||||||
- `/** */` Doc comments
|
- `/** */` 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
|
```typespec
|
||||||
@doc("This is a sample model")
|
@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
|
```typespec
|
||||||
@doc("Templated {name}", Type)
|
@doc("Templated {name}", Type)
|
||||||
model Template<Type extends {}> {
|
model Template<Type extends {}> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// doc will read "Templated A"
|
// The documentation will read "Templated A"
|
||||||
model A is Template<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
|
```typespec
|
||||||
/**
|
/**
|
||||||
|
@ -64,11 +64,11 @@ op read(
|
||||||
): Widget | Error;
|
): 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
|
### 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 |
|
| 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
|
||||||
|
|
||||||
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.
|
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.
|
||||||
The enums members are comma `,` separated and can be TypeSpec `identifier`s or `string literal`s.
|
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
enum Direction {
|
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
|
```typespec
|
||||||
enum Direction {
|
enum Direction {
|
||||||
|
@ -36,7 +35,7 @@ enum Direction {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Values can also be integers.
|
These values can also be integers.
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
enum Foo {
|
enum Foo {
|
||||||
|
@ -47,7 +46,7 @@ enum Foo {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
or float
|
Or even floating-point numbers.
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
enum Hour {
|
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
|
```typespec
|
||||||
enum DirectionExt {
|
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
|
```typespec
|
||||||
alias North = Direction.North;
|
alias North = Direction.North;
|
||||||
|
|
|
@ -3,30 +3,30 @@ id: imports
|
||||||
title: 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
|
```typespec
|
||||||
import "./models/foo.tsp";
|
import "./models/foo.tsp";
|
||||||
```
|
```
|
||||||
|
|
||||||
## Import Js file
|
## Importing a JavaScript file
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
import "./decorators.js";
|
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
|
```typespec
|
||||||
import "@typespec/rest";
|
import "/rest";
|
||||||
```
|
```
|
||||||
|
|
||||||
```json
|
```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
|
```typespec
|
||||||
import "./models"; // same as `import "./models/main.tsp";
|
import "./models"; // equivalent to `import "./models/main.tsp";
|
||||||
```
|
```
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
|
|
|
@ -5,9 +5,9 @@ title: Interfaces
|
||||||
|
|
||||||
# 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
|
```typespec
|
||||||
interface SampleInterface {
|
interface SampleInterface {
|
||||||
|
@ -18,9 +18,9 @@ interface SampleInterface {
|
||||||
|
|
||||||
## Composing interfaces
|
## 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
|
```typespec
|
||||||
interface A {
|
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
|
```typespec
|
||||||
interface C extends A, B {
|
interface C extends A, B {
|
||||||
|
@ -40,7 +40,7 @@ interface C extends A, B {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
which is equivalent to
|
This is equivalent to:
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
interface C {
|
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
|
```typespec
|
||||||
interface ReadWrite<T> {
|
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
|
```typespec
|
||||||
interface ReadWrite<T> {
|
interface ReadWrite<T> {
|
||||||
|
@ -77,7 +77,7 @@ op myWrite is MyReadWrite.write<int32>;
|
||||||
```
|
```
|
||||||
|
|
||||||
:::caution
|
:::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.
|
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;
|
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
|
```typespec
|
||||||
alias MyReadWrite = ReadWrite<string>;
|
alias MyReadWrite = ReadWrite<string>;
|
||||||
|
|
|
@ -5,13 +5,13 @@ title: Intersections
|
||||||
|
|
||||||
# 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
|
```typespec
|
||||||
alias Dog = Animal & Pet;
|
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
|
```typespec
|
||||||
alias Dog = {
|
alias Dog = {
|
||||||
|
|
|
@ -5,22 +5,22 @@ title: Models
|
||||||
|
|
||||||
# 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)
|
- [Record](#record)
|
||||||
- [Array](#array)
|
- [Array](#array)
|
||||||
|
|
||||||
### Record
|
### 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`.
|
- The name can be an `identifier` or `string literal`.
|
||||||
- type can be any type reference
|
- The type can be any type reference.
|
||||||
- properties are ordered. See [ordering of properties](#ordering-of-properties)
|
- Properties are arranged in a specific order. Refer to [property ordering](#property-ordering) for more details.
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
model Dog {
|
model Dog {
|
||||||
|
@ -31,7 +31,7 @@ model Dog {
|
||||||
|
|
||||||
#### Optional properties
|
#### Optional properties
|
||||||
|
|
||||||
Properties can be marked as optional using the `?` punctuation.
|
Properties can be designated as optional by using the `?` symbol.
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
model Dog {
|
model Dog {
|
||||||
|
@ -41,7 +41,7 @@ model Dog {
|
||||||
|
|
||||||
#### Default values
|
#### 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
|
```typespec
|
||||||
model Dog {
|
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:
|
Example:
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ model Cat is Pet {
|
||||||
furColor: string;
|
furColor: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resulting property order for cat:
|
// The resulting property order for Cat is:
|
||||||
// name, age, meow, address, furColor
|
// 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.
|
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 the `...` operator
|
||||||
- Using `is` operator
|
- Using the `is` operator
|
||||||
- Using `extends` 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.
|
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.
|
||||||
This means that the property in the model could be of a different and incompatible type with the Record value type.
|
|
||||||
|
|
||||||
```tsp
|
```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 {
|
model Person {
|
||||||
age: int32;
|
age: int32;
|
||||||
...Record<string>;
|
...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
|
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.
|
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.
|
||||||
|
|
||||||
In many languages this would probably result in the same emitted code as `is` and is recommended to just use `is Record<T>` instead.
|
|
||||||
|
|
||||||
```tsp
|
```tsp
|
||||||
model Person extends Record<string> {
|
model Person extends Record<string> {
|
||||||
|
@ -135,7 +132,7 @@ model Person extends Record<string> {
|
||||||
|
|
||||||
#### `never`
|
#### `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.
|
This can be useful in a model template to omit a property.
|
||||||
|
|
||||||
|
@ -150,18 +147,18 @@ model UKAddress is Address<never>;
|
||||||
```
|
```
|
||||||
|
|
||||||
:::note
|
:::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
|
||||||
|
|
||||||
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
|
## Model composition
|
||||||
|
|
||||||
### Spread
|
### 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
|
```typespec
|
||||||
model Animal {
|
model Animal {
|
||||||
|
@ -177,7 +174,7 @@ model Dog {
|
||||||
...Pet;
|
...Pet;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dog is equivalent to the following declaration:
|
// The Dog model is equivalent to the following declaration:
|
||||||
model Dog {
|
model Dog {
|
||||||
species: string;
|
species: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -186,7 +183,7 @@ model Dog {
|
||||||
|
|
||||||
### Extends
|
### 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
|
```typespec
|
||||||
model Animal {
|
model Animal {
|
||||||
|
@ -198,7 +195,7 @@ model Dog extends Animal {}
|
||||||
|
|
||||||
### Is
|
### 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
|
```typespec
|
||||||
@decorator
|
@decorator
|
||||||
|
@ -208,7 +205,7 @@ model Thing<T> {
|
||||||
|
|
||||||
model StringThing is Thing<string>;
|
model StringThing is Thing<string>;
|
||||||
|
|
||||||
// StringThing declaration is equivalent to the following declaration:
|
// The StringThing declaration is equivalent to the following declaration:
|
||||||
@decorator
|
@decorator
|
||||||
model StringThing {
|
model StringThing {
|
||||||
property: string;
|
property: string;
|
||||||
|
@ -217,7 +214,7 @@ model StringThing {
|
||||||
|
|
||||||
## Model templates
|
## Model templates
|
||||||
|
|
||||||
[See templates](./templates.md) for details on templates
|
Refer to [templates](./templates.md) for more details on templates.
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
model Page<Item> {
|
model Page<Item> {
|
||||||
|
@ -232,7 +229,7 @@ model DogPage {
|
||||||
|
|
||||||
## Meta type references
|
## Meta type references
|
||||||
|
|
||||||
Some model property meta types can be referenced using `::`
|
Some model property meta types can be referenced using `::`.
|
||||||
|
|
||||||
| Name | Example | Description |
|
| Name | Example | Description |
|
||||||
| ---- | ---------------- | ---------------------------------------- |
|
| ---- | ---------------- | ---------------------------------------- |
|
||||||
|
|
|
@ -5,11 +5,11 @@ title: Namespaces
|
||||||
|
|
||||||
# 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
|
## Basics
|
||||||
|
|
||||||
Create a namespace with the `namespace` keyword.
|
You can create a namespace using the `namespace` keyword.
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
namespace SampleNamespace {
|
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
|
```typespec
|
||||||
model Foo {
|
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
|
```typespec
|
||||||
namespace Foo {
|
namespace Foo {
|
||||||
|
@ -41,7 +41,7 @@ namespace Foo {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
or this can be simplified using `.` notation
|
Alternatively, you can simplify this using `.` notation:
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
namespace Foo.Bar.Baz {
|
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
|
```typespec
|
||||||
model A {
|
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
|
```typespec
|
||||||
namespace SampleNamespace;
|
namespace SampleNamespace;
|
||||||
|
@ -67,11 +67,11 @@ namespace SampleNamespace;
|
||||||
model SampleModel {}
|
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
|
```typespec
|
||||||
using SampleNamespace;
|
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
|
```typespec
|
||||||
namespace One {
|
namespace One {
|
||||||
|
@ -90,9 +90,9 @@ namespace One {
|
||||||
|
|
||||||
namespace Two {
|
namespace Two {
|
||||||
using One;
|
using One;
|
||||||
alias B = A; // ok
|
alias B = A; // This is valid
|
||||||
}
|
}
|
||||||
|
|
||||||
alias C = One.A; // not ok
|
alias C = One.A; // This is not valid
|
||||||
alias C = Two.B; // ok
|
alias C = Two.B; // This is valid
|
||||||
```
|
```
|
||||||
|
|
|
@ -5,9 +5,9 @@ title: Operations
|
||||||
|
|
||||||
# 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
|
```typespec
|
||||||
op ping(): void;
|
op ping(): void;
|
||||||
|
@ -15,7 +15,7 @@ op ping(): void;
|
||||||
|
|
||||||
## Parameters
|
## 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
|
```typespec
|
||||||
op feedDog(...CommonParams, name: string): void;
|
op feedDog(...CommonParams, name: string): void;
|
||||||
|
@ -23,7 +23,7 @@ op feedDog(...CommonParams, name: string): void;
|
||||||
|
|
||||||
## Return type
|
## 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
|
```typespec
|
||||||
model DogNotFound {
|
model DogNotFound {
|
||||||
|
@ -33,33 +33,33 @@ model DogNotFound {
|
||||||
op getDog(name: string): Dog | 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
|
```typespec
|
||||||
op Delete(id: string): void;
|
op Delete(id: string): void;
|
||||||
```
|
```
|
||||||
|
|
||||||
its signature can be reused like this:
|
You can reuse its signature like so:
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
op deletePet is Delete;
|
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
|
```typespec
|
||||||
op ReadResource<T>(id: string): T;
|
op ReadResource<T>(id: string): T;
|
||||||
```
|
```
|
||||||
|
|
||||||
The operation template can then be referenced via `is`:
|
You can reference the operation template using `is`:
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
op readPet is ReadResource<Pet>;
|
op readPet is ReadResource<Pet>;
|
||||||
|
@ -67,7 +67,7 @@ op readPet is ReadResource<Pet>;
|
||||||
|
|
||||||
## Referencing model properties
|
## Referencing model properties
|
||||||
|
|
||||||
Model properties can be referenced using the `.` operator for identifiers.
|
You can reference model properties using the `.` operator for identifiers.
|
||||||
|
|
||||||
```tsp
|
```tsp
|
||||||
alias PetName = Pet.name;
|
alias PetName = Pet.name;
|
||||||
|
@ -75,9 +75,9 @@ alias PetName = Pet.name;
|
||||||
|
|
||||||
## Meta type references
|
## Meta type references
|
||||||
|
|
||||||
Some operation meta types can be referenced using `::`
|
Certain operation meta types can be referenced using `::`
|
||||||
|
|
||||||
| Name | Example | Description |
|
| Name | Example | Description |
|
||||||
| ---------- | --------------------- | ----------------------------------------- |
|
| ---------- | --------------------- | ------------------------------------------ |
|
||||||
| parameters | `readPet::parameters` | Reference the parameters model expression |
|
| parameters | `readPet::parameters` | References the parameters model expression |
|
||||||
| returnType | `readPet::returnType` | Reference the operation return type |
|
| returnType | `readPet::returnType` | References the operation return type |
|
||||||
|
|
|
@ -5,20 +5,20 @@ title: Overview
|
||||||
|
|
||||||
# Language 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
|
## 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 -->
|
<!-- prettier-ignore -->
|
||||||
```typespec
|
```typespec
|
||||||
model Dog {}
|
model Dog {}
|
||||||
namespace Dog {}
|
namespace Dog {}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Imports
|
## Imports
|
||||||
|
|
||||||
_Details: [Imports](./imports.md)_
|
_For more details, see: [Imports](./imports.md)_
|
||||||
|
|
||||||
| Feature | Example |
|
| Feature | Example |
|
||||||
| -------------------- | ------------------------- |
|
| -------------------- | ------------------------- |
|
||||||
|
@ -28,7 +28,7 @@ _Details: [Imports](./imports.md)_
|
||||||
|
|
||||||
## Namespaces
|
## Namespaces
|
||||||
|
|
||||||
_Details: [Namespaces](./namespaces.md)_
|
_For more details, see: [Namespaces](./namespaces.md)_
|
||||||
|
|
||||||
| Feature | Example |
|
| Feature | Example |
|
||||||
| ----------------- | ---------------------------- |
|
| ----------------- | ---------------------------- |
|
||||||
|
@ -39,7 +39,7 @@ _Details: [Namespaces](./namespaces.md)_
|
||||||
|
|
||||||
## Decorators
|
## Decorators
|
||||||
|
|
||||||
_Details: [Decorators](./decorators.md)_
|
_For more details, see: [Decorators](./decorators.md)_
|
||||||
|
|
||||||
| Feature | Example |
|
| Feature | Example |
|
||||||
| ---------------------------- | ----------------------------------------------------------------------------------- |
|
| ---------------------------- | ----------------------------------------------------------------------------------- |
|
||||||
|
@ -51,7 +51,7 @@ _Details: [Decorators](./decorators.md)_
|
||||||
|
|
||||||
## Scalars
|
## Scalars
|
||||||
|
|
||||||
_Details: [Scalars](./scalars.md)_
|
_For more details, see: [Scalars](./scalars.md)_
|
||||||
|
|
||||||
| Feature | Example |
|
| Feature | Example |
|
||||||
| ------------------ | ------------------------------------------- |
|
| ------------------ | ------------------------------------------- |
|
||||||
|
@ -61,7 +61,7 @@ _Details: [Scalars](./scalars.md)_
|
||||||
|
|
||||||
## Models
|
## Models
|
||||||
|
|
||||||
_Details: [Models](./models.md)_
|
_For more details, see: [Models](./models.md)_
|
||||||
|
|
||||||
| Feature | Example |
|
| Feature | Example |
|
||||||
| ------------------------------ | ------------------------------------- |
|
| ------------------------------ | ------------------------------------- |
|
||||||
|
@ -76,7 +76,7 @@ _Details: [Models](./models.md)_
|
||||||
|
|
||||||
## Operations
|
## Operations
|
||||||
|
|
||||||
_Details: [Operations](./operations.md)_
|
_For more details, see: [Operations](./operations.md)_
|
||||||
|
|
||||||
| Feature | Example |
|
| Feature | Example |
|
||||||
| ----------------------------- | ------------------------------------------------ |
|
| ----------------------------- | ------------------------------------------------ |
|
||||||
|
@ -89,7 +89,7 @@ _Details: [Operations](./operations.md)_
|
||||||
|
|
||||||
## Interfaces
|
## Interfaces
|
||||||
|
|
||||||
_Details: [Interfaces](./interfaces.md)_
|
_For more details, see: [Interfaces](./interfaces.md)_
|
||||||
|
|
||||||
| Feature | Example |
|
| Feature | Example |
|
||||||
| --------------------- | -------------------------------------- |
|
| --------------------- | -------------------------------------- |
|
||||||
|
@ -99,7 +99,7 @@ _Details: [Interfaces](./interfaces.md)_
|
||||||
|
|
||||||
## Templates
|
## Templates
|
||||||
|
|
||||||
_Details: [Templates](./templates.md)_
|
_For more details, see: [Templates](./templates.md)_
|
||||||
|
|
||||||
| Feature | Example |
|
| Feature | Example |
|
||||||
| --------------------------------- | --------------------------------------------------- |
|
| --------------------------------- | --------------------------------------------------- |
|
||||||
|
@ -111,7 +111,7 @@ _Details: [Templates](./templates.md)_
|
||||||
|
|
||||||
## Enums
|
## Enums
|
||||||
|
|
||||||
_Details: [Enums](./enums.md)_
|
_For more details, see: [Enums](./enums.md)_
|
||||||
|
|
||||||
| Feature | Example |
|
| Feature | Example |
|
||||||
| ------------------ | ---------------------------------------------- |
|
| ------------------ | ---------------------------------------------- |
|
||||||
|
@ -123,7 +123,7 @@ _Details: [Enums](./enums.md)_
|
||||||
|
|
||||||
## Unions
|
## Unions
|
||||||
|
|
||||||
_Details: [Unions](./unions.md)_
|
_For more details, see: [Unions](./unions.md)_
|
||||||
|
|
||||||
| Feature | Example |
|
| Feature | Example |
|
||||||
| ----------------------- | -------------------------------- |
|
| ----------------------- | -------------------------------- |
|
||||||
|
@ -132,7 +132,7 @@ _Details: [Unions](./unions.md)_
|
||||||
|
|
||||||
## Intersections
|
## Intersections
|
||||||
|
|
||||||
_Details: [Intersections](./intersections.md)_
|
_For more details, see: [Intersections](./intersections.md)_
|
||||||
|
|
||||||
| Feature | Example |
|
| Feature | Example |
|
||||||
| ------------------------ | -------------- |
|
| ------------------------ | -------------- |
|
||||||
|
@ -140,7 +140,7 @@ _Details: [Intersections](./intersections.md)_
|
||||||
|
|
||||||
## Type literals
|
## Type literals
|
||||||
|
|
||||||
_Details: [Type literals](./type-literals.md)_
|
_For more details, see: [Type literals](./type-literals.md)_
|
||||||
|
|
||||||
| Feature | Example |
|
| Feature | Example |
|
||||||
| ----------------- | -------------------------------------------------------- |
|
| ----------------- | -------------------------------------------------------- |
|
||||||
|
@ -152,7 +152,7 @@ _Details: [Type literals](./type-literals.md)_
|
||||||
|
|
||||||
## Aliases
|
## Aliases
|
||||||
|
|
||||||
_Details: [Aliases](./alias.md)_
|
_For more details, see: [Aliases](./alias.md)_
|
||||||
|
|
||||||
| Feature | Example |
|
| Feature | Example |
|
||||||
| ----------------- | --------------------------------- |
|
| ----------------- | --------------------------------- |
|
||||||
|
|
|
@ -4,25 +4,25 @@ title: Scalars
|
||||||
|
|
||||||
# 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
|
```typespec
|
||||||
scalar ternary;
|
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
|
```typespec
|
||||||
scalar Password extends string;
|
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
|
```typespec
|
||||||
@doc(Type)
|
@doc(Type)
|
||||||
|
|
|
@ -5,9 +5,9 @@ title: Templates
|
||||||
|
|
||||||
# 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)
|
- [aliases](./alias.md)
|
||||||
- [models](./models.md)
|
- [models](./models.md)
|
||||||
|
@ -27,7 +27,7 @@ model DogPage {
|
||||||
|
|
||||||
## Default values
|
## 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
|
```typespec
|
||||||
model Page<Item = string> {
|
model Page<Item = string> {
|
||||||
|
@ -38,13 +38,13 @@ model Page<Item = string> {
|
||||||
|
|
||||||
## Parameter constraints
|
## 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
|
```typespec
|
||||||
alias Foo<Type extends string> = Type;
|
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
|
```typespec
|
||||||
alias Bar = Foo<123>;
|
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;
|
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
|
```typespec
|
||||||
alias Foo<Type extends string = "Abc"> = Type
|
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'
|
^ 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
|
```typespec
|
||||||
// Invalid
|
// Invalid
|
||||||
|
@ -77,7 +77,7 @@ alias Foo<T extends string = "Abc", U> = ...;
|
||||||
|
|
||||||
## Named template arguments
|
## 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
|
```typespec
|
||||||
alias Test<T, U extends numeric = int32, V extends string = "example"> = ...;
|
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
|
id: type-literals
|
||||||
title: Type literals
|
title: Type Literals
|
||||||
---
|
---
|
||||||
|
|
||||||
# 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
|
||||||
|
|
||||||
String literals can be represented using double quotes `"`
|
String literals are represented using double quotes `"`.
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
alias Str = "Hello World!";
|
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
|
```typespec
|
||||||
alias Str = """
|
alias Str = """
|
||||||
|
@ -27,14 +27,14 @@ This is a multi line string
|
||||||
""";
|
""";
|
||||||
```
|
```
|
||||||
|
|
||||||
- Opening `"""` must be followed by a new line.
|
- The opening `"""` must be followed by a new line.
|
||||||
- Closing `"""` must be preceded 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
|
```typespec
|
||||||
model MultiLineContainer {
|
model MultiLineContainer {
|
||||||
|
@ -64,7 +64,7 @@ two
|
||||||
|
|
||||||
## String template literal
|
## 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
|
```typespec
|
||||||
alias hello = "bonjour";
|
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 literal
|
||||||
|
|
||||||
Numeric literals can be declared by using the raw number
|
Numeric literals are declared by using the raw number.
|
||||||
|
|
||||||
```typespec
|
```typespec
|
||||||
alias Kilo = 1000;
|
alias Kilo = 1000;
|
||||||
|
@ -89,7 +89,7 @@ alias PI = 3.14;
|
||||||
|
|
||||||
## Boolean literal
|
## 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
|
```typespec
|
||||||
alias InTypeSpec = true;
|
alias InTypeSpec = true;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
---
|
---
|
||||||
id: type-relations
|
id: type-relations
|
||||||
title: Type relations
|
title: Type Relations
|
||||||
---
|
---
|
||||||
|
|
||||||
# Types Relations
|
# Type relations
|
||||||
|
|
||||||
## Type hierarchy
|
## Type hierarchy
|
||||||
|
|
||||||
|
@ -43,9 +43,9 @@ graph RL
|
||||||
|
|
||||||
## Model with properties
|
## 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
|
```typespec
|
||||||
model T {
|
model T {
|
||||||
|
@ -85,7 +85,7 @@ model S {
|
||||||
|
|
||||||
## `Record<T>`
|
## `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
|
```typespec
|
||||||
// Represent an object where all the values are int32.
|
// 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
|
```typespec
|
||||||
alias T = Record<int32>;
|
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
|
```typespec
|
||||||
model Foo is S {
|
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
|
||||||
|
|
||||||
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
|
- Union expressions
|
||||||
- named unions
|
- Named unions
|
||||||
|
|
||||||
## Union expressions
|
## 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
|
```typespec
|
||||||
alias Breed = Beagle | GermanShepherd | GoldenRetriever;
|
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
|
||||||
|
|
||||||
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
|
```typespec
|
||||||
union Breed {
|
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
|
## 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
|
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.
|
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.
|
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.
|
||||||
|
|
||||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче