From 49958f26499906f144e3d07113f6cfef39841da7 Mon Sep 17 00:00:00 2001 From: George Fu Date: Tue, 12 Nov 2024 14:15:28 -0500 Subject: [PATCH] feat: allow undefined when property optional for exactOptionalPropertyTypes (#1448) * feat: allow undefined when property optional for exactOptionalPropertyTypes * update protocol tests --- .../smithy-rpcv2-cbor/src/models/models_0.ts | 200 +++++++++--------- .../codegen/StructuredMemberWriter.java | 3 + .../codegen/TypeScriptSettings.java | 2 +- .../codegen/StructureGeneratorTest.java | 4 +- 4 files changed, 106 insertions(+), 103 deletions(-) diff --git a/private/smithy-rpcv2-cbor/src/models/models_0.ts b/private/smithy-rpcv2-cbor/src/models/models_0.ts index f2ffaf796fd..caf9a41e11e 100644 --- a/private/smithy-rpcv2-cbor/src/models/models_0.ts +++ b/private/smithy-rpcv2-cbor/src/models/models_0.ts @@ -34,7 +34,7 @@ export class ValidationException extends __BaseException { * A member can appear in this list more than once if it failed to satisfy multiple constraints. * @public */ - fieldList?: ValidationExceptionField[]; + fieldList?: ValidationExceptionField[] | undefined; /** * @internal @@ -54,14 +54,14 @@ export class ValidationException extends __BaseException { * @public */ export interface ClientOptionalDefaults { - member?: number; + member?: number | undefined; } /** * @public */ export interface ComplexNestedErrorData { - Foo?: string; + Foo?: string | undefined; } /** @@ -71,8 +71,8 @@ export interface ComplexNestedErrorData { export class ComplexError extends __BaseException { readonly name: "ComplexError" = "ComplexError"; readonly $fault: "client" = "client"; - TopLevel?: string; - Nested?: ComplexNestedErrorData; + TopLevel?: string | undefined; + Nested?: ComplexNestedErrorData | undefined; /** * @internal */ @@ -111,36 +111,36 @@ export enum TestIntEnum { * @public */ export interface Defaults { - defaultString?: string; - defaultBoolean?: boolean; - defaultList?: string[]; - defaultTimestamp?: Date; - defaultBlob?: Uint8Array; - defaultByte?: number; - defaultShort?: number; - defaultInteger?: number; - defaultLong?: number; - defaultFloat?: number; - defaultDouble?: number; - defaultMap?: Record; - defaultEnum?: TestEnum; - defaultIntEnum?: TestIntEnum; - emptyString?: string; - falseBoolean?: boolean; - emptyBlob?: Uint8Array; - zeroByte?: number; - zeroShort?: number; - zeroInteger?: number; - zeroLong?: number; - zeroFloat?: number; - zeroDouble?: number; + defaultString?: string | undefined; + defaultBoolean?: boolean | undefined; + defaultList?: string[] | undefined; + defaultTimestamp?: Date | undefined; + defaultBlob?: Uint8Array | undefined; + defaultByte?: number | undefined; + defaultShort?: number | undefined; + defaultInteger?: number | undefined; + defaultLong?: number | undefined; + defaultFloat?: number | undefined; + defaultDouble?: number | undefined; + defaultMap?: Record | undefined; + defaultEnum?: TestEnum | undefined; + defaultIntEnum?: TestIntEnum | undefined; + emptyString?: string | undefined; + falseBoolean?: boolean | undefined; + emptyBlob?: Uint8Array | undefined; + zeroByte?: number | undefined; + zeroShort?: number | undefined; + zeroInteger?: number | undefined; + zeroLong?: number | undefined; + zeroFloat?: number | undefined; + zeroDouble?: number | undefined; } /** * @public */ export interface GreetingStruct { - hi?: string; + hi?: string | undefined; } /** @@ -152,21 +152,21 @@ export interface EmptyStructure {} * @public */ export interface Float16Output { - value?: number; + value?: number | undefined; } /** * @public */ export interface FractionalSecondsOutput { - datetime?: Date; + datetime?: Date | undefined; } /** * @public */ export interface GreetingWithErrorsOutput { - greeting?: string; + greeting?: string | undefined; } /** @@ -176,7 +176,7 @@ export interface GreetingWithErrorsOutput { export class InvalidGreeting extends __BaseException { readonly name: "InvalidGreeting" = "InvalidGreeting"; readonly $fault: "client" = "client"; - Message?: string; + Message?: string | undefined; /** * @internal */ @@ -195,57 +195,57 @@ export class InvalidGreeting extends __BaseException { * @public */ export interface OperationWithDefaultsInput { - defaults?: Defaults; - clientOptionalDefaults?: ClientOptionalDefaults; - topLevelDefault?: string; - otherTopLevelDefault?: number; + defaults?: Defaults | undefined; + clientOptionalDefaults?: ClientOptionalDefaults | undefined; + topLevelDefault?: string | undefined; + otherTopLevelDefault?: number | undefined; } /** * @public */ export interface OperationWithDefaultsOutput { - defaultString?: string; - defaultBoolean?: boolean; - defaultList?: string[]; - defaultTimestamp?: Date; - defaultBlob?: Uint8Array; - defaultByte?: number; - defaultShort?: number; - defaultInteger?: number; - defaultLong?: number; - defaultFloat?: number; - defaultDouble?: number; - defaultMap?: Record; - defaultEnum?: TestEnum; - defaultIntEnum?: TestIntEnum; - emptyString?: string; - falseBoolean?: boolean; - emptyBlob?: Uint8Array; - zeroByte?: number; - zeroShort?: number; - zeroInteger?: number; - zeroLong?: number; - zeroFloat?: number; - zeroDouble?: number; + defaultString?: string | undefined; + defaultBoolean?: boolean | undefined; + defaultList?: string[] | undefined; + defaultTimestamp?: Date | undefined; + defaultBlob?: Uint8Array | undefined; + defaultByte?: number | undefined; + defaultShort?: number | undefined; + defaultInteger?: number | undefined; + defaultLong?: number | undefined; + defaultFloat?: number | undefined; + defaultDouble?: number | undefined; + defaultMap?: Record | undefined; + defaultEnum?: TestEnum | undefined; + defaultIntEnum?: TestIntEnum | undefined; + emptyString?: string | undefined; + falseBoolean?: boolean | undefined; + emptyBlob?: Uint8Array | undefined; + zeroByte?: number | undefined; + zeroShort?: number | undefined; + zeroInteger?: number | undefined; + zeroLong?: number | undefined; + zeroFloat?: number | undefined; + zeroDouble?: number | undefined; } /** * @public */ export interface SimpleStructure { - value?: string; + value?: string | undefined; } /** * @public */ export interface RpcV2CborDenseMapsInputOutput { - denseStructMap?: Record; - denseNumberMap?: Record; - denseBooleanMap?: Record; - denseStringMap?: Record; - denseSetMap?: Record; + denseStructMap?: Record | undefined; + denseNumberMap?: Record | undefined; + denseBooleanMap?: Record | undefined; + denseStringMap?: Record | undefined; + denseSetMap?: Record | undefined; } /** @@ -274,85 +274,85 @@ export enum IntegerEnum { * @public */ export interface StructureListMember { - a?: string; - b?: string; + a?: string | undefined; + b?: string | undefined; } /** * @public */ export interface RpcV2CborListInputOutput { - stringList?: string[]; - stringSet?: string[]; - integerList?: number[]; - booleanList?: boolean[]; - timestampList?: Date[]; - enumList?: FooEnum[]; - intEnumList?: IntegerEnum[]; + stringList?: string[] | undefined; + stringSet?: string[] | undefined; + integerList?: number[] | undefined; + booleanList?: boolean[] | undefined; + timestampList?: Date[] | undefined; + enumList?: FooEnum[] | undefined; + intEnumList?: IntegerEnum[] | undefined; /** * A list of lists of strings. * @public */ - nestedStringList?: string[][]; + nestedStringList?: string[][] | undefined; - structureList?: StructureListMember[]; - blobList?: Uint8Array[]; + structureList?: StructureListMember[] | undefined; + blobList?: Uint8Array[] | undefined; } /** * @public */ export interface RpcV2CborSparseMapsInputOutput { - sparseStructMap?: Record; - sparseNumberMap?: Record; - sparseBooleanMap?: Record; - sparseStringMap?: Record; - sparseSetMap?: Record; + sparseStructMap?: Record | undefined; + sparseNumberMap?: Record | undefined; + sparseBooleanMap?: Record | undefined; + sparseStringMap?: Record | undefined; + sparseSetMap?: Record | undefined; } /** * @public */ export interface SimpleScalarStructure { - trueBooleanValue?: boolean; - falseBooleanValue?: boolean; - byteValue?: number; - doubleValue?: number; - floatValue?: number; - integerValue?: number; - longValue?: number; - shortValue?: number; - stringValue?: string; - blobValue?: Uint8Array; + trueBooleanValue?: boolean | undefined; + falseBooleanValue?: boolean | undefined; + byteValue?: number | undefined; + doubleValue?: number | undefined; + floatValue?: number | undefined; + integerValue?: number | undefined; + longValue?: number | undefined; + shortValue?: number | undefined; + stringValue?: string | undefined; + blobValue?: Uint8Array | undefined; } /** * @public */ export interface SparseNullsOperationInputOutput { - sparseStringList?: string[]; - sparseStringMap?: Record; + sparseStringList?: string[] | undefined; + sparseStringMap?: Record | undefined; } /** * @public */ export interface RecursiveShapesInputOutputNested1 { - foo?: string; - nested?: RecursiveShapesInputOutputNested2; + foo?: string | undefined; + nested?: RecursiveShapesInputOutputNested2 | undefined; } /** * @public */ export interface RecursiveShapesInputOutputNested2 { - bar?: string; - recursiveMember?: RecursiveShapesInputOutputNested1; + bar?: string | undefined; + recursiveMember?: RecursiveShapesInputOutputNested1 | undefined; } /** * @public */ export interface RecursiveShapesInputOutput { - nested?: RecursiveShapesInputOutputNested1; + nested?: RecursiveShapesInputOutputNested1 | undefined; } diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/StructuredMemberWriter.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/StructuredMemberWriter.java index ee76483efb3..9edab34d0a1 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/StructuredMemberWriter.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/StructuredMemberWriter.java @@ -106,6 +106,9 @@ void writeMembers(TypeScriptWriter writer, Shape shape) { String optionalSuffix = shape.isUnionShape() || !isRequiredMember(member) ? "?" : ""; String typeSuffix = requiredMemberMode == RequiredMemberMode.NULLABLE && isRequiredMember(member) ? " | undefined" : ""; + if (optionalSuffix.equals("?")) { + typeSuffix = " | undefined"; // support exactOptionalPropertyTypes. + } writer.write("${L}${L}${L}: ${T}${L};", memberPrefix, memberName, optionalSuffix, symbolProvider.toSymbol(member), typeSuffix); diff --git a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/TypeScriptSettings.java b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/TypeScriptSettings.java index da555865ac0..29601f09e15 100644 --- a/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/TypeScriptSettings.java +++ b/smithy-typescript-codegen/src/main/java/software/amazon/smithy/typescript/codegen/TypeScriptSettings.java @@ -558,7 +558,7 @@ public enum RequiredMemberMode { NULLABLE("nullable"), /** - * This will dissallow members marked as {@link RequiredTrait} to be {@code undefined}. + * This will disallow members marked as {@link RequiredTrait} to be {@code undefined}. * Use this mode with CAUTION because it comes with certain risks. When a server drops * {@link RequiredTrait} from an output shape (and it is replaced with {@link DefaultTrait} * as defined by the spec), if the server does not always serialize a value, diff --git a/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/StructureGeneratorTest.java b/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/StructureGeneratorTest.java index 4ae57404ef6..9ce6432a8fb 100644 --- a/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/StructureGeneratorTest.java +++ b/smithy-typescript-codegen/src/test/java/software/amazon/smithy/typescript/codegen/StructureGeneratorTest.java @@ -81,7 +81,7 @@ public void properlyGeneratesOptionalNonMessageMemberOfException() { "export class Err extends __BaseException {\n" + " readonly name: \"Err\" = \"Err\";\n" + " readonly $fault: \"client\" = \"client\";\n" - + " foo?: string;\n" + + " foo?: string | undefined;\n" + " /**\n" + " * @internal\n" + " */\n" @@ -525,7 +525,7 @@ public void generatesNonErrorStructures() { String output = writer.toString(); assertThat(output, containsString("export interface Bar {")); - assertThat(output, containsString("foo?: string;")); + assertThat(output, containsString("foo?: string | undefined;")); } private StructureShape createNonErrorStructure() {