Skip to content

Commit

Permalink
wraps records with enum keys into Partial<T> to avoid TS wanting all …
Browse files Browse the repository at this point in the history
…the keys to be present; {key} syntax cannot be used with enum keys
  • Loading branch information
angryziber committed Feb 16, 2025
1 parent 93dd121 commit 5be5463
Show file tree
Hide file tree
Showing 3 changed files with 11 additions and 10 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* jdbc: add helpful details to Postgres exception "no hstore extension installed"
* core: TSID now can use a preconfigured random seed using TSID_SEED, good for test data
* json: TSGenerator updates
* outputs records and arrays with shorter syntax, primarily to prevent records of enums from being exhaustive
* wraps records with enum keys into Partial<T> to avoid TS wanting all the keys to be present
* outputs provided custom types as separate types and references them
* accepts -o and -p arguments for simpler usage
* optionally can generate test data from Kotlin objects using -t
Expand Down
10 changes: 6 additions & 4 deletions json/src/TSGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -102,16 +102,18 @@ open class TSGenerator(
cls.isSubclassOf(Enum::class) -> tsName(cls)
cls.isSubclassOf(Boolean::class) -> "boolean"
cls.isSubclassOf(Number::class) -> "number"
cls.isSubclassOf(Iterable::class) -> tsType(args.first().type) + "[]"
cls.isSubclassOf(Iterable::class) -> "Array"
cls.java.isArray -> "Array" + (cls.java.componentType?.let { if (it.isPrimitive) "<" + tsType(it.kotlin.createType()) + ">" else "" } ?: "")
cls.isSubclassOf(Map::class) -> "{[k: ${tsType(args.first().type)}]: ${tsType(args.last().type)}}"
cls.isSubclassOf(Map::class) -> "Record"
cls == KProperty1::class -> "keyof " + tsType(args.first().type)
cls.isSubclassOf(CharSequence::class) || Converter.supports(cls) -> "string"
cls.isData || cls.java.isInterface -> tsName(cls)
else -> "any"
}
return if (ts[0].isLowerCase() || ts[0] == '{' || ts.last() == ']') ts
else ts + (args.takeIf { it.isNotEmpty() }?.joinToString(prefix = "<", postfix = ">") { tsType(it.type) } ?: "")
var fullType = if (ts[0].isLowerCase()) ts
else ts + (args.takeIf { it.isNotEmpty() }?.joinToString(prefix = "<", postfix = ">") { tsType(it.type) } ?: "")
if (args.firstOrNull()?.type?.jvmErasure?.isSubclassOf(Enum::class) == true) fullType = "Partial<$fullType>"
return fullType
}

protected open fun tsName(type: KClass<*>) = type.java.name.substringAfterLast(".").replace("$", "")
Expand Down
9 changes: 4 additions & 5 deletions json/test/TSGeneratorTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import ch.tutteli.atrium.api.fluent.en_GB.toEqual
import ch.tutteli.atrium.api.verbs.expect
import org.junit.jupiter.api.Test
import java.time.LocalDate
import java.time.LocalTime
import java.util.*
import kotlin.reflect.KProperty1

Expand All @@ -29,11 +28,11 @@ class TSGeneratorTest {

expect(ts.render(SomeData::class)).toEqual( // language=TypeScript
"interface SomeData {age: number; any: any; birthDate?: LocalDate; bytes: Array<number>; field: keyof Person; " +
"id: MyId<SomeData>; list: SomeData[]; map: {[k: LocalTime]: Array<SomeData>}; name: string; other?: SomeData; " +
"status: SomeDataStatus; hello: SomeEnum}")
"id: MyId<SomeData>; list: Array<SomeData>; map: Partial<Record<SomeEnum, Array<SomeData>>>; name: string; " +
"other?: SomeData; status: SomeDataStatus; hello: SomeEnum}")

expect(ts.render(FieldRule::class)).toEqual( // language=TypeScript
"interface FieldRule<T> {field: keyof Hello; limits: {[k: any]: number}}")
"interface FieldRule<T> {field: keyof Hello; limits: Record<any, number>}")
}
}

Expand All @@ -48,7 +47,7 @@ enum class SomeEnum { HELLO, WORLD }
interface Person { val name: String; val hello get() = SomeEnum.HELLO; }

data class SomeData(override val name: String, val age: Int, val birthDate: LocalDate?, val id: MyId<SomeData>, val other: SomeData?,
val list: List<SomeData>, val map: Map<LocalTime, Array<SomeData>>, val any: Any, val status: Status = Status.ACTIVE,
val list: List<SomeData>, val map: Map<SomeEnum, Array<SomeData>>, val any: Any, val status: Status = Status.ACTIVE,
val field: KProperty1<Person, *>, val bytes: ByteArray): Person {
enum class Status { ACTIVE }
}
Expand Down

0 comments on commit 5be5463

Please sign in to comment.