我们有以下 OpenAPI 架构:
{
"openapi": "3.0.1",
"paths": {
"/v1/tool/breadcrumbs/{hierarchyId}/{categoryId}": {
"get": {
"tags": [
"V1-tool"
],
"summary": "Get Breadcrumbs details",
"operationId": "getBreadcrumbs",
"parameters": [
{
"name": "hierarchyId",
"in": "path",
"required": true,
"schema": {
"minimum": 1,
"type": "integer",
"format": "int32"
}
},
{
"name": "categoryId",
"in": "path",
"required": true,
"schema": {
"minimum": 1,
"type": "integer",
"format": "int32"
}
}
],
"responses": {
"200": {
"description": "default response",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Kitten"
}
}
}
}
}
},
"security": [
{
"Auth": []
}
]
}
},
"/v1/tool/hierarchies": {
"get": {
"tags": [
"V1-tool"
],
"summary": "Get all available hierarchies ",
"operationId": "getAllHierarchies",
"responses": {
"200": {
"description": "default response",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/HierarchyResponse"
}
}
}
}
}
},
"security": [
{
"Auth": []
}
]
}
},
"/v1/tool/search/{hierarchyId}/{searchTerm}": {
"get": {
"tags": [
"V1-tool"
],
"summary": "Free text search categories for a given hierarchy",
"operationId": "searchBy",
"parameters": [
{
"name": "hierarchyId",
"in": "path",
"required": true,
"schema": {
"minimum": 1,
"type": "integer",
"format": "int32"
}
},
{
"name": "searchTerm",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "default response",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Kitten"
}
}
}
}
}
},
"security": [
{
"Auth": []
}
]
}
},
"/v1/tool/category/{hierarchyId}/{categoryId}": {
"get": {
"tags": [
"V1-tool"
],
"summary": "Get Category data needed to render a Table View in the Category Management tool",
"operationId": "getTableView",
"parameters": [
{
"name": "hierarchyId",
"in": "path",
"required": true,
"schema": {
"minimum": 1,
"type": "integer",
"format": "int32"
}
},
{
"name": "categoryId",
"in": "path",
"required": true,
"schema": {
"minimum": 1,
"type": "integer",
"format": "int32"
}
}
],
"responses": {
"200": {
"description": "default response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/TableViewResponse"
}
}
}
}
},
"security": [
{
"Auth": []
}
]
}
}
},
"components": {
"schemas": {
"Kitten": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"Kitten"
]
},
"id": {
"type": "integer",
"format": "int32"
},
"name": {
"type": "string"
},
"uri": {
"type": "string"
}
}
},
"HierarchyResponse": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"Hierarchy"
]
},
"id": {
"type": "integer",
"format": "int32"
},
"name": {
"type": "string"
}
}
},
"AliasCategoryResponse": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"Alias"
]
},
"level": {
"type": "string"
},
"name": {
"type": "string"
},
"id": {
"type": "integer",
"format": "int32"
},
"uri": {
"type": "string"
}
}
},
"ChildCategoryResponse": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"Category"
]
},
"level": {
"type": "string"
},
"name": {
"type": "string"
},
"id": {
"type": "integer",
"format": "int32"
},
"uri": {
"type": "string"
}
}
},
"GblSubheaderResponse": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"Translation"
]
},
"brandCatalogId": {
"type": "integer",
"format": "int32"
},
"value": {
"type": "string"
}
}
},
"Item": {
"type": "object",
"properties": {
"type": {
"type": "string"
}
},
"oneOf": [
{
"$ref": "#/components/schemas/ChildCategoryResponse"
},
{
"$ref": "#/components/schemas/AliasCategoryResponse"
},
{
"$ref": "#/components/schemas/SubheaderResponse"
}
]
},
"ParentCategoryResponse": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"ParentCategory"
]
},
"id": {
"type": "integer",
"format": "int32"
},
"name": {
"type": "string"
}
}
},
"SubheaderResponse": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"Subheader"
]
},
"name": {
"type": "string"
},
"translatedNames": {
"type": "array",
"items": {
"$ref": "#/components/schemas/GblSubheaderResponse"
}
}
}
},
"TableViewResponse": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"DefaultCategory"
]
},
"uri": {
"type": "string"
},
"level": {
"type": "string"
},
"name": {
"type": "string"
},
"id": {
"type": "integer",
"format": "int32"
},
"parentCategory": {
"$ref": "#/components/schemas/ParentCategoryResponse"
},
"items": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Item"
}
}
}
}
},
"securitySchemes": {
"Auth": {
"type": "http",
"description": "Token Authentication e.g. Bearer <placeholder>",
"scheme": "bearer",
"bearerFormat": "JWT"
}
}
}
}
我们运行以下命令从上述模式文件生成流类型:
npx swagger-to-flowtype path/to/schema/file -d generated_types.js
这导致以下输出:// @flow strict
export type Kitten = { type: "Kitten", id: number, name: string, uri: string };
export type HierarchyResponse = { type: "Hierarchy", id: number, name: string };
export type AliasCategoryResponse = {
type: "Alias",
level: string,
name: string,
id: number,
uri: string
};
export type ChildCategoryResponse = {
type: "Category",
level: string,
name: string,
id: number,
uri: string
};
export type GblSubheaderResponse = {
type: "Translation",
brandCatalogId: number,
value: string
};
export type Item = { type: string };
export type ParentCategoryResponse = {
type: "ParentCategory",
id: number,
name: string
};
export type SubheaderResponse = {
type: "Subheader",
name: string,
translatedNames: Array<GblSubheaderResponse>
};
export type TableViewResponse = {
type: "DefaultCategory",
uri: string,
level: string,
name: string,
id: number,
parentCategory: ParentCategoryResponse,
items: Array<Item>
};
问题是 Item
类型定义为:export type Item = { type: string };
我们希望将其定义为:export type Item = ChildCategoryResponse | AliasCategoryResponse | SubheaderResponse;
我们的 OpenAPI 模式的相关部分是:"Item": {
"type": "object",
"properties": {
"type": {
"type": "string"
}
},
"oneOf": [
{
"$ref": "#/components/schemas/ChildCategoryResponse"
},
{
"$ref": "#/components/schemas/AliasCategoryResponse"
},
{
"$ref": "#/components/schemas/SubheaderResponse"
}
]
},
我们最初认为也许 swagger-to-flowtype
这里有一个错误,但使用相同的模式通过 swagger-typescript-api
生成 TypeScript 类型输出类似的东西:export enum Item {
Category = "Category",
Alias = "Alias",
Subheader = "Subheader",
}
...
export interface TableViewResponse {
type?: "DefaultCategory";
uri?: string;
level?: string;
name?: string;
/** @format int32 */
id?: number;
parentCategory?: ParentCategoryResponse;
items?: Item[];
}
请注意,在 Flow 和 TypeScript 类型中,我们应该有每个可能的对象形状的类型信息,Item
可能是,但我们没有任何此类类型信息。这是一个项目数组的具体示例:
const items: Array<Item> = [
{
type: "Alias",
level: "3",
name: "name",
id: 42,
uri: "uri"
},
{
type: "Category",
level: "2",
name: "name",
id: 45,
uri: "uri"
}
]
希望现在清楚为什么 export type Item = { type: string };
不是正确的类型定义。问题:
是否有不同的模式可以生成所需的
export type Item = ChildCategoryResponse | AliasCategoryResponse | SubheaderResponse
输出而不是当前的 export type Item = { type: string };
输出?
最佳答案
根据我的理解阅读 the OpenAPI specification , oneOf
和 type
不应该一起使用。它似乎没有明确说明,但规范中的示例使用其中一种,从不同时使用。我假设是 TypeScript 绑定(bind)生成器,看到 type
是 "object"
, 简单地构造基于 properties
的类型并忽略 oneOf
.
如果要区分type
属性作为在联合中的类型之间消除歧义,您应该使用 discriminator
而不是 properties
:
"Item": {
"oneOf": [
{
"$ref": "#/components/schemas/ChildCategoryResponse"
},
{
"$ref": "#/components/schemas/AliasCategoryResponse"
},
{
"$ref": "#/components/schemas/SubheaderResponse"
}
],
"discriminator": {
"propertyName": "type",
"mapping": {
"Category": {
"$ref": "#/components/schemas/ChildCategoryResponse"
},
"Alias": {
"$ref": "#/components/schemas/AliasCategoryResponse"
},
"Subheader": {
"$ref": "#/components/schemas/SubheaderResponse"
}
}
}
},
关于typescript - 从 OpenAPI 的 `oneOf` 关键字生成的不受欢迎的 Flow/TypeScript 类型。是否有另一个 OpenAPI 模式会产生更好的结果?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68381471/