# TypeSpec Client Customizations Reference ## Quick Setup ### 1. Project Structure ``` project/ ├── main.tsp # Your service definition ├── client.tsp # Client customizations └── tspconfig.yaml # Compiler configuration ``` ### 2. Basic client.tsp Template ```typespec import "./main.tsp"; import "@azure-tools/typespec-client-generator-core"; using Azure.ClientGenerator.Core; // Your customizations here ``` ## Client Customizations Namespace `client.tsp` should have a file-level namespace if types (e.g. models, interfaces, operations, etc) are defined in `client.tsp`. Do not add a file-level namespace if one already exists. They are only required when types are defined in `client.tsp`. ```typespec import "./main.tsp"; import "@azure-tools/typespec-client-generator-core"; using Azure.ClientGenerator.Core; namespace ClientCustomizations; // Your customizations here ``` ## Universal Scope Parameter **IMPORTANT**: All Azure.ClientGenerator.Core decorators support an optional `scope` parameter as their final parameter to target specific language emitters. ### Scope Syntax ```typespec @decoratorName(/* decorator-specific params */, scope?: string) ``` ### Scope Patterns #### Target Specific Languages ```typespec // Single language @@clientName(Foo, "Bar", "python") @@access(Foo.get, Access.internal, "csharp") // Multiple languages (comma-separated) @@clientName(Foo, "Bar", "python, javascript") ``` #### Exclude Languages (Negation) ```typespec // Exclude one language @@clientName(Foo, "Bar", "!csharp") // All languages EXCEPT C# // Exclude multiple languages @@clientName(Foo, "Bar", "!python, !go") // All languages EXCEPT python and go ``` #### Language Identifiers - `"csharp"` - C#/.NET - `"python"` - Python - `"java"` - Java - `"javascript"` - TypeScript/JavaScript - `"go"` - Go - `"rust"` - Rust ### Scope Best Practices #### DO: Use negation for single language exclusions ```typespec // Good: Exclude only C# @@clientName(get, "getFoo", "!csharp") // Bad: List all other languages @@clientName(get, "getFoo", "python, java, javascript, go") ``` #### DO: Combine scopes when logic is identical ```typespec // Good: Same customization for multiple languages @@clientName(name, "sharedName", "python, go") // Avoid: Duplicate decorators @@clientName(name, "sharedName", "python") @@clientName(name, "sharedName", "go") ``` #### DON'T: Overuse scopes without clear need ```typespec // Bad: Unnecessary scope for universal customization @@clientName(MyService, "MyClient", "csharp, python, java, javascript, go") // Good: No scope means all languages @@clientName(MyService, "MyClient") ``` ## Core Decorators ### @access **Purpose**: Control visibility of types and operations in generated clients. **Syntax**: `@access(value: Access.public | Access.internal, scope?: string)` **Usage**: ```typespec // Hide internal operations @@access(getFoo, Access.internal) // Make models referenced by internal operations public @@access(Foo, Access.public) // Language-specific access @@access(getFoo, Access.internal, "csharp") ``` **Propagation Rules**: - Operations marked `Access.internal` make their models internal - Operations marked `Access.public` make their models public - Namespace access propagates to contained types - Model access propagates to properties and inheritance hierarchy ### @client **Purpose**: Define root clients in the SDK. **Restrictions**: Cannot be used with `@clientLocation` decorator. Cannot be used as an augmentation (`@@`) decorator. **Important**: `@client` has to be used on a type defined in `client.tsp`, so a file-level namespace (e.g. `namespace ClientCustomizations;`) should be added if one does not exist. **Syntax**: `@client(options: ClientOptions, scope?: string)` **Usage**: ```typespec // Basic client @client({ service: MyService }) interface MyClient {} // Named client @client({ service: MyService, name: "CustomClient" }) interface MyClient {} // Split operations into multiple clients @client({ service: PetStore, name: "FoodClient" }) interface FoodClient { feed is PetStore.feed; } @client({ service: PetStore, name: "PetClient" }) interface PetClient { pet is PetStore.pet; } ``` ### @operationGroup **Purpose**: Define sub-clients (operation groups). **Restrictions**: Cannot be used with `@clientLocation` decorator. Cannot be used as an augmentation (`@@`) decorator. **Important**: `@operationGroup` has to be used on a type defined in `client.tsp`, so a file-level namespace (e.g. `namespace ClientCustomizations;`) should be added if one does not exist. **Syntax**: `@operationGroup(scope?: string)` **Usage**: ```typespec @client({ service: MyService }) namespace MyClient; @operationGroup interface Pets { list is MyService.listPets; get is MyService.getPet; } @operationGroup interface Users { list is MyService.listUsers; create is MyService.createUser; } ``` ### @clientLocation **Purpose**: Move operations between clients without restructuring. **Restrictions**: Cannot be used with `@client` or `@operationGroup` decorators. **Syntax**: `@clientLocation(target: Interface | Namespace | string, scope?: string)` **Usage**: ```typespec // Move to existing client @@clientLocation(MyService.upload, AdminOperations); // Move to new client @@clientLocation(MyService.archive, "ArchiveClient"); // Move to root client @@clientLocation(MyService.SubClient.health, MyService); // Move parameter to client initialization @@clientLocation(MyService.upload.subscriptionId, MyService); ``` ### @clientName **Purpose**: Override generated names for SDK elements. Takes precedence over all other naming mechanisms. **Important**: Always use PascalCase or camelCase for the rename parameter to make it easier to combine language scopes. SDKs will apply language-specific naming conventions automatically. **Syntax**: `@clientName(rename: string, scope?: string)` **Usage**: ```typespec // Rename Type @@clientName(PetStore, "PetStoreClient"); // Language-specific names @@clientName(foo, "pythonicFoo", "python") @@clientName(foo, "CSharpFoo", "csharp") ``` ### @clientNamespace **Purpose**: Change the namespace/package of generated types in the client SDK. **Syntax**: `@clientNamespace(rename: string, scope?: string)` **Usage**: ```typespec // Change client namespace @@clientNamespace(MyService, "MyClient"); // Move model to different namespace @@clientNamespace(MyService.MyModel, "MyClient.Models") ``` ### @clientInitialization **Purpose**: Add custom parameters to client initialization. **Important**: When `@clientInitialization` references a model defined in `client.tsp`, add a file-level namespace (e.g. `namespace ClientCustomizations;`) if one does not exist. **Syntax**: `@clientInitialization(options: ClientInitializationOptions, scope?: string)` **Usage**: ```typespec // Add initialization parameters model MyClientOptions { connectionString: string; } @@clientInitialization(MyService, { parameters: MyClientOptions }); // With parameter aliasing model MyClientOptions { @paramAlias("subscriptionId") subId: string; } @@clientInitialization(MyService, { parameters: MyClientOptions }); ``` ### @alternateType **Purpose**: Replace types in generated clients. **Syntax**: `@alternateType(alternate: Type | ExternalType, scope?: string)` **Usage**: ```typespec // Change property type @@alternateType(Foo.date, string); // Language-specific alternates @@alternateType(Foo.date, string, "python") // External type replacement @@alternateType(uri, { identity: "System.Uri", package: "System", }, "csharp") ``` ### @override **Purpose**: Customize method signatures in generated clients. **Restrictions**: Only operation parameter signatures can be customized. **Important**: When `@override` references an operation defined in `client.tsp`, a file-level namespace (e.g. `namespace ClientCustomizations;`) should be added if one does not exist. **Syntax**: `@override(override: Operation, scope?: string)` **Usage**: ```typespec // main.tsp // Original operation op myOperation(foo: string, bar: string): void; // client.tsp // Custom signature - combine into options model MyOperationOptions { foo: string; bar: string; } op myOperationCustom(options: MyOperationOptions): void; @@override(myOperation, myOperationCustom); ``` ### @scope **Purpose**: Include/exclude operations from specific languages. **Usage**: ```typespec @@scope(Foo.create, "!csharp") // All languages except C# @@scope(Foo.create, "python") // Python only @@scope(Foo.create, "java, go") // Java and Go only ``` ### @usage **Purpose**: Add usage information to models and enums. **Note**: The usages provided are _additive_. **Usage**: ```typespec // Add input and output usage to type @@usage(MyModel, Usage.input | Usage.output) ``` **Usage Values**: - `Usage.input`: Used in request - `Usage.output`: Used in response - `Usage.json`: Used with JSON content type - `Usage.xml`: Used with XML content type ### @clientDoc **Purpose**: Override documentation for client libraries. **Usage**: ```typespec // Replace type documentation with client documentation @@clientDoc(myOperation, "Client-specific documentation", DocumentationMode.replace) // Append type documentation with client documentation - for only python @@clientDoc(myModel, "Additional client notes", DocumentationMode.append, "python") ``` ## Language-specific Customizations ### @useSystemTextJsonConverter (C# only) **Purpose**: Use custom JSON converter for backward compatibility. **Usage**: ```typespec @@useSystemTextJsonConverter(MyModel, "csharp") ``` ## Best Practices ### Do's - Use `client.tsp` for all customizations - Use scope parameter for language-specific customizations - Prefer scope negation (`"!csharp"`) when excluding single languages - Combine scopes (`"python, java"`) when logic is identical across languages - Use a file-level namespace (e.g. `namespace ClientCustomizations;`) in `client.tsp` if any types are defined in `client.tsp`. ### Don'ts - Mix `@clientLocation` with `@client`/`@operationGroup` - Over-customize - prefer defaults when possible - Use legacy decorators for new services - Rename without considering the impact on breaking changes - Forget to specify scope for language-specific customizations ## Common Scenarios ### Scenario 1: Move Operations to Root Client ```typespec // Before: Operations in interfaces become sub-clients interface Pets { feed(): void; } interface Users { login(): void; } // After: All operations on root client @@clientLocation(Pets.feed, MyService); @@clientLocation(Users.login, MyService); ``` ### Scenario 2: Rename for Consistency ```typespec // Standardize naming across languages @@clientName(MyService, "MyServiceClient"); @@clientName(getUserInfo, "GetUserInformation"); // Language-specific naming @@clientName(uploadFile, "UploadFile", "csharp, python"); ``` ### Scenario 3: Add Client Parameters ```typespec // Elevate common parameters to client model MyServiceOptions { subscriptionId: string; resourceGroup: string; } @@clientInitialization(MyService, { parameters: MyServiceOptions }); ``` ### Scenario 4: Multi-Client Architecture ```typespec // Separate admin and user operations @client({ service: MyService, name: "AdminClient" }) interface AdminClient { deleteUser is MyService.deleteUser; manageRoles is MyService.manageRoles; } @client({ service: MyService, name: "UserClient" }) interface UserClient { getProfile is MyService.getProfile; updateProfile is MyService.updateProfile; } ``` ### Scenario 5: Language-specific clients ```typespec // Different client names for Java and others @client({ service: MyService, name: "Foo.MyServiceClient" }, "java") @client({ service: MyService, name: "MyServiceClient" }, "!java") interface MyServiceClient { getAllData is MyService.getAllData; } // Different clients for python and Go @client({ service: MyService, name: "MyClient" }, "python") interface MyClientPython { fetchData is MyService.fetchData; } @client({ service: MyService, name: "MyClient" }, "go") interface MyClientGo { fetchStream is MyService.fetchStream; } ``` ### Scenario 6: Rename custom client operations ```typespec // Create a client with operation names changed @client({ service: MyService, name: "MyClient" }) interface MyClient { getFoo is MyService.getFooData; } ``` This reference provides the essential patterns and decorators for TypeSpec client customizations. Focus on the core decorators (`@client`, `@operationGroup`, `@@clientLocation`, `@@clientName`, `@@access`) for most scenarios, and use advanced features selectively.