Error in Generic Generation with TypeFormatter
If I add the suggested formatter to add "I" to my types:
".WithTypeFormatter((type, F) => "I" + ((TypeLite.TsModels.TsClass)type).Name);"
Then, a class with a simple generic:
public class UserPreference { public ModuleType ModuleType { get; set; } public Dictionary<string, string> Preferences { get; set; } }
Gets the incorrect output:
declare module App.Platform { interface IUserPreference { ModuleType: App.Platform.ModuleType; Preferences: System.Collections.Generic.IKeyValuePair[]; } } declare module System.Collections.Generic { interface IKeyValuePair { Key: ITKey; Value: ITValue; } }
I would expect the dictionary to be defined as:
declare module System.Collections.Generic { interface IKeyValuePair<TKey, TValue> { Key: TKey; Value: TValue; } }
And the reference to it to be:
Preferences: System.Collections.Generic.KeyValuePair<string, string>[];
Note: I do get this without the TypeFormatter. It just seems like the type formatter is identifying the template parameters incorrectly.
Comments (8)
-
-
-
assigned issue to
-
assigned issue to
-
- edited description
-
- edited description
-
I got around this. The documentation for customisation leads to you replacing the formatter which writes the generics, so you need to either copy the base TsClass implementation in your own formatter, or take a reference to it and call it yourself.
var ts = TypeScript.Definitions().For(Assembly.LoadFrom(dll)); var baseClassFormatter = ts.ScriptGenerator.Formaters[typeof(TsClass)]; ts = ts.WithTypeFormatter((type, formatter) => { var format = baseClassFormatter(type, formatter); var isOpenGenericTypeArg = type.Type.IsGenericParameter && type.Type.FullName == null && type.Type.Name.StartsWith("T"); if (isOpenGenericTypeArg) //e.g. IDictionary<TKey, TResult> { return format; } return "I" + format; }) .WithMemberFormatter((identifier) => CamelCase(identifier.Name))
-
Thanks Tyson. This workaround works great. But for completeness, you also need to import TsModels for this to work:
<#@ import namespace="TypeLite.TsModels" #>
-
Thanks all. I just used this and it works great.
-
I could not able to make it with the given solution. Here is my C# Dictionary class
public class ComponentMap { public Dictionary<int, List<int>> MappingData { get; set; } public List<int> Numberss { get; set; } }
The generated TypeScript looks like
interface IComponentMap { MappingData: System.Collections.Generic.IKeyValuePair<System.IInt32,System.Collections.Generic.IList`1>[]; Numberss: number[]; }
with the error Cannot find namespace 'System'
My expectation was
MappingData: System.Collections.Generic.IKeyValuePair<number,number[]>;
TypeScript Generator Class
private static void GenerateTypeScriptContracts(string assemblyFile, string outputPath) { var assembly = Assembly.LoadFrom(assemblyFile); // If you want a subset of classes from this assembly, filter them here var models = assembly.GetTypes(); var ts = TypeScript.Definitions().For(assembly); TsTypeFormatter baseClassFormatter = ts.ScriptGenerator.Formaters[typeof(TsClass)]; var generator = new TypeScriptFluent() .WithConvertor<Guid>(c => "string") .WithTypeFormatter((type, f) => GenerateName(type, baseClassFormatter, f)); //.WithMemberFormatter((identifier) => CamelCase(identifier.Name)); foreach (var model in models) { generator.ModelBuilder.Add(model); } //Generate enums string fileNamePrefix = assembly.FullName.Split(',').First().ToLowerInvariant(); var enumDefinitions = generator.Generate(TsGeneratorOutput.Enums); File.WriteAllText(Path.Combine(outputPath, fileNamePrefix + ".enums.ts"), enumDefinitions); //Generate interface definitions for all classes var classDefinitions = generator.Generate(TsGeneratorOutput.Properties | TsGeneratorOutput.Fields); File.WriteAllText(Path.Combine(outputPath, fileNamePrefix + ".classes.d.ts"), classDefinitions); } private static string CamelCase(string name) { return char.ToLower(name[0]) + name.Substring(1); } // FIX: https://bitbucket.org/LukasKabrt/typelite/issues/88/error-in-generic-generation-with private static string GenerateName(TsType type, TsTypeFormatter classFormatter, ITsTypeFormatter formatter) { TsClass @class = (TsClass)type; string format; // NOTE: Modification to fix to handle interface on generic parameters. if (@class.GenericArguments.Any(x => !x.Type.IsGenericParameter)) { string genericArgs = string.Join(",", @class.GenericArguments.Select(x => FormatGenericParameter(x.Type))); format = $"{@class.Name}<{genericArgs}>"; } else { format = classFormatter(type, formatter); } var isOpenGenericTypeArg = type.Type.IsGenericParameter && type.Type.FullName == null && type.Type.Name.StartsWith("T"); if (isOpenGenericTypeArg) { //e.g. IDictionary<TKey, TResult> return format; } return "I" + format; } private static string FormatGenericParameter(Type type) { return type.IsGenericParameter ? type.Namespace + "." + type.Name : type.Namespace + ".I" + type.Name; }
- Log in to comment
.WithTypeFormatter((type, F) => "I" + ((TypeLite.TsModels.TsClass)type).Name)
adds 'I' in front of all types soIKeyValuePair
is correct, but it shouldn't probably be applied to open generics parametersTKey
andTValue
. It will be fixed in future.