Skip to content

Commit

Permalink
Use Partial<Record<Key, Value>> instead of (wrongly) mapped type (#163
Browse files Browse the repository at this point in the history
)

* fix namespace import name

* use `Record<Key, Value>` instead of a mapped type

* add dictionary test for enums as keys

* transpile dictionaries to `Partial<Record<TKey, TValue>>` instead of non-partial record

* Revert "fix namespace import name"

This reverts commit 5740466.

* import namespace `Tapper.Test.SourceTypes` where needed
  • Loading branch information
backfromexile committed Feb 4, 2024
1 parent 6e74b24 commit 8252470
Show file tree
Hide file tree
Showing 9 changed files with 122 additions and 20 deletions.
6 changes: 3 additions & 3 deletions src/Tapper/TypeMappers/DictionaryTypeMappers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public string MapTo(ITypeSymbol typeSymbol, ITranspilationOptions options)
var mapper0 = options.TypeMapperProvider.GetTypeMapper(namedTypeSymbol.TypeArguments[0]);
var mapper1 = options.TypeMapperProvider.GetTypeMapper(namedTypeSymbol.TypeArguments[1]);

return $"{{ [key: {mapper0.MapTo(namedTypeSymbol.TypeArguments[0], options)}]: {mapper1.MapTo(namedTypeSymbol.TypeArguments[1], options)} }}";
return $"Partial<Record<{mapper0.MapTo(namedTypeSymbol.TypeArguments[0], options)}, {mapper1.MapTo(namedTypeSymbol.TypeArguments[1], options)}>>";
}

throw new InvalidOperationException($"Dictionary2TypeMapper is not support {typeSymbol.ToDisplayString()}.");
Expand All @@ -50,7 +50,7 @@ public string MapTo(ITypeSymbol typeSymbol, ITranspilationOptions options)
var mapper0 = options.TypeMapperProvider.GetTypeMapper(namedTypeSymbol.TypeArguments[0]);
var mapper1 = options.TypeMapperProvider.GetTypeMapper(namedTypeSymbol.TypeArguments[1]);

return $"{{ [key: {mapper0.MapTo(namedTypeSymbol.TypeArguments[0], options)}]: {mapper1.MapTo(namedTypeSymbol.TypeArguments[1], options)} }}";
return $"Partial<Record<{mapper0.MapTo(namedTypeSymbol.TypeArguments[0], options)}, {mapper1.MapTo(namedTypeSymbol.TypeArguments[1], options)}>>";
}

throw new InvalidOperationException($"IDictionary2TypeMapper is not support {typeSymbol.ToDisplayString()}.");
Expand All @@ -75,7 +75,7 @@ public string MapTo(ITypeSymbol typeSymbol, ITranspilationOptions options)
var mapper0 = options.TypeMapperProvider.GetTypeMapper(namedTypeSymbol.TypeArguments[0]);
var mapper1 = options.TypeMapperProvider.GetTypeMapper(namedTypeSymbol.TypeArguments[1]);

return $"{{ [key: {mapper0.MapTo(namedTypeSymbol.TypeArguments[0], options)}]: {mapper1.MapTo(namedTypeSymbol.TypeArguments[1], options)} }}";
return $"Partial<Record<{mapper0.MapTo(namedTypeSymbol.TypeArguments[0], options)}, {mapper1.MapTo(namedTypeSymbol.TypeArguments[1], options)}>>";
}

throw new InvalidOperationException($"IReadOnlyDictionary2TypeMapper is not support {typeSymbol.ToDisplayString()}.");
Expand Down
2 changes: 1 addition & 1 deletion src/Tapper/TypeMappers/DictionaryTypeMappers.tt
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public class <#= type.Name.Replace("`", null) #>TypeMapper : ITypeMapper
var mapper0 = options.TypeMapperProvider.GetTypeMapper(namedTypeSymbol.TypeArguments[0]);
var mapper1 = options.TypeMapperProvider.GetTypeMapper(namedTypeSymbol.TypeArguments[1]);

return $"{{ [key: {mapper0.MapTo(namedTypeSymbol.TypeArguments[0], options)}]: {mapper1.MapTo(namedTypeSymbol.TypeArguments[1], options)} }}";
return $"Partial<Record<{mapper0.MapTo(namedTypeSymbol.TypeArguments[0], options)}, {mapper1.MapTo(namedTypeSymbol.TypeArguments[1], options)}>>";
}

