From 2e7519f744ace57c932b97a8f01976d58074610b Mon Sep 17 00:00:00 2001 From: Niels Swimberghe <3382717+Swimburger@users.noreply.github.com> Date: Mon, 30 Mar 2026 23:42:21 +0000 Subject: [PATCH 1/2] fix(typescript): coerce unknown-typed global headers to string in generated SDKs Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../base-client/BaseClientContextImpl.ts | 22 ++++++++++++++++--- generators/typescript/sdk/versions.yml | 12 ++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/generators/typescript/sdk/generator/src/contexts/base-client/BaseClientContextImpl.ts b/generators/typescript/sdk/generator/src/contexts/base-client/BaseClientContextImpl.ts index 6ae65a5c9959..a87b8afc2d95 100644 --- a/generators/typescript/sdk/generator/src/contexts/base-client/BaseClientContextImpl.ts +++ b/generators/typescript/sdk/generator/src/contexts/base-client/BaseClientContextImpl.ts @@ -224,11 +224,14 @@ export class BaseClientContextImpl implements BaseClientContext { for (const header of this.intermediateRepresentation.headers) { const type = context.type.getReferenceToType(header.valueType); + const typeNode = isUnknownType(header.valueType) + ? ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword) + : type.typeNode; if (endpointUtils.isLiteralHeader(header, context)) { properties.push({ kind: StructureKind.PropertySignature, name: getPropertyKey(this.getOptionKeyForHeader(header)), - type: getTextOfTsNode(context.type.getReferenceToType(header.valueType).typeNode), + type: getTextOfTsNode(typeNode), hasQuestionToken: true, docs: [`Override the ${header.name.wireValue} header`] }); @@ -236,7 +239,7 @@ export class BaseClientContextImpl implements BaseClientContext { properties.push({ kind: StructureKind.PropertySignature, name: getPropertyKey(this.getOptionKeyForHeader(header)), - type: getTextOfTsNode(context.coreUtilities.fetcher.Supplier._getReferenceToType(type.typeNode)), + type: getTextOfTsNode(context.coreUtilities.fetcher.Supplier._getReferenceToType(typeNode)), hasQuestionToken: type.isOptional, docs: [`Override the ${header.name.wireValue} header`] }); @@ -366,9 +369,12 @@ export class BaseClientContextImpl implements BaseClientContext { docs: ["A hook to abort the request."] }, ...this.intermediateRepresentation.headers.map((header) => { + const typeNode = isUnknownType(header.valueType) + ? ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword) + : context.type.getReferenceToType(header.valueType).typeNode; return { name: getPropertyKey(this.getOptionKeyForHeader(header)), - type: getTextOfTsNode(context.type.getReferenceToType(header.valueType).typeNode), + type: getTextOfTsNode(typeNode), hasQuestionToken: true, docs: [`Override the ${header.name.wireValue} header`] }; @@ -440,3 +446,13 @@ export class BaseClientContextImpl implements BaseClientContext { function isPropertyRequired(property: OptionalKind): boolean { return property.hasQuestionToken !== true; } + +/** + * HTTP headers are always strings, so when the IR type is `unknown`, + * we coerce the generated TypeScript type to `string` to avoid + * producing `Supplier` or `unknown` which causes type errors + * when assigning to header values. + */ +function isUnknownType(typeReference: FernIr.TypeReference): boolean { + return typeReference.type === "unknown"; +} diff --git a/generators/typescript/sdk/versions.yml b/generators/typescript/sdk/versions.yml index a31d60ad3a7f..e2475df0bb0d 100644 --- a/generators/typescript/sdk/versions.yml +++ b/generators/typescript/sdk/versions.yml @@ -1,4 +1,16 @@ # yaml-language-server: $schema=../../../fern-versions-yml.schema.json +- version: 3.60.4 + changelogEntry: + - summary: | + Fix global headers with `unknown` IR type producing `Supplier` in + `BaseClientOptions` and `unknown` in `BaseRequestOptions`, which caused + TypeScript compilation errors (`Type 'unknown' is not assignable to type + 'string | undefined'`) in generated SDKs. HTTP headers are always strings, + so `unknown`-typed headers are now coerced to `string`. + type: fix + createdAt: "2026-03-30" + irVersion: 65 + - version: 3.60.3 changelogEntry: - summary: | From 436e867be1a64c1ffca9c2fb7f1e9a751c89251f Mon Sep 17 00:00:00 2001 From: Niels Swimberghe <3382717+Swimburger@users.noreply.github.com> Date: Mon, 30 Mar 2026 23:47:57 +0000 Subject: [PATCH 2/2] fix: also coerce unknown-typed idempotency headers to string Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../src/contexts/base-client/BaseClientContextImpl.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/generators/typescript/sdk/generator/src/contexts/base-client/BaseClientContextImpl.ts b/generators/typescript/sdk/generator/src/contexts/base-client/BaseClientContextImpl.ts index a87b8afc2d95..cdafa49ed5d5 100644 --- a/generators/typescript/sdk/generator/src/contexts/base-client/BaseClientContextImpl.ts +++ b/generators/typescript/sdk/generator/src/contexts/base-client/BaseClientContextImpl.ts @@ -415,10 +415,13 @@ export class BaseClientContextImpl implements BaseClientContext { for (const header of this.intermediateRepresentation.idempotencyHeaders) { if (!endpointUtils.isLiteralHeader(header, context)) { const type = context.type.getReferenceToType(header.valueType); + const typeNode = isUnknownType(header.valueType) + ? ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword) + : type.typeNode; properties.push({ kind: StructureKind.PropertySignature, name: getPropertyKey(this.getOptionKeyForHeader(header)), - type: getTextOfTsNode(type.typeNode), + type: getTextOfTsNode(typeNode), hasQuestionToken: type.isOptional }); }