Skip to content

Commit a651496

Browse files
committed
- #1 basic lists implementation
- #2 Basic maps implementation - #7 quick implementation of brand typing for value classes
1 parent 866b9a5 commit a651496

15 files changed

+228
-114
lines changed

docs/abstract-classes.md

+26-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
<!--- TEST_NAME AbstractClassesTest -->
22

3-
### Abstract class with a single field
4-
53
<!--- INCLUDE .*\.kt
64
import kotlinx.serialization.*
75
import dev.adamko.kxstsgen.*
86
-->
97

8+
### Abstract class with a single field
9+
1010
```kotlin
1111
@Serializable
1212
abstract class Color(val rgb: Int)
@@ -58,3 +58,27 @@ interface SimpleTypes {
5858
```
5959

6060
<!--- TEST -->
61+
62+
### Abstract class, abstract value
63+
64+
```kotlin
65+
@Serializable
66+
abstract class Color {
67+
abstract val rgb: Int
68+
}
69+
70+
fun main() {
71+
val tsGenerator = KxsTsGenerator()
72+
println(tsGenerator.generate(Color.serializer().descriptor))
73+
}
74+
```
75+
76+
> You can get the full code [here](./knit/example/example-abstract-class-abstract-field-01.kt).
77+
78+
```typescript
79+
interface Color {
80+
rgb: number;
81+
}
82+
```
83+
84+
<!--- TEST -->
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// This file was automatically generated from abstract-classes.md by Knit tool. Do not edit.
2+
@file:Suppress("PackageDirectoryMismatch", "unused")
3+
package example.exampleAbstractClassAbstractField01
4+
5+
import kotlinx.serialization.*
6+
import dev.adamko.kxstsgen.*
7+
8+
@Serializable
9+
abstract class Color {
10+
abstract val rgb: Int
11+
}
12+
13+
fun main() {
14+
val tsGenerator = KxsTsGenerator()
15+
println(tsGenerator.generate(Color.serializer().descriptor))
16+
}

docs/knit/example/example-map-primitive-01.kt

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import kotlinx.serialization.*
66
import dev.adamko.kxstsgen.*
77

88
@Serializable
9-
class Config {
10-
val properties: Map<String, String> = mapOf()
11-
}
9+
data class Config(
10+
val properties: Map<String, String>
11+
)
1212

1313
fun main() {
1414
val tsGenerator = KxsTsGenerator()

docs/knit/example/example-value-classes-03.kt

+12-5
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,18 @@ package example.exampleValueClasses03
55
import kotlinx.serialization.*
66
import dev.adamko.kxstsgen.*
77

8-
@Serializable
9-
@JvmInline
10-
value class UserCount(private val count: UInt)
8+
import kotlinx.serialization.builtins.serializer
9+
import dev.adamko.kxstsgen.KxsTsConfig.TypeAliasTypingConfig.BrandTyping
10+
1111

1212
fun main() {
13-
val tsGenerator = KxsTsGenerator()
14-
println(tsGenerator.generate(UserCount.serializer().descriptor))
13+
14+
val tsConfig = KxsTsConfig(typeAliasTyping = BrandTyping)
15+
16+
val tsGenerator = KxsTsGenerator(config = tsConfig)
17+
println(
18+
tsGenerator.generate(
19+
ULong.serializer().descriptor,
20+
)
21+
)
1522
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// This file was automatically generated from value-classes.md by Knit tool. Do not edit.
2+
@file:Suppress("PackageDirectoryMismatch", "unused")
3+
package example.exampleValueClasses04
4+
5+
import kotlinx.serialization.*
6+
import dev.adamko.kxstsgen.*
7+
8+
@Serializable
9+
@JvmInline
10+
value class UserCount(private val count: UInt)
11+
12+
fun main() {
13+
val tsGenerator = KxsTsGenerator()
14+
println(tsGenerator.generate(UserCount.serializer().descriptor))
15+
}

docs/knit/test/AbstractClassesTest.kt

+11
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,15 @@ class AbstractClassesTest {
3030
"}"
3131
)
3232
}
33+
34+
@Test
35+
fun testExampleAbstractClassAbstractField01() {
36+
captureOutput("ExampleAbstractClassAbstractField01") {
37+
example.exampleAbstractClassAbstractField01.main()
38+
}.verifyOutputLines(
39+
"interface Color {",
40+
" rgb: number;",
41+
"}"
42+
)
43+
}
3344
}

docs/knit/test/ListsTests.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class ListsTests {
1111
example.exampleListPrimitive01.main()
1212
}.verifyOutputLines(
1313
"interface CalendarEvent {",
14-
" attendeeNames: string[]",
14+
" attendeeNames: string[];",
1515
"}"
1616
)
1717
}

