From 08090698ee72178775c8eb59cd682ccad8a448b7 Mon Sep 17 00:00:00 2001 From: maliming Date: Wed, 24 Jun 2026 13:08:14 +0800 Subject: [PATCH] Declare array parameters as readonly in Angular proxy generation --- .../tests/proxy-readonly-array-param.spec.ts | 34 +++++++++++++++++++ .../packages/schematics/src/utils/service.ts | 6 ++++ 2 files changed, 40 insertions(+) create mode 100644 npm/ng-packs/packages/schematics/src/tests/proxy-readonly-array-param.spec.ts diff --git a/npm/ng-packs/packages/schematics/src/tests/proxy-readonly-array-param.spec.ts b/npm/ng-packs/packages/schematics/src/tests/proxy-readonly-array-param.spec.ts new file mode 100644 index 0000000000..1e9042c641 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/tests/proxy-readonly-array-param.spec.ts @@ -0,0 +1,34 @@ +import { describe, expect, it } from 'vitest'; +import { createActionToSignatureMapper } from '../utils/service'; +import { Action } from '../models/api-definition'; + +// Real signature-generation pipeline for #25668: array parameters should be declared `readonly`. +const buildSignature = (name: string, typeSimple: string, type: string) => + createActionToSignatureMapper()({ + uniqueName: 'GetAsync', + name: 'GetAsync', + httpMethod: 'GET', + url: 'api/app/my', + supportedVersions: [], + parametersOnMethod: [{ name, typeAsString: '', type, typeSimple, isOptional: false, defaultValue: null }], + parameters: [], + returnValue: { type: 'System.Void', typeSimple: 'void' }, + } as unknown as Action); + +const paramType = (name: string, typeSimple: string, type: string) => + (buildSignature(name, typeSimple, type).parameters.find(p => p.name === name) as { type: string }) + .type; + +describe('proxy generation - readonly array params', () => { + it('declares a primitive array parameter as readonly', () => { + expect(paramType('ids', '[string]', 'System.String[]')).toBe('readonly string[]'); + }); + + it('declares a complex array parameter as readonly', () => { + expect(paramType('items', '[Volo.Abp.Dto]', 'Volo.Abp.Dto[]')).toBe('readonly Dto[]'); + }); + + it('leaves a non-array parameter unchanged', () => { + expect(paramType('name', 'string', 'System.String')).toBe('string'); + }); +}); diff --git a/npm/ng-packs/packages/schematics/src/utils/service.ts b/npm/ng-packs/packages/schematics/src/utils/service.ts index 1c808c9d3b..ee1ed4630d 100644 --- a/npm/ng-packs/packages/schematics/src/utils/service.ts +++ b/npm/ng-packs/packages/schematics/src/utils/service.ts @@ -259,6 +259,12 @@ export function createActionToSignatureMapper() { type = adaptType(p.type); } + // Array params are only forwarded to the HTTP client and never mutated, so declare them + // `readonly` to also accept readonly arrays from callers without a type error. + if (type.endsWith('[]')) { + type = `readonly ${type}`; + } + const parameter = new Property({ name: p.name, type }); parameter.setDefault(p.defaultValue); parameter.setOptional(p.isOptional);