@@ -8,117 +8,109 @@ import typescriptServices.ts.*
8
8
import kotlin.collections.Map
9
9
10
10
fun ObjectTypeToKotlinTypeMapper.mapType (type : Node ): KtType {
11
- val resolvedType: Type ? = typeChecker.getTypeAtLocation(type)
12
- return if (resolvedType != null ) {
13
- mapType(resolvedType, type)
14
- }
15
- else {
16
- (type.unsafeCast<TypeNode >()).toKotlinType(this )
17
- }
11
+ return mapToEnhancedType(type).singleType
18
12
}
19
13
20
- fun ObjectTypeToKotlinTypeMapper.mapTypeToUnion (type : Node ): KtTypeUnion {
14
+ fun ObjectTypeToKotlinTypeMapper.mapToEnhancedType (type : Node ): EnhancedKtType {
21
15
val resolvedType: Type ? = typeChecker.getTypeAtLocation(type)
22
16
return if (resolvedType != null ) {
23
- mapTypeToUnion (resolvedType, type)
17
+ mapToEnhancedType (resolvedType, type)
24
18
}
25
19
else {
26
- (type.unsafeCast<TypeNode >()).toKotlinTypeUnion (this )
20
+ (type.unsafeCast<TypeNode >()).toEnhancedType (this )
27
21
}
28
22
}
29
23
30
24
private fun ObjectTypeToKotlinTypeMapper.mapType (type : Type , declaration : Node ? ): KtType =
31
- mapTypeToUnion (type, declaration).singleType
25
+ mapToEnhancedType (type, declaration).singleType
32
26
33
- private fun ObjectTypeToKotlinTypeMapper.mapTypeToUnion (type : Type , declaration : Node ? ): KtTypeUnion {
27
+ private fun ObjectTypeToKotlinTypeMapper.mapToEnhancedType (type : Type , declaration : Node ? ): EnhancedKtType {
34
28
val resultingDeclaration = declaration ? : type.symbol?.declarations?.singleOrNull()
35
29
36
30
val flags = type.getFlags()
37
31
if (resultingDeclaration != null && type.symbol == null && TypeFlags .Any in flags) {
38
- return resultingDeclaration.unsafeCast<TypeNode >().toKotlinTypeUnion (this )
32
+ return resultingDeclaration.unsafeCast<TypeNode >().toEnhancedType (this )
39
33
}
40
34
41
35
if (resultingDeclaration?.kind == SyntaxKind .UnionType && TypeFlags .Union !in flags) {
42
- return resultingDeclaration.unsafeCast<UnionTypeNode >().toKotlinTypeUnion (this )
36
+ return resultingDeclaration.unsafeCast<UnionTypeNode >().toEnhancedType (this )
43
37
}
44
38
45
39
if (type in typesInMappingProcess) {
46
40
report(
47
41
" Recursion is detected when resolve type: \" ${type.symbol?.name} \" for the declaration at ${declaration?.location()} " ,
48
42
maxLevelToShow = DiagnosticLevel .WARNING_WITH_STACKTRACE
49
43
)
50
- return KtTypeUnion (KtType (DYNAMIC ))
44
+ return SingleKtType (KtType (DYNAMIC ))
51
45
}
52
46
53
47
typesInMappingProcess + = type
54
48
55
49
val mappedType = when {
56
- declaration?.kind == SyntaxKind .ThisType -> {
57
- val possibleTypes = mapTypeToUnion(type.unsafeCast<TypeParameter >().constraint!! , null ).possibleTypes
58
- .map { it.copy(comment = " this" ) }
59
- KtTypeUnion (possibleTypes)
60
- }
50
+ declaration?.kind == SyntaxKind .ThisType -> mapToEnhancedType(type.unsafeCast<TypeParameter >().constraint!! , null ).withComment(" this" )
61
51
62
- TypeFlags .Any in flags -> KtTypeUnion (KtType (ANY ))
63
- TypeFlags .String in flags -> KtTypeUnion (KtType (STRING ))
64
- TypeFlags .Boolean in flags -> KtTypeUnion (KtType (BOOLEAN ))
65
- TypeFlags .Number in flags -> KtTypeUnion (KtType (NUMBER ))
66
- TypeFlags .Void in flags -> KtTypeUnion (KtType (UNIT ))
52
+ TypeFlags .Any in flags -> SingleKtType (KtType (ANY ))
53
+ TypeFlags .String in flags -> SingleKtType (KtType (STRING ))
54
+ TypeFlags .Boolean in flags -> SingleKtType (KtType (BOOLEAN ))
55
+ TypeFlags .Number in flags -> SingleKtType (KtType (NUMBER ))
56
+ TypeFlags .Void in flags -> SingleKtType (KtType (UNIT ))
67
57
68
58
TypeFlags .Undefined in flags ||
69
- TypeFlags .Null in flags -> KtTypeUnion (KtType (NOTHING , isNullable = true ))
59
+ TypeFlags .Null in flags -> SingleKtType (KtType (NOTHING , isNullable = true ))
70
60
71
61
TypeFlags .StringLiteral in flags -> {
72
- KtTypeUnion (KtType (STRING , comment = " \" " + type.unsafeCast<LiteralType >().value + " \" " ))
62
+ SingleKtType (KtType (STRING , comment = " \" " + type.unsafeCast<LiteralType >().value + " \" " ))
73
63
}
74
64
// TODO: add test if it's allowed
75
65
TypeFlags .NumberLiteral in flags -> {
76
- KtTypeUnion (KtType (NUMBER , comment = type.unsafeCast<LiteralType >().value))
66
+ SingleKtType (KtType (NUMBER , comment = type.unsafeCast<LiteralType >().value))
77
67
}
78
68
// TODO: add test if it's allowed
79
69
TypeFlags .BooleanLiteral in flags -> {
80
- KtTypeUnion (KtType (BOOLEAN , comment = type.unsafeCast<LiteralType >().value))
70
+ SingleKtType (KtType (BOOLEAN , comment = type.unsafeCast<LiteralType >().value))
81
71
}
82
72
83
73
TypeFlags .Union in flags -> mapUnionType(type.unsafeCast<UnionType >())
84
74
TypeFlags .Intersection in flags -> mapIntersectionType(type.unsafeCast<IntersectionType >())
85
75
86
- TypeFlags .TypeParameter in flags -> KtTypeUnion (KtType (KtQualifiedName (unescapeIdentifier(type.getSymbol()!! .name))))
76
+ TypeFlags .TypeParameter in flags -> SingleKtType (KtType (KtQualifiedName (unescapeIdentifier(type.getSymbol()!! .name))))
87
77
88
78
TypeFlags .Object in flags -> {
89
79
val objectFlags = (type as ObjectType ).objectFlags
90
80
when {
91
- ObjectFlags .Anonymous in objectFlags -> KtTypeUnion (mapAnonymousType(type, declaration))
92
- ObjectFlags .ClassOrInterface in objectFlags -> KtTypeUnion (mapInterfaceType(type.unsafeCast<InterfaceType >(), declaration))
93
- ObjectFlags .Reference in objectFlags -> KtTypeUnion (mapTypeReference(type.unsafeCast<TypeReference >(), declaration))
81
+ ObjectFlags .Anonymous in objectFlags -> SingleKtType (mapAnonymousType(type, declaration))
82
+ ObjectFlags .ClassOrInterface in objectFlags -> SingleKtType (mapInterfaceType(type.unsafeCast<InterfaceType >(), declaration))
83
+ ObjectFlags .Reference in objectFlags -> SingleKtType (mapTypeReference(type.unsafeCast<TypeReference >(), declaration))
94
84
95
- else -> KtTypeUnion (KtType (ANY , isNullable = true ))
85
+ else -> SingleKtType (KtType (ANY , isNullable = true ))
96
86
}
97
87
}
98
88
99
- TypeFlags .Enum in flags -> KtTypeUnion (mapObjectType(type.unsafeCast<ObjectType >()))
89
+ TypeFlags .Enum in flags -> SingleKtType (mapObjectType(type.unsafeCast<ObjectType >()))
100
90
101
- else -> KtTypeUnion (KtType (ANY , isNullable = true ))
91
+ else -> SingleKtType (KtType (ANY , isNullable = true ))
102
92
}
103
93
104
94
typesInMappingProcess - = type
105
95
106
96
return mappedType
107
97
}
108
98
109
- private fun ObjectTypeToKotlinTypeMapper.mapUnionType (type : UnionOrIntersectionType ): KtTypeUnion {
110
- val notNullTypes = type.types.filter {
111
- TypeFlags .Undefined !in it.getFlags() &&
112
- TypeFlags .Null !in it.getFlags()
113
- }
114
- val nullable = notNullTypes.size != type.types.size || type.containsNull || type.containsUndefined
99
+ private fun ObjectTypeToKotlinTypeMapper.mapUnionType (type : UnionType ): EnhancedKtType {
100
+ val forceNullable = type.containsNull || type.containsUndefined
101
+ return mapUnionType(type.types.map { mapToEnhancedType(it, null ) }).forceNullable(forceNullable)
102
+ }
115
103
116
- val mappedTypes = notNullTypes.map { mapType(it, null ) }
117
- return KtTypeUnion (when {
118
- ! nullable -> mappedTypes.mergeToPreventCompilationConflicts()
119
- notNullTypes.size == 1 -> mappedTypes.map { it.copy(isNullable = true ) }
120
- else -> (mappedTypes + KtType (NOTHING , isNullable = true )).distinct()
121
- })
104
+ /* *
105
+ * Normalize to a KtTypeUnion such that equals will be true if the KotlinJS compiler would consider them
106
+ * conflicting if both were the only parameter to identically named functions.
107
+ */
108
+ private fun EnhancedKtType.normalizeToDetectCompilationConflicts (): EnhancedKtType {
109
+ return when (this ) {
110
+ is KtTypeUnion -> copy(possibleTypes = possibleTypes.map { it.normalizeToDetectCompilationConflicts() })
111
+ is KtTypeIntersection -> copy(requiredTypes = requiredTypes.map { it.normalizeToDetectCompilationConflicts() })
112
+ is SingleKtType -> copy(singleType = singleType.normalizeToDetectCompilationConflicts())
113
+ }
122
114
}
123
115
124
116
/* *
@@ -134,7 +126,19 @@ private fun KtType.normalizeToDetectCompilationConflicts(): KtType {
134
126
* Comments are not taken into account for the comparison, but are preserved by concatenation using "|" since a "union".
135
127
* This is especially useful for Typescript string literal unions.
136
128
*/
137
- private fun List<KtType>.mergeToPreventCompilationConflicts (): List <KtType > {
129
+ fun List<EnhancedKtType>.mergeToPreventCompilationConflicts (): List <EnhancedKtType > {
130
+ return groupBy { it.normalizeToDetectCompilationConflicts() }.map { entry ->
131
+ val comments = entry.value.mapNotNull { it.comment }
132
+ entry.key.withComment(comment = if (comments.isEmpty()) null else comments.joinToString(" | " ))
133
+ }
134
+ }
135
+
136
+ /* *
137
+ * Handle the case where a function is overloaded with the same Kotlin parameter types by merging them.
138
+ * Comments are not taken into account for the comparison, but are preserved by concatenation using "|" since a "union".
139
+ * This is especially useful for Typescript string literal unions.
140
+ */
141
+ fun List<KtType>.mergeToPreventCompilationConflicts (): List <KtType > {
138
142
return groupBy { it.normalizeToDetectCompilationConflicts() }.map { entry ->
139
143
val comments = entry.value.mapNotNull { it.comment }
140
144
entry.key.copy(comment = if (comments.isEmpty()) null else comments.joinToString(" | " ))
@@ -153,10 +157,8 @@ private inline val UnionOrIntersectionType.containsNull: Boolean
153
157
return jsTypeOf(array.containsNull) == " boolean" && array.containsNull.unsafeCast<Boolean >()
154
158
}
155
159
156
- private fun ObjectTypeToKotlinTypeMapper.mapIntersectionType (type : IntersectionType ): KtTypeUnion {
157
- return KtTypeUnion (mapType(type.types.first(), null ).copy(
158
- comment = type.types.joinToString(" & " ) { mapType(it, null ).stringify() }
159
- ))
160
+ private fun ObjectTypeToKotlinTypeMapper.mapIntersectionType (type : IntersectionType ): EnhancedKtType {
161
+ return KtTypeIntersection (type.types.map { SingleKtType (mapType(it, null )) })
160
162
}
161
163
162
164
private fun ObjectTypeToKotlinTypeMapper.mapTypeReference (type : TypeReference , declaration : Node ? ): KtType {
@@ -184,7 +186,7 @@ private fun ObjectTypeToKotlinTypeMapper.mapInterfaceType(type: InterfaceType, d
184
186
185
187
private fun ObjectTypeToKotlinTypeMapper.mapTypeArguments (
186
188
typeArguments : Array <Type >? , declaration : Node ?
187
- ): Sequence <KtType > {
189
+ ): Sequence <EnhancedKtType > {
188
190
val typeArgsFromDeclaration = if (declaration != null ) {
189
191
when (declaration.kind as Any ) {
190
192
SyntaxKind .ExpressionWithTypeArguments ,
@@ -211,14 +213,14 @@ private fun ObjectTypeToKotlinTypeMapper.mapTypeArguments(
211
213
.asSequence()
212
214
.zip(typeArgsFromDeclaration + generateSequence { 0 }.map { null })
213
215
return typeArgsWithDeclarations.map { (argType, arg) ->
214
- mapType (argType, arg)
216
+ mapToEnhancedType (argType, arg)
215
217
}
216
218
}
217
219
218
220
// TODO: is it correct name???
219
221
private fun ObjectTypeToKotlinTypeMapper.mapObjectType (type : Type ): KtType {
220
222
val fqn = buildFqn(type.getSymbol()!! )
221
- if (fqn == KtQualifiedName (" Function" )) return KtType (KtQualifiedName (" Function" ), typeArgs = listOf (starType()))
223
+ if (fqn == KtQualifiedName (" Function" )) return KtType (KtQualifiedName (" Function" ), typeArgs = listOf (SingleKtType ( starType() )))
222
224
return KtType (when (fqn) {
223
225
KtQualifiedName (" Object" ) -> ANY
224
226
else -> {
@@ -326,6 +328,14 @@ fun KtType.replaceTypeParameters(substitution: kotlin.collections.Map<String, Kt
326
328
)
327
329
}
328
330
331
+ fun EnhancedKtType.replaceTypeParameters (substitution : kotlin.collections.Map <String , KtType >): EnhancedKtType {
332
+ return when (this ) {
333
+ is KtTypeUnion -> this .copy(possibleTypes = this .possibleTypes.map { it.replaceTypeParameters(substitution) })
334
+ is KtTypeIntersection -> this .copy(requiredTypes = this .requiredTypes.map { it.replaceTypeParameters(substitution) })
335
+ is SingleKtType -> copy(singleType = singleType.replaceTypeParameters(substitution))
336
+ }
337
+ }
338
+
329
339
private fun KtCallSignature.replaceTypeParameters (substitution : Map <String , KtType >): KtCallSignature =
330
340
copy(
331
341
params = params.map { it.replaceTypeParameters(substitution) },
0 commit comments