docs/knit/test/ValueClassesTest.kt

+9
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,15 @@ class ValueClassesTest {
3333
fun testExampleValueClasses03() {
3434
captureOutput("ExampleValueClasses03") {
3535
example.exampleValueClasses03.main()
36+
}.verifyOutputLines(
37+
"type ULong = number & { __ULong__: void };"
38+
)
39+
}
40+
41+
@Test
42+
fun testExampleValueClasses04() {
43+
captureOutput("ExampleValueClasses04") {
44+
example.exampleValueClasses04.main()
3645
}.verifyOutputLines(
3746
"type UInt = number;",
3847
"",

docs/lists.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ fun main() {
2323
2424
```typescript
2525
interface CalendarEvent {
26-
attendeeNames: string[]
26+
attendeeNames: string[];
2727
}
2828
```
2929

docs/maps.md

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
<!--- TEST_NAME MapsTests -->
22

3-
### Primitive lists
4-
53
<!--- INCLUDE .*\.kt
64
import kotlinx.serialization.*
75
import dev.adamko.kxstsgen.*
86
-->
97

8+
### Primitive lists
9+
10+
1011
```kotlin
1112
@Serializable
12-
class Config {
13-
val properties: Map<String, String> = mapOf()
14-
}
13+
data class Config(
14+
val properties: Map<String, String>
15+
)
1516

1617
fun main() {
1718
val tsGenerator = KxsTsGenerator()

docs/value-classes.md

+39-3
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,47 @@ type UInt = number;
6262
type ULong = number;
6363
```
6464

65-
(At present this is not very useful as Typescript will make no distinction between any of these
66-
numbers, even though they are distinct in Kotlin. 'Brand typing' might be introduced in the future.)
65+
<!--- TEST -->
66+
67+
68+
### Brand typing
69+
70+
To make value classes a little more strict, we can use brand typing
71+
72+
73+
```kotlin
74+
import kotlinx.serialization.builtins.serializer
75+
import dev.adamko.kxstsgen.KxsTsConfig.TypeAliasTypingConfig.BrandTyping
76+
```
77+
78+
<!-- IMPORT -->
79+
80+
```kotlin
81+
82+
fun main() {
83+
84+
val tsConfig = KxsTsConfig(typeAliasTyping = BrandTyping)
85+
86+
val tsGenerator = KxsTsGenerator(config = tsConfig)
87+
println(
88+
tsGenerator.generate(
89+
ULong.serializer().descriptor,
90+
)
91+
)
92+
}
93+
```
94+
95+
<!-- PREFIX -->
96+
97+
> You can get the full code [here](./knit/example/example-value-classes-03.kt).
98+
99+
```typescript
100+
type ULong = number & { __ULong__: void };
101+
```
67102

68103
<!--- TEST -->
69104

105+
70106
### Nested value classes
71107

72108
If the value class contains another value class, then the outer class will be aliased to other value
@@ -83,7 +119,7 @@ fun main() {
83119
}
84120
```
85121

86-
> You can get the full code [here](./knit/example/example-value-classes-03.kt).
122+
> You can get the full code [here](./knit/example/example-value-classes-04.kt).
87123
88124
```typescript
89125
type UInt = number;

modules/kxs-ts-gen-core/src/commonMain/kotlin/dev.adamko.kxstsgen/KxsTsConfig.kt

+9-3
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,22 @@ import kotlinx.serialization.descriptors.SerialDescriptor
55

66
data class KxsTsConfig(
77
val indent: String = " ",
8-
val namespaceConfig: NamespaceConfig = NamespaceConfig.None,
8+
val namespaceConfig: NamespaceConfig = NamespaceConfig.Disabled,
9+
val typeAliasTyping: TypeAliasTypingConfig = TypeAliasTypingConfig.None,
910
) {
1011

1112
sealed interface NamespaceConfig {
1213
/** Use the prefix of the [SerialDescriptor] */
1314
object UseDescriptorNamePrefix : NamespaceConfig
1415
/** don't generate a namespace */
15-
object None : NamespaceConfig
16+
object Disabled : NamespaceConfig
1617
@JvmInline
17-
value class Hardcoded(val namespace: String) : NamespaceConfig
18+
value class Static(val namespace: String) : NamespaceConfig
19+
}
20+
21+
sealed interface TypeAliasTypingConfig {
22+
object None : TypeAliasTypingConfig
23+
object BrandTyping : TypeAliasTypingConfig
1824
}
1925

2026
}

modules/kxs-ts-gen-core/src/commonMain/kotlin/dev.adamko.kxstsgen/KxsTsGenerator.kt

+22-25
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ class TsConverter(
6262
convertToTsElement(null, target)
6363
}
6464

65-
private fun convertToTsElement(requestor: TsElementId?, target: SerialDescriptor): TsElementId {
65+
private fun convertToTsElement(requestor: TsElementId?, target: SerialDescriptor): TsElement {
6666

6767
val targetId = TsElementId(target.serialName.removeSuffix("?"))
6868

@@ -112,7 +112,7 @@ class TsConverter(
112112
}
113113

114114
result
115-
}.id
115+
}
116116
}
117117

118118

@@ -123,18 +123,18 @@ class TsConverter(
123123

124124
if (structDescriptor.isInline) {
125125
val fieldDescriptor = structDescriptor.elementDescriptors.first()
126-
val typeId = convertToTsElement(targetId, fieldDescriptor)
127-
val typeReference = TsTypeReference(typeId, fieldDescriptor.isNullable)
128-
return TsTypeAlias(targetId, setOf(typeReference))
126+
val fieldType = convertToTsElement(targetId, fieldDescriptor)
127+
val fieldTyping = TsTyping(fieldType, fieldDescriptor.isNullable)
128+
return TsTypeAlias(targetId, fieldTyping)
129129
} else {
130130

131-
val properties = structDescriptor.elementDescriptors.mapIndexed { index, field ->
131+
val properties = structDescriptor.elementDescriptors.mapIndexed { index, fieldDescriptor ->
132132
val name = structDescriptor.getElementName(index)
133-
val fieldTypeId = convertToTsElement(targetId, field)
134-
val fieldTypeReference = TsTypeReference(fieldTypeId, field.isNullable)
133+
val fieldType = convertToTsElement(targetId, fieldDescriptor)
134+
val fieldTyping = TsTyping(fieldType, fieldDescriptor.isNullable)
135135
when {
136-
structDescriptor.isElementOptional(index) -> TsProperty.Optional(name, fieldTypeReference)
137-
else -> TsProperty.Required(name, fieldTypeReference)
136+
structDescriptor.isElementOptional(index) -> TsProperty.Optional(name, fieldTyping)
137+
else -> TsProperty.Required(name, fieldTyping)
138138
}
139139
}
140140

@@ -146,10 +146,6 @@ class TsConverter(
146146
targetId: TsElementId,
147147
enumDescriptor: SerialDescriptor,
148148
): TsElement {
149-
// if (descriptor.elementsCount > 0) {
150-
// convertStructure(descriptor)
151-
// }
152-
153149
return TsStructure.TsEnum(
154150
targetId,
155151
enumDescriptor.elementNames.toSet(),
@@ -160,12 +156,10 @@ class TsConverter(
160156
targetId: TsElementId,
161157
listDescriptor: SerialDescriptor,
162158
): TsStructure.TsList {
163-
164-
val typeDescriptor = listDescriptor.elementDescriptors.first()
165-
val typeId =
166-
TsTypeReference(convertToTsElement(targetId, typeDescriptor), typeDescriptor.isNullable)
167-
168-
return TsStructure.TsList(targetId, typeId)
159+
val elementDescriptor = listDescriptor.elementDescriptors.first()
160+
val elementType = convertToTsElement(targetId, elementDescriptor)
161+
val elementTyping = TsTyping(elementType, elementDescriptor.isNullable)
162+
return TsStructure.TsList(targetId, elementTyping)
169163
}
170164

171165
private fun convertMap(
@@ -174,10 +168,13 @@ class TsConverter(
174168
): TsStructure.TsMap {
175169

176170
val (keyDescriptor, valueDescriptor) = mapDescriptor.elementDescriptors.toList()
177-
val keyType =
178-
TsTypeReference(convertToTsElement(targetId, keyDescriptor), keyDescriptor.isNullable)
179-
val valueType =
180-
TsTypeReference(convertToTsElement(targetId, valueDescriptor), valueDescriptor.isNullable)
181-
return TsStructure.TsMap(targetId, keyType, valueType)
171+
172+
val keyType = convertToTsElement(targetId, keyDescriptor)
173+
val keyTyping = TsTyping(keyType, keyDescriptor.isNullable)
174+
175+
val valueType = convertToTsElement(targetId, valueDescriptor)
176+
val valueTyping = TsTyping(valueType, valueDescriptor.isNullable)
177+
178+
return TsStructure.TsMap(targetId, keyTyping, valueTyping)
182179
}
183180
}

0 commit comments

Comments
 (0)