I have a class,
public class Instance : IResource
{
public Dictionary Value { get; set; }
and it is mapped to
<Collection types (any type implementing IEnumerable
) is converted to arrays. Dictionary<>
implements IEnumerable<KeyValuePair<>>
and thus is converted to an array. The item-type is then expanded to its fully qualified name (FQN): System.Collections.Generic.KeyValuePair
.
Using Type Converters will let you change the type-name, but not the FQN. So it is only applicable to local types. In the case of dictionaries, you can't change the item type by inheritance.
You could either create a new dictionary type, without inheriting from Dictionary<>
. Another way around this problem, is to also use Type Formatters:
ts.WithConvertor<Dictionary<string,object>>(t => {
// Embed the real type in $
// "System.Collections.Generic.${ [key: string]: any }$[]"
return "${ [key: string]: any }$";
});
ts.WithFormatter((string memberTypeName, bool isMemberCollection) => {
// Extract the content inside $
string[] pieces = memberTypeName.Split('$');
if (pieces.Length == 3) return pieces[1];
// Default behaviour
return memberTypeName + (isMemberCollection ? "[]" : "");
});
A quick and dirty workaround is to use a regex to alter output:
Using
<#= Regex.Replace( ts.Generate(TsGeneratorOutput.Properties)
, @":\s*System\.Collections\.Generic\.KeyValuePair\<(?<k>[^\,]+),(?<v>[^\,]+)\>\[\];"
, m=>": {[key: "+m.Groups["k"].Value+"]: "+m.Groups["v"].Value+"};"
, RegexOptions.Multiline)
#>
Transforms a field
myField: System.Collections.Generic.KeyValuePair<string,OtherClass>[];
to
myField: {[key: string]: OtherClass};
Here's a more generalized (and updated) solution that builds off of Markus Jarderot's answer:
static void RegisterDictionaryMemberFormatter(this TsGenerator tsGenerator)
{
tsGenerator.SetMemberTypeFormatter((tsProperty, memberTypeName) => {
var dictionaryInterface =
tsProperty.PropertyType.Type.GetInterface(typeof(IDictionary<,>).Name) ??
tsProperty.PropertyType.Type.GetInterface(typeof(IDictionary).Name);
if (dictionaryInterface != null)
{
return tsGenerator.GetFullyQualifiedTypeName(new TsClass(dictionaryInterface));
}
else
{
return tsGenerator.DefaultMemberTypeFormatter(tsProperty, memberTypeName);
}
});
}
// and if you like the fluent syntax...
static TypeScriptFluent WithDictionaryMemberFormatter(this TypeScriptFluent typeScriptFluent)
{
typeScriptFluent.ScriptGenerator.RegisterDictionaryMemberFormatter();
return typeScriptFluent;
}
Use it like this:
var ts = TypeLite.TypeScript.Definitions().For(typeof(SomeClass).Assembly);
ts.ScriptGenerator.RegisterDictionaryMemberFormatter();
// alternatively with fluent syntax:
var ts = TypeLite.TypeScript.Definitions()
.For(typeof(SomeClass).Assembly)
.WithDictionaryMemberFormatter();
N.B. this only fixes type signatures of properties (or fields) that have dictionary types. Also definitions for IDictionary
are not automatically emitted, you'd have to add them manually:
declare module System.Collections.Generic {
interface IDictionary<TKey extends String, TValue> {
[key: string]: TValue;
}
}
declare module System.Collections {
interface IDictionary {
[key: string]: any;
}
}