From 48399e582a88c520ebb536445b173727c749a62e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 16 May 2026 19:42:28 +0000 Subject: [PATCH 1/3] Initial plan From 2043b45cbdb85f727e43ef99cd1954af1b30791e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 16 May 2026 20:55:35 +0000 Subject: [PATCH 2/3] Add test case for declaration emit readonly as const Agent-Logs-Url: https://github.com/microsoft/typescript-go/sessions/a6a59cb3-682f-426b-8fed-1bddcd23ab2c Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- .../cases/compiler/declarationEmitReadonlyAsConst.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 testdata/tests/cases/compiler/declarationEmitReadonlyAsConst.ts diff --git a/testdata/tests/cases/compiler/declarationEmitReadonlyAsConst.ts b/testdata/tests/cases/compiler/declarationEmitReadonlyAsConst.ts new file mode 100644 index 0000000000..e7703234b2 --- /dev/null +++ b/testdata/tests/cases/compiler/declarationEmitReadonlyAsConst.ts @@ -0,0 +1,11 @@ +// @strict: true +// @declaration: true + +export const value = { + method(): string { + return "a"; + }, + prop: { + nested: 1, + }, +} as const; From 08f943e6f557055e993bf1cbd1157ca98fdc1def Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 16 May 2026 21:09:20 +0000 Subject: [PATCH 3/3] Fix declaration emit to include readonly for as const object literal properties Fix pseudotypenodebuilder to use the ObjectLiteralExpression node instead of the property name when checking isConstContext. The property name's parent could be a MethodDeclaration which isConstContext doesn't traverse through, causing readonly to be dropped from method shorthands and outer properties in as const objects. Agent-Logs-Url: https://github.com/microsoft/typescript-go/sessions/a6a59cb3-682f-426b-8fed-1bddcd23ab2c Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- internal/checker/pseudotypenodebuilder.go | 2 +- ...ionEmitConstObjectLiteralGenericMethod1.js | 4 +-- .../declarationEmitReadonlyAsConst.js | 31 +++++++++++++++++++ .../declarationEmitReadonlyAsConst.symbols | 21 +++++++++++++ .../declarationEmitReadonlyAsConst.types | 26 ++++++++++++++++ 5 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 testdata/baselines/reference/compiler/declarationEmitReadonlyAsConst.js create mode 100644 testdata/baselines/reference/compiler/declarationEmitReadonlyAsConst.symbols create mode 100644 testdata/baselines/reference/compiler/declarationEmitReadonlyAsConst.types diff --git a/internal/checker/pseudotypenodebuilder.go b/internal/checker/pseudotypenodebuilder.go index 4ae347daea..138d53b385 100644 --- a/internal/checker/pseudotypenodebuilder.go +++ b/internal/checker/pseudotypenodebuilder.go @@ -195,7 +195,7 @@ func (b *NodeBuilderImpl) pseudoTypeToNode(t *pseudochecker.PseudoType) *ast.Nod // something a true syntactic ID emitter couldn't possibly know (since the signature could // be from across files). This can't *really* happen in any cases ID doesn't already error on, though. // Just something to keep in mind if the ID checker keeps growing. - isConst := b.ch.isConstContext(elements[0].Name) + isConst := b.ch.isConstContext(elements[0].Name.Parent.Parent) newElements := make([]*ast.Node, 0, len(elements)) for _, e := range elements { diff --git a/testdata/baselines/reference/compiler/declarationEmitConstObjectLiteralGenericMethod1.js b/testdata/baselines/reference/compiler/declarationEmitConstObjectLiteralGenericMethod1.js index 5c4eb42255..63317c37af 100644 --- a/testdata/baselines/reference/compiler/declarationEmitConstObjectLiteralGenericMethod1.js +++ b/testdata/baselines/reference/compiler/declarationEmitConstObjectLiteralGenericMethod1.js @@ -24,6 +24,6 @@ export const obj1 = { //// [declarationEmitConstObjectLiteralGenericMethod1.d.ts] export declare const obj1: { - id(value: T): T; - pair(left: T_1, right: T_1): T_1[]; + readonly id: (value: T) => T; + readonly pair: (left: T_1, right: T_1) => T_1[]; }; diff --git a/testdata/baselines/reference/compiler/declarationEmitReadonlyAsConst.js b/testdata/baselines/reference/compiler/declarationEmitReadonlyAsConst.js new file mode 100644 index 0000000000..1b6b25352a --- /dev/null +++ b/testdata/baselines/reference/compiler/declarationEmitReadonlyAsConst.js @@ -0,0 +1,31 @@ +//// [tests/cases/compiler/declarationEmitReadonlyAsConst.ts] //// + +//// [declarationEmitReadonlyAsConst.ts] +export const value = { + method(): string { + return "a"; + }, + prop: { + nested: 1, + }, +} as const; + + +//// [declarationEmitReadonlyAsConst.js] +export const value = { + method() { + return "a"; + }, + prop: { + nested: 1, + }, +}; + + +//// [declarationEmitReadonlyAsConst.d.ts] +export declare const value: { + readonly method: () => string; + readonly prop: { + readonly nested: 1; + }; +}; diff --git a/testdata/baselines/reference/compiler/declarationEmitReadonlyAsConst.symbols b/testdata/baselines/reference/compiler/declarationEmitReadonlyAsConst.symbols new file mode 100644 index 0000000000..ff3e36844b --- /dev/null +++ b/testdata/baselines/reference/compiler/declarationEmitReadonlyAsConst.symbols @@ -0,0 +1,21 @@ +//// [tests/cases/compiler/declarationEmitReadonlyAsConst.ts] //// + +=== declarationEmitReadonlyAsConst.ts === +export const value = { +>value : Symbol(value, Decl(declarationEmitReadonlyAsConst.ts, 0, 12)) + + method(): string { +>method : Symbol(method, Decl(declarationEmitReadonlyAsConst.ts, 0, 22)) + + return "a"; + }, + prop: { +>prop : Symbol(prop, Decl(declarationEmitReadonlyAsConst.ts, 3, 4)) + + nested: 1, +>nested : Symbol(nested, Decl(declarationEmitReadonlyAsConst.ts, 4, 9)) + + }, +} as const; +>const : Symbol(const) + diff --git a/testdata/baselines/reference/compiler/declarationEmitReadonlyAsConst.types b/testdata/baselines/reference/compiler/declarationEmitReadonlyAsConst.types new file mode 100644 index 0000000000..286decdf90 --- /dev/null +++ b/testdata/baselines/reference/compiler/declarationEmitReadonlyAsConst.types @@ -0,0 +1,26 @@ +//// [tests/cases/compiler/declarationEmitReadonlyAsConst.ts] //// + +=== declarationEmitReadonlyAsConst.ts === +export const value = { +>value : { readonly method: () => string; readonly prop: { readonly nested: 1; }; } +>{ method(): string { return "a"; }, prop: { nested: 1, },} as const : { readonly method: () => string; readonly prop: { readonly nested: 1; }; } +>{ method(): string { return "a"; }, prop: { nested: 1, },} : { readonly method: () => string; readonly prop: { readonly nested: 1; }; } + + method(): string { +>method : () => string + + return "a"; +>"a" : "a" + + }, + prop: { +>prop : { readonly nested: 1; } +>{ nested: 1, } : { readonly nested: 1; } + + nested: 1, +>nested : 1 +>1 : 1 + + }, +} as const; +