Skip to content

Commit f0e8163

Browse files
tadeleshiscai-msft
and
iscai-msft
authored
[tcgc] convert to same type for array or dictionary of same type (#2626)
fix: #2595 --------- Co-authored-by: iscai-msft <[email protected]>
1 parent b1a79f9 commit f0e8163

File tree

6 files changed

+139
-24
lines changed

6 files changed

+139
-24
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
changeKind: fix
3+
packages:
4+
- "@azure-tools/typespec-client-generator-core"
5+
---
6+
7+
Convert to same type for array or dictionary of same type.

packages/typespec-client-generator-core/src/context.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ import { defaultDecoratorsAllowList } from "./configs.js";
1919
import { handleClientExamples } from "./example.js";
2020
import {
2121
getKnownScalars,
22+
SdkArrayType,
2223
SdkContext,
24+
SdkDictionaryType,
2325
SdkEnumType,
2426
SdkHttpOperation,
2527
SdkModelPropertyType,
@@ -56,6 +58,7 @@ export function createTCGCContext(program: Program, emitterName?: string): TCGCC
5658
Type,
5759
SdkModelType | SdkEnumType | SdkUnionType | SdkNullableType
5860
>(),
61+
__arrayDictionaryCache: new Map<Type, SdkDictionaryType | SdkArrayType>(),
5962
__modelPropertyCache: new Map<ModelProperty, SdkModelPropertyType>(),
6063
__generatedNames: new Map<Union | Model | TspLiteralType, string>(),
6164
__httpOperationCache: new Map<Operation, HttpOperation>(),

packages/typespec-client-generator-core/src/interfaces.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export interface TCGCContext {
5454
flattenUnionAsEnum?: boolean;
5555

5656
__referencedTypeCache: Map<Type, SdkModelType | SdkEnumType | SdkUnionType | SdkNullableType>;
57+
__arrayDictionaryCache: Map<Type, SdkDictionaryType | SdkArrayType>;
5758
__modelPropertyCache: Map<ModelProperty, SdkModelPropertyType>;
5859
__generatedNames: Map<Type, string>;
5960
__httpOperationCache: Map<Operation, HttpOperation>;

packages/typespec-client-generator-core/src/types.ts

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -437,32 +437,38 @@ export function getSdkArrayOrDictWithDiagnostics(
437437
// if model with both indexer and properties or name should be a model with additional properties
438438
if (type.indexer !== undefined && type.properties.size === 0) {
439439
if (!isNeverType(type.indexer.key)) {
440-
const valueType = diagnostics.pipe(
441-
getClientTypeWithDiagnostics(context, type.indexer.value!, operation),
442-
);
443-
const name = type.indexer.key.name;
444-
if (name === "string" && type.name === "Record") {
445-
// model MyModel is Record<> {} should be model with additional properties
446-
if (type.sourceModel?.kind === "Model" && type.sourceModel?.name === "Record") {
447-
return diagnostics.wrap(undefined);
440+
let sdkType = context.__arrayDictionaryCache.get(type);
441+
if (!sdkType) {
442+
const valueType = diagnostics.pipe(
443+
getClientTypeWithDiagnostics(context, type.indexer.value!, operation),
444+
);
445+
const name = type.indexer.key.name;
446+
if (name === "string" && type.name === "Record") {
447+
// model MyModel is Record<> {} should be model with additional properties
448+
if (type.sourceModel?.kind === "Model" && type.sourceModel?.name === "Record") {
449+
return diagnostics.wrap(undefined);
450+
} else {
451+
// other cases are dict
452+
sdkType = {
453+
...diagnostics.pipe(getSdkTypeBaseHelper(context, type, "dict")),
454+
keyType: diagnostics.pipe(
455+
getClientTypeWithDiagnostics(context, type.indexer.key, operation),
456+
),
457+
valueType: valueType,
458+
};
459+
}
460+
} else if (name === "integer") {
461+
// only array's index key name is integer
462+
sdkType = {
463+
...diagnostics.pipe(getSdkTypeBaseHelper(context, type, "array")),
464+
name: getLibraryName(context, type),
465+
valueType: valueType,
466+
crossLanguageDefinitionId: getCrossLanguageDefinitionId(context, type, operation),
467+
};
448468
}
449-
// other cases are dict
450-
return diagnostics.wrap({
451-
...diagnostics.pipe(getSdkTypeBaseHelper(context, type, "dict")),
452-
keyType: diagnostics.pipe(
453-
getClientTypeWithDiagnostics(context, type.indexer.key, operation),
454-
),
455-
valueType: valueType,
456-
});
457-
} else if (name === "integer") {
458-
// only array's index key name is integer
459-
return diagnostics.wrap({
460-
...diagnostics.pipe(getSdkTypeBaseHelper(context, type, "array")),
461-
name: getLibraryName(context, type),
462-
valueType: valueType,
463-
crossLanguageDefinitionId: getCrossLanguageDefinitionId(context, type, operation),
464-
});
469+
context.__arrayDictionaryCache.set(type, sdkType!);
465470
}
471+
return diagnostics.wrap(sdkType);
466472
}
467473
}
468474
return diagnostics.wrap(undefined);

packages/typespec-client-generator-core/test/types/array.test.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,48 @@ it("alias of EmbeddingVector", async () => {
9191
strictEqual(property.type.crossLanguageDefinitionId, "Azure.Core.EmbeddingVector");
9292
strictEqual(property.type.valueType.kind, "int32");
9393
});
94+
95+
it("same type's array come to same type", async () => {
96+
await runner.compile(`
97+
@service
98+
namespace TestClient {
99+
model Test {
100+
prop: string;
101+
}
102+
103+
model TestArray {
104+
prop1: Test[];
105+
prop2: Test[];
106+
prop3: string[];
107+
prop4: string[];
108+
prop5: Test[][];
109+
prop6: Test[][];
110+
prop7: Record<Test>[];
111+
prop8: Record<Test>[];
112+
prop9: Record<Record<Test>>[];
113+
prop10: Record<Record<Test>>[];
114+
}
115+
116+
op get(): TestArray;
117+
}
118+
`);
119+
const testArrayModel = runner.context.sdkPackage.models[0];
120+
strictEqual(testArrayModel.kind, "model");
121+
strictEqual(testArrayModel.name, "TestArray");
122+
strictEqual(testArrayModel.properties.length, 10);
123+
const prop1 = testArrayModel.properties[0];
124+
const prop2 = testArrayModel.properties[1];
125+
const prop3 = testArrayModel.properties[2];
126+
const prop4 = testArrayModel.properties[3];
127+
const prop5 = testArrayModel.properties[4];
128+
const prop6 = testArrayModel.properties[5];
129+
const prop7 = testArrayModel.properties[6];
130+
const prop8 = testArrayModel.properties[7];
131+
const prop9 = testArrayModel.properties[8];
132+
const prop10 = testArrayModel.properties[9];
133+
strictEqual(prop1.type, prop2.type);
134+
strictEqual(prop3.type, prop4.type);
135+
strictEqual(prop5.type, prop6.type);
136+
strictEqual(prop7.type, prop8.type);
137+
strictEqual(prop9.type, prop10.type);
138+
});
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { strictEqual } from "assert";
2+
import { beforeEach, it } from "vitest";
3+
import { SdkTestRunner, createSdkTestRunner } from "../test-host.js";
4+
5+
let runner: SdkTestRunner;
6+
7+
beforeEach(async () => {
8+
runner = await createSdkTestRunner({ emitterName: "@azure-tools/typespec-java" });
9+
});
10+
11+
it("same type's dictionary come to same type", async () => {
12+
await runner.compile(`
13+
@service
14+
namespace TestClient {
15+
model Test {
16+
prop: string;
17+
}
18+
19+
model TestDictionary {
20+
prop1: Record<Test>;
21+
prop2: Record<Test>;
22+
prop3: Record<string>;
23+
prop4: Record<string>;
24+
prop5: Record<Test[]>;
25+
prop6: Record<Test[]>;
26+
prop7: Record<Record<Test>>;
27+
prop8: Record<Record<Test>>;
28+
prop9: Record<Test[][]>;
29+
prop10: Record<Test[][]>;
30+
}
31+
op get(): TestDictionary;
32+
}
33+
`);
34+
const testArrayModel = runner.context.sdkPackage.models[0];
35+
strictEqual(testArrayModel.kind, "model");
36+
strictEqual(testArrayModel.name, "TestDictionary");
37+
strictEqual(testArrayModel.properties.length, 10);
38+
const prop1 = testArrayModel.properties[0];
39+
const prop2 = testArrayModel.properties[1];
40+
const prop3 = testArrayModel.properties[2];
41+
const prop4 = testArrayModel.properties[3];
42+
const prop5 = testArrayModel.properties[4];
43+
const prop6 = testArrayModel.properties[5];
44+
const prop7 = testArrayModel.properties[6];
45+
const prop8 = testArrayModel.properties[7];
46+
const prop9 = testArrayModel.properties[8];
47+
const prop10 = testArrayModel.properties[9];
48+
strictEqual(prop1.type, prop2.type);
49+
strictEqual(prop3.type, prop4.type);
50+
strictEqual(prop5.type, prop6.type);
51+
strictEqual(prop7.type, prop8.type);
52+
strictEqual(prop9.type, prop10.type);
53+
});

0 commit comments

Comments
 (0)