package cz.habarta.typescript.generator;
import cz.habarta.typescript.generator.compiler.Symbol;
import cz.habarta.typescript.generator.util.Utils;
import java.util.*;
/**
* Represents TypeScript type.
* That means something which can appear in type position (after ":" character).
*/
public abstract class TsType {
public static final TsType Any = new BasicType("any");
public static final TsType Boolean = new BasicType("boolean");
public static final TsType Number = new BasicType("number");
public static final TsType String = new BasicType("string");
public static final TsType Date = new BasicType("Date");
public static final TsType Void = new BasicType("void");
@Override
public boolean equals(Object rhs) {
return rhs != null && this.getClass() == rhs.getClass() && this.toString().equals(rhs.toString());
}
@Override
public int hashCode() {
return this.toString().hashCode();
}
public TsType.OptionalType optional() {
return new TsType.OptionalType(this);
}
public abstract String format(Settings settings);
protected static List<String> format(List<TsType> types, Settings settings) {
final List<String> formatted = new ArrayList<>();
for (TsType type : types) {
formatted.add(type.format(settings));
}
return formatted;
}
@Override
public String toString() {
return format(new Settings());
}
public static class BasicType extends TsType {
public final String name;
public BasicType(String name) {
this.name = name;
}
@Override
public String format(Settings settings) {
return name;
}
}
public static class VerbatimType extends TsType {
public final String verbatimType;
public VerbatimType(String verbatimType) {
this.verbatimType = verbatimType;
}
@Override
public String format(Settings settings) {
return verbatimType;
}
}
/**
* Identifier which references some type, for example interface or type alias.
*/
public static class ReferenceType extends TsType {
public final Symbol symbol;
public ReferenceType(Symbol symbol) {
this.symbol = symbol;
}
@Override
public String format(Settings settings) {
return symbol.getFullName();
}
}
public static class GenericReferenceType extends TsType.ReferenceType {
public final List<TsType> typeArguments;
public GenericReferenceType(Symbol symbol, TsType... typeArguments) {
this(symbol, Arrays.asList(typeArguments));
}
public GenericReferenceType(Symbol symbol, List<TsType> typeArguments) {
super(symbol);
this.typeArguments = typeArguments;
}
@Override
public String format(Settings settings) {
return symbol.getFullName() + "<" + Utils.join(format(typeArguments, settings), ", ") + ">";
}
}
public static class GenericVariableType extends TsType.BasicType {
public GenericVariableType(String name) {
super(name);
}
}
public static class EnumReferenceType extends ReferenceType {
public EnumReferenceType(Symbol symbol) {
super(symbol);
}
}
public static class BasicArrayType extends TsType {
public final TsType elementType;
public BasicArrayType(TsType elementType) {
this.elementType = elementType;
}
@Override
public String format(Settings settings) {
// https://github.com/Microsoft/TypeScript/pull/914
// TypeScript Specification A.1
return elementType instanceof UnionType
? "(" + elementType.format(settings) + ")" + "[]"
: elementType.format(settings) + "[]";
}
}
public static class IndexedArrayType extends TsType {
public final TsType indexType;
public final TsType elementType;
public IndexedArrayType(TsType indexType, TsType elementType) {
this.indexType = indexType;
this.elementType = elementType;
}
@Override
public String format(Settings settings) {
return "{ [index: " + indexType.format(settings) + "]: " + elementType.format(settings) + " }";
}
}
public static class UnionType extends TsType {
public final List<TsType> types;
public UnionType(List<? extends TsType> types) {
this.types = new ArrayList<TsType>(types);
}
@Override
public String format(Settings settings) {
return types.isEmpty()
? "never"
: Utils.join(format(types, settings), " | ");
}
}
public static class StringLiteralType extends TsType {
public final String literal;
public StringLiteralType(String literal) {
this.literal = literal;
}
@Override
public String format(Settings settings) {
return settings.quotes + literal + settings.quotes;
}
}
public static class OptionalType extends TsType {
public final TsType type;
public OptionalType(TsType type) {
this.type = type;
}
@Override
public String format(Settings settings) {
return type.format(settings);
}
}
public static class ObjectType extends TsType {
public final List<TsProperty> properties;
public ObjectType(TsProperty... properties) {
this(Utils.removeNulls(Arrays.asList(properties)));
}
public ObjectType(List<TsProperty> properties) {
this.properties = properties;
}
@Override
public String format(Settings settings) {
final List<String> props = new ArrayList<>();
for (TsProperty property : properties) {
props.add(property.format(settings));
}
if (props.isEmpty()) {
return "{}";
} else {
return "{ " + Utils.join(props, " ") + " }";
}
}
}
public static TsType transformTsType(TsType tsType, Transformer transformer) {
final TsType type = transformer.transform(tsType);
if (type instanceof TsType.GenericReferenceType) {
final GenericReferenceType genericReferenceType = (TsType.GenericReferenceType) type;
final List<TsType> typeArguments = new ArrayList<>();
for (TsType typeArgument : genericReferenceType.typeArguments) {
typeArguments.add(transformTsType(typeArgument, transformer));
}
return new TsType.GenericReferenceType(genericReferenceType.symbol, typeArguments);
}
if (type instanceof TsType.OptionalType) {
final TsType.OptionalType optionalType = (TsType.OptionalType) type;
return new TsType.OptionalType(transformTsType(optionalType.type, transformer));
}
if (type instanceof TsType.BasicArrayType) {
final TsType.BasicArrayType basicArrayType = (TsType.BasicArrayType) type;
return new TsType.BasicArrayType(transformTsType(basicArrayType.elementType, transformer));
}
if (type instanceof TsType.IndexedArrayType) {
final TsType.IndexedArrayType indexedArrayType = (TsType.IndexedArrayType) type;
return new TsType.IndexedArrayType(
transformTsType(indexedArrayType.indexType, transformer),
transformTsType(indexedArrayType.elementType, transformer));
}
if (type instanceof TsType.ObjectType) {
final TsType.ObjectType objectType = (TsType.ObjectType) type;
final List<TsProperty> properties = new ArrayList<>();
for (TsProperty property : objectType.properties) {
properties.add(new TsProperty(property.name, transformTsType(property.tsType, transformer)));
}
return new TsType.ObjectType(properties);
}
return type;
}
public static interface Transformer {
public TsType transform(TsType tsType);
}
}