@@ -8,117 +8,109 @@ import typescriptServices.ts.*
88import kotlin.collections.Map
99
1010fun 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
1812}
1913
20- fun ObjectTypeToKotlinTypeMapper.mapTypeToUnion (type : Node ): KtTypeUnion {
14+ fun ObjectTypeToKotlinTypeMapper.mapToEnhancedType (type : Node ): EnhancedKtType {
2115 val resolvedType: Type ? = typeChecker.getTypeAtLocation(type)
2216 return if (resolvedType != null ) {
23- mapTypeToUnion (resolvedType, type)
17+ mapToEnhancedType (resolvedType, type)
2418 }
2519 else {
26- (type.unsafeCast<TypeNode >()).toKotlinTypeUnion (this )
20+ (type.unsafeCast<TypeNode >()).toEnhancedType (this )
2721 }
2822}
2923
3024private fun ObjectTypeToKotlinTypeMapper.mapType (type : Type , declaration : Node ? ): KtType =
31- mapTypeToUnion (type, declaration).singleType
25+ mapToEnhancedType (type, declaration).singleType
3226
33- private fun ObjectTypeToKotlinTypeMapper.mapTypeToUnion (type : Type , declaration : Node ? ): KtTypeUnion {
27+ private fun ObjectTypeToKotlinTypeMapper.mapToEnhancedType (type : Type , declaration : Node ? ): EnhancedKtType {
3428 val resultingDeclaration = declaration ? : type.symbol?.declarations?.singleOrNull()
3529
3630 val flags = type.getFlags()
3731 if (resultingDeclaration != null && type.symbol == null && TypeFlags .Any in flags) {
38- return resultingDeclaration.unsafeCast<TypeNode >().toKotlinTypeUnion (this )
32+ return resultingDeclaration.unsafeCast<TypeNode >().toEnhancedType (this )
3933 }
4034
4135 if (resultingDeclaration?.kind == SyntaxKind .UnionType && TypeFlags .Union !in flags) {
42- return resultingDeclaration.unsafeCast<UnionTypeNode >().toKotlinTypeUnion (this )
36+ return resultingDeclaration.unsafeCast<UnionTypeNode >().toEnhancedType (this )
4337 }
4438
4539 if (type in typesInMappingProcess) {
4640 report(
4741 " Recursion is detected when resolve type: \" ${type.symbol?.name} \" for the declaration at ${declaration?.location()} " ,
4842 maxLevelToShow = DiagnosticLevel .WARNING_WITH_STACKTRACE
4943 )
50- return KtTypeUnion (KtType (DYNAMIC ))
44+ return SingleKtType (KtType (DYNAMIC ))
5145 }
5246
5347 typesInMappingProcess + = type
5448
5549 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" )
6151
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 ))
6757
6858 TypeFlags .Undefined in flags ||
69- TypeFlags .Null in flags -> KtTypeUnion (KtType (NOTHING , isNullable = true ))
59+ TypeFlags .Null in flags -> SingleKtType (KtType (NOTHING , isNullable = true ))
7060
7161 TypeFlags .StringLiteral in flags -> {
72- KtTypeUnion (KtType (STRING , comment = " \" " + type.unsafeCast<LiteralType >().value + " \" " ))
62+ SingleKtType (KtType (STRING , comment = " \" " + type.unsafeCast<LiteralType >().value + " \" " ))
7363 }
7464 // TODO: add test if it's allowed
7565 TypeFlags .NumberLiteral in flags -> {
76- KtTypeUnion (KtType (NUMBER , comment = type.unsafeCast<LiteralType >().value))
66+ SingleKtType (KtType (NUMBER , comment = type.unsafeCast<LiteralType >().value))
7767 }
7868 // TODO: add test if it's allowed
7969 TypeFlags .BooleanLiteral in flags -> {
80- KtTypeUnion (KtType (BOOLEAN , comment = type.unsafeCast<LiteralType >().value))
70+ SingleKtType (KtType (BOOLEAN , comment = type.unsafeCast<LiteralType >().value))
8171 }
8272
8373 TypeFlags .Union in flags -> mapUnionType(type.unsafeCast<UnionType >())
8474 TypeFlags .Intersection in flags -> mapIntersectionType(type.unsafeCast<IntersectionType >())
8575
86- TypeFlags .TypeParameter in flags -> KtTypeUnion (KtType (KtQualifiedName (unescapeIdentifier(type.getSymbol()!! .name))))
76+ TypeFlags .TypeParameter in flags -> SingleKtType (KtType (KtQualifiedName (unescapeIdentifier(type.getSymbol()!! .name))))
8777
8878 TypeFlags .Object in flags -> {
8979 val objectFlags = (type as ObjectType ).objectFlags
9080 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))
9484
95- else -> KtTypeUnion (KtType (ANY , isNullable = true ))
85+ else -> SingleKtType (KtType (ANY , isNullable = true ))
9686 }
9787 }
9888
99- TypeFlags .Enum in flags -> KtTypeUnion (mapObjectType(type.unsafeCast<ObjectType >()))
89+ TypeFlags .Enum in flags -> SingleKtType (mapObjectType(type.unsafeCast<ObjectType >()))
10090
101- else -> KtTypeUnion (KtType (ANY , isNullable = true ))
91+ else -> SingleKtType (KtType (ANY , isNullable = true ))
10292 }
10393
10494 typesInMappingProcess - = type
10595
10696 return mappedType
10797}
10898
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+ }
115103
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+ }
122114}
123115
124116/* *
@@ -134,7 +126,19 @@ private fun KtType.normalizeToDetectCompilationConflicts(): KtType {
134126 * Comments are not taken into account for the comparison, but are preserved by concatenation using "|" since a "union".
135127 * This is especially useful for Typescript string literal unions.
136128 */
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 > {
138142 return groupBy { it.normalizeToDetectCompilationConflicts() }.map { entry ->
139143 val comments = entry.value.mapNotNull { it.comment }
140144 entry.key.copy(comment = if (comments.isEmpty()) null else comments.joinToString(" | " ))
@@ -153,10 +157,8 @@ private inline val UnionOrIntersectionType.containsNull: Boolean
153157 return jsTypeOf(array.containsNull) == " boolean" && array.containsNull.unsafeCast<Boolean >()
154158 }
155159
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 )) })
160162}
161163
162164private fun ObjectTypeToKotlinTypeMapper.mapTypeReference (type : TypeReference , declaration : Node ? ): KtType {
@@ -184,7 +186,7 @@ private fun ObjectTypeToKotlinTypeMapper.mapInterfaceType(type: InterfaceType, d
184186
185187private fun ObjectTypeToKotlinTypeMapper.mapTypeArguments (
186188 typeArguments : Array <Type >? , declaration : Node ?
187- ): Sequence <KtType > {
189+ ): Sequence <EnhancedKtType > {
188190 val typeArgsFromDeclaration = if (declaration != null ) {
189191 when (declaration.kind as Any ) {
190192 SyntaxKind .ExpressionWithTypeArguments ,
@@ -211,14 +213,14 @@ private fun ObjectTypeToKotlinTypeMapper.mapTypeArguments(
211213 .asSequence()
212214 .zip(typeArgsFromDeclaration + generateSequence { 0 }.map { null })
213215 return typeArgsWithDeclarations.map { (argType, arg) ->
214- mapType (argType, arg)
216+ mapToEnhancedType (argType, arg)
215217 }
216218}
217219
218220// TODO: is it correct name???
219221private fun ObjectTypeToKotlinTypeMapper.mapObjectType (type : Type ): KtType {
220222 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() )))
222224 return KtType (when (fqn) {
223225 KtQualifiedName (" Object" ) -> ANY
224226 else -> {
@@ -326,6 +328,14 @@ fun KtType.replaceTypeParameters(substitution: kotlin.collections.Map<String, Kt
326328 )
327329 }
328330
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+
329339private fun KtCallSignature.replaceTypeParameters (substitution : Map <String , KtType >): KtCallSignature =
330340 copy(
331341 params = params.map { it.replaceTypeParameters(substitution) },
0 commit comments