Skip to content

Commit

Permalink
Add support generic bounds
Browse files Browse the repository at this point in the history
  • Loading branch information
Oleg Schelykalnov committed Sep 10, 2019
1 parent c231299 commit e539950
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,31 @@ public Result processType(Type javaType, Context context) {
// generic structural type used without type arguments
if (javaClass.getTypeParameters().length > 0) {
final List<TsType> tsTypeArguments = new ArrayList<>();
final List<Class<?>> discoveredClasses = new ArrayList<>();
for (int i = 0; i < javaClass.getTypeParameters().length; i++) {
tsTypeArguments.add(TsType.Any);
TypeVariable<?> typeVariable = javaClass.getTypeParameters()[i];
final List<TsType> bounds = new ArrayList<>();
for (int j = 0; j < typeVariable.getBounds().length; j++) {
Type boundType = typeVariable.getBounds()[j];
if (!Object.class.equals(boundType)) {
Result res = context.processType(boundType);
bounds.add(res.getTsType());
discoveredClasses.addAll(res.getDiscoveredClasses());
}
}
switch (bounds.size()) {
case 0:
tsTypeArguments.add(TsType.Any);
break;
case 1:
tsTypeArguments.add(bounds.get(0));
break;
default:
tsTypeArguments.add(new TsType.IntersectionType(bounds));
break;
}
}
return new Result(new TsType.GenericReferenceType(context.getSymbol(javaClass), tsTypeArguments));
return new Result(new TsType.GenericReferenceType(context.getSymbol(javaClass), tsTypeArguments), discoveredClasses);
}
// structural type
return new Result(new TsType.ReferenceType(context.getSymbol(javaClass)), javaClass);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,21 @@ public GenericVariableType(String name) {

}

public static class BoundedGenericVariableType extends GenericVariableType {

public final TsType bound;

public BoundedGenericVariableType(String name, TsType bound) {
super(name);
this.bound = bound;
}

@Override
public String format(Settings settings) {
return super.format(settings) + (bound != null ? " extends " + bound.format(settings) : "");
}
}

public static class EnumReferenceType extends ReferenceType {
public EnumReferenceType(Symbol symbol) {
super(symbol);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ public TsModel javaToTypeScript(Model model) {
final TsType optionsType = settings.restOptionsType != null
? new TsType.VerbatimType(settings.restOptionsType)
: null;
final TsType.GenericVariableType optionsGenericVariable = settings.restOptionsTypeIsGeneric
? new TsType.GenericVariableType(settings.restOptionsType)
final TsType.BoundedGenericVariableType optionsGenericVariable = settings.restOptionsTypeIsGeneric
? new TsType.BoundedGenericVariableType(settings.restOptionsType, null)
: null;
final List<RestApplicationModel> restApplicationsWithInterface = model.getRestApplications().stream()
.filter(restApplication -> restApplication.getType().generateInterface.apply(settings))
Expand Down Expand Up @@ -319,10 +319,26 @@ private boolean mappedToClass(Class<?> cls) {
return cls != null && !cls.isInterface() && settings.getMapClassesAsClassesFilter().test(cls.getName());
}

private static List<TsType.GenericVariableType> getTypeParameters(Class<?> cls) {
final List<TsType.GenericVariableType> typeParameters = new ArrayList<>();
private List<TsType.BoundedGenericVariableType> getTypeParameters(Class<?> cls) {
final List<TsType.BoundedGenericVariableType> typeParameters = new ArrayList<>();
for (TypeVariable<?> typeParameter : cls.getTypeParameters()) {
typeParameters.add(new TsType.GenericVariableType(typeParameter.getName()));
final List<TsType> bounds = new ArrayList<>();
for (Type bound : typeParameter.getBounds()) {
if (!Object.class.equals(bound)) {
bounds.add(javaToTypeScript(bound));
}
}
switch (bounds.size()) {
case 0:
typeParameters.add(new TsType.BoundedGenericVariableType(typeParameter.getName(), null));
break;
case 1:
typeParameters.add(new TsType.BoundedGenericVariableType(typeParameter.getName(), bounds.get(0)));
break;
default:
typeParameters.add(new TsType.BoundedGenericVariableType(typeParameter.getName(), new TsType.IntersectionType(bounds)));
break;
}
}
return typeParameters;
}
Expand Down Expand Up @@ -531,8 +547,8 @@ private Symbol createRestResponseType(SymbolTable symbolTable, TsModel tsModel)
}

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

private void createRestClients(TsModel tsModel, SymbolTable symbolTable, List<RestApplicationModel> restApplications,
Symbol responseSymbol, TsType.GenericVariableType optionsGenericVariable, TsType optionsType) {
Symbol responseSymbol, TsType.BoundedGenericVariableType optionsGenericVariable, TsType optionsType) {
final Symbol httpClientSymbol = symbolTable.getSyntheticSymbol("HttpClient");
final List<TsType.GenericVariableType> typeParameters = Utils.listFromNullable(optionsGenericVariable);
final List<TsType.BoundedGenericVariableType> typeParameters = Utils.listFromNullable(optionsGenericVariable);

// HttpClient interface
final TsType.GenericVariableType returnGenericVariable = new TsType.GenericVariableType("R");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
public class TsBeanModel extends TsDeclarationModel {

private final boolean isClass;
private final List<TsType.GenericVariableType> typeParameters;
private final List<TsType.BoundedGenericVariableType> typeParameters;
private final TsType parent;
private final List<TsType> extendsList;
private final List<TsType> implementsList;
Expand All @@ -28,7 +28,7 @@ public TsBeanModel(
TsBeanCategory category,
boolean isClass,
Symbol name,
List<TsType.GenericVariableType> typeParameters,
List<TsType.BoundedGenericVariableType> typeParameters,
TsType parent,
List<TsType> extendsList,
List<TsType> implementsList,
Expand All @@ -44,7 +44,7 @@ private TsBeanModel(
TsBeanCategory category,
boolean isClass,
Symbol name,
List<TsType.GenericVariableType> typeParameters,
List<TsType.BoundedGenericVariableType> typeParameters,
TsType parent,
List<TsType> extendsList,
List<TsType> implementsList,
Expand Down Expand Up @@ -75,7 +75,7 @@ public boolean isClass() {
return isClass;
}

public List<TsType.GenericVariableType> getTypeParameters() {
public List<TsType.BoundedGenericVariableType> getTypeParameters() {
return typeParameters;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ public void testObjectEnum() {
public void testJavaLangEnum1() {
final Settings settings = TestUtils.settings();
final String output = new TypeScriptGenerator(settings).generateTypeScript(Input.from(Child.NoEnumFactory.class));
assertTrue(output.contains("interface Enum<E>"));
assertTrue(output.contains("interface Enum<E extends Enum<E>>"));
}

private static @interface Child {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,21 @@ public void testArbitraryGenericParameter() {
assertEquals(expected, output.trim());
}

@Test
public void testGenericBoundsParameter() {
final Settings settings = TestUtils.settings();
final String output = new TypeScriptGenerator(settings).generateTypeScript(Input.from(G.class));
final String nl = settings.newline;
final String expected =
"interface G<T extends F> {" + nl +
" x: T;" + nl +
"}" + nl +
"" + nl +
"interface F {" + nl +
"}";
assertEquals(expected, output.trim());
}

class A<U,V> {
public A<String, String> x;
public A<A<String, B>, List<String>> y;
Expand All @@ -167,6 +182,10 @@ class E extends D<F> {
class F {
}

class G<T extends F> {
public T x;
}

abstract class IA implements IB<String>, Comparable<IA> {
}

Expand Down

0 comments on commit e539950

Please sign in to comment.