Skip to content

Commit

Permalink
Global nullable option
Browse files Browse the repository at this point in the history
  • Loading branch information
fakefeik committed Feb 19, 2019
1 parent 89ee356 commit a7bbcce
Show file tree
Hide file tree
Showing 16 changed files with 190 additions and 72 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
export type ArrayRootType = {
ints?: null | number[];
nullableInts?: null | Nullable<number>[];
byteArray?: null | Byte[];
byteArray?: null | string;
nullableByteArray?: null | Nullable<Byte>[];
enums?: null | AnotherEnum[];
nullableEnums?: null | Nullable<AnotherEnum>[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
export type ArrayRootType = {
ints?: null | number[];
nullableInts?: null | Nullable<number>[];
byteArray?: null | Byte[];
byteArray?: null | string;
nullableByteArray?: null | Nullable<Byte>[];
enums?: null | AnotherEnum[];
nullableEnums?: null | Nullable<AnotherEnum>[];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

export type GlobalNullableRootType = {
int: number;
nullableInt?: null | number;
nullableInts?: null | Nullable<number>[];
intGeneric?: null | GenericClass<number>;
nullableIntGeneric?: null | GenericClass<Nullable<number>>;
};
export type Nullable<T> = {
hasValue: boolean;
value: T;
};
export type GenericClass<T> = {
genericType?: T;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

export type GlobalNullableRootType = {
int: number;
nullableInt?: null | number;
nullableInts?: null | Nullable<number>[];
intGeneric?: null | GenericClass<number>;
nullableIntGeneric?: null | GenericClass<Nullable<number>>;
};
export type Nullable<T> = {
hasValue: boolean;
value: T;
};
export type GenericClass<T> = {
genericType?: T;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

export type GlobalNullableRootType = {
int: number;
nullableInt?: ?number;
nullableInts?: ??number[];
intGeneric?: ?GenericClass<number>;
nullableIntGeneric?: ?GenericClass<?number>;
};
export type GenericClass<T> = {
genericType?: T;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

export type GlobalNullableRootType = {
int: number;
nullableInt?: Nullable<number>;
nullableInts?: Nullable<Nullable<number>[]>;
intGeneric?: Nullable<GenericClass<number>>;
nullableIntGeneric?: Nullable<GenericClass<Nullable<number>>>;
};
export type GenericClass<T> = {
genericType?: T;
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
export type ArrayRootType = {
ints?: null | number[];
nullableInts?: null | Nullable<number>[];
byteArray?: null | Byte[];
byteArray?: null | string;
nullableByteArray?: null | Nullable<Byte>[];
enums?: null | AnotherEnum[];
nullableEnums?: null | Nullable<AnotherEnum>[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
export type ArrayRootType = {
ints?: null | number[];
nullableInts?: null | Nullable<number>[];
byteArray?: null | Byte[];
byteArray?: null | string;
nullableByteArray?: null | Nullable<Byte>[];
enums?: null | AnotherEnum[];
nullableEnums?: null | Nullable<AnotherEnum>[];
Expand Down
10 changes: 10 additions & 0 deletions TypeScript.ContractGenerator.Tests/OptionsTests.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Linq;

using FluentAssertions;
Expand Down Expand Up @@ -42,5 +43,14 @@ public void ExplicitNullabilityTest(bool explicitNullabilityEnabled, string expe
var expectedCode = GetExpectedCode($"Options.Expected/{expectedFileName}");
generatedCode.Should().Be(expectedCode);
}

[TestCase(true, "global-nullable-enabled")]
[TestCase(false, "global-nullable-disabled")]
public void GlobalNullableTest(bool useGlobalNullable, string expectedFileName)
{
var generatedCode = GenerateCode(new FlowTypeGenerationOptions {UseGlobalNullable = useGlobalNullable}, CustomTypeGenerator.Null, typeof(GlobalNullableRootType)).Single().Replace("\r\n", "\n");
var expectedCode = GetExpectedCode($"Options.Expected/{expectedFileName}");
generatedCode.Should().Be(expectedCode);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,18 @@
<None Update="Files\Options.Expected\enum-generation-fixed-strings.ts">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Files\Options.Expected\global-nullable-disabled.js">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Files\Options.Expected\global-nullable-disabled.ts">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Files\Options.Expected\global-nullable-enabled.js">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Files\Options.Expected\global-nullable-enabled.ts">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
16 changes: 16 additions & 0 deletions TypeScript.ContractGenerator.Tests/Types/GlobalNullableRootType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace SkbKontur.TypeScript.ContractGenerator.Tests.Types
{
public class GlobalNullableRootType
{
public int Int { get; set; }
public int? NullableInt { get; set; }
public int?[] NullableInts { get; set; }
public GenericClass<int> IntGeneric { get; set; }
public GenericClass<int?> NullableIntGeneric { get; set; }
}

public class GenericClass<T>
{
public T GenericType { get; set; }
}
}
2 changes: 2 additions & 0 deletions TypeScript.ContractGenerator/FlowTypeGenerationOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ public class FlowTypeGenerationOptions

public bool EnableExplicitNullability { get; set; } = true;

public bool UseGlobalNullable { get; set; }

[NotNull]
public static FlowTypeGenerationOptions Default { get; } = new FlowTypeGenerationOptions();
}
Expand Down
69 changes: 32 additions & 37 deletions TypeScript.ContractGenerator/FlowTypeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,48 +76,43 @@ public ITypeBuildingContext ResolveType(Type type)
return flowTypeDeclarations[type];
}
var typeLocation = customTypeGenerator.GetTypeLocation(type);
var typeBuildingContext = customTypeGenerator.ResolveType(typeLocation, type, flowTypeUnitFactory);
if (typeBuildingContext == null)
{
if (BuildInTypeBuildingContext.Accept(type))
{
typeBuildingContext = new BuildInTypeBuildingContext(type);
}
if (type.IsArray)
{
typeBuildingContext = new ArrayTypeBuildingContext(type.GetElementType());
}
if (type.IsEnum)
{
var targetUnit = flowTypeUnitFactory.GetOrCreateTypeUnit(typeLocation);
typeBuildingContext = options.EnumGenerationMode == EnumGenerationMode.FixedStringsAndDictionary
? (ITypeBuildingContext)new FixedStringsAndDictionaryTypeBuildingContext(targetUnit, type)
: new TypeScriptEnumTypeBuildingContext(targetUnit, type);
}
if (type.IsGenericType && !type.IsGenericTypeDefinition)
{
typeBuildingContext = new GenericTypeTypeBuildingContext(type);
}
if (type.IsGenericParameter)
{
typeBuildingContext = new GenericParameterTypeBuildingContext(type);
}
if (type.IsGenericTypeDefinition)
{
var targetUnit = flowTypeUnitFactory.GetOrCreateTypeUnit(typeLocation);
typeBuildingContext = new CustomTypeTypeBuildingContext(targetUnit, type, options);
}
if (typeBuildingContext == null)
{
var targetUnit = flowTypeUnitFactory.GetOrCreateTypeUnit(typeLocation);
typeBuildingContext = new CustomTypeTypeBuildingContext(targetUnit, type, options);
}
}
var typeBuildingContext = customTypeGenerator.ResolveType(typeLocation, type, flowTypeUnitFactory) ?? GetTypeBuildingContext(typeLocation, type);
typeBuildingContext.Initialize(this);
flowTypeDeclarations.Add(type, typeBuildingContext);
return typeBuildingContext;
}

private ITypeBuildingContext GetTypeBuildingContext(string typeLocation, Type type)
{
if (BuildInTypeBuildingContext.Accept(type))
return new BuildInTypeBuildingContext(type);

if (type.IsArray)
return new ArrayTypeBuildingContext(type.GetElementType());

if (type.IsEnum)
{
var targetUnit = flowTypeUnitFactory.GetOrCreateTypeUnit(typeLocation);
return options.EnumGenerationMode == EnumGenerationMode.FixedStringsAndDictionary
? (ITypeBuildingContext)new FixedStringsAndDictionaryTypeBuildingContext(targetUnit, type)
: new TypeScriptEnumTypeBuildingContext(targetUnit, type);
}

if (options.UseGlobalNullable && type.IsGenericType && !type.IsGenericTypeDefinition && type.GetGenericTypeDefinition() == typeof(Nullable<>))
return new NullableTypeBuildingContext(type);

if (type.IsGenericType && !type.IsGenericTypeDefinition)
return new GenericTypeTypeBuildingContext(type);

if (type.IsGenericParameter)
return new GenericParameterTypeBuildingContext(type);

if (type.IsGenericTypeDefinition)
return new CustomTypeTypeBuildingContext(flowTypeUnitFactory.GetOrCreateTypeUnit(typeLocation), type, options);

return new CustomTypeTypeBuildingContext(flowTypeUnitFactory.GetOrCreateTypeUnit(typeLocation), type, options);
}

public FlowTypeType BuildAndImportType(FlowTypeUnit targetUnit, ICustomAttributeProvider attributeProvider, Type type)
{
var (isNullable, resultType) = FlowTypeGeneratorHelpers.ProcessNullable(attributeProvider, type);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using System;
using System.Linq;
using System.Collections.Generic;

using SkbKontur.TypeScript.ContractGenerator.CodeDom;

Expand All @@ -14,27 +14,13 @@ public BuildInTypeBuildingContext(Type type)

public static bool Accept(Type type)
{
return buildInTyped.Contains(type);
return builtinTypes.ContainsKey(type);
}

public FlowTypeType ReferenceFrom(FlowTypeUnit targetUnit, ITypeGenerator typeGenerator)
{
if (type == typeof(string))
return new FlowTypeBuildInType("string");
if (type == typeof(bool))
return new FlowTypeBuildInType("boolean");
if (type == typeof(int))
return new FlowTypeBuildInType("number");
if (type == typeof(decimal))
return new FlowTypeBuildInType("number");
if (type == typeof(long))
return new FlowTypeBuildInType("string");
if (type == typeof(DateTime))
return new FlowTypeBuildInType("(Date | string)");
if (type == typeof(byte[]))
return new FlowTypeBuildInType("string");
if (type == typeof(void))
return new FlowTypeBuildInType("void");
if (builtinTypes.ContainsKey(type))
return new FlowTypeBuildInType(builtinTypes[type]);
throw new ArgumentOutOfRangeException();
}

Expand All @@ -50,16 +36,16 @@ public void BuildDefinition(ITypeGenerator typeGenerator)

private readonly Type type;

private static readonly Type[] buildInTyped =
private static readonly Dictionary<Type, string> builtinTypes = new Dictionary<Type, string>
{
typeof(string),
typeof(bool),
typeof(int),
typeof(decimal),
typeof(long),
typeof(DateTime),
typeof(void),
typeof(byte[]),
{typeof(string), "string"},
{typeof(bool), "boolean"},
{typeof(int), "number"},
{typeof(decimal), "number"},
{typeof(long), "string"},
{typeof(DateTime), "(Date | string)"},
{typeof(byte[]), "string"},
{typeof(void), "void"}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,19 +55,32 @@ protected virtual FlowTypeTypeDefintion CreateComplexFlowTypeDefinition(ITypeGen
{
var (isNullable, type) = FlowTypeGeneratorHelpers.ProcessNullable(property, property.PropertyType);

var propertyType = typeGenerator.BuildAndImportType(Unit, null, type);
result.Members.Add(new FlowTypeTypeMemberDeclaration
{
Name = BuildPropertyName(property.Name),
Optional = isNullable && options.EnableOptionalProperties,
Type = property.PropertyType.IsGenericParameter
? new FlowTypeTypeReference(property.PropertyType.Name)
: isNullable && options.EnableExplicitNullability ? OrNull(propertyType) : propertyType,
Type = GetMaybeNullableComplexType(typeGenerator, type, property, isNullable),
});
}
return result;
}

private FlowTypeType GetMaybeNullableComplexType(ITypeGenerator typeGenerator, Type type, PropertyInfo property, bool isNullable)
{
var propertyType = typeGenerator.BuildAndImportType(Unit, null, type);

if (property.PropertyType.IsGenericParameter)
return new FlowTypeTypeReference(property.PropertyType.Name);

if (isNullable && options.EnableExplicitNullability && !options.UseGlobalNullable)
return OrNull(propertyType);

if (isNullable && options.EnableExplicitNullability && options.UseGlobalNullable)
return new FlowTypeNullableType(propertyType);

return propertyType;
}

private static FlowTypeUnionType OrNull(FlowTypeType buildAndImportType)
{
return new FlowTypeUnionType(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;

using SkbKontur.TypeScript.ContractGenerator.CodeDom;

namespace SkbKontur.TypeScript.ContractGenerator.TypeBuilders
{
public class NullableTypeBuildingContext : ITypeBuildingContext
{
public NullableTypeBuildingContext(Type nullableType)
{
itemType = nullableType.GetGenericArguments()[0];
}

public bool IsDefinitionBuilt => true;

public void Initialize(ITypeGenerator typeGenerator)
{
}

public void BuildDefinition(ITypeGenerator typeGenerator)
{
}

public FlowTypeType ReferenceFrom(FlowTypeUnit targetUnit, ITypeGenerator typeGenerator)
{
var itemFlowType = typeGenerator.ResolveType(itemType).ReferenceFrom(targetUnit, typeGenerator);
return new FlowTypeNullableType(itemFlowType);
}

private readonly Type itemType;
}
}

0 comments on commit a7bbcce

Please sign in to comment.