Feature: `valueof` for string, numeric and boolean literals (#1877)
This commit is contained in:
Родитель
d53a6f2b2a
Коммит
6733c5b0a8
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"changes": [
|
||||
{
|
||||
"packageName": "@typespec/compiler",
|
||||
"comment": "Added new keyword `valueof` designed to request for a value type in a decorator parameter.",
|
||||
"type": "none"
|
||||
},
|
||||
{
|
||||
"packageName": "@typespec/compiler",
|
||||
"comment": "**BREAKING** Decorator API will not be marshalling values unless the parameter type is using `valueof`. `extern dec foo(target, value: string)` should be changed to `extern dec foo(target, value: valueof string)`.",
|
||||
"type": "none"
|
||||
},
|
||||
{
|
||||
"packageName": "@typespec/compiler",
|
||||
"comment": "**DEPRECATION** To make transition to valueof smoother if using a template parameter inside a decorator that is now using valueof the existing parmater constraint will still be compatible but emit a warning.",
|
||||
"type": "none"
|
||||
}
|
||||
],
|
||||
"packageName": "@typespec/compiler"
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"changes": [
|
||||
{
|
||||
"packageName": "@typespec/http",
|
||||
"comment": "Update decorators to use `valueof`",
|
||||
"type": "none"
|
||||
}
|
||||
],
|
||||
"packageName": "@typespec/http"
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"changes": [
|
||||
{
|
||||
"packageName": "@typespec/openapi",
|
||||
"comment": "Update decorators to use `valueof`",
|
||||
"type": "none"
|
||||
}
|
||||
],
|
||||
"packageName": "@typespec/openapi"
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"changes": [
|
||||
{
|
||||
"packageName": "@typespec/protobuf",
|
||||
"comment": "Update decorators to use `valueof`",
|
||||
"type": "minor"
|
||||
}
|
||||
],
|
||||
"packageName": "@typespec/protobuf"
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"changes": [
|
||||
{
|
||||
"packageName": "@typespec/rest",
|
||||
"comment": "Update decorators to use `valueof`",
|
||||
"type": "none"
|
||||
}
|
||||
],
|
||||
"packageName": "@typespec/rest"
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"changes": [
|
||||
{
|
||||
"packageName": "@typespec/versioning",
|
||||
"comment": "Update decorators to use `valueof`",
|
||||
"type": "none"
|
||||
}
|
||||
],
|
||||
"packageName": "@typespec/versioning"
|
||||
}
|
|
@ -40,7 +40,7 @@ extern dec track(target: Model | Enum);
|
|||
A decorator parameter can be marked optional using `?`
|
||||
|
||||
```typespec
|
||||
extern dec track(target: Model | Enum, name?: StringLiteral);
|
||||
extern dec track(target: Model | Enum, name?: valueof string);
|
||||
```
|
||||
|
||||
### Rest parameters
|
||||
|
@ -48,7 +48,29 @@ extern dec track(target: Model | Enum, name?: StringLiteral);
|
|||
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`
|
||||
|
||||
```typespec
|
||||
extern dec track(target: Model | Enum, ...names: StringLiteral[]);
|
||||
extern dec track(target: Model | Enum, ...names: valueof string[]);
|
||||
```
|
||||
|
||||
## Ask for a value type
|
||||
|
||||
It is common that decorators parameter will expect a value(e.g. a string or a number). However just using `: string` as the type will also allow a user of the decorator to pass `string` itself or a custom scalar extending string as well as union of strings.
|
||||
Instead the decorator can use `valueof <T>` to specify that it is expecting a value of that kind.
|
||||
|
||||
| Example | Description |
|
||||
| ----------------- | ---------------- |
|
||||
| `valueof string` | Expect a string |
|
||||
| `valueof float64` | Expect a float |
|
||||
| `valueof int32` | Expect a number |
|
||||
| `valueof boolean` | Expect a boolean |
|
||||
|
||||
```tsp
|
||||
extern dec tag(target: unknown, value: valueof string);
|
||||
|
||||
// bad
|
||||
@tag(string)
|
||||
|
||||
// good
|
||||
@tag("This is the tag name")
|
||||
```
|
||||
|
||||
## Implement the decorator in JS
|
||||
|
@ -63,7 +85,7 @@ Decorators can be implemented in JavaScript by prefixing the function name with
|
|||
// model.ts
|
||||
import type { DecoratorContext, Type } from "@typespec/compiler";
|
||||
|
||||
export function $logType(context: DecoratorContext, target: Type, name: string) {
|
||||
export function $logType(context: DecoratorContext, target: Type, name: valueof string) {
|
||||
console.log(name + ": " + targetType.kind);
|
||||
}
|
||||
```
|
||||
|
@ -92,13 +114,13 @@ model Dog {
|
|||
|
||||
### Decorator parameter marshalling
|
||||
|
||||
For certain TypeSpec types(Literal types) the decorator do not receive the actual type but a marshalled value. This is to simplify the most common cases.
|
||||
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.
|
||||
|
||||
| TypeSpec Type | Marshalled value in JS |
|
||||
| ---------------- | ---------------------- |
|
||||
| `StringLiteral` | `string` |
|
||||
| `NumericLiteral` | `number` |
|
||||
| `BooleanLiteral` | `boolean` |
|
||||
| TypeSpec Type | Marshalled value in JS |
|
||||
| ----------------- | ---------------------- |
|
||||
| `valueof string` | `string` |
|
||||
| `valueof numeric` | `number` |
|
||||
| `valueof boolean` | `boolean` |
|
||||
|
||||
for all the other types they are not transformed.
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ toc_max_heading_level: 3
|
|||
Mark this type as deprecated
|
||||
|
||||
```typespec
|
||||
@deprecated(message: string)
|
||||
@deprecated(message: valueof string)
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -21,7 +21,7 @@ Mark this type as deprecated
|
|||
#### Parameters
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| message | `scalar string` | Deprecation message. |
|
||||
| message | `valueof scalar string` | Deprecation message. |
|
||||
|
||||
#### Examples
|
||||
|
||||
|
@ -36,7 +36,7 @@ op Action<T>(): T;
|
|||
Specify the property to be used to discriminate this type.
|
||||
|
||||
```typespec
|
||||
@discriminator(propertyName: string)
|
||||
@discriminator(propertyName: valueof string)
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -46,7 +46,7 @@ Specify the property to be used to discriminate this type.
|
|||
#### Parameters
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| propertyName | `scalar string` | The property name to use for discrimination |
|
||||
| propertyName | `valueof scalar string` | The property name to use for discrimination |
|
||||
|
||||
#### Examples
|
||||
|
||||
|
@ -72,7 +72,7 @@ model Dog extends Pet {kind: "dog", bark: boolean}
|
|||
Attach a documentation string.
|
||||
|
||||
```typespec
|
||||
@doc(doc: string, formatArgs?: {})
|
||||
@doc(doc: valueof string, formatArgs?: {})
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -82,7 +82,7 @@ Attach a documentation string.
|
|||
#### Parameters
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| doc | `scalar string` | Documentation string |
|
||||
| doc | `valueof scalar string` | Documentation string |
|
||||
| formatArgs | `model {}` | Record with key value pair that can be interpolated in the doc. |
|
||||
|
||||
#### Examples
|
||||
|
@ -162,7 +162,7 @@ This differs from the `@pattern` decorator which is meant to specify a regular e
|
|||
The format names are open ended and are left to emitter to interpret.
|
||||
|
||||
```typespec
|
||||
@format(format: string)
|
||||
@format(format: valueof string)
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -172,7 +172,7 @@ The format names are open ended and are left to emitter to interpret.
|
|||
#### Parameters
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| format | `scalar string` | format name. |
|
||||
| format | `valueof scalar string` | format name. |
|
||||
|
||||
#### Examples
|
||||
|
||||
|
@ -187,7 +187,7 @@ scalar uuid extends string;
|
|||
Specifies how a templated type should name their instances.
|
||||
|
||||
```typespec
|
||||
@friendlyName(name: string, formatArgs?: unknown)
|
||||
@friendlyName(name: valueof string, formatArgs?: unknown)
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -197,7 +197,7 @@ Specifies how a templated type should name their instances.
|
|||
#### Parameters
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| name | `scalar string` | name the template instance should take |
|
||||
| name | `valueof scalar string` | name the template instance should take |
|
||||
| formatArgs | `(intrinsic) unknown` | Model with key value used to interpolate the name |
|
||||
|
||||
#### Examples
|
||||
|
@ -216,7 +216,7 @@ nextLink: string;
|
|||
A debugging decorator used to inspect a type.
|
||||
|
||||
```typespec
|
||||
@inspectType(text: string)
|
||||
@inspectType(text: valueof string)
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -226,7 +226,7 @@ A debugging decorator used to inspect a type.
|
|||
#### Parameters
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| text | `scalar string` | Custom text to log |
|
||||
| text | `valueof scalar string` | Custom text to log |
|
||||
|
||||
|
||||
|
||||
|
@ -235,7 +235,7 @@ A debugging decorator used to inspect a type.
|
|||
A debugging decorator used to inspect a type name.
|
||||
|
||||
```typespec
|
||||
@inspectTypeName(text: string)
|
||||
@inspectTypeName(text: valueof string)
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -245,7 +245,7 @@ A debugging decorator used to inspect a type name.
|
|||
#### Parameters
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| text | `scalar string` | Custom text to log |
|
||||
| text | `valueof scalar string` | Custom text to log |
|
||||
|
||||
|
||||
|
||||
|
@ -254,7 +254,7 @@ A debugging decorator used to inspect a type name.
|
|||
Mark a model property as the key to identify instances of that type
|
||||
|
||||
```typespec
|
||||
@key(altName?: string)
|
||||
@key(altName?: valueof string)
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -264,7 +264,7 @@ Mark a model property as the key to identify instances of that type
|
|||
#### Parameters
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| altName | `scalar string` | Name of the property. If not specified, the decorated property name is used. |
|
||||
| altName | `valueof scalar string` | Name of the property. If not specified, the decorated property name is used. |
|
||||
|
||||
#### Examples
|
||||
|
||||
|
@ -310,7 +310,7 @@ Invalid,
|
|||
Specify the maximum number of items this array should have.
|
||||
|
||||
```typespec
|
||||
@maxItems(value: integer)
|
||||
@maxItems(value: valueof integer)
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -320,7 +320,7 @@ Specify the maximum number of items this array should have.
|
|||
#### Parameters
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| value | `scalar integer` | Maximum number |
|
||||
| value | `valueof scalar integer` | Maximum number |
|
||||
|
||||
#### Examples
|
||||
|
||||
|
@ -335,7 +335,7 @@ model Endpoints is string[];
|
|||
Specify the maximum length this string type should be.
|
||||
|
||||
```typespec
|
||||
@maxLength(value: integer)
|
||||
@maxLength(value: valueof integer)
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -345,7 +345,7 @@ Specify the maximum length this string type should be.
|
|||
#### Parameters
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| value | `scalar integer` | Maximum length |
|
||||
| value | `valueof scalar integer` | Maximum length |
|
||||
|
||||
#### Examples
|
||||
|
||||
|
@ -360,7 +360,7 @@ scalar Username extends string;
|
|||
Specify the maximum value this numeric type should be.
|
||||
|
||||
```typespec
|
||||
@maxValue(value: numeric)
|
||||
@maxValue(value: valueof numeric)
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -370,7 +370,7 @@ Specify the maximum value this numeric type should be.
|
|||
#### Parameters
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| value | `scalar numeric` | Maximum value |
|
||||
| value | `valueof scalar numeric` | Maximum value |
|
||||
|
||||
#### Examples
|
||||
|
||||
|
@ -386,7 +386,7 @@ Specify the maximum value this numeric type should be, exclusive of the given
|
|||
value.
|
||||
|
||||
```typespec
|
||||
@maxValueExclusive(value: numeric)
|
||||
@maxValueExclusive(value: valueof numeric)
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -396,7 +396,7 @@ value.
|
|||
#### Parameters
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| value | `scalar numeric` | Maximum value |
|
||||
| value | `valueof scalar numeric` | Maximum value |
|
||||
|
||||
#### Examples
|
||||
|
||||
|
@ -411,7 +411,7 @@ scalar distance is float64;
|
|||
Specify the minimum number of items this array should have.
|
||||
|
||||
```typespec
|
||||
@minItems(value: integer)
|
||||
@minItems(value: valueof integer)
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -421,7 +421,7 @@ Specify the minimum number of items this array should have.
|
|||
#### Parameters
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| value | `scalar integer` | Minimum number |
|
||||
| value | `valueof scalar integer` | Minimum number |
|
||||
|
||||
#### Examples
|
||||
|
||||
|
@ -436,7 +436,7 @@ model Endpoints is string[];
|
|||
Specify the minimum length this string type should be.
|
||||
|
||||
```typespec
|
||||
@minLength(value: integer)
|
||||
@minLength(value: valueof integer)
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -446,7 +446,7 @@ Specify the minimum length this string type should be.
|
|||
#### Parameters
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| value | `scalar integer` | Minimum length |
|
||||
| value | `valueof scalar integer` | Minimum length |
|
||||
|
||||
#### Examples
|
||||
|
||||
|
@ -461,7 +461,7 @@ scalar Username extends string;
|
|||
Specify the minimum value this numeric type should be.
|
||||
|
||||
```typespec
|
||||
@minValue(value: numeric)
|
||||
@minValue(value: valueof numeric)
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -471,7 +471,7 @@ Specify the minimum value this numeric type should be.
|
|||
#### Parameters
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| value | `scalar numeric` | Minimum value |
|
||||
| value | `valueof scalar numeric` | Minimum value |
|
||||
|
||||
#### Examples
|
||||
|
||||
|
@ -487,7 +487,7 @@ Specify the minimum value this numeric type should be, exclusive of the given
|
|||
value.
|
||||
|
||||
```typespec
|
||||
@minValueExclusive(value: numeric)
|
||||
@minValueExclusive(value: valueof numeric)
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -497,7 +497,7 @@ value.
|
|||
#### Parameters
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| value | `scalar numeric` | Minimum value |
|
||||
| value | `valueof scalar numeric` | Minimum value |
|
||||
|
||||
#### Examples
|
||||
|
||||
|
@ -542,7 +542,7 @@ The following syntax is allowed: alternations (`|`), quantifiers (`?`, `*`, `+`,
|
|||
Advanced features like look-around, capture groups, and references are not supported.
|
||||
|
||||
```typespec
|
||||
@pattern(pattern: string)
|
||||
@pattern(pattern: valueof string)
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -552,7 +552,7 @@ Advanced features like look-around, capture groups, and references are not suppo
|
|||
#### Parameters
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| pattern | `scalar string` | Regular expression. |
|
||||
| pattern | `valueof scalar string` | Regular expression. |
|
||||
|
||||
#### Examples
|
||||
|
||||
|
@ -567,7 +567,7 @@ scalar LowerAlpha extends string;
|
|||
Provide an alternative name for this type.
|
||||
|
||||
```typespec
|
||||
@projectedName(targetName: string, projectedName: string)
|
||||
@projectedName(targetName: valueof string, projectedName: valueof string)
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -577,8 +577,8 @@ Provide an alternative name for this type.
|
|||
#### Parameters
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| targetName | `scalar string` | Projection target |
|
||||
| projectedName | `scalar string` | Alternative name |
|
||||
| targetName | `valueof scalar string` | Projection target |
|
||||
| projectedName | `valueof scalar string` | Alternative name |
|
||||
|
||||
#### Examples
|
||||
|
||||
|
@ -657,7 +657,7 @@ namespace PetStore;
|
|||
Typically a short, single-line description.
|
||||
|
||||
```typespec
|
||||
@summary(summary: string)
|
||||
@summary(summary: valueof string)
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -667,7 +667,7 @@ Typically a short, single-line description.
|
|||
#### Parameters
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| summary | `scalar string` | Summary string. |
|
||||
| summary | `valueof scalar string` | Summary string. |
|
||||
|
||||
#### Examples
|
||||
|
||||
|
@ -682,7 +682,7 @@ model Pet {}
|
|||
Attaches a tag to an operation, interface, or namespace. Multiple `@tag` decorators can be specified to attach multiple tags to a TypeSpec element.
|
||||
|
||||
```typespec
|
||||
@tag(tag: string)
|
||||
@tag(tag: valueof string)
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -692,7 +692,7 @@ Attaches a tag to an operation, interface, or namespace. Multiple `@tag` decorat
|
|||
#### Parameters
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| tag | `scalar string` | Tag value |
|
||||
| tag | `valueof scalar string` | Tag value |
|
||||
|
||||
|
||||
|
||||
|
@ -715,7 +715,7 @@ with standard emitters that interpret them as follows:
|
|||
See also: [Automatic visibility](https://microsoft.github.io/typespec/standard-library/http/operations#automatic-visibility)
|
||||
|
||||
```typespec
|
||||
@visibility(...visibilities: string[])
|
||||
@visibility(...visibilities: valueof string[])
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -725,7 +725,7 @@ See also: [Automatic visibility](https://microsoft.github.io/typespec/standard-l
|
|||
#### Parameters
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| visibilities | `model string[]` | List of visibilities which apply to this property. |
|
||||
| visibilities | `valueof model string[]` | List of visibilities which apply to this property. |
|
||||
|
||||
#### Examples
|
||||
|
||||
|
@ -746,7 +746,7 @@ name: string;
|
|||
Set the visibility of key properties in a model if not already set.
|
||||
|
||||
```typespec
|
||||
@withDefaultKeyVisibility(visibility: unknown)
|
||||
@withDefaultKeyVisibility(visibility: valueof string)
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -756,7 +756,7 @@ Set the visibility of key properties in a model if not already set.
|
|||
#### Parameters
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| visibility | `(intrinsic) unknown` | The desired default visibility value. If a key property already has a `visibility` decorator then the default visibility is not applied. |
|
||||
| visibility | `valueof scalar string` | The desired default visibility value. If a key property already has a `visibility` decorator then the default visibility is not applied. |
|
||||
|
||||
|
||||
|
||||
|
@ -843,7 +843,7 @@ When using an emitter that applies visibility automatically, it is generally
|
|||
not necessary to use this decorator.
|
||||
|
||||
```typespec
|
||||
@withVisibility(...visibilities: string[])
|
||||
@withVisibility(...visibilities: valueof string[])
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -853,7 +853,7 @@ not necessary to use this decorator.
|
|||
#### Parameters
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| visibilities | `model string[]` | List of visibilities which apply to this property. |
|
||||
| visibilities | `valueof model string[]` | List of visibilities which apply to this property. |
|
||||
|
||||
#### Examples
|
||||
|
||||
|
|
|
@ -127,7 +127,7 @@ op create(@header({name: "X-Color", format: "csv"}) colors: string[]): void;
|
|||
Specify if inapplicable metadata should be included in the payload for the given entity.
|
||||
|
||||
```typespec
|
||||
@TypeSpec.Http.includeInapplicableMetadataInPayload(value: boolean)
|
||||
@TypeSpec.Http.includeInapplicableMetadataInPayload(value: valueof boolean)
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -136,9 +136,9 @@ Specify if inapplicable metadata should be included in the payload for the given
|
|||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| ----- | ---------------- | --------------------------------------------------------------- |
|
||||
| value | `scalar boolean` | If true, inapplicable metadata will be included in the payload. |
|
||||
| Name | Type | Description |
|
||||
| ----- | ------------------------ | --------------------------------------------------------------- |
|
||||
| value | `valueof scalar boolean` | If true, inapplicable metadata will be included in the payload. |
|
||||
|
||||
### `@patch` {#@TypeSpec.Http.patch}
|
||||
|
||||
|
@ -167,7 +167,7 @@ None
|
|||
Explicitly specify that this property is to be interpolated as a path parameter.
|
||||
|
||||
```typespec
|
||||
@TypeSpec.Http.path(paramName?: string)
|
||||
@TypeSpec.Http.path(paramName?: valueof string)
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -176,9 +176,9 @@ Explicitly specify that this property is to be interpolated as a path parameter.
|
|||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| --------- | --------------- | --------------------------------------------------- |
|
||||
| paramName | `scalar string` | Optional name of the parameter in the url template. |
|
||||
| Name | Type | Description |
|
||||
| --------- | ----------------------- | --------------------------------------------------- |
|
||||
| paramName | `valueof scalar string` | Optional name of the parameter in the url template. |
|
||||
|
||||
#### Examples
|
||||
|
||||
|
@ -267,7 +267,7 @@ it will be used as a prefix to the route URI of the operation.
|
|||
`@route` can only be applied to operations, namespaces, and interfaces.
|
||||
|
||||
```typespec
|
||||
@TypeSpec.Http.route(path: string, options?: (anonymous model))
|
||||
@TypeSpec.Http.route(path: valueof string, options?: (anonymous model))
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -278,7 +278,7 @@ it will be used as a prefix to the route URI of the operation.
|
|||
|
||||
| Name | Type | Description |
|
||||
| ------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| path | `scalar string` | Relative route path. Cannot include query parameters. |
|
||||
| path | `valueof scalar string` | Relative route path. Cannot include query parameters. |
|
||||
| options | `model (anonymous model)` | Set of parameters used to configure the route. Supports `{shared: true}` which indicates that the route may be shared by several operations. |
|
||||
|
||||
#### Examples
|
||||
|
@ -293,7 +293,7 @@ op getWidget(@path id: string): Widget;
|
|||
Specify the endpoint for this service.
|
||||
|
||||
```typespec
|
||||
@TypeSpec.Http.server(url: string, description: string, parameters?: Record<unknown>)
|
||||
@TypeSpec.Http.server(url: valueof string, description: valueof string, parameters?: Record<unknown>)
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -304,8 +304,8 @@ Specify the endpoint for this service.
|
|||
|
||||
| Name | Type | Description |
|
||||
| ----------- | ----------------------- | ------------------------------------------------------- |
|
||||
| url | `scalar string` | Server endpoint |
|
||||
| description | `scalar string` | Description of the endpoint |
|
||||
| url | `valueof scalar string` | Server endpoint |
|
||||
| description | `valueof scalar string` | Description of the endpoint |
|
||||
| parameters | `model Record<unknown>` | Optional set of parameters used to interpolate the url. |
|
||||
|
||||
#### Examples
|
||||
|
|
|
@ -39,7 +39,7 @@ op listPets(): Pet[] | PetStoreResponse;
|
|||
Attach some custom data to the OpenAPI element generated from this type.
|
||||
|
||||
```typespec
|
||||
@OpenAPI.extension(key: string, value: unknown)
|
||||
@OpenAPI.extension(key: valueof string, value: unknown)
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -48,10 +48,10 @@ Attach some custom data to the OpenAPI element generated from this type.
|
|||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| ----- | --------------------- | ----------------------------------- |
|
||||
| key | `scalar string` | Extension key. Must start with `x-` |
|
||||
| value | `(intrinsic) unknown` | Extension value. |
|
||||
| Name | Type | Description |
|
||||
| ----- | ----------------------- | ----------------------------------- |
|
||||
| key | `valueof scalar string` | Extension key. Must start with `x-` |
|
||||
| value | `(intrinsic) unknown` | Extension value. |
|
||||
|
||||
#### Examples
|
||||
|
||||
|
@ -66,7 +66,7 @@ op read(): string;
|
|||
Specify the OpenAPI `externalDocs` property for this type.
|
||||
|
||||
```typespec
|
||||
@OpenAPI.externalDocs(url: string, description?: string)
|
||||
@OpenAPI.externalDocs(url: valueof string, description?: valueof string)
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -75,10 +75,10 @@ Specify the OpenAPI `externalDocs` property for this type.
|
|||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| ----------- | --------------- | ----------------------- |
|
||||
| url | `scalar string` | Url to the docs |
|
||||
| description | `scalar string` | Description of the docs |
|
||||
| Name | Type | Description |
|
||||
| ----------- | ----------------------- | ----------------------- |
|
||||
| url | `valueof scalar string` | Url to the docs |
|
||||
| description | `valueof scalar string` | Description of the docs |
|
||||
|
||||
#### Examples
|
||||
|
||||
|
@ -92,7 +92,7 @@ op listPets(): Pet[];
|
|||
Specify the OpenAPI `operationId` property for this operation.
|
||||
|
||||
```typespec
|
||||
@OpenAPI.operationId(operationId: string)
|
||||
@OpenAPI.operationId(operationId: valueof string)
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -101,9 +101,9 @@ Specify the OpenAPI `operationId` property for this operation.
|
|||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| ----------- | --------------- | ------------------- |
|
||||
| operationId | `scalar string` | Operation id value. |
|
||||
| Name | Type | Description |
|
||||
| ----------- | ----------------------- | ------------------- |
|
||||
| operationId | `valueof scalar string` | Operation id value. |
|
||||
|
||||
#### Examples
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ The field index of a Protobuf message must:
|
|||
- not fall within any range that was [marked reserved](#
|
||||
|
||||
```typespec
|
||||
@TypeSpec.Protobuf.field(index: uint32)
|
||||
@TypeSpec.Protobuf.field(index: valueof uint32)
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -29,9 +29,9 @@ The field index of a Protobuf message must:
|
|||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| ----- | --------------- | ------------------------------------ |
|
||||
| index | `scalar uint32` | The whole-number index of the field. |
|
||||
| Name | Type | Description |
|
||||
| ----- | ----------------------- | ------------------------------------ |
|
||||
| index | `valueof scalar uint32` | The whole-number index of the field. |
|
||||
|
||||
#### Examples
|
||||
|
||||
|
@ -108,7 +108,7 @@ See _[Protobuf Language Guide - Reserved Fields](https://protobuf.dev/programmin
|
|||
information.
|
||||
|
||||
```typespec
|
||||
@TypeSpec.Protobuf.reserve(...reservations: string | [uint32, uint32] | uint32[])
|
||||
@TypeSpec.Protobuf.reserve(...reservations: valueof string | [uint32, uint32] | uint32[])
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -117,9 +117,9 @@ information.
|
|||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| ------------ | ---------------------------------------------- | ---------------------------- |
|
||||
| reservations | `model string \| [uint32, uint32] \| uint32[]` | a list of field reservations |
|
||||
| Name | Type | Description |
|
||||
| ------------ | ------------------------------------------------------ | ---------------------------- |
|
||||
| reservations | `valueof model string \| [uint32, uint32] \| uint32[]` | a list of field reservations |
|
||||
|
||||
#### Examples
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ toc_max_heading_level: 3
|
|||
Specify this operation is an action. (Scoped to a resource item /pets/{petId}/my-action)
|
||||
|
||||
```typespec
|
||||
@TypeSpec.Rest.action(name?: string)
|
||||
@TypeSpec.Rest.action(name?: valueof string)
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -22,16 +22,16 @@ Specify this operation is an action. (Scoped to a resource item /pets/{petId}/my
|
|||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | --------------- | ----------------------------------------------------------------------------- |
|
||||
| name | `scalar string` | Name of the action. If not specified, the name of the operation will be used. |
|
||||
| Name | Type | Description |
|
||||
| ---- | ----------------------- | ----------------------------------------------------------------------------- |
|
||||
| name | `valueof scalar string` | Name of the action. If not specified, the name of the operation will be used. |
|
||||
|
||||
### `@actionSeparator` {#@TypeSpec.Rest.actionSeparator}
|
||||
|
||||
Defines the separator string that is inserted before the action name in auto-generated routes for actions.
|
||||
|
||||
```typespec
|
||||
@TypeSpec.Rest.actionSeparator(seperator: / | : | /:)
|
||||
@TypeSpec.Rest.actionSeparator(seperator: valueof / | : | /:)
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -40,9 +40,9 @@ Defines the separator string that is inserted before the action name in auto-gen
|
|||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| --------- | -------------------- | ---------------------------------------------------------------- |
|
||||
| seperator | `union / \| : \| /:` | Seperator seperating the action segment from the rest of the url |
|
||||
| Name | Type | Description |
|
||||
| --------- | ---------------------------- | ---------------------------------------------------------------- |
|
||||
| seperator | `valueof union / \| : \| /:` | Seperator seperating the action segment from the rest of the url |
|
||||
|
||||
### `@autoRoute` {#@TypeSpec.Rest.autoRoute}
|
||||
|
||||
|
@ -74,7 +74,7 @@ get(@segment("pets") @path id: string): void; //-> route: /pets/{id}
|
|||
Specify this operation is a collection action. (Scopped to a resource, /pets/my-action)
|
||||
|
||||
```typespec
|
||||
@TypeSpec.Rest.collectionAction(resourceType: Model, name?: string)
|
||||
@TypeSpec.Rest.collectionAction(resourceType: Model, name?: valueof string)
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -83,17 +83,17 @@ Specify this operation is a collection action. (Scopped to a resource, /pets/my-
|
|||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| ------------ | --------------- | ----------------------------------------------------------------------------- |
|
||||
| resourceType | `Model` | Resource marked with |
|
||||
| name | `scalar string` | Name of the action. If not specified, the name of the operation will be used. |
|
||||
| Name | Type | Description |
|
||||
| ------------ | ----------------------- | ----------------------------------------------------------------------------- |
|
||||
| resourceType | `Model` | Resource marked with |
|
||||
| name | `valueof scalar string` | Name of the action. If not specified, the name of the operation will be used. |
|
||||
|
||||
### `@copyResourceKeyParameters` {#@TypeSpec.Rest.copyResourceKeyParameters}
|
||||
|
||||
Copy the resource key parameters on the model
|
||||
|
||||
```typespec
|
||||
@TypeSpec.Rest.copyResourceKeyParameters(filter?: string)
|
||||
@TypeSpec.Rest.copyResourceKeyParameters(filter?: valueof string)
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -102,9 +102,9 @@ Copy the resource key parameters on the model
|
|||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| ------ | --------------- | ------------------------------------- |
|
||||
| filter | `scalar string` | Filter to exclude certain properties. |
|
||||
| Name | Type | Description |
|
||||
| ------ | ----------------------- | ------------------------------------- |
|
||||
| filter | `valueof scalar string` | Filter to exclude certain properties. |
|
||||
|
||||
### `@createsOrReplacesResource` {#@TypeSpec.Rest.createsOrReplacesResource}
|
||||
|
||||
|
@ -237,7 +237,7 @@ Specify that this is a Read operation for a given resource.
|
|||
Mark this model as a resource type with a name.
|
||||
|
||||
```typespec
|
||||
@TypeSpec.Rest.resource(collectionName: string)
|
||||
@TypeSpec.Rest.resource(collectionName: valueof string)
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -246,16 +246,16 @@ Mark this model as a resource type with a name.
|
|||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| -------------- | --------------- | ---------------------- |
|
||||
| collectionName | `scalar string` | type's collection name |
|
||||
| Name | Type | Description |
|
||||
| -------------- | ----------------------- | ---------------------- |
|
||||
| collectionName | `valueof scalar string` | type's collection name |
|
||||
|
||||
### `@segment` {#@TypeSpec.Rest.segment}
|
||||
|
||||
Defines the preceding path segment for a
|
||||
|
||||
```typespec
|
||||
@TypeSpec.Rest.segment(name: string)
|
||||
@TypeSpec.Rest.segment(name: valueof string)
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -264,9 +264,9 @@ Defines the preceding path segment for a
|
|||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | --------------- | ---------------------------------------------------------------------------------------------- |
|
||||
| name | `scalar string` | Segment that will be inserted into the operation route before the path parameter's name field. |
|
||||
| Name | Type | Description |
|
||||
| ---- | ----------------------- | ---------------------------------------------------------------------------------------------- |
|
||||
| name | `valueof scalar string` | Segment that will be inserted into the operation route before the path parameter's name field. |
|
||||
|
||||
#### Examples
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ Identifies when the target was removed.
|
|||
Identifies when the target has been renamed.
|
||||
|
||||
```typespec
|
||||
@TypeSpec.Versioning.renamedFrom(version: EnumMember, oldName: string)
|
||||
@TypeSpec.Versioning.renamedFrom(version: EnumMember, oldName: valueof string)
|
||||
```
|
||||
|
||||
#### Target
|
||||
|
@ -76,10 +76,10 @@ Identifies when the target has been renamed.
|
|||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| ------- | --------------- | ------------------------------------------- |
|
||||
| version | `EnumMember` | The version that the target was renamed in. |
|
||||
| oldName | `scalar string` | The previous name of the target. |
|
||||
| Name | Type | Description |
|
||||
| ------- | ----------------------- | ------------------------------------------- |
|
||||
| version | `EnumMember` | The version that the target was renamed in. |
|
||||
| oldName | `valueof scalar string` | The previous name of the target. |
|
||||
|
||||
### `@returnTypeChangedFrom` {#@TypeSpec.Versioning.returnTypeChangedFrom}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { $docFromComment, getDeprecated, getIndexer } from "../lib/decorators.js";
|
||||
import { createSymbol, createSymbolTable } from "./binder.js";
|
||||
import { ProjectionError, compilerAssert } from "./diagnostics.js";
|
||||
import { ProjectionError, compilerAssert, reportDeprecated } from "./diagnostics.js";
|
||||
import { validateInheritanceDiscriminatedUnions } from "./helpers/discriminator-utils.js";
|
||||
import { TypeNameOptions, getNamespaceFullName, getTypeName } from "./helpers/index.js";
|
||||
import { createDiagnostic } from "./messages.js";
|
||||
|
@ -124,6 +124,8 @@ import {
|
|||
UnionVariant,
|
||||
UnionVariantNode,
|
||||
UnknownType,
|
||||
ValueOfExpressionNode,
|
||||
ValueType,
|
||||
VoidType,
|
||||
} from "./types.js";
|
||||
import { MultiKeyMap, Mutable, createRekeyableMap, isArray, mutate } from "./util.js";
|
||||
|
@ -192,10 +194,10 @@ export interface Checker {
|
|||
* @returns [related, list of diagnostics]
|
||||
*/
|
||||
isTypeAssignableTo(
|
||||
source: Type,
|
||||
target: Type,
|
||||
source: Type | ValueType,
|
||||
target: Type | ValueType,
|
||||
diagnosticTarget: DiagnosticTarget
|
||||
): [boolean, Diagnostic[]];
|
||||
): [boolean, readonly Diagnostic[]];
|
||||
|
||||
/**
|
||||
* Check if the given type is one of the built-in standard TypeSpec Types.
|
||||
|
@ -734,7 +736,7 @@ export function createChecker(program: Program): Checker {
|
|||
});
|
||||
|
||||
if (node.constraint) {
|
||||
type.constraint = getTypeForNode(node.constraint);
|
||||
type.constraint = getTypeOrValueTypeForNode(node.constraint);
|
||||
}
|
||||
if (node.default) {
|
||||
type.default = checkTemplateParameterDefault(
|
||||
|
@ -768,7 +770,7 @@ export function createChecker(program: Program): Checker {
|
|||
nodeDefault: Expression,
|
||||
templateParameters: readonly TemplateParameterDeclarationNode[],
|
||||
index: number,
|
||||
constraint: Type | undefined
|
||||
constraint: Type | ValueType | undefined
|
||||
) {
|
||||
function visit(node: Node) {
|
||||
const type = getTypeForNode(node);
|
||||
|
@ -895,7 +897,9 @@ export function createChecker(program: Program): Checker {
|
|||
let [valueNode, value] = args[i];
|
||||
if (declaredType.constraint) {
|
||||
if (!checkTypeAssignable(value, declaredType.constraint, valueNode)) {
|
||||
value = declaredType.constraint;
|
||||
// TODO-TIM check if we expose this below
|
||||
value =
|
||||
declaredType.constraint?.kind === "Value" ? unknownType : declaredType.constraint;
|
||||
}
|
||||
}
|
||||
values.push(value);
|
||||
|
@ -906,7 +910,12 @@ export function createChecker(program: Program): Checker {
|
|||
values.push(defaultValue);
|
||||
} else {
|
||||
tooFew = true;
|
||||
values.push(declaredType.constraint ?? unknownType);
|
||||
values.push(
|
||||
// TODO-TIM check if we expose this below
|
||||
declaredType.constraint?.kind === "Value"
|
||||
? unknownType
|
||||
: declaredType.constraint ?? unknownType
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1205,6 +1214,17 @@ export function createChecker(program: Program): Checker {
|
|||
return unionType;
|
||||
}
|
||||
|
||||
function checkValueOfExpression(
|
||||
node: ValueOfExpressionNode,
|
||||
mapper: TypeMapper | undefined
|
||||
): ValueType {
|
||||
const target = getTypeForNode(node.target, mapper);
|
||||
return {
|
||||
kind: "Value",
|
||||
target,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Intersection produces a model type from the properties of its operands.
|
||||
* So this doesn't work if we don't have a known set of properties (e.g.
|
||||
|
@ -1313,12 +1333,20 @@ export function createChecker(program: Program): Checker {
|
|||
if (links.declaredType) {
|
||||
return links.declaredType as FunctionParameter;
|
||||
}
|
||||
if (node.rest && node.type && node.type.kind !== SyntaxKind.ArrayExpression) {
|
||||
if (
|
||||
node.rest &&
|
||||
node.type &&
|
||||
!(
|
||||
node.type.kind === SyntaxKind.ArrayExpression ||
|
||||
(node.type.kind === SyntaxKind.ValueOfExpression &&
|
||||
node.type.target.kind === SyntaxKind.ArrayExpression)
|
||||
)
|
||||
) {
|
||||
reportCheckerDiagnostic(
|
||||
createDiagnostic({ code: "rest-parameter-array", target: node.type })
|
||||
);
|
||||
}
|
||||
const type = node.type ? getTypeForNode(node.type) : unknownType;
|
||||
const type = node.type ? getTypeOrValueTypeForNode(node.type) : unknownType;
|
||||
|
||||
const parameterType: FunctionParameter = createType({
|
||||
kind: "FunctionParameter",
|
||||
|
@ -1334,6 +1362,13 @@ export function createChecker(program: Program): Checker {
|
|||
return parameterType;
|
||||
}
|
||||
|
||||
function getTypeOrValueTypeForNode(node: Node, mapper?: TypeMapper) {
|
||||
if (node.kind === SyntaxKind.ValueOfExpression) {
|
||||
return checkValueOfExpression(node, mapper);
|
||||
}
|
||||
return getTypeForNode(node, mapper);
|
||||
}
|
||||
|
||||
function mergeModelTypes(
|
||||
node:
|
||||
| ModelStatementNode
|
||||
|
@ -2951,7 +2986,7 @@ export function createChecker(program: Program): Checker {
|
|||
if (doc) {
|
||||
type.decorators.unshift({
|
||||
decorator: $docFromComment,
|
||||
args: [{ value: createLiteralType(doc) }],
|
||||
args: [{ value: createLiteralType(doc), jsValue: doc }],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -3018,7 +3053,7 @@ export function createChecker(program: Program): Checker {
|
|||
|
||||
const symbolLinks = getSymbolLinks(sym);
|
||||
|
||||
const args = checkDecoratorArguments(decNode, mapper);
|
||||
let args = checkDecoratorArguments(decNode, mapper);
|
||||
let hasError = false;
|
||||
if (symbolLinks.declaredType === undefined) {
|
||||
const decoratorDeclNode: DecoratorDeclarationStatementNode | undefined =
|
||||
|
@ -3036,12 +3071,13 @@ export function createChecker(program: Program): Checker {
|
|||
"Expected to find a decorator type."
|
||||
);
|
||||
// Means we have a decorator declaration.
|
||||
hasError = checkDecoratorUsage(targetType, symbolLinks.declaredType, args, decNode);
|
||||
[hasError, args] = checkDecoratorUsage(targetType, symbolLinks.declaredType, args, decNode);
|
||||
}
|
||||
if (hasError) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
definition: symbolLinks.declaredType,
|
||||
decorator: sym.value ?? ((...args: any[]) => {}),
|
||||
node: decNode,
|
||||
args,
|
||||
|
@ -3053,7 +3089,7 @@ export function createChecker(program: Program): Checker {
|
|||
declaration: Decorator,
|
||||
args: DecoratorArgument[],
|
||||
decoratorNode: Node
|
||||
): boolean {
|
||||
): [boolean, DecoratorArgument[]] {
|
||||
let hasError = false;
|
||||
const [targetValid] = isTypeAssignableTo(targetType, declaration.target.type, decoratorNode);
|
||||
if (!targetValid) {
|
||||
|
@ -3097,14 +3133,21 @@ export function createChecker(program: Program): Checker {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
const resolvedArgs: DecoratorArgument[] = [];
|
||||
for (const [index, parameter] of declaration.parameters.entries()) {
|
||||
if (parameter.rest) {
|
||||
const restType =
|
||||
parameter.type.kind === "Model" ? parameter.type.indexer?.value : undefined;
|
||||
const restType = getIndexType(
|
||||
parameter.type.kind === "Value" ? parameter.type.target : parameter.type
|
||||
);
|
||||
if (restType) {
|
||||
for (let i = index; i < args.length; i++) {
|
||||
const arg = args[i];
|
||||
if (arg && arg.value) {
|
||||
resolvedArgs.push({
|
||||
...arg,
|
||||
jsValue: resolveDecoratorArgJsValue(arg.value, parameter.type.kind === "Value"),
|
||||
});
|
||||
if (!checkArgumentAssignable(arg.value, restType, arg.node!)) {
|
||||
hasError = true;
|
||||
}
|
||||
|
@ -3115,17 +3158,34 @@ export function createChecker(program: Program): Checker {
|
|||
}
|
||||
const arg = args[index];
|
||||
if (arg && arg.value) {
|
||||
resolvedArgs.push({
|
||||
...arg,
|
||||
jsValue: resolveDecoratorArgJsValue(arg.value, parameter.type.kind === "Value"),
|
||||
});
|
||||
if (!checkArgumentAssignable(arg.value, parameter.type, arg.node!)) {
|
||||
hasError = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return hasError;
|
||||
return [hasError, resolvedArgs];
|
||||
}
|
||||
|
||||
function getIndexType(type: Type): Type | undefined {
|
||||
return type.kind === "Model" ? type.indexer?.value : undefined;
|
||||
}
|
||||
|
||||
function resolveDecoratorArgJsValue(value: Type, valueOf: boolean) {
|
||||
if (valueOf) {
|
||||
if (value.kind === "Boolean" || value.kind === "String" || value.kind === "Number") {
|
||||
return literalTypeToValue(value);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function checkArgumentAssignable(
|
||||
argumentType: Type,
|
||||
parameterType: Type,
|
||||
parameterType: Type | ValueType,
|
||||
diagnosticTarget: DiagnosticTarget
|
||||
): boolean {
|
||||
const [valid] = isTypeAssignableTo(argumentType, parameterType, diagnosticTarget);
|
||||
|
@ -3174,6 +3234,7 @@ export function createChecker(program: Program): Checker {
|
|||
const type = getTypeForNode(argNode, mapper);
|
||||
return {
|
||||
value: type,
|
||||
jsValue: type,
|
||||
node: argNode,
|
||||
};
|
||||
});
|
||||
|
@ -4618,8 +4679,8 @@ export function createChecker(program: Program): Checker {
|
|||
* @param diagnosticTarget Target for the diagnostic, unless something better can be inferred.
|
||||
*/
|
||||
function checkTypeAssignable(
|
||||
source: Type,
|
||||
target: Type,
|
||||
source: Type | ValueType,
|
||||
target: Type | ValueType,
|
||||
diagnosticTarget: DiagnosticTarget
|
||||
): boolean {
|
||||
const [related, diagnostics] = isTypeAssignableTo(source, target, diagnosticTarget);
|
||||
|
@ -4636,15 +4697,37 @@ export function createChecker(program: Program): Checker {
|
|||
* @param diagnosticTarget Target for the diagnostic, unless something better can be inferred.
|
||||
*/
|
||||
function isTypeAssignableTo(
|
||||
source: Type,
|
||||
target: Type,
|
||||
source: Type | ValueType,
|
||||
target: Type | ValueType,
|
||||
diagnosticTarget: DiagnosticTarget
|
||||
): [boolean, Diagnostic[]] {
|
||||
): [boolean, readonly Diagnostic[]] {
|
||||
// BACKCOMPAT: Added May 2023 sprint, to be removed by June 2023 sprint
|
||||
if (source.kind === "TemplateParameter" && source.constraint && target.kind === "Value") {
|
||||
const [assignable] = isTypeAssignableTo(source.constraint, target.target, diagnosticTarget);
|
||||
if (assignable) {
|
||||
const constraint = getTypeName(source.constraint);
|
||||
reportDeprecated(
|
||||
program,
|
||||
`Template constrainted to '${constraint}' will not be assignable to '${getTypeName(
|
||||
target
|
||||
)}' in the future. Update the constraint to be 'valueof ${constraint}'`,
|
||||
diagnosticTarget
|
||||
);
|
||||
return [true, []];
|
||||
}
|
||||
}
|
||||
|
||||
if (source.kind === "TemplateParameter") {
|
||||
source = source.constraint ?? unknownType;
|
||||
}
|
||||
if (source === target) return [true, []];
|
||||
if (target.kind === "Value") {
|
||||
return isAssignableToValueType(source, target, diagnosticTarget);
|
||||
}
|
||||
|
||||
if (source.kind === "Value") {
|
||||
return [false, [createUnassignableDiagnostic(source, target, diagnosticTarget)]];
|
||||
}
|
||||
const isSimpleTypeRelated = isSimpleTypeAssignableTo(source, target);
|
||||
if (isSimpleTypeRelated === true) {
|
||||
return [true, []];
|
||||
|
@ -4710,6 +4793,25 @@ export function createChecker(program: Program): Checker {
|
|||
return [false, [createUnassignableDiagnostic(source, target, diagnosticTarget)]];
|
||||
}
|
||||
|
||||
function isAssignableToValueType(
|
||||
source: Type | ValueType,
|
||||
target: ValueType,
|
||||
diagnosticTarget: DiagnosticTarget
|
||||
): [boolean, readonly Diagnostic[]] {
|
||||
if (source.kind === "Value") {
|
||||
return isTypeAssignableTo(source.target, target.target, diagnosticTarget);
|
||||
}
|
||||
const [assignable, diagnostics] = isTypeAssignableTo(source, target.target, diagnosticTarget);
|
||||
if (!assignable) {
|
||||
return [assignable, diagnostics];
|
||||
}
|
||||
|
||||
if (!isValueType(source)) {
|
||||
return [false, [createUnassignableDiagnostic(source, target, diagnosticTarget)]];
|
||||
}
|
||||
return [true, []];
|
||||
}
|
||||
|
||||
function isReflectionType(type: Type): type is Model & { name: ReflectionTypeName } {
|
||||
return (
|
||||
type.kind === "Model" &&
|
||||
|
@ -4846,7 +4948,7 @@ export function createChecker(program: Program): Checker {
|
|||
source: Model,
|
||||
target: Model & { indexer: ModelIndexer },
|
||||
diagnosticTarget: DiagnosticTarget
|
||||
): [boolean, Diagnostic[]] {
|
||||
): [boolean, readonly Diagnostic[]] {
|
||||
// Model expressions should be able to be assigned.
|
||||
if (source.name === "" && target.indexer.key.name !== "integer") {
|
||||
return isIndexConstraintValid(target.indexer.value, source, diagnosticTarget);
|
||||
|
@ -4878,7 +4980,7 @@ export function createChecker(program: Program): Checker {
|
|||
constraintType: Type,
|
||||
type: Model,
|
||||
diagnosticTarget: DiagnosticTarget
|
||||
): [boolean, Diagnostic[]] {
|
||||
): [boolean, readonly Diagnostic[]] {
|
||||
for (const prop of type.properties.values()) {
|
||||
const [related, diagnostics] = isTypeAssignableTo(
|
||||
prop.type,
|
||||
|
@ -4907,7 +5009,7 @@ export function createChecker(program: Program): Checker {
|
|||
source: Tuple,
|
||||
target: Tuple,
|
||||
diagnosticTarget: DiagnosticTarget
|
||||
): [boolean, Diagnostic[]] {
|
||||
): [boolean, readonly Diagnostic[]] {
|
||||
if (source.values.length !== target.values.length) {
|
||||
return [
|
||||
false,
|
||||
|
@ -4973,8 +5075,8 @@ export function createChecker(program: Program): Checker {
|
|||
}
|
||||
|
||||
function createUnassignableDiagnostic(
|
||||
source: Type,
|
||||
target: Type,
|
||||
source: Type | ValueType,
|
||||
target: Type | ValueType,
|
||||
diagnosticTarget: DiagnosticTarget
|
||||
) {
|
||||
return createDiagnostic({
|
||||
|
@ -5347,7 +5449,7 @@ function finishTypeForProgramAndChecker<T extends Type>(
|
|||
if (docComment) {
|
||||
typeDef.decorators.unshift({
|
||||
decorator: $docFromComment,
|
||||
args: [{ value: program.checker.createLiteralType(docComment) }],
|
||||
args: [{ value: program.checker.createLiteralType(docComment), jsValue: docComment }],
|
||||
});
|
||||
}
|
||||
for (const decApp of typeDef.decorators) {
|
||||
|
@ -5372,7 +5474,7 @@ function applyDecoratorToType(program: Program, decApp: DecoratorApplication, ta
|
|||
|
||||
// peel `fn` off to avoid setting `this`.
|
||||
try {
|
||||
const args = marshalArgumentsForJS(decApp.args.map((x) => x.value));
|
||||
const args = decApp.args.map((x) => x.jsValue);
|
||||
const fn = decApp.decorator;
|
||||
const context = createDecoratorContext(program, decApp);
|
||||
fn(context, target, ...args);
|
||||
|
|
|
@ -10,6 +10,7 @@ import {
|
|||
Operation,
|
||||
Scalar,
|
||||
Type,
|
||||
ValueType,
|
||||
} from "../types.js";
|
||||
|
||||
export interface TypeNameOptions {
|
||||
|
@ -17,7 +18,7 @@ export interface TypeNameOptions {
|
|||
printable?: boolean;
|
||||
}
|
||||
|
||||
export function getTypeName(type: Type, options?: TypeNameOptions): string {
|
||||
export function getTypeName(type: Type | ValueType, options?: TypeNameOptions): string {
|
||||
switch (type.kind) {
|
||||
case "Namespace":
|
||||
return getNamespaceFullName(type, options);
|
||||
|
@ -51,6 +52,8 @@ export function getTypeName(type: Type, options?: TypeNameOptions): string {
|
|||
return type.value.toString();
|
||||
case "Intrinsic":
|
||||
return type.name;
|
||||
case "Value":
|
||||
return `valueof ${getTypeName(type.target, options)}`;
|
||||
}
|
||||
|
||||
return "(unnamed type)";
|
||||
|
|
|
@ -97,6 +97,7 @@ import {
|
|||
UnionStatementNode,
|
||||
UnionVariantNode,
|
||||
UsingStatementNode,
|
||||
ValueOfExpressionNode,
|
||||
VoidKeywordNode,
|
||||
} from "./types.js";
|
||||
import { isArray, mutate } from "./util.js";
|
||||
|
@ -1050,6 +1051,18 @@ function createParser(code: string | SourceFile, options: ParseOptions = {}): Pa
|
|||
}
|
||||
return expr;
|
||||
}
|
||||
|
||||
function parseValueOfExpression(): ValueOfExpressionNode {
|
||||
const pos = tokenPos();
|
||||
parseExpected(Token.ValueOfKeyword);
|
||||
const target = parseExpression();
|
||||
|
||||
return {
|
||||
kind: SyntaxKind.ValueOfExpression,
|
||||
target,
|
||||
...finishNode(pos),
|
||||
};
|
||||
}
|
||||
function parseReferenceExpression(
|
||||
message?: keyof CompilerDiagnostics["token-expected"]
|
||||
): TypeReferenceNode {
|
||||
|
@ -1242,6 +1255,8 @@ function createParser(code: string | SourceFile, options: ParseOptions = {}): Pa
|
|||
function parsePrimaryExpression(): Expression {
|
||||
while (true) {
|
||||
switch (token()) {
|
||||
case Token.ValueOfKeyword:
|
||||
return parseValueOfExpression();
|
||||
case Token.Identifier:
|
||||
return parseReferenceExpression();
|
||||
case Token.StringLiteral:
|
||||
|
@ -2957,6 +2972,8 @@ export function visitChildren<T>(node: Node, cb: NodeCallback<T>): T | undefined
|
|||
return visitNode(cb, node.id) || visitNode(cb, node.type);
|
||||
case SyntaxKind.TypeReference:
|
||||
return visitNode(cb, node.target) || visitEach(cb, node.arguments);
|
||||
case SyntaxKind.ValueOfExpression:
|
||||
return visitNode(cb, node.target);
|
||||
case SyntaxKind.TupleExpression:
|
||||
return visitEach(cb, node.values);
|
||||
case SyntaxKind.UnionExpression:
|
||||
|
|
|
@ -553,14 +553,8 @@ export function createProjector(
|
|||
for (const dec of decs) {
|
||||
const args: DecoratorArgument[] = [];
|
||||
for (const arg of dec.args) {
|
||||
// filter out primitive arguments
|
||||
if (typeof arg.value !== "object") {
|
||||
args.push(arg);
|
||||
continue;
|
||||
}
|
||||
|
||||
const projected = projectType(arg.value);
|
||||
args.push({ ...arg, value: projected });
|
||||
const jsValue = typeof arg.jsValue === "object" ? projectType(arg.jsValue) : arg.jsValue;
|
||||
args.push({ ...arg, value: projectType(arg.value), jsValue });
|
||||
}
|
||||
|
||||
decorators.push({ ...dec, args });
|
||||
|
|
|
@ -118,6 +118,7 @@ export enum Token {
|
|||
IfKeyword,
|
||||
DecKeyword,
|
||||
FnKeyword,
|
||||
ValueOfKeyword,
|
||||
// Add new statement keyword above
|
||||
|
||||
/** @internal */ __EndStatementKeyword,
|
||||
|
@ -228,6 +229,7 @@ export const TokenDisplay = getTokenDisplayTable([
|
|||
[Token.IfKeyword, "'if'"],
|
||||
[Token.DecKeyword, "'dec'"],
|
||||
[Token.FnKeyword, "'fn'"],
|
||||
[Token.ValueOfKeyword, "'valueof'"],
|
||||
[Token.ExtendsKeyword, "'extends'"],
|
||||
[Token.TrueKeyword, "'true'"],
|
||||
[Token.FalseKeyword, "'false'"],
|
||||
|
@ -257,6 +259,7 @@ export const Keywords: ReadonlyMap<string, Token> = new Map([
|
|||
["alias", Token.AliasKeyword],
|
||||
["dec", Token.DecKeyword],
|
||||
["fn", Token.FnKeyword],
|
||||
["valueof", Token.ValueOfKeyword],
|
||||
["true", Token.TrueKeyword],
|
||||
["false", Token.FalseKeyword],
|
||||
["return", Token.ReturnKeyword],
|
||||
|
|
|
@ -18,10 +18,16 @@ export type DecoratorArgumentValue = Type | number | string | boolean;
|
|||
|
||||
export interface DecoratorArgument {
|
||||
value: Type;
|
||||
/**
|
||||
* Marshalled value for use in Javascript.
|
||||
*/
|
||||
jsValue: Type | string | number | boolean;
|
||||
node?: Node;
|
||||
}
|
||||
|
||||
export interface DecoratorApplication {
|
||||
definition?: Decorator;
|
||||
// TODO-TIM deprecate replace with `implementation`?
|
||||
decorator: DecoratorFunction;
|
||||
args: DecoratorArgument[];
|
||||
node?: DecoratorExpressionNode | AugmentDecoratorStatementNode;
|
||||
|
@ -137,6 +143,11 @@ export interface Projector {
|
|||
projectedGlobalNamespace?: Namespace;
|
||||
}
|
||||
|
||||
export interface ValueType {
|
||||
kind: "Value"; // Todo remove?
|
||||
target: Type;
|
||||
}
|
||||
|
||||
export interface IntrinsicType extends BaseType {
|
||||
kind: "Intrinsic";
|
||||
name: "ErrorType" | "void" | "never" | "unknown" | "null";
|
||||
|
@ -508,7 +519,7 @@ export interface UnionVariant extends BaseType, DecoratedType {
|
|||
export interface TemplateParameter extends BaseType {
|
||||
kind: "TemplateParameter";
|
||||
node: TemplateParameterDeclarationNode;
|
||||
constraint?: Type;
|
||||
constraint?: Type | ValueType;
|
||||
default?: Type;
|
||||
}
|
||||
|
||||
|
@ -536,7 +547,7 @@ export interface FunctionParameter extends BaseType {
|
|||
kind: "FunctionParameter";
|
||||
node: FunctionParameterNode;
|
||||
name: string;
|
||||
type: Type;
|
||||
type: Type | ValueType;
|
||||
optional: boolean;
|
||||
rest: boolean;
|
||||
}
|
||||
|
@ -723,6 +734,7 @@ export enum SyntaxKind {
|
|||
VoidKeyword,
|
||||
NeverKeyword,
|
||||
UnknownKeyword,
|
||||
ValueOfExpression,
|
||||
TypeReference,
|
||||
ProjectionReference,
|
||||
TemplateParameterDeclaration,
|
||||
|
@ -1012,6 +1024,7 @@ export type Expression =
|
|||
| UnionExpressionNode
|
||||
| IntersectionExpressionNode
|
||||
| TypeReferenceNode
|
||||
| ValueOfExpressionNode
|
||||
| IdentifierNode
|
||||
| StringLiteralNode
|
||||
| NumericLiteralNode
|
||||
|
@ -1246,6 +1259,11 @@ export interface IntersectionExpressionNode extends BaseNode {
|
|||
readonly options: readonly Expression[];
|
||||
}
|
||||
|
||||
export interface ValueOfExpressionNode extends BaseNode {
|
||||
readonly kind: SyntaxKind.ValueOfExpression;
|
||||
readonly target: Expression;
|
||||
}
|
||||
|
||||
export interface TypeReferenceNode extends BaseNode {
|
||||
readonly kind: SyntaxKind.TypeReference;
|
||||
readonly target: MemberExpressionNode | IdentifierNode;
|
||||
|
@ -1276,7 +1294,7 @@ export type Modifier = ExternKeywordNode;
|
|||
* Represent a decorator declaration
|
||||
* @example
|
||||
* ```typespec
|
||||
* extern dec doc(target: Type, value: StringLiteral);
|
||||
* extern dec doc(target: Type, value: valueof string);
|
||||
* ```
|
||||
*/
|
||||
export interface DecoratorDeclarationStatementNode extends BaseNode, DeclarationNode {
|
||||
|
|
|
@ -65,6 +65,7 @@ import {
|
|||
UnionExpressionNode,
|
||||
UnionStatementNode,
|
||||
UnionVariantNode,
|
||||
ValueOfExpressionNode,
|
||||
} from "../../core/types.js";
|
||||
import { isArray } from "../../core/util.js";
|
||||
import { commentHandler } from "./comment-handler.js";
|
||||
|
@ -193,6 +194,8 @@ export function printNode(
|
|||
return printUnionVariant(path as AstPath<UnionVariantNode>, options, print);
|
||||
case SyntaxKind.TypeReference:
|
||||
return printTypeReference(path as AstPath<TypeReferenceNode>, options, print);
|
||||
case SyntaxKind.ValueOfExpression:
|
||||
return printValueOfExpression(path as AstPath<ValueOfExpressionNode>, options, print);
|
||||
case SyntaxKind.TemplateParameterDeclaration:
|
||||
return printTemplateParameterDeclaration(
|
||||
path as AstPath<TemplateParameterDeclarationNode>,
|
||||
|
@ -1230,6 +1233,15 @@ export function printTypeReference(
|
|||
return [type, template];
|
||||
}
|
||||
|
||||
export function printValueOfExpression(
|
||||
path: prettier.AstPath<ValueOfExpressionNode>,
|
||||
options: TypeSpecPrettierOptions,
|
||||
print: PrettierChildPrint
|
||||
): prettier.doc.builders.Doc {
|
||||
const type = path.call(print, "target");
|
||||
return ["valueof ", type];
|
||||
}
|
||||
|
||||
function printTemplateParameterDeclaration(
|
||||
path: AstPath<TemplateParameterDeclarationNode>,
|
||||
options: TypeSpecPrettierOptions,
|
||||
|
|
|
@ -6,6 +6,7 @@ import {
|
|||
} from "../core/decorator-utils.js";
|
||||
import {
|
||||
StdTypeName,
|
||||
StringLiteral,
|
||||
getDiscriminatedUnion,
|
||||
getTypeName,
|
||||
ignoreDiagnostics,
|
||||
|
@ -744,12 +745,12 @@ export function $withUpdateableProperties(context: DecoratorContext, target: Typ
|
|||
export function $withoutOmittedProperties(
|
||||
context: DecoratorContext,
|
||||
target: Model,
|
||||
omitProperties: string | Union
|
||||
omitProperties: StringLiteral | Union
|
||||
) {
|
||||
// Get the property or properties to omit
|
||||
const omitNames = new Set<string>();
|
||||
if (typeof omitProperties === "string") {
|
||||
omitNames.add(omitProperties);
|
||||
if (omitProperties.kind === "String") {
|
||||
omitNames.add(omitProperties.value);
|
||||
} else {
|
||||
for (const variant of omitProperties.variants.values()) {
|
||||
if (variant.type.kind === "String") {
|
||||
|
@ -990,7 +991,9 @@ export function $withDefaultKeyVisibility(
|
|||
...keyProp.decorators,
|
||||
{
|
||||
decorator: $visibility,
|
||||
args: [{ value: context.program.checker.createLiteralType(visibility) }],
|
||||
args: [
|
||||
{ value: context.program.checker.createLiteralType(visibility), jsValue: visibility },
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace TypeSpec;
|
|||
* model Pet {}
|
||||
* ```
|
||||
*/
|
||||
extern dec summary(target: unknown, summary: string);
|
||||
extern dec summary(target: unknown, summary: valueof string);
|
||||
|
||||
/**
|
||||
* Attach a documentation string.
|
||||
|
@ -27,7 +27,7 @@ extern dec summary(target: unknown, summary: string);
|
|||
* model Pet {}
|
||||
* ```
|
||||
*/
|
||||
extern dec doc(target: unknown, doc: string, formatArgs?: {});
|
||||
extern dec doc(target: unknown, doc: valueof string, formatArgs?: {});
|
||||
|
||||
/**
|
||||
* Mark this type as deprecated
|
||||
|
@ -40,7 +40,7 @@ extern dec doc(target: unknown, doc: string, formatArgs?: {});
|
|||
* op Action<T>(): T;
|
||||
* ```
|
||||
*/
|
||||
extern dec deprecated(target: unknown, message: string);
|
||||
extern dec deprecated(target: unknown, message: valueof string);
|
||||
|
||||
/**
|
||||
* Service options.
|
||||
|
@ -107,7 +107,7 @@ extern dec error(target: Model);
|
|||
* scalar uuid extends string;
|
||||
* ```
|
||||
*/
|
||||
extern dec format(target: string | bytes | ModelProperty, format: string);
|
||||
extern dec format(target: string | bytes | ModelProperty, format: valueof string);
|
||||
|
||||
/**
|
||||
* Specify the the pattern this string should respect using simple regular expression syntax.
|
||||
|
@ -122,7 +122,7 @@ extern dec format(target: string | bytes | ModelProperty, format: string);
|
|||
* scalar LowerAlpha extends string;
|
||||
* ```
|
||||
*/
|
||||
extern dec pattern(target: string | bytes | ModelProperty, pattern: string);
|
||||
extern dec pattern(target: string | bytes | ModelProperty, pattern: valueof string);
|
||||
|
||||
/**
|
||||
* Specify the minimum length this string type should be.
|
||||
|
@ -134,7 +134,7 @@ extern dec pattern(target: string | bytes | ModelProperty, pattern: string);
|
|||
* scalar Username extends string;
|
||||
* ```
|
||||
*/
|
||||
extern dec minLength(target: string | ModelProperty, value: integer);
|
||||
extern dec minLength(target: string | ModelProperty, value: valueof integer);
|
||||
|
||||
/**
|
||||
* Specify the maximum length this string type should be.
|
||||
|
@ -146,7 +146,7 @@ extern dec minLength(target: string | ModelProperty, value: integer);
|
|||
* scalar Username extends string;
|
||||
* ```
|
||||
*/
|
||||
extern dec maxLength(target: string | ModelProperty, value: integer);
|
||||
extern dec maxLength(target: string | ModelProperty, value: valueof integer);
|
||||
|
||||
/**
|
||||
* Specify the minimum number of items this array should have.
|
||||
|
@ -158,7 +158,7 @@ extern dec maxLength(target: string | ModelProperty, value: integer);
|
|||
* model Endpoints is string[];
|
||||
* ```
|
||||
*/
|
||||
extern dec minItems(target: unknown[] | ModelProperty, value: integer);
|
||||
extern dec minItems(target: unknown[] | ModelProperty, value: valueof integer);
|
||||
|
||||
/**
|
||||
* Specify the maximum number of items this array should have.
|
||||
|
@ -170,7 +170,7 @@ extern dec minItems(target: unknown[] | ModelProperty, value: integer);
|
|||
* model Endpoints is string[];
|
||||
* ```
|
||||
*/
|
||||
extern dec maxItems(target: unknown[] | ModelProperty, value: integer);
|
||||
extern dec maxItems(target: unknown[] | ModelProperty, value: valueof integer);
|
||||
|
||||
/**
|
||||
* Specify the minimum value this numeric type should be.
|
||||
|
@ -182,7 +182,7 @@ extern dec maxItems(target: unknown[] | ModelProperty, value: integer);
|
|||
* scalar Age is int32;
|
||||
* ```
|
||||
*/
|
||||
extern dec minValue(target: numeric | ModelProperty, value: numeric);
|
||||
extern dec minValue(target: numeric | ModelProperty, value: valueof numeric);
|
||||
|
||||
/**
|
||||
* Specify the maximum value this numeric type should be.
|
||||
|
@ -194,7 +194,7 @@ extern dec minValue(target: numeric | ModelProperty, value: numeric);
|
|||
* scalar Age is int32;
|
||||
* ```
|
||||
*/
|
||||
extern dec maxValue(target: numeric | ModelProperty, value: numeric);
|
||||
extern dec maxValue(target: numeric | ModelProperty, value: valueof numeric);
|
||||
|
||||
/**
|
||||
* Specify the minimum value this numeric type should be, exclusive of the given
|
||||
|
@ -207,7 +207,7 @@ extern dec maxValue(target: numeric | ModelProperty, value: numeric);
|
|||
* scalar distance is float64;
|
||||
* ```
|
||||
*/
|
||||
extern dec minValueExclusive(target: numeric | ModelProperty, value: numeric);
|
||||
extern dec minValueExclusive(target: numeric | ModelProperty, value: valueof numeric);
|
||||
|
||||
/**
|
||||
* Specify the maximum value this numeric type should be, exclusive of the given
|
||||
|
@ -220,7 +220,7 @@ extern dec minValueExclusive(target: numeric | ModelProperty, value: numeric);
|
|||
* scalar distance is float64;
|
||||
* ```
|
||||
*/
|
||||
extern dec maxValueExclusive(target: numeric | ModelProperty, value: numeric);
|
||||
extern dec maxValueExclusive(target: numeric | ModelProperty, value: valueof numeric);
|
||||
|
||||
/**
|
||||
* Mark this string as a secret value that should be treated carefully to avoid exposure
|
||||
|
@ -237,7 +237,7 @@ extern dec secret(target: string | ModelProperty);
|
|||
* Attaches a tag to an operation, interface, or namespace. Multiple `@tag` decorators can be specified to attach multiple tags to a TypeSpec element.
|
||||
* @param tag Tag value
|
||||
*/
|
||||
extern dec tag(target: Namespace | Interface | Operation, tag: string);
|
||||
extern dec tag(target: Namespace | Interface | Operation, tag: valueof string);
|
||||
|
||||
/**
|
||||
* Specifies how a templated type should name their instances.
|
||||
|
@ -253,7 +253,7 @@ extern dec tag(target: Namespace | Interface | Operation, tag: string);
|
|||
* }
|
||||
* ```
|
||||
*/
|
||||
extern dec friendlyName(target: unknown, name: string, formatArgs?: unknown);
|
||||
extern dec friendlyName(target: unknown, name: valueof string, formatArgs?: unknown);
|
||||
|
||||
/**
|
||||
* Provide a set of known values to a string type.
|
||||
|
@ -283,7 +283,7 @@ extern dec knownValues(target: string | numeric | ModelProperty, values: Enum);
|
|||
* }
|
||||
* ```
|
||||
*/
|
||||
extern dec key(target: ModelProperty, altName?: string);
|
||||
extern dec key(target: ModelProperty, altName?: valueof string);
|
||||
|
||||
/**
|
||||
* Specify this operation is an overload of the given operation.
|
||||
|
@ -313,7 +313,11 @@ extern dec overload(target: Operation, overloadbase: Operation);
|
|||
* }
|
||||
* ```
|
||||
*/
|
||||
extern dec projectedName(target: unknown, targetName: string, projectedName: string);
|
||||
extern dec projectedName(
|
||||
target: unknown,
|
||||
targetName: valueof string,
|
||||
projectedName: valueof string
|
||||
);
|
||||
|
||||
/**
|
||||
* Specify the property to be used to discriminate this type.
|
||||
|
@ -337,7 +341,7 @@ extern dec projectedName(target: unknown, targetName: string, projectedName: str
|
|||
* model Dog extends Pet {kind: "dog", bark: boolean}
|
||||
* ```
|
||||
*/
|
||||
extern dec discriminator(target: Model | Union, propertyName: string);
|
||||
extern dec discriminator(target: Model | Union, propertyName: valueof string);
|
||||
|
||||
/**
|
||||
* Known encoding to use on utcDateTime or offsetDateTime
|
||||
|
@ -444,7 +448,7 @@ extern dec encode(
|
|||
* }
|
||||
* ```
|
||||
*/
|
||||
extern dec visibility(target: ModelProperty, ...visibilities: string[]);
|
||||
extern dec visibility(target: ModelProperty, ...visibilities: valueof string[]);
|
||||
|
||||
/**
|
||||
* Removes properties that are not considered to be present or applicable
|
||||
|
@ -486,14 +490,14 @@ extern dec visibility(target: ModelProperty, ...visibilities: string[]);
|
|||
* }
|
||||
* ```
|
||||
*/
|
||||
extern dec withVisibility(target: Model, ...visibilities: string[]);
|
||||
extern dec withVisibility(target: Model, ...visibilities: valueof string[]);
|
||||
|
||||
/**
|
||||
* Set the visibility of key properties in a model if not already set.
|
||||
*
|
||||
* @param visibility The desired default visibility value. If a key property already has a `visibility` decorator then the default visibility is not applied.
|
||||
*/
|
||||
extern dec withDefaultKeyVisibility(target: Model, visibility: unknown);
|
||||
extern dec withDefaultKeyVisibility(target: Model, visibility: valueof string);
|
||||
|
||||
/**
|
||||
* Returns the model with non-updateable properties removed.
|
||||
|
@ -524,10 +528,10 @@ extern dec withoutOmittedProperties(target: Model, omit: string | Union);
|
|||
* A debugging decorator used to inspect a type.
|
||||
* @param text Custom text to log
|
||||
*/
|
||||
extern dec inspectType(target: unknown, text: string);
|
||||
extern dec inspectType(target: unknown, text: valueof string);
|
||||
|
||||
/**
|
||||
* A debugging decorator used to inspect a type name.
|
||||
* @param text Custom text to log
|
||||
*/
|
||||
extern dec inspectTypeName(target: unknown, text: string);
|
||||
extern dec inspectTypeName(target: unknown, text: valueof string);
|
||||
|
|
|
@ -193,6 +193,6 @@ model OmitDefaults<T> {
|
|||
*/
|
||||
@doc("The template for setting the default visibility of key properties.")
|
||||
@withDefaultKeyVisibility(Visibility)
|
||||
model DefaultKeyVisibility<T, Visibility extends string> {
|
||||
model DefaultKeyVisibility<T, Visibility extends valueof string> {
|
||||
...T;
|
||||
}
|
||||
|
|
|
@ -218,6 +218,17 @@ const identifierExpression: MatchRule = {
|
|||
match: identifier,
|
||||
};
|
||||
|
||||
const valueOfExpression: BeginEndRule = {
|
||||
key: "valueof",
|
||||
scope: meta,
|
||||
begin: `\\b(valueof)`,
|
||||
beginCaptures: {
|
||||
"1": { scope: "keyword.other.tsp" },
|
||||
},
|
||||
end: `(?=>)|${universalEnd}`,
|
||||
patterns: [expression],
|
||||
};
|
||||
|
||||
const typeArguments: BeginEndRule = {
|
||||
key: "type-arguments",
|
||||
scope: meta,
|
||||
|
@ -777,6 +788,7 @@ expression.patterns = [
|
|||
token,
|
||||
directive,
|
||||
parenthesizedExpression,
|
||||
valueOfExpression,
|
||||
typeArguments,
|
||||
tupleExpression,
|
||||
modelExpression,
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
SyntaxKind,
|
||||
Type,
|
||||
UnionVariant,
|
||||
ValueType,
|
||||
} from "../core/types.js";
|
||||
import { printId } from "../formatter/print/printer.js";
|
||||
|
||||
|
@ -28,7 +29,7 @@ export function getSymbolSignature(program: Program, sym: Sym): string {
|
|||
return getTypeSignature(type);
|
||||
}
|
||||
|
||||
function getTypeSignature(type: Type): string {
|
||||
function getTypeSignature(type: Type | ValueType): string {
|
||||
switch (type.kind) {
|
||||
case "Scalar":
|
||||
case "Enum":
|
||||
|
@ -39,10 +40,13 @@ function getTypeSignature(type: Type): string {
|
|||
return fence(`${type.kind.toLowerCase()} ${getPrintableTypeName(type)}`);
|
||||
case "Decorator":
|
||||
return fence(getDecoratorSignature(type));
|
||||
|
||||
case "Function":
|
||||
return fence(getFunctionSignature(type));
|
||||
case "Operation":
|
||||
return fence(getOperationSignature(type));
|
||||
case "Value":
|
||||
return `valueof ${getTypeSignature(type)}`;
|
||||
case "String":
|
||||
// BUG: https://github.com/microsoft/typespec/issues/1350 - should escape string literal values
|
||||
return `(string)\n${fence(`"${type.value}"`)}`;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { deepEqual, strictEqual } from "assert";
|
||||
import { Model, Operation, Type } from "../../core/types.js";
|
||||
import { Model, Operation, StringLiteral, Type } from "../../core/types.js";
|
||||
import { TestHost, createTestHost, expectDiagnosticEmpty } from "../../testing/index.js";
|
||||
|
||||
describe("compiler: checker: augment decorators", () => {
|
||||
|
@ -37,8 +37,8 @@ describe("compiler: checker: augment decorators", () => {
|
|||
let customName: string | undefined;
|
||||
|
||||
testHost.addJsFile("test.js", {
|
||||
$customName(_: any, t: Type, n: string) {
|
||||
customName = n;
|
||||
$customName(_: any, t: Type, n: StringLiteral) {
|
||||
customName = n.value;
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -127,9 +127,9 @@ describe("compiler: checker: augment decorators", () => {
|
|||
let runOnTarget: Type | undefined;
|
||||
|
||||
testHost.addJsFile("test.js", {
|
||||
$customName(_: any, t: Type, n: string) {
|
||||
$customName(_: any, t: Type, n: StringLiteral) {
|
||||
runOnTarget = t;
|
||||
customName = n;
|
||||
customName = n.value;
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -260,18 +260,18 @@ describe("compiler: checker: augment decorators", () => {
|
|||
let runOnTarget: Type | undefined;
|
||||
|
||||
testHost.addJsFile("test.js", {
|
||||
$customName(_: any, t: Type, n: string) {
|
||||
$customName(_: any, t: Type, n: StringLiteral) {
|
||||
runOnTarget = t;
|
||||
customName = n;
|
||||
customName = n.value;
|
||||
},
|
||||
});
|
||||
|
||||
testHost.addTypeSpecFile(
|
||||
"test.tsp",
|
||||
`
|
||||
import "./test.js";
|
||||
import "./test.js";
|
||||
|
||||
${code}
|
||||
${code}
|
||||
`
|
||||
);
|
||||
|
||||
|
@ -319,9 +319,9 @@ describe("compiler: checker: augment decorators", () => {
|
|||
let runOnTarget: Type | undefined;
|
||||
|
||||
testHost.addJsFile("test.js", {
|
||||
$customName(_: any, t: Type, n: string) {
|
||||
$customName(_: any, t: Type, n: StringLiteral) {
|
||||
runOnTarget = t;
|
||||
customName = n;
|
||||
customName = n.value;
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -136,7 +136,7 @@ describe("compiler: checker: decorators", () => {
|
|||
|
||||
it("calls a decorator with arguments", async () => {
|
||||
const { Foo } = await runner.compile(`
|
||||
extern dec testDec(target: unknown, arg1: string, arg2: string);
|
||||
extern dec testDec(target: unknown, arg1: valueof string, arg2: valueof string);
|
||||
|
||||
@testDec("one", "two")
|
||||
@test
|
||||
|
@ -148,7 +148,7 @@ describe("compiler: checker: decorators", () => {
|
|||
|
||||
it("calls a decorator with optional arguments", async () => {
|
||||
const { Foo } = await runner.compile(`
|
||||
extern dec testDec(target: unknown, arg1: string, arg2?: string);
|
||||
extern dec testDec(target: unknown, arg1: valueof string, arg2?: valueof string);
|
||||
|
||||
@testDec("one")
|
||||
@test
|
||||
|
@ -160,7 +160,7 @@ describe("compiler: checker: decorators", () => {
|
|||
|
||||
it("calls a decorator with rest arguments", async () => {
|
||||
const { Foo } = await runner.compile(`
|
||||
extern dec testDec(target: unknown, arg1: string, ...args: string[]);
|
||||
extern dec testDec(target: unknown, arg1: valueof string, ...args: valueof string[]);
|
||||
|
||||
@testDec("one", "two", "three", "four")
|
||||
@test
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { deepStrictEqual, ok, strictEqual } from "assert";
|
||||
import { Diagnostic, Model, ModelPropertyNode, Type } from "../../core/index.js";
|
||||
import { Diagnostic, FunctionParameterNode, Model, Type } from "../../core/index.js";
|
||||
import {
|
||||
BasicTestRunner,
|
||||
DiagnosticMatch,
|
||||
|
@ -19,7 +19,9 @@ interface RelatedTypeOptions {
|
|||
describe("compiler: checker: type relations", () => {
|
||||
let runner: BasicTestRunner;
|
||||
beforeEach(async () => {
|
||||
runner = createTestWrapper(await createTestHost());
|
||||
const host = await createTestHost();
|
||||
host.addJsFile("mock.js", { $mock: () => null });
|
||||
runner = createTestWrapper(host);
|
||||
});
|
||||
|
||||
async function checkTypeAssignable({ source, target, commonCode }: RelatedTypeOptions): Promise<{
|
||||
|
@ -28,20 +30,21 @@ describe("compiler: checker: type relations", () => {
|
|||
expectedDiagnosticPos: number;
|
||||
}> {
|
||||
const { source: code, pos } = extractCursor(`
|
||||
import "./mock.js";
|
||||
${commonCode ?? ""}
|
||||
|
||||
@test model Test {
|
||||
source: ┆${source};
|
||||
target: ${target};
|
||||
}`);
|
||||
const { Test } = (await runner.compile(code)) as { Test: Model };
|
||||
const sourceProp = Test.properties.get("source")!.type;
|
||||
const targetProp = Test.properties.get("target")!.type;
|
||||
extern dec mock(target: unknown, source: ┆${source}, value: ${target});
|
||||
`);
|
||||
await runner.compile(code);
|
||||
const decDeclaration = runner.program
|
||||
.getGlobalNamespaceType()
|
||||
.decoratorDeclarations.get("mock");
|
||||
const sourceProp = decDeclaration?.parameters[0].type!;
|
||||
const targetProp = decDeclaration?.parameters[1].type!;
|
||||
|
||||
const [related, diagnostics] = runner.program.checker.isTypeAssignableTo(
|
||||
sourceProp,
|
||||
targetProp,
|
||||
(Test.properties.get("source")!.node! as ModelPropertyNode).value
|
||||
(decDeclaration?.parameters[0].node! as FunctionParameterNode).type!
|
||||
);
|
||||
return { related, diagnostics, expectedDiagnosticPos: pos };
|
||||
}
|
||||
|
@ -828,4 +831,157 @@ describe("compiler: checker: type relations", () => {
|
|||
});
|
||||
testReflectionType("UnionVariant", "Foo.a", `union Foo {a: string, b: int32};`);
|
||||
});
|
||||
|
||||
describe("Value target", () => {
|
||||
describe("valueof string", () => {
|
||||
it("can assign string literal", async () => {
|
||||
await expectTypeAssignable({ source: `"foo bar"`, target: "valueof string" });
|
||||
});
|
||||
|
||||
it("cannot assign numeric literal", async () => {
|
||||
await expectTypeNotAssignable(
|
||||
{ source: `123`, target: "valueof string" },
|
||||
{
|
||||
code: "unassignable",
|
||||
message: "Type '123' is not assignable to type 'string'",
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it("cannot assign string scalar", async () => {
|
||||
await expectTypeNotAssignable(
|
||||
{ source: `string`, target: "valueof string" },
|
||||
{
|
||||
code: "unassignable",
|
||||
message: "Type 'string' is not assignable to type 'valueof string'",
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("valueof boolean", () => {
|
||||
it("can assign boolean literal", async () => {
|
||||
await expectTypeAssignable({ source: `true`, target: "valueof boolean" });
|
||||
});
|
||||
|
||||
it("cannot assign numeric literal", async () => {
|
||||
await expectTypeNotAssignable(
|
||||
{ source: `123`, target: "valueof boolean" },
|
||||
{
|
||||
code: "unassignable",
|
||||
message: "Type '123' is not assignable to type 'boolean'",
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it("cannot assign boolean scalar", async () => {
|
||||
await expectTypeNotAssignable(
|
||||
{ source: `boolean`, target: "valueof boolean" },
|
||||
{
|
||||
code: "unassignable",
|
||||
message: "Type 'boolean' is not assignable to type 'valueof boolean'",
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("valueof int16", () => {
|
||||
it("can assign int16 literal", async () => {
|
||||
await expectTypeAssignable({ source: `12`, target: "valueof int16" });
|
||||
});
|
||||
|
||||
it("can assign valueof int8", async () => {
|
||||
await expectTypeAssignable({ source: `valueof int8`, target: "valueof int16" });
|
||||
});
|
||||
|
||||
it("cannot assign int too large", async () => {
|
||||
await expectTypeNotAssignable(
|
||||
{ source: `123456`, target: "valueof int16" },
|
||||
{
|
||||
code: "unassignable",
|
||||
message: "Type '123456' is not assignable to type 'int16'",
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it("cannot assign float", async () => {
|
||||
await expectTypeNotAssignable(
|
||||
{ source: `12.6`, target: "valueof int16" },
|
||||
{
|
||||
code: "unassignable",
|
||||
message: "Type '12.6' is not assignable to type 'int16'",
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it("cannot assign string literal", async () => {
|
||||
await expectTypeNotAssignable(
|
||||
{ source: `"foo bar"`, target: "valueof int16" },
|
||||
{
|
||||
code: "unassignable",
|
||||
message: "Type 'foo bar' is not assignable to type 'int16'",
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it("cannot assign int16 scalar", async () => {
|
||||
await expectTypeNotAssignable(
|
||||
{ source: `int16`, target: "valueof int16" },
|
||||
{
|
||||
code: "unassignable",
|
||||
message: "Type 'int16' is not assignable to type 'valueof int16'",
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("valueof float32", () => {
|
||||
it("can assign float32 literal", async () => {
|
||||
await expectTypeAssignable({ source: `12.6`, target: "valueof float32" });
|
||||
});
|
||||
|
||||
it("cannot assign string literal", async () => {
|
||||
await expectTypeNotAssignable(
|
||||
{ source: `"foo bar"`, target: "valueof float32" },
|
||||
{
|
||||
code: "unassignable",
|
||||
message: "Type 'foo bar' is not assignable to type 'float32'",
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it("cannot assign float32 scalar", async () => {
|
||||
await expectTypeNotAssignable(
|
||||
{ source: `float32`, target: "valueof float32" },
|
||||
{
|
||||
code: "unassignable",
|
||||
message: "Type 'float32' is not assignable to type 'valueof float32'",
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("can use valueof in template parameter constraints", async () => {
|
||||
const diagnostics = await runner.diagnose(`
|
||||
model Foo<T extends valueof string> {
|
||||
@doc(T)
|
||||
prop1: int16;
|
||||
}`);
|
||||
expectDiagnosticEmpty(diagnostics);
|
||||
});
|
||||
|
||||
// BackCompat added May 2023 Sprint: by June 2023 sprint. From this PR: https://github.com/microsoft/typespec/pull/1877
|
||||
it("BACKCOMPAT: can use valueof in template parameter constraints", async () => {
|
||||
const diagnostics = await runner.diagnose(`
|
||||
model Foo<T extends string> {
|
||||
@doc(T)
|
||||
prop1: int16;
|
||||
}`);
|
||||
expectDiagnostics(diagnostics, {
|
||||
code: "deprecated",
|
||||
message:
|
||||
"Deprecated: Template constrainted to 'string' will not be assignable to 'valueof string' in the future. Update the constraint to be 'valueof string'",
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -40,7 +40,7 @@ describe("compiler: scalars", () => {
|
|||
const { A } = await runner.compile(`
|
||||
@doc(T)
|
||||
@test
|
||||
scalar A<T extends string>;
|
||||
scalar A<T extends valueof string>;
|
||||
|
||||
alias B = A<"123">;
|
||||
`);
|
||||
|
@ -49,11 +49,11 @@ describe("compiler: scalars", () => {
|
|||
strictEqual(A.name, "A");
|
||||
});
|
||||
|
||||
// https://github.com/microsoft/typespec/issues/1764
|
||||
// Test for https://github.com/microsoft/typespec/issues/1764
|
||||
it("template parameter are scoped to the scalar", async () => {
|
||||
const { A, B } = await runner.compile(`
|
||||
@test @doc(T) scalar A<T extends string>;
|
||||
@test @doc(T) scalar B<T extends string>;
|
||||
@test @doc(T) scalar A<T extends valueof string>;
|
||||
@test @doc(T) scalar B<T extends valueof string>;
|
||||
|
||||
alias AIns = A<"">;
|
||||
alias BIns = B<"">;
|
||||
|
|
|
@ -139,7 +139,7 @@ describe("compiler: built-in decorators", () => {
|
|||
|
||||
expectDiagnostics(diagnostics, {
|
||||
code: "invalid-argument",
|
||||
message: `Argument '123' is not assignable to parameter of type 'string'`,
|
||||
message: `Argument '123' is not assignable to parameter of type 'valueof string'`,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -296,7 +296,7 @@ describe("compiler: built-in decorators", () => {
|
|||
expectDiagnostics(diagnostics, [
|
||||
{
|
||||
code: "invalid-argument",
|
||||
message: "Argument '4' is not assignable to parameter of type 'string'",
|
||||
message: "Argument '4' is not assignable to parameter of type 'valueof string'",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
|
|
@ -218,6 +218,15 @@ describe("compiler: parser", () => {
|
|||
]);
|
||||
});
|
||||
|
||||
describe("valueof expressions", () => {
|
||||
parseEach([
|
||||
"alias A = valueof string;",
|
||||
"alias A = valueof int32;",
|
||||
"alias A = valueof {a: string, b: int32};",
|
||||
"alias A = valueof int8[];",
|
||||
]);
|
||||
});
|
||||
|
||||
describe("template instantiations", () => {
|
||||
parseEach(["model A { x: Foo<number, string>; }", "model B { x: Foo<number, string>[]; }"]);
|
||||
});
|
||||
|
|
|
@ -56,6 +56,7 @@ const Token = {
|
|||
else: createToken("else", "keyword.other.tsp"),
|
||||
to: createToken("to", "keyword.other.tsp"),
|
||||
from: createToken("from", "keyword.other.tsp"),
|
||||
valueof: createToken("valueof", "keyword.other.tsp"),
|
||||
other: (text: string) => createToken(text, "keyword.other.tsp"),
|
||||
},
|
||||
|
||||
|
@ -159,6 +160,24 @@ function testColorization(description: string, tokenize: Tokenize) {
|
|||
});
|
||||
});
|
||||
|
||||
describe("valueof", () => {
|
||||
it("simple valueof", async () => {
|
||||
const tokens = await tokenize("model Foo<T extends valueof string> {}");
|
||||
deepStrictEqual(tokens, [
|
||||
Token.keywords.model,
|
||||
Token.identifiers.type("Foo"),
|
||||
Token.punctuation.typeParameters.begin,
|
||||
Token.identifiers.type("T"),
|
||||
Token.keywords.extends,
|
||||
Token.keywords.valueof,
|
||||
Token.identifiers.type("string"),
|
||||
Token.punctuation.typeParameters.end,
|
||||
Token.punctuation.openBrace,
|
||||
Token.punctuation.closeBrace,
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("decorators", () => {
|
||||
it("simple parameterless decorator", async () => {
|
||||
const tokens = await tokenize("@foo");
|
||||
|
|
|
@ -235,7 +235,7 @@ describe("compiler: server: completion", () => {
|
|||
kind: CompletionItemKind.Function,
|
||||
documentation: {
|
||||
kind: MarkupKind.Markdown,
|
||||
value: "```typespec\ndec doc(target: unknown, doc: string, formatArgs?: {})\n```",
|
||||
value: "```typespec\ndec doc(target: unknown, doc: valueof string, formatArgs?: {})\n```",
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
@ -254,7 +254,7 @@ describe("compiler: server: completion", () => {
|
|||
kind: CompletionItemKind.Function,
|
||||
documentation: {
|
||||
kind: MarkupKind.Markdown,
|
||||
value: "```typespec\ndec doc(target: unknown, doc: string, formatArgs?: {})\n```",
|
||||
value: "```typespec\ndec doc(target: unknown, doc: valueof string, formatArgs?: {})\n```",
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
@ -292,7 +292,7 @@ describe("compiler: server: completion", () => {
|
|||
kind: CompletionItemKind.Function,
|
||||
documentation: {
|
||||
kind: MarkupKind.Markdown,
|
||||
value: "```typespec\ndec doc(target: unknown, doc: string, formatArgs?: {})\n```",
|
||||
value: "```typespec\ndec doc(target: unknown, doc: valueof string, formatArgs?: {})\n```",
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
|
|
@ -9,7 +9,7 @@ import { NodeHost } from "../core/node-host.js";
|
|||
import { CompilerOptions } from "../core/options.js";
|
||||
import { getAnyExtensionFromPath, resolvePath } from "../core/path-utils.js";
|
||||
import { Program, compile as compileProgram } from "../core/program.js";
|
||||
import { CompilerHost, Diagnostic, Type } from "../core/types.js";
|
||||
import { CompilerHost, Diagnostic, StringLiteral, Type } from "../core/types.js";
|
||||
import { createStringMap, getSourceFileKindFromExt } from "../core/util.js";
|
||||
import { expectDiagnosticEmpty } from "./expect.js";
|
||||
import { createTestWrapper } from "./test-utils.js";
|
||||
|
@ -253,7 +253,8 @@ async function createTestHostInternal(): Promise<TestHost> {
|
|||
fileSystem.addTypeSpecFile(".tsp/test-lib/main.tsp", 'import "./test.js";');
|
||||
fileSystem.addJsFile(".tsp/test-lib/test.js", {
|
||||
namespace: "TypeSpec",
|
||||
$test(_: any, target: Type, name?: string) {
|
||||
$test(_: any, target: Type, nameLiteral?: StringLiteral) {
|
||||
let name = nameLiteral?.value;
|
||||
if (!name) {
|
||||
if (
|
||||
target.kind === "Model" ||
|
||||
|
|
|
@ -70,7 +70,7 @@ extern dec query(target: ModelProperty, queryNameOrOptions?: string | QueryOptio
|
|||
* op read(@path explicit: string, implicit: string): void;
|
||||
* ```
|
||||
*/
|
||||
extern dec path(target: ModelProperty, paramName?: string);
|
||||
extern dec path(target: ModelProperty, paramName?: valueof string);
|
||||
|
||||
/**
|
||||
* Explicitly specify that this property is to be set as the body
|
||||
|
@ -186,8 +186,8 @@ extern dec head(target: Operation);
|
|||
*/
|
||||
extern dec server(
|
||||
target: Namespace,
|
||||
url: string,
|
||||
description: string,
|
||||
url: valueof string,
|
||||
description: valueof string,
|
||||
parameters?: Record<unknown>
|
||||
);
|
||||
|
||||
|
@ -209,7 +209,7 @@ extern dec useAuth(target: Namespace, auth: {} | Union | {}[]);
|
|||
* Specify if inapplicable metadata should be included in the payload for the given entity.
|
||||
* @param value If true, inapplicable metadata will be included in the payload.
|
||||
*/
|
||||
extern dec includeInapplicableMetadataInPayload(target: unknown, value: boolean);
|
||||
extern dec includeInapplicableMetadataInPayload(target: unknown, value: valueof boolean);
|
||||
|
||||
/**
|
||||
* Defines the relative route URI for the target operation
|
||||
|
@ -232,7 +232,7 @@ extern dec includeInapplicableMetadataInPayload(target: unknown, value: boolean)
|
|||
*/
|
||||
extern dec route(
|
||||
target: Namespace | Interface | Operation,
|
||||
path: string,
|
||||
path: valueof string,
|
||||
options?: {
|
||||
shared?: boolean,
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import {
|
|||
Namespace,
|
||||
Operation,
|
||||
Program,
|
||||
StringLiteral,
|
||||
Tuple,
|
||||
Type,
|
||||
Union,
|
||||
|
@ -38,15 +39,15 @@ const headerFieldsKey = createStateSymbol("header");
|
|||
export function $header(
|
||||
context: DecoratorContext,
|
||||
entity: ModelProperty,
|
||||
headerNameOrOptions?: string | Model
|
||||
headerNameOrOptions?: StringLiteral | Model
|
||||
) {
|
||||
const options: HeaderFieldOptions = {
|
||||
type: "header",
|
||||
name: entity.name.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(),
|
||||
};
|
||||
if (headerNameOrOptions) {
|
||||
if (typeof headerNameOrOptions === "string") {
|
||||
options.name = headerNameOrOptions;
|
||||
if (headerNameOrOptions.kind === "String") {
|
||||
options.name = headerNameOrOptions.value;
|
||||
} else {
|
||||
const name = headerNameOrOptions.properties.get("name")?.type;
|
||||
if (name?.kind === "String") {
|
||||
|
@ -89,15 +90,15 @@ const queryFieldsKey = createStateSymbol("query");
|
|||
export function $query(
|
||||
context: DecoratorContext,
|
||||
entity: ModelProperty,
|
||||
queryNameOrOptions?: string | Model
|
||||
queryNameOrOptions?: StringLiteral | Model
|
||||
) {
|
||||
const options: QueryParameterOptions = {
|
||||
type: "query",
|
||||
name: entity.name,
|
||||
};
|
||||
if (queryNameOrOptions) {
|
||||
if (typeof queryNameOrOptions === "string") {
|
||||
options.name = queryNameOrOptions;
|
||||
if (queryNameOrOptions.kind === "String") {
|
||||
options.name = queryNameOrOptions.value;
|
||||
} else {
|
||||
const name = queryNameOrOptions.properties.get("name")?.type;
|
||||
if (name?.kind === "String") {
|
||||
|
|
|
@ -343,7 +343,7 @@ describe("http: decorators", () => {
|
|||
expectDiagnostics(diagnostics, [
|
||||
{
|
||||
code: "invalid-argument",
|
||||
message: "Argument '123' is not assignable to parameter of type 'string'",
|
||||
message: "Argument '123' is not assignable to parameter of type 'valueof string'",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
@ -462,7 +462,7 @@ describe("http: decorators", () => {
|
|||
|
||||
expectDiagnostics(diagnostics, {
|
||||
code: "invalid-argument",
|
||||
message: "Argument '123' is not assignable to parameter of type 'string'",
|
||||
message: "Argument '123' is not assignable to parameter of type 'valueof string'",
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -474,7 +474,7 @@ describe("http: decorators", () => {
|
|||
|
||||
expectDiagnostics(diagnostics, {
|
||||
code: "invalid-argument",
|
||||
message: "Argument '123' is not assignable to parameter of type 'string'",
|
||||
message: "Argument '123' is not assignable to parameter of type 'valueof string'",
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace OpenAPI;
|
|||
* op read(): string;
|
||||
* ```
|
||||
*/
|
||||
extern dec operationId(target: Operation, operationId: string);
|
||||
extern dec operationId(target: Operation, operationId: valueof string);
|
||||
|
||||
/**
|
||||
* Attach some custom data to the OpenAPI element generated from this type.
|
||||
|
@ -30,7 +30,7 @@ extern dec operationId(target: Operation, operationId: string);
|
|||
* op read(): string;
|
||||
* ```
|
||||
*/
|
||||
extern dec extension(target: unknown, key: string, value: unknown);
|
||||
extern dec extension(target: unknown, key: valueof string, value: unknown);
|
||||
|
||||
/**
|
||||
* Specify that this model is to be treated as the OpenAPI `default` response.
|
||||
|
@ -59,4 +59,4 @@ extern dec defaultResponse(target: Model);
|
|||
* op listPets(): Pet[];
|
||||
* ```
|
||||
*/
|
||||
extern dec externalDocs(target: unknown, url: string, description?: string);
|
||||
extern dec externalDocs(target: unknown, url: valueof string, description?: valueof string);
|
||||
|
|
|
@ -32,7 +32,7 @@ describe("openapi: decorators", () => {
|
|||
|
||||
expectDiagnostics(diagnostics, {
|
||||
code: "invalid-argument",
|
||||
message: "Argument '123' is not assignable to parameter of type 'string'",
|
||||
message: "Argument '123' is not assignable to parameter of type 'valueof string'",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -77,7 +77,7 @@ describe("openapi: decorators", () => {
|
|||
|
||||
expectDiagnostics(diagnostics, {
|
||||
code: "invalid-argument",
|
||||
message: "Argument '123' is not assignable to parameter of type 'string'",
|
||||
message: "Argument '123' is not assignable to parameter of type 'valueof string'",
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -106,7 +106,7 @@ describe("openapi: decorators", () => {
|
|||
|
||||
expectDiagnostics(diagnostics, {
|
||||
code: "invalid-argument",
|
||||
message: "Argument '123' is not assignable to parameter of type 'string'",
|
||||
message: "Argument '123' is not assignable to parameter of type 'valueof string'",
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -119,7 +119,7 @@ describe("openapi: decorators", () => {
|
|||
|
||||
expectDiagnostics(diagnostics, {
|
||||
code: "invalid-argument",
|
||||
message: "Argument '123' is not assignable to parameter of type 'string'",
|
||||
message: "Argument '123' is not assignable to parameter of type 'valueof string'",
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -175,7 +175,7 @@ extern dec message(target: {});
|
|||
* }
|
||||
* ```
|
||||
*/
|
||||
extern dec field(target: TypeSpec.Reflection.ModelProperty, index: uint32);
|
||||
extern dec field(target: TypeSpec.Reflection.ModelProperty, index: valueof uint32);
|
||||
|
||||
/**
|
||||
* Reserve a field index, range, or name. If a field definition collides with a reservation, the emitter will produce
|
||||
|
@ -211,7 +211,7 @@ extern dec field(target: TypeSpec.Reflection.ModelProperty, index: uint32);
|
|||
* }
|
||||
* ```
|
||||
*/
|
||||
extern dec reserve(target: {}, ...reservations: (string | [uint32, uint32] | uint32)[]);
|
||||
extern dec reserve(target: {}, ...reservations: valueof (string | [uint32, uint32] | uint32)[]);
|
||||
|
||||
/**
|
||||
* Declares that a TypeSpec interface constitutes a Protobuf service. The contents of the interface will be converted to
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
Operation,
|
||||
Program,
|
||||
resolvePath,
|
||||
StringLiteral,
|
||||
Tuple,
|
||||
Type,
|
||||
} from "@typespec/compiler";
|
||||
|
@ -91,8 +92,13 @@ export function $_map(ctx: DecoratorContext, target: Model) {
|
|||
ctx.program.stateSet(state._map).add(target);
|
||||
}
|
||||
|
||||
export function $externRef(ctx: DecoratorContext, target: Model, path: string, name: string) {
|
||||
ctx.program.stateMap(state.externRef).set(target, [path, name]);
|
||||
export function $externRef(
|
||||
ctx: DecoratorContext,
|
||||
target: Model,
|
||||
path: StringLiteral,
|
||||
name: StringLiteral
|
||||
) {
|
||||
ctx.program.stateMap(state.externRef).set(target, [path.value, name.value]);
|
||||
}
|
||||
|
||||
export function $stream(ctx: DecoratorContext, target: Operation, mode: EnumMember) {
|
||||
|
|
|
@ -12,10 +12,14 @@ import {
|
|||
TemplateParameterDeclarationNode,
|
||||
Type,
|
||||
UnionVariant,
|
||||
ValueType,
|
||||
} from "@typespec/compiler";
|
||||
|
||||
/** @internal */
|
||||
export function getTypeSignature(type: Type): string {
|
||||
export function getTypeSignature(type: Type | ValueType): string {
|
||||
if (type.kind === "Value") {
|
||||
return `valueof ${getTypeSignature(type.target)}`;
|
||||
}
|
||||
if (isReflectionType(type)) {
|
||||
return type.name;
|
||||
}
|
||||
|
@ -42,6 +46,7 @@ export function getTypeSignature(type: Type): string {
|
|||
return `(number) ${type.value.toString()}`;
|
||||
case "Intrinsic":
|
||||
return `(intrinsic) ${type.name}`;
|
||||
|
||||
case "FunctionParameter":
|
||||
return getFunctionParameterSignature(type);
|
||||
case "ModelProperty":
|
||||
|
|
|
@ -27,7 +27,7 @@ extern dec autoRoute(target: Interface | Operation);
|
|||
* get(@segment("pets") @path id: string): void; //-> route: /pets/{id}
|
||||
* }
|
||||
*/
|
||||
extern dec segment(target: Model | ModelProperty | Operation, name: string);
|
||||
extern dec segment(target: Model | ModelProperty | Operation, name: valueof string);
|
||||
|
||||
/**
|
||||
* Returns the URL segment of a given model if it has `@segment` and `@key` decorator.
|
||||
|
@ -40,14 +40,17 @@ extern dec segmentOf(target: Operation, type: Model);
|
|||
*
|
||||
* @param seperator Seperator seperating the action segment from the rest of the url
|
||||
*/
|
||||
extern dec actionSeparator(target: Model | ModelProperty | Operation, seperator: "/" | ":" | "/:");
|
||||
extern dec actionSeparator(
|
||||
target: Model | ModelProperty | Operation,
|
||||
seperator: valueof "/" | ":" | "/:"
|
||||
);
|
||||
|
||||
/**
|
||||
* Mark this model as a resource type with a name.
|
||||
*
|
||||
* @param collectionName type's collection name
|
||||
*/
|
||||
extern dec resource(target: Model, collectionName: string);
|
||||
extern dec resource(target: Model, collectionName: valueof string);
|
||||
|
||||
/**
|
||||
* Mark model as a child of the given parent resource.
|
||||
|
@ -108,20 +111,20 @@ extern dec listsResource(target: Operation, resourceType: Model);
|
|||
* Specify this operation is an action. (Scoped to a resource item /pets/{petId}/my-action)
|
||||
* @param name Name of the action. If not specified, the name of the operation will be used.
|
||||
*/
|
||||
extern dec action(target: Operation, name?: string);
|
||||
extern dec action(target: Operation, name?: valueof string);
|
||||
|
||||
/**
|
||||
* Specify this operation is a collection action. (Scopped to a resource, /pets/my-action)
|
||||
* @param resourceType Resource marked with @resource
|
||||
* @param name Name of the action. If not specified, the name of the operation will be used.
|
||||
*/
|
||||
extern dec collectionAction(target: Operation, resourceType: Model, name?: string);
|
||||
extern dec collectionAction(target: Operation, resourceType: Model, name?: valueof string);
|
||||
|
||||
/**
|
||||
* Copy the resource key parameters on the model
|
||||
* @param filter Filter to exclude certain properties.
|
||||
*/
|
||||
extern dec copyResourceKeyParameters(target: Model, filter?: string);
|
||||
extern dec copyResourceKeyParameters(target: Model, filter?: valueof string);
|
||||
|
||||
namespace Private {
|
||||
extern dec resourceLocation(target: string, resourceType: Model);
|
||||
|
|
|
@ -108,7 +108,7 @@ function cloneKeyProperties(context: DecoratorContext, target: Model, resourceTy
|
|||
},
|
||||
{
|
||||
decorator: $resourceTypeForKeyParam,
|
||||
args: [{ node: target.node, value: resourceType }],
|
||||
args: [{ node: target.node, value: resourceType, jsValue: resourceType }],
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -286,7 +286,9 @@ export function $resource(context: DecoratorContext, entity: Model, collectionNa
|
|||
// Manually push the decorator onto the property so that it's copyable in KeysOf<T>
|
||||
key.keyProperty.decorators.push({
|
||||
decorator: $segment,
|
||||
args: [{ value: context.program.checker.createLiteralType(collectionName) }],
|
||||
args: [
|
||||
{ value: context.program.checker.createLiteralType(collectionName), jsValue: collectionName },
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -424,7 +424,7 @@ describe("rest: routes", () => {
|
|||
strictEqual(diagnostics[0].code, "invalid-argument");
|
||||
strictEqual(
|
||||
diagnostics[0].message,
|
||||
`Argument 'x' is not assignable to parameter of type '/ | : | /:'`
|
||||
`Argument 'x' is not assignable to parameter of type 'valueof / | : | /:'`
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
import { $doc } from "@typespec/compiler";
|
||||
|
||||
export function $fancyDoc(program, target, text) {
|
||||
text = `<blink>${text}</blink>`;
|
||||
$doc(program, target, text);
|
||||
const str = `<blink>${text.value}</blink>`;
|
||||
$doc(program, target, str);
|
||||
}
|
||||
|
||||
export function $evenFancierDoc(program, target, ...args) {
|
||||
args[0] = `<marquee><blink>${args[0]}</blink></marquee>`;
|
||||
args[0] = `<marquee><blink>${args[0].value}</blink></marquee>`;
|
||||
$doc(program, target, ...args);
|
||||
}
|
||||
|
|
|
@ -437,8 +437,12 @@ UnionExpressionOrHigher :
|
|||
`|`? UnionExpressionOrHigher `|` IntersectionExpressionOrHigher
|
||||
|
||||
IntersectionExpressionOrHigher :
|
||||
ValueOfExpressionOrHigher
|
||||
`&`? IntersectionExpressionOrHigher `&` ValueOfExpressionOrHigher
|
||||
|
||||
ValueOfExpressionOrHigher :
|
||||
ArrayExpressionOrHigher
|
||||
`&`? IntersectionExpressionOrHigher `&` ArrayExpressionOrHigher
|
||||
`valueof` ArrayExpressionOrHigher
|
||||
|
||||
ArrayExpressionOrHigher :
|
||||
PrimaryExpression
|
||||
|
|
|
@ -33,7 +33,7 @@ namespace TypeSpec {
|
|||
* @param version The version that the target was renamed in.
|
||||
* @param oldName The previous name of the target.
|
||||
*/
|
||||
extern dec renamedFrom(target: unknown, version: EnumMember, oldName: string);
|
||||
extern dec renamedFrom(target: unknown, version: EnumMember, oldName: valueof string);
|
||||
|
||||
/**
|
||||
* Identifies when a target was made optional.
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
{ "path": "packages/internal-build-utils/tsconfig.json" },
|
||||
{ "path": "packages/tmlanguage-generator/tsconfig.json" },
|
||||
{ "path": "packages/html-program-viewer/tsconfig.json" },
|
||||
{ "path": "packages/protobuf/tsconfig.json" },
|
||||
{ "path": "packages/openapi3/tsconfig.json" },
|
||||
{ "path": "packages/bundler/tsconfig.json" },
|
||||
{ "path": "packages/playground/tsconfig.json" },
|
||||
|
|
Загрузка…
Ссылка в новой задаче