diff --git a/common/changes/@typespec/compiler/reflection-model-scalar_2023-04-17-15-46.json b/common/changes/@typespec/compiler/reflection-model-scalar_2023-04-17-15-46.json new file mode 100644 index 00000000000..271711c8a4d --- /dev/null +++ b/common/changes/@typespec/compiler/reflection-model-scalar_2023-04-17-15-46.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@typespec/compiler", + "comment": "Added `Model` and `Scalar` to Reflection namespace", + "type": "none" + } + ], + "packageName": "@typespec/compiler" +} \ No newline at end of file diff --git a/common/changes/@typespec/openapi/reflection-model-scalar_2023-04-18-16-41.json b/common/changes/@typespec/openapi/reflection-model-scalar_2023-04-18-16-41.json new file mode 100644 index 00000000000..dfd5c02fb78 --- /dev/null +++ b/common/changes/@typespec/openapi/reflection-model-scalar_2023-04-18-16-41.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@typespec/openapi", + "comment": "Update decorator declaration to use `Model` instead of `object`", + "type": "none" + } + ], + "packageName": "@typespec/openapi" +} \ No newline at end of file diff --git a/common/changes/@typespec/openapi3/reflection-model-scalar_2023-04-18-16-41.json b/common/changes/@typespec/openapi3/reflection-model-scalar_2023-04-18-16-41.json new file mode 100644 index 00000000000..39d04a8cb92 --- /dev/null +++ b/common/changes/@typespec/openapi3/reflection-model-scalar_2023-04-18-16-41.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@typespec/openapi3", + "comment": "Update decorator declaration to use `Model` instead of `object`", + "type": "none" + } + ], + "packageName": "@typespec/openapi3" +} \ No newline at end of file diff --git a/common/changes/@typespec/rest/reflection-model-scalar_2023-04-18-16-41.json b/common/changes/@typespec/rest/reflection-model-scalar_2023-04-18-16-41.json new file mode 100644 index 00000000000..b87ee272482 --- /dev/null +++ b/common/changes/@typespec/rest/reflection-model-scalar_2023-04-18-16-41.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@typespec/rest", + "comment": "Update decorator declaration to use `Model` instead of `object`", + "type": "none" + } + ], + "packageName": "@typespec/rest" +} \ No newline at end of file diff --git a/docs/extending-typespec/create-decorators.md b/docs/extending-typespec/create-decorators.md index 140140a5ce7..0e0ebbcfa5f 100644 --- a/docs/extending-typespec/create-decorators.md +++ b/docs/extending-typespec/create-decorators.md @@ -20,7 +20,7 @@ This part is optional but provides great value: A decorator signature can be declared using the `dec` keyword. As we are implementing the decorator in JS (only choice right now), we must apply the `extern` modifier as well. ```typespec -extern dec logType(target: TypeSpec.Reflection.Type, name: TypeSpec.Reflection.StringLiteral); +extern dec logType(target: unknown, name: string); ``` ## Decorator target diff --git a/docs/standard-library/built-in-decorators.md b/docs/standard-library/built-in-decorators.md index 6920c543d3c..a23aa8171bd 100644 --- a/docs/standard-library/built-in-decorators.md +++ b/docs/standard-library/built-in-decorators.md @@ -29,12 +29,12 @@ dec deprecated(target: unknown, message: string) Specify the property to be used to discriminate this type. ```typespec -dec discriminator(target: object | Union, propertyName: string) +dec discriminator(target: Model | Union, propertyName: string) ``` #### Target -`union object | Union` +`union Model | Union` #### Parameters | Name | Type | Description | @@ -84,12 +84,12 @@ dec doc(target: unknown, doc: string, formatArgs?: object) Specify that this model is an error type. Operations return error types when the operation has failed. ```typespec -dec error(target: object) +dec error(target: Model) ``` #### Target -`model object` +`Model` #### Parameters None @@ -550,12 +550,12 @@ name: string; ```typespec -dec withDefaultKeyVisibility(target: object, visibility: unknown) +dec withDefaultKeyVisibility(target: Model, visibility: unknown) ``` #### Target -`model object` +`Model` #### Parameters | Name | Type | Description | @@ -567,12 +567,12 @@ dec withDefaultKeyVisibility(target: object, visibility: unknown) ```typespec -dec withoutDefaultValues(target: object) +dec withoutDefaultValues(target: Model) ``` #### Target -`model object` +`Model` #### Parameters None @@ -582,12 +582,12 @@ None ```typespec -dec withoutOmittedProperties(target: object, omit: string | Union) +dec withoutOmittedProperties(target: Model, omit: string | Union) ``` #### Target -`model object` +`Model` #### Parameters | Name | Type | Description | @@ -599,12 +599,12 @@ dec withoutOmittedProperties(target: object, omit: string | Union) ```typespec -dec withUpdateableProperties(target: object) +dec withUpdateableProperties(target: Model) ``` #### Target -`model object` +`Model` #### Parameters None @@ -623,12 +623,12 @@ When using an emitter that applies visibility automatically, it is generally not necessary to use this decorator. ```typespec -dec withVisibility(target: object, ...visibilities: string[]) +dec withVisibility(target: Model, ...visibilities: string[]) ``` #### Target -`model object` +`Model` #### Parameters | Name | Type | Description | diff --git a/docs/standard-library/openapi/reference/decorators.md b/docs/standard-library/openapi/reference/decorators.md index 71e00c58b8d..ee669934fc6 100644 --- a/docs/standard-library/openapi/reference/decorators.md +++ b/docs/standard-library/openapi/reference/decorators.md @@ -14,12 +14,12 @@ Specify that this model is to be treated as the OpenAPI `default` response. This differs from the compiler built-in `@error` decorator as this does not necessarily represent an error. ```typespec -dec OpenAPI.defaultResponse(target: object) +dec OpenAPI.defaultResponse(target: Model) ``` #### Target -`model object` +`Model` #### Parameters diff --git a/docs/standard-library/rest/reference/decorators.md b/docs/standard-library/rest/reference/decorators.md index 68c8b4c424a..4028cfae4d2 100644 --- a/docs/standard-library/rest/reference/decorators.md +++ b/docs/standard-library/rest/reference/decorators.md @@ -31,12 +31,12 @@ dec TypeSpec.Rest.action(target: Operation, name?: string) Defines the separator string that is inserted before the action name in auto-generated routes for actions. ```typespec -dec TypeSpec.Rest.actionSeparator(target: object | ModelProperty | Operation, seperator: / | : | /:) +dec TypeSpec.Rest.actionSeparator(target: Model | ModelProperty | Operation, seperator: / | : | /:) ``` #### Target -`union object | ModelProperty | Operation` +`union Model | ModelProperty | Operation` #### Parameters @@ -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 -dec TypeSpec.Rest.collectionAction(target: Operation, resourceType: object, name?: string) +dec TypeSpec.Rest.collectionAction(target: Operation, resourceType: Model, name?: string) ``` #### Target @@ -85,7 +85,7 @@ dec TypeSpec.Rest.collectionAction(target: Operation, resourceType: object, name | Name | Type | Description | | ------------ | --------------- | ----------- | -| resourceType | `model object` | | +| resourceType | `Model` | | | name | `scalar string` | | ### `@createsOrReplacesResource` {#@TypeSpec.Rest.createsOrReplacesResource} @@ -93,7 +93,7 @@ dec TypeSpec.Rest.collectionAction(target: Operation, resourceType: object, name Specify that this is a CreateOrReplace operation for a given resource. ```typespec -dec TypeSpec.Rest.createsOrReplacesResource(target: Operation, resourceType: object) +dec TypeSpec.Rest.createsOrReplacesResource(target: Operation, resourceType: Model) ``` #### Target @@ -102,16 +102,16 @@ dec TypeSpec.Rest.createsOrReplacesResource(target: Operation, resourceType: obj #### Parameters -| Name | Type | Description | -| ------------ | -------------- | ----------- | -| resourceType | `model object` | | +| Name | Type | Description | +| ------------ | ------- | ----------- | +| resourceType | `Model` | | ### `@createsOrUpdatesResource` {#@TypeSpec.Rest.createsOrUpdatesResource} Specify that this is a CreatesOrUpdate operation for a given resource. ```typespec -dec TypeSpec.Rest.createsOrUpdatesResource(target: Operation, resourceType: object) +dec TypeSpec.Rest.createsOrUpdatesResource(target: Operation, resourceType: Model) ``` #### Target @@ -120,16 +120,16 @@ dec TypeSpec.Rest.createsOrUpdatesResource(target: Operation, resourceType: obje #### Parameters -| Name | Type | Description | -| ------------ | -------------- | ----------- | -| resourceType | `model object` | | +| Name | Type | Description | +| ------------ | ------- | ----------- | +| resourceType | `Model` | | ### `@createsResource` {#@TypeSpec.Rest.createsResource} Specify that this is a Create operation for a given resource. ```typespec -dec TypeSpec.Rest.createsResource(target: Operation, resourceType: object) +dec TypeSpec.Rest.createsResource(target: Operation, resourceType: Model) ``` #### Target @@ -138,16 +138,16 @@ dec TypeSpec.Rest.createsResource(target: Operation, resourceType: object) #### Parameters -| Name | Type | Description | -| ------------ | -------------- | ----------- | -| resourceType | `model object` | | +| Name | Type | Description | +| ------------ | ------- | ----------- | +| resourceType | `Model` | | ### `@deletesResource` {#@TypeSpec.Rest.deletesResource} Specify that this is a Delete operation for a given resource. ```typespec -dec TypeSpec.Rest.deletesResource(target: Operation, resourceType: object) +dec TypeSpec.Rest.deletesResource(target: Operation, resourceType: Model) ``` #### Target @@ -156,16 +156,16 @@ dec TypeSpec.Rest.deletesResource(target: Operation, resourceType: object) #### Parameters -| Name | Type | Description | -| ------------ | -------------- | ----------- | -| resourceType | `model object` | | +| Name | Type | Description | +| ------------ | ------- | ----------- | +| resourceType | `Model` | | ### `@listsResource` {#@TypeSpec.Rest.listsResource} Specify that this is a List operation for a given resource. ```typespec -dec TypeSpec.Rest.listsResource(target: Operation, resourceType: object) +dec TypeSpec.Rest.listsResource(target: Operation, resourceType: Model) ``` #### Target @@ -174,16 +174,16 @@ dec TypeSpec.Rest.listsResource(target: Operation, resourceType: object) #### Parameters -| Name | Type | Description | -| ------------ | -------------- | ----------- | -| resourceType | `model object` | | +| Name | Type | Description | +| ------------ | ------- | ----------- | +| resourceType | `Model` | | ### `@readsResource` {#@TypeSpec.Rest.readsResource} Specify that this is a Read operation for a given resource. ```typespec -dec TypeSpec.Rest.readsResource(target: Operation, resourceType: object) +dec TypeSpec.Rest.readsResource(target: Operation, resourceType: Model) ``` #### Target @@ -192,21 +192,21 @@ dec TypeSpec.Rest.readsResource(target: Operation, resourceType: object) #### Parameters -| Name | Type | Description | -| ------------ | -------------- | ----------- | -| resourceType | `model object` | | +| Name | Type | Description | +| ------------ | ------- | ----------- | +| resourceType | `Model` | | ### `@resource` {#@TypeSpec.Rest.resource} Mark this model as a resource type with a name. ```typespec -dec TypeSpec.Rest.resource(target: object, collectionName: string) +dec TypeSpec.Rest.resource(target: Model, collectionName: string) ``` #### Target -`model object` +`Model` #### Parameters @@ -219,12 +219,12 @@ dec TypeSpec.Rest.resource(target: object, collectionName: string) Defines the preceding path segment for a ```typespec -dec TypeSpec.Rest.segment(target: object | ModelProperty | Operation, name: string) +dec TypeSpec.Rest.segment(target: Model | ModelProperty | Operation, name: string) ``` #### Target -`union object | ModelProperty | Operation` +`union Model | ModelProperty | Operation` #### Parameters @@ -237,7 +237,7 @@ dec TypeSpec.Rest.segment(target: object | ModelProperty | Operation, name: stri Returns the URL segment of a given model if it has `@segment` and `@key` decorator. ```typespec -dec TypeSpec.Rest.segmentOf(target: Operation, type: object) +dec TypeSpec.Rest.segmentOf(target: Operation, type: Model) ``` #### Target @@ -246,16 +246,16 @@ dec TypeSpec.Rest.segmentOf(target: Operation, type: object) #### Parameters -| Name | Type | Description | -| ---- | -------------- | ----------- | -| type | `model object` | | +| Name | Type | Description | +| ---- | ------- | ----------- | +| type | `Model` | | ### `@updatesResource` {#@TypeSpec.Rest.updatesResource} Specify that this is a Update operation for a given resource. ```typespec -dec TypeSpec.Rest.updatesResource(target: Operation, resourceType: object) +dec TypeSpec.Rest.updatesResource(target: Operation, resourceType: Model) ``` #### Target @@ -264,6 +264,6 @@ dec TypeSpec.Rest.updatesResource(target: Operation, resourceType: object) #### Parameters -| Name | Type | Description | -| ------------ | -------------- | ----------- | -| resourceType | `model object` | | +| Name | Type | Description | +| ------------ | ------- | ----------- | +| resourceType | `Model` | | diff --git a/packages/compiler/core/checker.ts b/packages/compiler/core/checker.ts index 583828b19b7..bc923adbaa9 100644 --- a/packages/compiler/core/checker.ts +++ b/packages/compiler/core/checker.ts @@ -5362,14 +5362,15 @@ function isTemplatedNode(node: Node): node is TemplateableNode { * Mapping from the reflection models to Type["kind"] value */ const ReflectionNameToKind = { - Model: "Model", - ModelProperty: "ModelProperty", - Interface: "Interface", Enum: "Enum", EnumMember: "EnumMember", - TemplateParameter: "TemplateParameter", + Interface: "Interface", + Model: "Model", + ModelProperty: "ModelProperty", Namespace: "Namespace", Operation: "Operation", + Scalar: "Scalar", + TemplateParameter: "TemplateParameter", Tuple: "Tuple", Union: "Union", UnionVariant: "UnionVariant", diff --git a/packages/compiler/lib/decorators.tsp b/packages/compiler/lib/decorators.tsp index 9ad75fc8dfb..b84d62790b6 100644 --- a/packages/compiler/lib/decorators.tsp +++ b/packages/compiler/lib/decorators.tsp @@ -92,7 +92,7 @@ extern dec service(target: Namespace, options?: ServiceOptions); * } * ``` */ -extern dec error(target: object); +extern dec error(target: Model); /** * Specify a known data format hint for this string type. For example `uuid`, `uri`, etc. @@ -335,7 +335,7 @@ extern dec projectedName(target: unknown, targetName: string, projectedName: str * model Dog extends Pet {kind: "dog", bark: boolean} * ``` */ -extern dec discriminator(target: object | Union, propertyName: string); +extern dec discriminator(target: Model | Union, propertyName: string); /** * Indicates that a property is only considered to be present or applicable ("visible") with @@ -407,11 +407,11 @@ extern dec visibility(target: ModelProperty, ...visibilities: string[]); * } * ``` */ -extern dec withVisibility(target: object, ...visibilities: string[]); -extern dec withDefaultKeyVisibility(target: object, visibility: unknown); -extern dec withUpdateableProperties(target: object); -extern dec withoutDefaultValues(target: object); -extern dec withoutOmittedProperties(target: object, omit: string | Union); +extern dec withVisibility(target: Model, ...visibilities: string[]); +extern dec withDefaultKeyVisibility(target: Model, visibility: unknown); +extern dec withUpdateableProperties(target: Model); +extern dec withoutDefaultValues(target: Model); +extern dec withoutOmittedProperties(target: Model, omit: string | Union); //--------------------------------------------------------------------------- // Debugging diff --git a/packages/compiler/lib/reflection.tsp b/packages/compiler/lib/reflection.tsp index aa193a75469..38876f7798f 100644 --- a/packages/compiler/lib/reflection.tsp +++ b/packages/compiler/lib/reflection.tsp @@ -1,10 +1,12 @@ namespace TypeSpec.Reflection; -model ModelProperty {} -model Interface {} model Enum {} model EnumMember {} +model Interface {} +model Model {} +model ModelProperty {} model Namespace {} model Operation {} +model Scalar {} model Union {} model UnionVariant {} diff --git a/packages/compiler/test/checker/relation.test.ts b/packages/compiler/test/checker/relation.test.ts index 7c00ba4c699..5cd6297c32e 100644 --- a/packages/compiler/test/checker/relation.test.ts +++ b/packages/compiler/test/checker/relation.test.ts @@ -1,5 +1,5 @@ import { deepStrictEqual, ok, strictEqual } from "assert"; -import { Diagnostic, Model, ModelPropertyNode } from "../../core/index.js"; +import { Diagnostic, Model, ModelPropertyNode, Type } from "../../core/index.js"; import { BasicTestRunner, DiagnosticMatch, @@ -764,4 +764,55 @@ describe("compiler: checker: type relations", () => { expectDiagnosticEmpty(diagnostics); }); }); + + describe("Reflection", () => { + function testReflectionType(name: Type["kind"], ref: string, code: string) { + describe(`Reflection.${name}`, () => { + it(`can assign ${name}`, async () => { + await expectTypeAssignable({ + source: ref, + target: `TypeSpec.Reflection.${name}`, + commonCode: code, + }); + }); + + it(`cannot assign union of ${name}`, async () => { + await expectTypeNotAssignable( + { + source: `${ref} | ${ref}`, + target: `TypeSpec.Reflection.${name}`, + commonCode: code, + }, + { code: "unassignable" } + ); + }); + }); + } + + testReflectionType("Enum", "Foo", `enum Foo {a, b, c}`); + testReflectionType("EnumMember", "Foo.a", `enum Foo {a, b, c}`); + testReflectionType("Interface", "Foo", `interface Foo {a(): void}`); + testReflectionType("Model", "Foo", `model Foo {a: string, b: string}`); + testReflectionType("ModelProperty", "Foo.a", `model Foo {a: string, b: string}`); + testReflectionType("Namespace", "Foo", `namespace Foo {}`); + testReflectionType("Operation", "foo", `op foo(): void;`); + testReflectionType("Scalar", "foo", `scalar foo;`); + describe(`Reflection.Union`, () => { + it(`can assign union expression`, async () => { + await expectTypeAssignable({ + source: "Foo", + target: `TypeSpec.Reflection.Union`, + commonCode: `alias Foo = "abc" | "def";`, + }); + }); + it(`can assign named union`, async () => { + await expectTypeAssignable({ + source: "Foo", + target: `TypeSpec.Reflection.Union`, + commonCode: `union Foo {a: string, b: int32};`, + }); + }); + }); + testReflectionType("UnionVariant", "Foo.a", `union Foo {a: string, b: int32};`); + }); }); diff --git a/packages/compiler/test/decorators/decorators.test.ts b/packages/compiler/test/decorators/decorators.test.ts index 9433306858a..c8c4c9ccdc5 100644 --- a/packages/compiler/test/decorators/decorators.test.ts +++ b/packages/compiler/test/decorators/decorators.test.ts @@ -188,7 +188,7 @@ describe("compiler: built-in decorators", () => { strictEqual(diagnostics[0].code, "decorator-wrong-target"); strictEqual( diagnostics[0].message, - `Cannot apply @error decorator to A since it is not assignable to object` + `Cannot apply @error decorator to A since it is not assignable to Model` ); }); }); diff --git a/packages/openapi/lib/decorators.tsp b/packages/openapi/lib/decorators.tsp index 7c3f0d7c2d3..7ed7c4f9438 100644 --- a/packages/openapi/lib/decorators.tsp +++ b/packages/openapi/lib/decorators.tsp @@ -45,7 +45,7 @@ extern dec extension(target: unknown, key: string, value: unknown); * op listPets(): Pet[] | PetStoreResponse; * ``` */ -extern dec defaultResponse(target: object); +extern dec defaultResponse(target: Model); /** * Specify the OpenAPI `externalDocs` property for this type. diff --git a/packages/openapi3/test/discriminator.test.ts b/packages/openapi3/test/discriminator.test.ts index e1207302e2e..d94d72d78d0 100644 --- a/packages/openapi3/test/discriminator.test.ts +++ b/packages/openapi3/test/discriminator.test.ts @@ -12,7 +12,7 @@ describe("openapi3: discriminated unions", () => { expectDiagnostics(diagnostics, { code: "decorator-wrong-target", message: - "Cannot apply @discriminator decorator to Foo since it is not assignable to object | Union", + "Cannot apply @discriminator decorator to Foo since it is not assignable to Model | Union", }); }); diff --git a/packages/rest/lib/rest-decorators.tsp b/packages/rest/lib/rest-decorators.tsp index 90c0895651f..c86b4368f76 100644 --- a/packages/rest/lib/rest-decorators.tsp +++ b/packages/rest/lib/rest-decorators.tsp @@ -27,75 +27,75 @@ extern dec autoRoute(target: Interface | Operation); * get(@segment("pets") @path id: string): void; //-> route: /pets/{id} * } */ -extern dec segment(target: object | ModelProperty | Operation, name: string); +extern dec segment(target: Model | ModelProperty | Operation, name: string); /** * Returns the URL segment of a given model if it has `@segment` and `@key` decorator. */ -extern dec segmentOf(target: Operation, type: object); +extern dec segmentOf(target: Operation, type: Model); /** * Defines the separator string that is inserted before the action name in auto-generated routes for actions. * * @param seperator Seperator seperating the action segment from the rest of the url */ -extern dec actionSeparator(target: object | ModelProperty | Operation, seperator: "/" | ":" | "/:"); +extern dec actionSeparator(target: Model | ModelProperty | Operation, seperator: "/" | ":" | "/:"); /** * Mark this model as a resource type with a name. * * @param collectionName type's collection name */ -extern dec resource(target: object, collectionName: string); +extern dec resource(target: Model, collectionName: string); /** * Specify that this is a Read operation for a given resource. * * @param resource Resource marked with @resource */ -extern dec readsResource(target: Operation, resourceType: object); +extern dec readsResource(target: Operation, resourceType: Model); /** * Specify that this is a Create operation for a given resource. * * @param resource Resource marked with @resource */ -extern dec createsResource(target: Operation, resourceType: object); +extern dec createsResource(target: Operation, resourceType: Model); /** * Specify that this is a CreateOrReplace operation for a given resource. * * @param resource Resource marked with @resource */ -extern dec createsOrReplacesResource(target: Operation, resourceType: object); +extern dec createsOrReplacesResource(target: Operation, resourceType: Model); /** * Specify that this is a CreatesOrUpdate operation for a given resource. * * @param resource Resource marked with @resource */ -extern dec createsOrUpdatesResource(target: Operation, resourceType: object); +extern dec createsOrUpdatesResource(target: Operation, resourceType: Model); /** * Specify that this is a Update operation for a given resource. * * @param resource Resource marked with @resource */ -extern dec updatesResource(target: Operation, resourceType: object); +extern dec updatesResource(target: Operation, resourceType: Model); /** * Specify that this is a Delete operation for a given resource. * * @param resource Resource marked with @resource */ -extern dec deletesResource(target: Operation, resourceType: object); +extern dec deletesResource(target: Operation, resourceType: Model); /** * Specify that this is a List operation for a given resource. * * @param resource Resource marked with @resource */ -extern dec listsResource(target: Operation, resourceType: object); +extern dec listsResource(target: Operation, resourceType: Model); /** * Specify this operation is an action. (Scopped to a resource item /pets/{petId}/my-action) @@ -105,8 +105,8 @@ extern dec action(target: Operation, name?: string); /** * Specify this operation is a collection action. (Scopped to a resource, /pets/my-action) */ -extern dec collectionAction(target: Operation, resourceType: object, name?: string); +extern dec collectionAction(target: Operation, resourceType: Model, name?: string); namespace Private { - extern dec resourceLocation(target: string, resourceType: object); + extern dec resourceLocation(target: string, resourceType: Model); }