throw new InvalidOperationException($"<#= type.Name.Replace("`", null) #>TypeMapper is not support {typeSymbol.ToDisplayString()}.");
Expand Down
13 changes: 13 additions & 0 deletions tests/Tapper.Test.SourceTypes/DictionaryClasses.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#nullable enable
using System;
using System.Collections.Generic;
using Tapper.Test.SourceTypes;

namespace Tapper.Tests.SourceTypes;

Expand All @@ -27,6 +28,12 @@ public class ClassIncludeDictionaryFieldIReadOnlyDictionarystringDateTime
public IReadOnlyDictionary<string, DateTime> FieldOfIReadOnlyDictionarystringDateTime;
}

[TranspilationSource]
public class ClassIncludeDictionaryFieldIReadOnlyDictionaryEnum1long
{
public IReadOnlyDictionary<Enum1, long> FieldOfIReadOnlyDictionaryEnum1long;
}

[TranspilationSource]
public class ClassIncludeDictionaryPropertyDictionaryintstring
{
Expand All @@ -45,5 +52,11 @@ public class ClassIncludeDictionaryPropertyIReadOnlyDictionarystringDateTime
public IReadOnlyDictionary<string, DateTime> PropertyOfIReadOnlyDictionarystringDateTime { get; set; }
}

[TranspilationSource]
public class ClassIncludeDictionaryPropertyIReadOnlyDictionaryEnum1long
{
public IReadOnlyDictionary<Enum1, long> PropertyOfIReadOnlyDictionaryEnum1long { get; set; }
}


#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
2 changes: 2 additions & 0 deletions tests/Tapper.Test.SourceTypes/DictionaryClasses.tt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"Dictionary<int, string>",
"IDictionary<float, Guid>",
"IReadOnlyDictionary<string, DateTime>",
"IReadOnlyDictionary<Enum1, long>",
};

static string Format(string source)
Expand All @@ -24,6 +25,7 @@
#nullable enable
using System;
using System.Collections.Generic;
using Tapper.Test.SourceTypes;

namespace Tapper.Tests.SourceTypes;

Expand Down
25 changes: 19 additions & 6 deletions tests/Tapper.Tests/DictionaryClassesAnswer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System;
using System.Collections.Generic;
using Tapper.Tests.SourceTypes;
using Tapper.Test.SourceTypes;

namespace Tapper.Tests;

Expand All @@ -17,38 +18,50 @@ static DictionaryTypeTranspilationAnswer()
Dict[nameof(ClassIncludeDictionaryFieldDictionaryintstring)] = @"/** Transpiled from Tapper.Tests.SourceTypes.ClassIncludeDictionaryFieldDictionaryintstring */
export type ClassIncludeDictionaryFieldDictionaryintstring = {
/** Transpiled from System.Collections.Generic.Dictionary<int, string> */
FieldOfDictionaryintstring: { [key: number]: string };
FieldOfDictionaryintstring: Partial<Record<number, string>>;
}
";
Dict[nameof(ClassIncludeDictionaryFieldIDictionaryfloatGuid)] = @"/** Transpiled from Tapper.Tests.SourceTypes.ClassIncludeDictionaryFieldIDictionaryfloatGuid */
export type ClassIncludeDictionaryFieldIDictionaryfloatGuid = {
/** Transpiled from System.Collections.Generic.IDictionary<float, System.Guid> */
FieldOfIDictionaryfloatGuid: { [key: number]: string };
FieldOfIDictionaryfloatGuid: Partial<Record<number, string>>;
}
";
Dict[nameof(ClassIncludeDictionaryFieldIReadOnlyDictionarystringDateTime)] = @"/** Transpiled from Tapper.Tests.SourceTypes.ClassIncludeDictionaryFieldIReadOnlyDictionarystringDateTime */
export type ClassIncludeDictionaryFieldIReadOnlyDictionarystringDateTime = {
/** Transpiled from System.Collections.Generic.IReadOnlyDictionary<string, System.DateTime> */
FieldOfIReadOnlyDictionarystringDateTime: { [key: string]: (Date | string) };
FieldOfIReadOnlyDictionarystringDateTime: Partial<Record<string, (Date | string)>>;
}
";
Dict[nameof(ClassIncludeDictionaryFieldIReadOnlyDictionaryEnum1long)] = @"/** Transpiled from Tapper.Tests.SourceTypes.ClassIncludeDictionaryFieldIReadOnlyDictionaryEnum1long */
export type ClassIncludeDictionaryFieldIReadOnlyDictionaryEnum1long = {
/** Transpiled from System.Collections.Generic.IReadOnlyDictionary<Tapper.Test.SourceTypes.Enum1, long> */
FieldOfIReadOnlyDictionaryEnum1long: Partial<Record<Enum1, number>>;
}
";

Dict[nameof(ClassIncludeDictionaryPropertyDictionaryintstring)] = @"/** Transpiled from Tapper.Tests.SourceTypes.ClassIncludeDictionaryPropertyDictionaryintstring */
export type ClassIncludeDictionaryPropertyDictionaryintstring = {
/** Transpiled from System.Collections.Generic.Dictionary<int, string> */
PropertyOfDictionaryintstring: { [key: number]: string };
PropertyOfDictionaryintstring: Partial<Record<number, string>>;
}
";
Dict[nameof(ClassIncludeDictionaryPropertyIDictionaryfloatGuid)] = @"/** Transpiled from Tapper.Tests.SourceTypes.ClassIncludeDictionaryPropertyIDictionaryfloatGuid */
export type ClassIncludeDictionaryPropertyIDictionaryfloatGuid = {
/** Transpiled from System.Collections.Generic.IDictionary<float, System.Guid> */
PropertyOfIDictionaryfloatGuid: { [key: number]: string };
PropertyOfIDictionaryfloatGuid: Partial<Record<number, string>>;
}
";
Dict[nameof(ClassIncludeDictionaryPropertyIReadOnlyDictionarystringDateTime)] = @"/** Transpiled from Tapper.Tests.SourceTypes.ClassIncludeDictionaryPropertyIReadOnlyDictionarystringDateTime */
export type ClassIncludeDictionaryPropertyIReadOnlyDictionarystringDateTime = {
/** Transpiled from System.Collections.Generic.IReadOnlyDictionary<string, System.DateTime> */
PropertyOfIReadOnlyDictionarystringDateTime: { [key: string]: (Date | string) };
PropertyOfIReadOnlyDictionarystringDateTime: Partial<Record<string, (Date | string)>>;
}
";
Dict[nameof(ClassIncludeDictionaryPropertyIReadOnlyDictionaryEnum1long)] = @"/** Transpiled from Tapper.Tests.SourceTypes.ClassIncludeDictionaryPropertyIReadOnlyDictionaryEnum1long */
export type ClassIncludeDictionaryPropertyIReadOnlyDictionaryEnum1long = {
/** Transpiled from System.Collections.Generic.IReadOnlyDictionary<Tapper.Test.SourceTypes.Enum1, long> */
PropertyOfIReadOnlyDictionaryEnum1long: Partial<Record<Enum1, number>>;
}
";
}
Expand Down
11 changes: 7 additions & 4 deletions tests/Tapper.Tests/DictionaryClassesAnswer.tt
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,18 @@
<#
Dictionary<string, string> _dictionaryTypeMap = new()
{
{ "Dictionary<int, string>", "{ [key: number]: string }" },
{ "IDictionary<float, Guid>", "{ [key: number]: string }" },
{ "IReadOnlyDictionary<string, DateTime>", "{ [key: string]: (Date | string) }"}
{ "Dictionary<int, string>", "Partial<Record<number, string>>" },
{ "IDictionary<float, Guid>", "Partial<Record<number, string>>" },
{ "IReadOnlyDictionary<string, DateTime>", "Partial<Record<string, (Date | string)>>" },
{ "IReadOnlyDictionary<Enum1, long>", "Partial<Record<Enum1, number>>" }
};

Dictionary<string, string> _dictonaryType = new()
{
{ "Dictionary<int, string>", "System.Collections.Generic.Dictionary<int, string>" },
{ "IDictionary<float, Guid>", "System.Collections.Generic.IDictionary<float, System.Guid>" },
{ "IReadOnlyDictionary<string, DateTime>","System.Collections.Generic.IReadOnlyDictionary<string, System.DateTime>" },
{ "IReadOnlyDictionary<string, DateTime>", "System.Collections.Generic.IReadOnlyDictionary<string, System.DateTime>" },
{ "IReadOnlyDictionary<Enum1, long>","System.Collections.Generic.IReadOnlyDictionary<Tapper.Test.SourceTypes.Enum1, long>" }
};

static string Format(string source)
Expand All @@ -32,6 +34,7 @@
using System;
using System.Collections.Generic;
using Tapper.Tests.SourceTypes;
using Tapper.Test.SourceTypes;

namespace Tapper.Tests;

Expand Down
69 changes: 69 additions & 0 deletions tests/Tapper.Tests/DictionaryClassesTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Tapper.Tests.SourceTypes;
using Tapper.Test.SourceTypes;
using Tapper.TypeMappers;
using Xunit;
using Xunit.Abstractions;
Expand Down Expand Up @@ -124,6 +125,40 @@ public void Test_ClassIncludeDictionaryFieldIReadOnlyDictionarystringDateTime()
Assert.Equal(gt, code, ignoreLineEndingDifferences: true);
}

[Fact]
public void Test_ClassIncludeDictionaryFieldIReadOnlyDictionaryEnum1long()
{
var compilation = CompilationSingleton.Compilation;

var options = new TranspilationOptions(
compilation,
SerializerOption.Json,
NamingStyle.None,
EnumStyle.Value,
NewLineOption.Lf,
2,
false,
true
);

var codeGenerator = new TypeScriptCodeGenerator(compilation, options);

var type = typeof(ClassIncludeDictionaryFieldIReadOnlyDictionaryEnum1long);
var typeSymbol = compilation.GetTypeByMetadataName(type.FullName!)!;

var writer = new CodeWriter();

codeGenerator.AddType(typeSymbol, ref writer);

var code = writer.ToString();
var gt = DictionaryTypeTranspilationAnswer.Dict[nameof(ClassIncludeDictionaryFieldIReadOnlyDictionaryEnum1long)];

_output.WriteLine(code);
_output.WriteLine(gt);

Assert.Equal(gt, code, ignoreLineEndingDifferences: true);
}


[Fact]
public void Test_ClassIncludeDictionaryPropertyDictionaryintstring()
Expand Down Expand Up @@ -227,4 +262,38 @@ public void Test_ClassIncludeDictionaryPropertyIReadOnlyDictionarystringDateTime
Assert.Equal(gt, code, ignoreLineEndingDifferences: true);
}

[Fact]
public void Test_ClassIncludeDictionaryPropertyIReadOnlyDictionaryEnum1long()
{
var compilation = CompilationSingleton.Compilation;

var options = new TranspilationOptions(
compilation,
SerializerOption.Json,
NamingStyle.None,
EnumStyle.Value,
NewLineOption.Lf,
2,
false,
true
);

var codeGenerator = new TypeScriptCodeGenerator(compilation, options);

var type = typeof(ClassIncludeDictionaryPropertyIReadOnlyDictionaryEnum1long);
var typeSymbol = compilation.GetTypeByMetadataName(type.FullName!)!;

var writer = new CodeWriter();

codeGenerator.AddType(typeSymbol, ref writer);

var code = writer.ToString();
var gt = DictionaryTypeTranspilationAnswer.Dict[nameof(ClassIncludeDictionaryPropertyIReadOnlyDictionaryEnum1long)];

_output.WriteLine(code);
_output.WriteLine(gt);

Assert.Equal(gt, code, ignoreLineEndingDifferences: true);
}

}
8 changes: 5 additions & 3 deletions tests/Tapper.Tests/DictionaryClassesTest.tt
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
<#
Dictionary<string, string> _dictionaryTypeMap = new()
{
{ "Dictionary<int, string>", "{ [key: number]: string }" },
{ "IDictionary<float, Guid>", "{ [key: number]: string }" },
{ "IReadOnlyDictionary<string, DateTime>", "{ [key: string]: number }"}
{ "Dictionary<int, string>", "Partial<Record<number, string>>" },
{ "IDictionary<float, Guid>", "Partial<Record<number, string>>" },
{ "IReadOnlyDictionary<string, DateTime>", "Partial<Record<string, number>>" },
{ "IReadOnlyDictionary<Enum1, long>", "Partial<Record<Enum1, number>>" }
};

static string Format(string source)
Expand All @@ -27,6 +28,7 @@ using System.IO;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Tapper.Tests.SourceTypes;
using Tapper.Test.SourceTypes;
using Tapper.TypeMappers;
using Xunit;
using Xunit.Abstractions;
Expand Down
6 changes: 3 additions & 3 deletions tests/Tapper.Tests/Tapper.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,9 @@
<DependentUpon>CollectionClassesTest.tt</DependentUpon>
</Compile>
<Compile Update="DictionaryClassesAnswer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>DictionaryClassesAnswer.tt</DependentUpon>
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>DictionaryClassesAnswer.tt</DependentUpon>
</Compile>
<Compile Update="DictionaryClassesTest.cs">
<DesignTime>True</DesignTime>
Expand Down

0 comments on commit 8252470

Please sign in to comment.