diff --git a/TypeScript.ContractGenerator.Tests/Files/CustomGenerator/array-types.expected.js b/TypeScript.ContractGenerator.Tests/Files/CustomGenerator/array-types.expected.js index 8fe42bb..8b4f976 100644 --- a/TypeScript.ContractGenerator.Tests/Files/CustomGenerator/array-types.expected.js +++ b/TypeScript.ContractGenerator.Tests/Files/CustomGenerator/array-types.expected.js @@ -1,11 +1,11 @@ export type ArrayRootType = { ints?: null | number[]; - nullableInts?: null | Nullable[]; + nullableInts?: null | Array; byteArray?: null | string; - nullableByteArray?: null | Nullable[]; + nullableByteArray?: null | Array; enums?: null | AnotherEnum[]; - nullableEnums?: null | Nullable[]; + nullableEnums?: null | Array; strings?: null | string[]; customTypes?: null | AnotherCustomType[]; stringsList?: null | string[]; @@ -13,10 +13,6 @@ export type ArrayRootType = { [key: string]: AnotherCustomType; }; }; -export type Nullable = { - hasValue: boolean; - value: T; -}; export type Byte = { }; export type AnotherEnum = 'B' | 'C'; diff --git a/TypeScript.ContractGenerator.Tests/Files/CustomGenerator/array-types.expected.ts b/TypeScript.ContractGenerator.Tests/Files/CustomGenerator/array-types.expected.ts index f4baf84..d51848d 100644 --- a/TypeScript.ContractGenerator.Tests/Files/CustomGenerator/array-types.expected.ts +++ b/TypeScript.ContractGenerator.Tests/Files/CustomGenerator/array-types.expected.ts @@ -1,11 +1,11 @@ export type ArrayRootType = { ints?: null | number[]; - nullableInts?: null | Nullable[]; + nullableInts?: null | Array; byteArray?: null | string; - nullableByteArray?: null | Nullable[]; + nullableByteArray?: null | Array; enums?: null | AnotherEnum[]; - nullableEnums?: null | Nullable[]; + nullableEnums?: null | Array; strings?: null | string[]; customTypes?: null | AnotherCustomType[]; stringsList?: null | string[]; @@ -13,10 +13,6 @@ export type ArrayRootType = { [key in string]?: AnotherCustomType; }; }; -export type Nullable = { - hasValue: boolean; - value: T; -}; export type Byte = { }; export type AnotherEnum = 'B' | 'C'; diff --git a/TypeScript.ContractGenerator.Tests/Files/Options.Expected/explicit-nullability-disabled.js b/TypeScript.ContractGenerator.Tests/Files/Options.Expected/explicit-nullability-disabled.js index 4ea18c0..8cd34c7 100644 --- a/TypeScript.ContractGenerator.Tests/Files/Options.Expected/explicit-nullability-disabled.js +++ b/TypeScript.ContractGenerator.Tests/Files/Options.Expected/explicit-nullability-disabled.js @@ -1,8 +1,15 @@ -export type NotNullRootType = { +export type ExplicitNullabilityRootType = { someNotNullClass: SomeClass; someNullableClass?: SomeClass; notNullString: string; + nullableString?: string; + notNullInt: number; + nullableInt?: number; + notNullArray: number[]; + nullableArray?: number[]; + notNullNullablesArray: number[]; + nullableNullablesArray?: number[]; }; export type SomeClass = { a: number; diff --git a/TypeScript.ContractGenerator.Tests/Files/Options.Expected/explicit-nullability-disabled.ts b/TypeScript.ContractGenerator.Tests/Files/Options.Expected/explicit-nullability-disabled.ts index 4ea18c0..8cd34c7 100644 --- a/TypeScript.ContractGenerator.Tests/Files/Options.Expected/explicit-nullability-disabled.ts +++ b/TypeScript.ContractGenerator.Tests/Files/Options.Expected/explicit-nullability-disabled.ts @@ -1,8 +1,15 @@ -export type NotNullRootType = { +export type ExplicitNullabilityRootType = { someNotNullClass: SomeClass; someNullableClass?: SomeClass; notNullString: string; + nullableString?: string; + notNullInt: number; + nullableInt?: number; + notNullArray: number[]; + nullableArray?: number[]; + notNullNullablesArray: number[]; + nullableNullablesArray?: number[]; }; export type SomeClass = { a: number; diff --git a/TypeScript.ContractGenerator.Tests/Files/Options.Expected/explicit-nullability-enabled.js b/TypeScript.ContractGenerator.Tests/Files/Options.Expected/explicit-nullability-enabled.js index 477480c..086de82 100644 --- a/TypeScript.ContractGenerator.Tests/Files/Options.Expected/explicit-nullability-enabled.js +++ b/TypeScript.ContractGenerator.Tests/Files/Options.Expected/explicit-nullability-enabled.js @@ -1,8 +1,15 @@ -export type NotNullRootType = { +export type ExplicitNullabilityRootType = { someNotNullClass: SomeClass; someNullableClass?: null | SomeClass; notNullString: string; + nullableString?: null | string; + notNullInt: number; + nullableInt?: null | number; + notNullArray: number[]; + nullableArray?: null | number[]; + notNullNullablesArray: Array; + nullableNullablesArray?: null | Array; }; export type SomeClass = { a: number; diff --git a/TypeScript.ContractGenerator.Tests/Files/Options.Expected/explicit-nullability-enabled.ts b/TypeScript.ContractGenerator.Tests/Files/Options.Expected/explicit-nullability-enabled.ts index 477480c..086de82 100644 --- a/TypeScript.ContractGenerator.Tests/Files/Options.Expected/explicit-nullability-enabled.ts +++ b/TypeScript.ContractGenerator.Tests/Files/Options.Expected/explicit-nullability-enabled.ts @@ -1,8 +1,15 @@ -export type NotNullRootType = { +export type ExplicitNullabilityRootType = { someNotNullClass: SomeClass; someNullableClass?: null | SomeClass; notNullString: string; + nullableString?: null | string; + notNullInt: number; + nullableInt?: null | number; + notNullArray: number[]; + nullableArray?: null | number[]; + notNullNullablesArray: Array; + nullableNullablesArray?: null | Array; }; export type SomeClass = { a: number; diff --git a/TypeScript.ContractGenerator.Tests/Files/Options.Expected/global-nullable-disabled.js b/TypeScript.ContractGenerator.Tests/Files/Options.Expected/global-nullable-disabled.js index cd63271..1587e09 100644 --- a/TypeScript.ContractGenerator.Tests/Files/Options.Expected/global-nullable-disabled.js +++ b/TypeScript.ContractGenerator.Tests/Files/Options.Expected/global-nullable-disabled.js @@ -2,13 +2,9 @@ export type GlobalNullableRootType = { int: number; nullableInt?: null | number; - nullableInts?: null | Nullable[]; + nullableInts?: null | Array; intGeneric?: null | GenericClass; - nullableIntGeneric?: null | GenericClass>; -}; -export type Nullable = { - hasValue: boolean; - value: T; + nullableIntGeneric?: null | GenericClass; }; export type GenericClass = { genericType?: T; diff --git a/TypeScript.ContractGenerator.Tests/Files/Options.Expected/global-nullable-disabled.ts b/TypeScript.ContractGenerator.Tests/Files/Options.Expected/global-nullable-disabled.ts index cd63271..1587e09 100644 --- a/TypeScript.ContractGenerator.Tests/Files/Options.Expected/global-nullable-disabled.ts +++ b/TypeScript.ContractGenerator.Tests/Files/Options.Expected/global-nullable-disabled.ts @@ -2,13 +2,9 @@ export type GlobalNullableRootType = { int: number; nullableInt?: null | number; - nullableInts?: null | Nullable[]; + nullableInts?: null | Array; intGeneric?: null | GenericClass; - nullableIntGeneric?: null | GenericClass>; -}; -export type Nullable = { - hasValue: boolean; - value: T; + nullableIntGeneric?: null | GenericClass; }; export type GenericClass = { genericType?: T; diff --git a/TypeScript.ContractGenerator.Tests/Files/SimpleGenerator/array-types.expected.js b/TypeScript.ContractGenerator.Tests/Files/SimpleGenerator/array-types.expected.js index 21823aa..740c104 100644 --- a/TypeScript.ContractGenerator.Tests/Files/SimpleGenerator/array-types.expected.js +++ b/TypeScript.ContractGenerator.Tests/Files/SimpleGenerator/array-types.expected.js @@ -1,20 +1,16 @@ export type ArrayRootType = { ints?: null | number[]; - nullableInts?: null | Nullable[]; + nullableInts?: null | Array; byteArray?: null | string; - nullableByteArray?: null | Nullable[]; + nullableByteArray?: null | Array; enums?: null | AnotherEnum[]; - nullableEnums?: null | Nullable[]; + nullableEnums?: null | Array; strings?: null | string[]; customTypes?: null | AnotherCustomType[]; stringsList?: null | List; customTypesDict?: null | Dictionary; }; -export type Nullable = { - hasValue: boolean; - value: T; -}; export type Byte = { }; export type AnotherEnum = 'B' | 'C'; diff --git a/TypeScript.ContractGenerator.Tests/Files/SimpleGenerator/array-types.expected.ts b/TypeScript.ContractGenerator.Tests/Files/SimpleGenerator/array-types.expected.ts index 6823a51..866546e 100644 --- a/TypeScript.ContractGenerator.Tests/Files/SimpleGenerator/array-types.expected.ts +++ b/TypeScript.ContractGenerator.Tests/Files/SimpleGenerator/array-types.expected.ts @@ -1,20 +1,16 @@ export type ArrayRootType = { ints?: null | number[]; - nullableInts?: null | Nullable[]; + nullableInts?: null | Array; byteArray?: null | string; - nullableByteArray?: null | Nullable[]; + nullableByteArray?: null | Array; enums?: null | AnotherEnum[]; - nullableEnums?: null | Nullable[]; + nullableEnums?: null | Array; strings?: null | string[]; customTypes?: null | AnotherCustomType[]; stringsList?: null | List; customTypesDict?: null | Dictionary; }; -export type Nullable = { - hasValue: boolean; - value: T; -}; export type Byte = { }; export type AnotherEnum = 'B' | 'C'; diff --git a/TypeScript.ContractGenerator.Tests/Files/SimpleGenerator/generic-types.expected.js b/TypeScript.ContractGenerator.Tests/Files/SimpleGenerator/generic-types.expected.js index c345c33..e1306cb 100644 --- a/TypeScript.ContractGenerator.Tests/Files/SimpleGenerator/generic-types.expected.js +++ b/TypeScript.ContractGenerator.Tests/Files/SimpleGenerator/generic-types.expected.js @@ -1,8 +1,8 @@ export type GenericContainingRootType = { genericIntChild?: null | GenericChildType; - genericNullableIntChild?: null | GenericChildType>; - arrayGenericNullableIntChild?: null | GenericChildType>[]; + genericNullableIntChild?: null | GenericChildType; + arrayGenericNullableIntChild?: null | GenericChildType[]; severalGenericParameters?: null | ChildWithSeveralGenericParameters; genericChildType?: null | GenericChildType>; genericHell?: null | GenericChildType>>; @@ -11,10 +11,6 @@ export type GenericChildType = { childType?: T; childTypes?: null | T[]; }; -export type Nullable = { - hasValue: boolean; - value: T; -}; export type ChildWithSeveralGenericParameters = { item1?: T1; item2?: T2; diff --git a/TypeScript.ContractGenerator.Tests/Files/SimpleGenerator/generic-types.expected.ts b/TypeScript.ContractGenerator.Tests/Files/SimpleGenerator/generic-types.expected.ts index c345c33..e1306cb 100644 --- a/TypeScript.ContractGenerator.Tests/Files/SimpleGenerator/generic-types.expected.ts +++ b/TypeScript.ContractGenerator.Tests/Files/SimpleGenerator/generic-types.expected.ts @@ -1,8 +1,8 @@ export type GenericContainingRootType = { genericIntChild?: null | GenericChildType; - genericNullableIntChild?: null | GenericChildType>; - arrayGenericNullableIntChild?: null | GenericChildType>[]; + genericNullableIntChild?: null | GenericChildType; + arrayGenericNullableIntChild?: null | GenericChildType[]; severalGenericParameters?: null | ChildWithSeveralGenericParameters; genericChildType?: null | GenericChildType>; genericHell?: null | GenericChildType>>; @@ -11,10 +11,6 @@ export type GenericChildType = { childType?: T; childTypes?: null | T[]; }; -export type Nullable = { - hasValue: boolean; - value: T; -}; export type ChildWithSeveralGenericParameters = { item1?: T1; item2?: T2; diff --git a/TypeScript.ContractGenerator.Tests/OptionsTests.cs b/TypeScript.ContractGenerator.Tests/OptionsTests.cs index d93ed6e..6faf0ca 100644 --- a/TypeScript.ContractGenerator.Tests/OptionsTests.cs +++ b/TypeScript.ContractGenerator.Tests/OptionsTests.cs @@ -1,4 +1,3 @@ -using System; using System.Linq; using FluentAssertions; @@ -39,7 +38,7 @@ public void OptionalPropertiesTest(bool optionalPropertiesEnabled, string expect [TestCase(false, "explicit-nullability-disabled")] public void ExplicitNullabilityTest(bool explicitNullabilityEnabled, string expectedFileName) { - var generatedCode = GenerateCode(new FlowTypeGenerationOptions {EnableExplicitNullability = explicitNullabilityEnabled}, CustomTypeGenerator.Null, typeof(NotNullRootType)).Single().Replace("\r\n", "\n"); + var generatedCode = GenerateCode(new FlowTypeGenerationOptions {EnableExplicitNullability = explicitNullabilityEnabled}, CustomTypeGenerator.Null, typeof(ExplicitNullabilityRootType)).Single().Replace("\r\n", "\n"); var expectedCode = GetExpectedCode($"Options.Expected/{expectedFileName}"); generatedCode.Should().Be(expectedCode); } diff --git a/TypeScript.ContractGenerator.Tests/Types/ExplicitNullabilityRootType.cs b/TypeScript.ContractGenerator.Tests/Types/ExplicitNullabilityRootType.cs new file mode 100644 index 0000000..0e94876 --- /dev/null +++ b/TypeScript.ContractGenerator.Tests/Types/ExplicitNullabilityRootType.cs @@ -0,0 +1,29 @@ +namespace SkbKontur.TypeScript.ContractGenerator.Tests.Types +{ + public class ExplicitNullabilityRootType + { + [NotNull] + public SomeClass SomeNotNullClass { get; set; } + + public SomeClass SomeNullableClass { get; set; } + + [NotNull] + public string NotNullString { get; set; } + + public string NullableString { get; set; } + + public int NotNullInt { get; set; } + + public int? NullableInt { get; set; } + + [NotNull] + public int[] NotNullArray { get; set; } + + public int[] NullableArray { get; set; } + + [NotNull] + public int?[] NotNullNullablesArray { get; set; } + + public int?[] NullableNullablesArray { get; set; } + } +} diff --git a/TypeScript.ContractGenerator/CodeDom/FlowTypeArrayType.cs b/TypeScript.ContractGenerator/CodeDom/FlowTypeArrayType.cs index 489403e..df70e02 100644 --- a/TypeScript.ContractGenerator/CodeDom/FlowTypeArrayType.cs +++ b/TypeScript.ContractGenerator/CodeDom/FlowTypeArrayType.cs @@ -7,11 +7,15 @@ public FlowTypeArrayType(FlowTypeType itemType) ItemType = itemType; } - public FlowTypeType ItemType { get; private set; } + private FlowTypeType ItemType { get; } public override string GenerateCode(ICodeGenerationContext context) { - return ItemType.GenerateCode(context) + "[]"; + var innerTypeCode = ItemType.GenerateCode(context); + if (!(ItemType is FlowTypeUnionType)) + return innerTypeCode + "[]"; + + return $"Array<{innerTypeCode}>"; } } } \ No newline at end of file diff --git a/TypeScript.ContractGenerator/CodeDom/FlowTypeOrNullType.cs b/TypeScript.ContractGenerator/CodeDom/FlowTypeOrNullType.cs new file mode 100644 index 0000000..67d093b --- /dev/null +++ b/TypeScript.ContractGenerator/CodeDom/FlowTypeOrNullType.cs @@ -0,0 +1,14 @@ +namespace SkbKontur.TypeScript.ContractGenerator.CodeDom +{ + public class FlowTypeOrNullType : FlowTypeUnionType + { + public FlowTypeOrNullType(FlowTypeType innerType) + : base(new[] + { + new FlowTypeBuildInType("null"), + innerType + }) + { + } + } +} \ No newline at end of file diff --git a/TypeScript.ContractGenerator/FlowTypeGenerator.cs b/TypeScript.ContractGenerator/FlowTypeGenerator.cs index 4a9a70c..a6a8081 100644 --- a/TypeScript.ContractGenerator/FlowTypeGenerator.cs +++ b/TypeScript.ContractGenerator/FlowTypeGenerator.cs @@ -98,8 +98,13 @@ private ITypeBuildingContext GetTypeBuildingContext(string typeLocation, Type ty : new TypeScriptEnumTypeBuildingContext(targetUnit, type); } - if (options.UseGlobalNullable && type.IsGenericType && !type.IsGenericTypeDefinition && type.GetGenericTypeDefinition() == typeof(Nullable<>)) - return new NullableTypeBuildingContext(type); + if (type.IsGenericType && !type.IsGenericTypeDefinition && type.GetGenericTypeDefinition() == typeof(Nullable<>)) + { + var underlyingType = type.GenericTypeArguments.Single(); + if (options.EnableExplicitNullability) + return new NullableTypeBuildingContext(underlyingType, options.UseGlobalNullable); + return GetTypeBuildingContext(typeLocation, underlyingType); + } if (type.IsGenericType && !type.IsGenericTypeDefinition) return new GenericTypeTypeBuildingContext(type); diff --git a/TypeScript.ContractGenerator/TypeBuilders/CustomTypeTypeBuildingContext.cs b/TypeScript.ContractGenerator/TypeBuilders/CustomTypeTypeBuildingContext.cs index 079619e..452e0a1 100644 --- a/TypeScript.ContractGenerator/TypeBuilders/CustomTypeTypeBuildingContext.cs +++ b/TypeScript.ContractGenerator/TypeBuilders/CustomTypeTypeBuildingContext.cs @@ -73,7 +73,7 @@ private FlowTypeType GetMaybeNullableComplexType(ITypeGenerator typeGenerator, T return new FlowTypeTypeReference(property.PropertyType.Name); if (isNullable && options.EnableExplicitNullability && !options.UseGlobalNullable) - return OrNull(propertyType); + return new FlowTypeOrNullType(propertyType); if (isNullable && options.EnableExplicitNullability && options.UseGlobalNullable) return new FlowTypeNullableType(propertyType); @@ -81,17 +81,6 @@ private FlowTypeType GetMaybeNullableComplexType(ITypeGenerator typeGenerator, T return propertyType; } - private static FlowTypeUnionType OrNull(FlowTypeType buildAndImportType) - { - return new FlowTypeUnionType( - new[] - { - new FlowTypeBuildInType("null"), - buildAndImportType - } - ); - } - private static string BuildPropertyName(string propertyName) { return propertyName.ToLowerCamelCase(); diff --git a/TypeScript.ContractGenerator/TypeBuilders/NullableTypeBuildingContext.cs b/TypeScript.ContractGenerator/TypeBuilders/NullableTypeBuildingContext.cs index 0eb00ae..c33fc68 100644 --- a/TypeScript.ContractGenerator/TypeBuilders/NullableTypeBuildingContext.cs +++ b/TypeScript.ContractGenerator/TypeBuilders/NullableTypeBuildingContext.cs @@ -6,9 +6,10 @@ namespace SkbKontur.TypeScript.ContractGenerator.TypeBuilders { public class NullableTypeBuildingContext : ITypeBuildingContext { - public NullableTypeBuildingContext(Type nullableType) + public NullableTypeBuildingContext(Type nullableUnderlyingType, bool useGlobalNullable) { - itemType = nullableType.GetGenericArguments()[0]; + itemType = nullableUnderlyingType; + this.useGlobalNullable = useGlobalNullable; } public bool IsDefinitionBuilt => true; @@ -24,9 +25,12 @@ public void BuildDefinition(ITypeGenerator typeGenerator) public FlowTypeType ReferenceFrom(FlowTypeUnit targetUnit, ITypeGenerator typeGenerator) { var itemFlowType = typeGenerator.ResolveType(itemType).ReferenceFrom(targetUnit, typeGenerator); - return new FlowTypeNullableType(itemFlowType); + return useGlobalNullable + ? (FlowTypeType)new FlowTypeNullableType(itemFlowType) + : new FlowTypeOrNullType(itemFlowType); } private readonly Type itemType; + private readonly bool useGlobalNullable; } } \ No newline at end of file