Skip to content

Commit

Permalink
fix: prevent infinite recursion with NoUndefined and RecursiveRequire…
Browse files Browse the repository at this point in the history
…d re: DocumentType (#1455)

* fix: prevent infinite recursion with NoUndefined and RecursiveRequired re: DocumentType

* formatting

---------

Co-authored-by: George Fu <[email protected]>
  • Loading branch information
gschwart and kuhe authored Nov 14, 2024
1 parent 012775c commit fcd5ca8
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 22 deletions.
5 changes: 5 additions & 0 deletions .changeset/three-trainers-know.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@smithy/types": patch
---

prevent infinite recursion with NoUndefined and RecursiveRequired re: DocumentType
40 changes: 28 additions & 12 deletions packages/types/src/transform/no-undefined.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { Client } from "../client";
import { CommandIO } from "../command";
import type { HttpHandlerOptions } from "../http";
import type { MetadataBearer } from "../response";
import { DocumentType } from "../shapes";
import type { Exact } from "./exact";
import type { AssertiveClient, NoUndefined, UncheckedClient } from "./no-undefined";

Expand All @@ -13,6 +14,7 @@ type A = {
required: string | undefined;
optional?: string;
nested: A;
document: DocumentType;
};

{
Expand All @@ -22,6 +24,8 @@ type A = {
const assert1: Exact<T["required"], string> = true as const;
const assert2: Exact<T["nested"]["required"], string> = true as const;
const assert3: Exact<T["nested"]["nested"]["required"], string> = true as const;
const assert4: Exact<T["document"], DocumentType> = true as const;
const assert5: Exact<T["nested"]["document"], DocumentType> = true as const;
}

{
Expand All @@ -30,13 +34,15 @@ type A = {
b: number | undefined;
c: string | number | undefined;
optional?: string;
document: DocumentType | undefined;
};

type MyOutput = {
a?: string;
b?: number;
c?: string | number;
r?: MyOutput;
document?: DocumentType;
} & MetadataBearer;

type MyConfig = {
Expand Down Expand Up @@ -66,17 +72,20 @@ type A = {
a: "",
b: 0,
c: 0,
document: { aa: "b" },
};
const get = c.getObject(input);
const output = null as unknown as Awaited<typeof get>;

const assert1: Exact<typeof output.a, string | undefined> = true as const;
const assert2: Exact<typeof output.b, number | undefined> = true as const;
const assert3: Exact<typeof output.c, string | number | undefined> = true as const;
const assert4: Exact<typeof output.document, DocumentType | undefined> = true as const;
if (output.r) {
const assert4: Exact<typeof output.r.a, string | undefined> = true as const;
const assert5: Exact<typeof output.r.b, number | undefined> = true as const;
const assert6: Exact<typeof output.r.c, string | number | undefined> = true as const;
const assert5: Exact<typeof output.r.a, string | undefined> = true as const;
const assert6: Exact<typeof output.r.b, number | undefined> = true as const;
const assert7: Exact<typeof output.r.c, string | number | undefined> = true as const;
const assert8: Exact<typeof output.r.document, DocumentType | undefined> = true as const;
}
}

Expand All @@ -88,16 +97,19 @@ type A = {
a: "",
b: 0,
c: 0,
document: { aa: "b" },
};
const get = c.getObject(input);
const output = null as unknown as Awaited<typeof get>;

const assert1: Exact<typeof output.a, string> = true as const;
const assert2: Exact<typeof output.b, number> = true as const;
const assert3: Exact<typeof output.c, string | number> = true as const;
const assert4: Exact<typeof output.r.a, string> = true as const;
const assert5: Exact<typeof output.r.b, number> = true as const;
const assert6: Exact<typeof output.r.c, string | number> = true as const;
const assert4: Exact<typeof output.document, DocumentType> = true as const;
const assert5: Exact<typeof output.r.a, string> = true as const;
const assert6: Exact<typeof output.r.b, number> = true as const;
const assert7: Exact<typeof output.r.c, string | number> = true as const;
const assert8: Exact<typeof output.r.document, DocumentType> = true as const;
}

{
Expand All @@ -109,10 +121,12 @@ type A = {
const assert1: Exact<typeof output.a, string | undefined> = true as const;
const assert2: Exact<typeof output.b, number | undefined> = true as const;
const assert3: Exact<typeof output.c, string | number | undefined> = true as const;
const assert4: Exact<typeof output.document, DocumentType | undefined> = true as const;
if (output.r) {
const assert4: Exact<typeof output.r.a, string | undefined> = true as const;
const assert5: Exact<typeof output.r.b, number | undefined> = true as const;
const assert6: Exact<typeof output.r.c, string | number | undefined> = true as const;
const assert5: Exact<typeof output.r.a, string | undefined> = true as const;
const assert6: Exact<typeof output.r.b, number | undefined> = true as const;
const assert7: Exact<typeof output.r.c, string | number | undefined> = true as const;
const assert8: Exact<typeof output.r.document, DocumentType | undefined> = true as const;
}
}

Expand All @@ -125,10 +139,12 @@ type A = {
const assert1: Exact<typeof output.a, string | undefined> = true as const;
const assert2: Exact<typeof output.b, number | undefined> = true as const;
const assert3: Exact<typeof output.c, string | number | undefined> = true as const;
const assert4: Exact<typeof output.document, DocumentType | undefined> = true as const;
if (output.r) {
const assert4: Exact<typeof output.r.a, string | undefined> = true as const;
const assert5: Exact<typeof output.r.b, number | undefined> = true as const;
const assert6: Exact<typeof output.r.c, string | number | undefined> = true as const;
const assert5: Exact<typeof output.r.a, string | undefined> = true as const;
const assert6: Exact<typeof output.r.b, number | undefined> = true as const;
const assert7: Exact<typeof output.r.c, string | number | undefined> = true as const;
const assert8: Exact<typeof output.r.document, DocumentType | undefined> = true as const;
}
}
}
25 changes: 15 additions & 10 deletions packages/types/src/transform/no-undefined.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { InvokeMethod, InvokeMethodOptionalArgs } from "../client";
import type { GetOutputType } from "../command";
import type { DocumentType } from "../shapes";

/**
* @public
Expand Down Expand Up @@ -34,11 +35,13 @@ export type UncheckedClient<Client extends object> = UncheckedClientOutputTypes<
*/
export type NoUndefined<T> = T extends Function
? T
: [T] extends [object]
? {
[key in keyof T]: NoUndefined<T[key]>;
}
: Exclude<T, undefined>;
: T extends DocumentType
? T
: [T] extends [object]
? {
[key in keyof T]: NoUndefined<T[key]>;
}
: Exclude<T, undefined>;

/**
* @internal
Expand All @@ -47,11 +50,13 @@ export type NoUndefined<T> = T extends Function
*/
export type RecursiveRequired<T> = T extends Function
? T
: [T] extends [object]
? {
[key in keyof T]-?: RecursiveRequired<T[key]>;
}
: Exclude<T, undefined>;
: T extends DocumentType
? T
: [T] extends [object]
? {
[key in keyof T]-?: RecursiveRequired<T[key]>;
}
: Exclude<T, undefined>;

/**
* @internal
Expand Down

0 comments on commit fcd5ca8

Please sign in to comment.