Skip to content

Commit e539950

Browse files
author
Oleg Schelykalnov
committed
Add support generic bounds
1 parent c231299 commit e539950

File tree

6 files changed

+87
-16
lines changed

6 files changed

+87
-16
lines changed

typescript-generator-core/src/main/java/cz/habarta/typescript/generator/DefaultTypeProcessor.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,31 @@ public Result processType(Type javaType, Context context) {
6565
// generic structural type used without type arguments
6666
if (javaClass.getTypeParameters().length > 0) {
6767
final List<TsType> tsTypeArguments = new ArrayList<>();
68+
final List<Class<?>> discoveredClasses = new ArrayList<>();
6869
for (int i = 0; i < javaClass.getTypeParameters().length; i++) {
69-
tsTypeArguments.add(TsType.Any);
70+
TypeVariable<?> typeVariable = javaClass.getTypeParameters()[i];
71+
final List<TsType> bounds = new ArrayList<>();
72+
for (int j = 0; j < typeVariable.getBounds().length; j++) {
73+
Type boundType = typeVariable.getBounds()[j];
74+
if (!Object.class.equals(boundType)) {
75+
Result res = context.processType(boundType);
76+
bounds.add(res.getTsType());
77+
discoveredClasses.addAll(res.getDiscoveredClasses());
78+
}
79+
}
80+
switch (bounds.size()) {
81+
case 0:
82+
tsTypeArguments.add(TsType.Any);
83+
break;
84+
case 1:
85+
tsTypeArguments.add(bounds.get(0));
86+
break;
87+
default:
88+
tsTypeArguments.add(new TsType.IntersectionType(bounds));
89+
break;
90+
}
7091
}
71-
return new Result(new TsType.GenericReferenceType(context.getSymbol(javaClass), tsTypeArguments));
92+
return new Result(new TsType.GenericReferenceType(context.getSymbol(javaClass), tsTypeArguments), discoveredClasses);
7293
}
7394
// structural type
7495
return new Result(new TsType.ReferenceType(context.getSymbol(javaClass)), javaClass);

typescript-generator-core/src/main/java/cz/habarta/typescript/generator/TsType.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,21 @@ public GenericVariableType(String name) {
142142

143143
}
144144

145+
public static class BoundedGenericVariableType extends GenericVariableType {
146+
147+
public final TsType bound;
148+
149+
public BoundedGenericVariableType(String name, TsType bound) {
150+
super(name);
151+
this.bound = bound;
152+
}
153+
154+
@Override
155+
public String format(Settings settings) {
156+
return super.format(settings) + (bound != null ? " extends " + bound.format(settings) : "");
157+
}
158+
}
159+
145160
public static class EnumReferenceType extends ReferenceType {
146161
public EnumReferenceType(Symbol symbol) {
147162
super(symbol);

typescript-generator-core/src/main/java/cz/habarta/typescript/generator/compiler/ModelCompiler.java

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,8 @@ public TsModel javaToTypeScript(Model model) {
122122
final TsType optionsType = settings.restOptionsType != null
123123
? new TsType.VerbatimType(settings.restOptionsType)
124124
: null;
125-
final TsType.GenericVariableType optionsGenericVariable = settings.restOptionsTypeIsGeneric
126-
? new TsType.GenericVariableType(settings.restOptionsType)
125+
final TsType.BoundedGenericVariableType optionsGenericVariable = settings.restOptionsTypeIsGeneric
126+
? new TsType.BoundedGenericVariableType(settings.restOptionsType, null)
127127
: null;
128128
final List<RestApplicationModel> restApplicationsWithInterface = model.getRestApplications().stream()
129129
.filter(restApplication -> restApplication.getType().generateInterface.apply(settings))
@@ -319,10 +319,26 @@ private boolean mappedToClass(Class<?> cls) {
319319
return cls != null && !cls.isInterface() && settings.getMapClassesAsClassesFilter().test(cls.getName());
320320
}
321321

322-
private static List<TsType.GenericVariableType> getTypeParameters(Class<?> cls) {
323-
final List<TsType.GenericVariableType> typeParameters = new ArrayList<>();
322+
private List<TsType.BoundedGenericVariableType> getTypeParameters(Class<?> cls) {
323+
final List<TsType.BoundedGenericVariableType> typeParameters = new ArrayList<>();
324324
for (TypeVariable<?> typeParameter : cls.getTypeParameters()) {
325-
typeParameters.add(new TsType.GenericVariableType(typeParameter.getName()));
325+
final List<TsType> bounds = new ArrayList<>();
326+
for (Type bound : typeParameter.getBounds()) {
327+
if (!Object.class.equals(bound)) {
328+
bounds.add(javaToTypeScript(bound));
329+
}
330+
}
331+
switch (bounds.size()) {
332+
case 0:
333+
typeParameters.add(new TsType.BoundedGenericVariableType(typeParameter.getName(), null));
334+
break;
335+
case 1:
336+
typeParameters.add(new TsType.BoundedGenericVariableType(typeParameter.getName(), bounds.get(0)));
337+
break;
338+
default:
339+
typeParameters.add(new TsType.BoundedGenericVariableType(typeParameter.getName(), new TsType.IntersectionType(bounds)));
340+
break;
341+
}
326342
}
327343
return typeParameters;
328344
}
@@ -531,8 +547,8 @@ private Symbol createRestResponseType(SymbolTable symbolTable, TsModel tsModel)
531547
}
532548

533549
private void createRestInterfaces(TsModel tsModel, SymbolTable symbolTable, List<RestApplicationModel> restApplications,
534-
Symbol responseSymbol, TsType.GenericVariableType optionsGenericVariable, TsType optionsType) {
535-
final List<TsType.GenericVariableType> typeParameters = Utils.listFromNullable(optionsGenericVariable);
550+
Symbol responseSymbol, TsType.BoundedGenericVariableType optionsGenericVariable, TsType optionsType) {
551+
final List<TsType.BoundedGenericVariableType> typeParameters = Utils.listFromNullable(optionsGenericVariable);
536552
final Map<Symbol, List<TsMethodModel>> groupedMethods = processRestMethods(tsModel, restApplications, symbolTable, null, responseSymbol, optionsType, false);
537553
for (Map.Entry<Symbol, List<TsMethodModel>> entry : groupedMethods.entrySet()) {
538554
final TsBeanModel interfaceModel = new TsBeanModel(null, TsBeanCategory.Service, false, entry.getKey(), typeParameters, null, null, null, null, null, entry.getValue(), null);
@@ -541,9 +557,9 @@ private void createRestInterfaces(TsModel tsModel, SymbolTable symbolTable, List
541557
}
542558

543559
private void createRestClients(TsModel tsModel, SymbolTable symbolTable, List<RestApplicationModel> restApplications,
544-
Symbol responseSymbol, TsType.GenericVariableType optionsGenericVariable, TsType optionsType) {
560+
Symbol responseSymbol, TsType.BoundedGenericVariableType optionsGenericVariable, TsType optionsType) {
545561
final Symbol httpClientSymbol = symbolTable.getSyntheticSymbol("HttpClient");
546-
final List<TsType.GenericVariableType> typeParameters = Utils.listFromNullable(optionsGenericVariable);
562+
final List<TsType.BoundedGenericVariableType> typeParameters = Utils.listFromNullable(optionsGenericVariable);
547563

548564
// HttpClient interface
549565
final TsType.GenericVariableType returnGenericVariable = new TsType.GenericVariableType("R");

typescript-generator-core/src/main/java/cz/habarta/typescript/generator/emitter/TsBeanModel.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
public class TsBeanModel extends TsDeclarationModel {
1212

1313
private final boolean isClass;
14-
private final List<TsType.GenericVariableType> typeParameters;
14+
private final List<TsType.BoundedGenericVariableType> typeParameters;
1515
private final TsType parent;
1616
private final List<TsType> extendsList;
1717
private final List<TsType> implementsList;
@@ -28,7 +28,7 @@ public TsBeanModel(
2828
TsBeanCategory category,
2929
boolean isClass,
3030
Symbol name,
31-
List<TsType.GenericVariableType> typeParameters,
31+
List<TsType.BoundedGenericVariableType> typeParameters,
3232
TsType parent,
3333
List<TsType> extendsList,
3434
List<TsType> implementsList,
@@ -44,7 +44,7 @@ private TsBeanModel(
4444
TsBeanCategory category,
4545
boolean isClass,
4646
Symbol name,
47-
List<TsType.GenericVariableType> typeParameters,
47+
List<TsType.BoundedGenericVariableType> typeParameters,
4848
TsType parent,
4949
List<TsType> extendsList,
5050
List<TsType> implementsList,
@@ -75,7 +75,7 @@ public boolean isClass() {
7575
return isClass;
7676
}
7777

78-
public List<TsType.GenericVariableType> getTypeParameters() {
78+
public List<TsType.BoundedGenericVariableType> getTypeParameters() {
7979
return typeParameters;
8080
}
8181

typescript-generator-core/src/test/java/cz/habarta/typescript/generator/EnumTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ public void testObjectEnum() {
202202
public void testJavaLangEnum1() {
203203
final Settings settings = TestUtils.settings();
204204
final String output = new TypeScriptGenerator(settings).generateTypeScript(Input.from(Child.NoEnumFactory.class));
205-
assertTrue(output.contains("interface Enum<E>"));
205+
assertTrue(output.contains("interface Enum<E extends Enum<E>>"));
206206
}
207207

208208
private static @interface Child {

typescript-generator-core/src/test/java/cz/habarta/typescript/generator/GenericsTest.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,21 @@ public void testArbitraryGenericParameter() {
144144
assertEquals(expected, output.trim());
145145
}
146146

147+
@Test
148+
public void testGenericBoundsParameter() {
149+
final Settings settings = TestUtils.settings();
150+
final String output = new TypeScriptGenerator(settings).generateTypeScript(Input.from(G.class));
151+
final String nl = settings.newline;
152+
final String expected =
153+
"interface G<T extends F> {" + nl +
154+
" x: T;" + nl +
155+
"}" + nl +
156+
"" + nl +
157+
"interface F {" + nl +
158+
"}";
159+
assertEquals(expected, output.trim());
160+
}
161+
147162
class A<U,V> {
148163
public A<String, String> x;
149164
public A<A<String, B>, List<String>> y;
@@ -167,6 +182,10 @@ class E extends D<F> {
167182
class F {
168183
}
169184

185+
class G<T extends F> {
186+
public T x;
187+
}
188+
170189
abstract class IA implements IB<String>, Comparable<IA> {
171190
}
172191

0 commit comments

Comments
 (0)