diff --git a/core/src/main/scala/org/scalablytyped/converter/internal/ts/TsTypeFormatter.scala b/core/src/main/scala/org/scalablytyped/converter/internal/ts/TsTypeFormatter.scala index 6ab8fe9ad5..67acec912c 100644 --- a/core/src/main/scala/org/scalablytyped/converter/internal/ts/TsTypeFormatter.scala +++ b/core/src/main/scala/org/scalablytyped/converter/internal/ts/TsTypeFormatter.scala @@ -125,7 +125,8 @@ class TsTypeFormatter(val keepComments: Boolean) { case TsTypeLiteral(l) => lit(l) case TsTypeObject(cs, members) => Comments.format(cs, keepComments) + s"{${members.map(member).mkString(", ")}}" case TsTypeFunction(s) => s"${sig(s)}" - case TsTypeConstructor(f) => s"new ${apply(f)}" + case TsTypeConstructor(true, f) => s"abstract new ${apply(f)}" + case TsTypeConstructor(false, f) => s"new ${apply(f)}" case TsTypeIs(ident, x) => s"${ident.value} is ${apply(x)}" case TsTypeTuple(elems) => s"[${elems.map(tupleElement).mkString(", ")}]" case TsTypeQuery(expr) => s"typeof ${qident(expr)}" diff --git a/core/src/main/scala/org/scalablytyped/converter/internal/ts/trees.scala b/core/src/main/scala/org/scalablytyped/converter/internal/ts/trees.scala index 1928dab1a2..42530f4abe 100644 --- a/core/src/main/scala/org/scalablytyped/converter/internal/ts/trees.scala +++ b/core/src/main/scala/org/scalablytyped/converter/internal/ts/trees.scala @@ -551,7 +551,7 @@ final case class TsTypeObject(comments: Comments, members: IArray[TsMember]) ext final case class TsTypeFunction(signature: TsFunSig) extends TsType -final case class TsTypeConstructor(signature: TsTypeFunction) extends TsType +final case class TsTypeConstructor(isAbstract: Boolean, signature: TsTypeFunction) extends TsType final case class TsTypeIs(ident: TsIdent, tpe: TsType) extends TsType diff --git a/importer-portable/src/main/scala/org/scalablytyped/converter/internal/importer/ImportType.scala b/importer-portable/src/main/scala/org/scalablytyped/converter/internal/importer/ImportType.scala index 98beb9c1db..4b7533a1a8 100644 --- a/importer-portable/src/main/scala/org/scalablytyped/converter/internal/importer/ImportType.scala +++ b/importer-portable/src/main/scala/org/scalablytyped/converter/internal/importer/ImportType.scala @@ -197,7 +197,7 @@ class ImportType(stdNames: QualifiedName.StdNames) { case TsTypeIntersect(types) => TypeRef.Intersection(types.map(apply(Wildcards.No, scope, importName)), NoComments) - case TsTypeConstructor(TsTypeFunction(sig)) => + case TsTypeConstructor(_, TsTypeFunction(sig)) => newableFunction(scope, importName, sig, NoComments) case keyof @ TsTypeKeyOf(of) => diff --git a/importer/src/test/scala/org/scalablytyped/converter/internal/ts/parser/ParserTests.scala b/importer/src/test/scala/org/scalablytyped/converter/internal/ts/parser/ParserTests.scala index d9949ff3a0..04f807fc7c 100644 --- a/importer/src/test/scala/org/scalablytyped/converter/internal/ts/parser/ParserTests.scala +++ b/importer/src/test/scala/org/scalablytyped/converter/internal/ts/parser/ParserTests.scala @@ -389,6 +389,7 @@ final class ParserTests extends AnyFunSuite { TsIdentSimple("ActionsClassConstructor"), Empty, TsTypeConstructor( + isAbstract = false, TsTypeFunction( TsFunSig( NoComments, @@ -2038,6 +2039,7 @@ type Readonly = { TsIdentSimple("T"), Some( TsTypeConstructor( + isAbstract = false, TsTypeFunction( TsFunSig( NoComments, @@ -2061,6 +2063,7 @@ type Readonly = { TsTypeExtends( T, TsTypeConstructor( + isAbstract = false, TsTypeFunction( TsFunSig( NoComments, @@ -2856,7 +2859,7 @@ export {}; shouldParseAs(content, TsParser.tsDeclFunction)( TsDeclFunction( NoComments, - true, + declared = true, TsIdentSimple("isNotTestHost"), TsFunSig( NoComments, @@ -2881,7 +2884,7 @@ export {}; shouldParseAs(content, TsParser.tsDeclFunction)( TsDeclFunction( NoComments, - true, + declared = true, TsIdentSimple("foo"), TsFunSig( NoComments, @@ -2900,4 +2903,62 @@ export {}; ), ) } + + test("T extends abstract new") { + val content = + "type ConstructorParameters any> = T extends abstract new (...args: infer P) => any ? P : never" + + shouldParseAs(content, TsParser.tsDeclTypeAlias)( + TsDeclTypeAlias( + NoComments, + declared = false, + TsIdentSimple("ConstructorParameters"), + IArray( + TsTypeParam( + NoComments, + TsIdentSimple("T"), + Some( + TsTypeConstructor( + isAbstract = true, + TsTypeFunction( + TsFunSig( + NoComments, + IArray(), + IArray(TsFunParam(NoComments, TsIdentSimple("args"), Some(TsTypeRef.any))), + Some(TsTypeRef.any), + ), + ), + ), + ), + None, + ), + ), + TsTypeConditional( + TsTypeExtends( + T, + TsTypeConstructor( + isAbstract = true, + TsTypeFunction( + TsFunSig( + NoComments, + IArray(), + IArray( + TsFunParam( + NoComments, + TsIdentSimple("any"), + Some(TsTypeInfer(TsTypeParam(NoComments, TsIdentSimple("P"), None, None))), + ), + ), + Some(TsTypeRef.any), + ), + ), + ), + ), + TsTypeRef(NoComments, TsQIdent(IArray(TsIdentSimple("P"))), IArray()), + TsTypeRef.never, + ), + CodePath.NoPath, + ), + ) + } } diff --git a/ts/src/main/scala/org/scalablytyped/converter/internal/ts/TreeTransformation.scala b/ts/src/main/scala/org/scalablytyped/converter/internal/ts/TreeTransformation.scala index 488c71735f..62c7b68dda 100644 --- a/ts/src/main/scala/org/scalablytyped/converter/internal/ts/TreeTransformation.scala +++ b/ts/src/main/scala/org/scalablytyped/converter/internal/ts/TreeTransformation.scala @@ -349,7 +349,7 @@ trait TreeTransformation[T] { self => val xx = enterTsTypeConstructor(withTree(t, x))(x) val tt = withTree(t, xx) xx match { - case TsTypeConstructor(_1) => TsTypeConstructor(visitTsTypeFunction(tt)(_1)) + case TsTypeConstructor(_1, _2) => TsTypeConstructor(_1, visitTsTypeFunction(tt)(_2)) } } final def visitTsTypeConditional(t: T)(x: TsTypeConditional): TsTypeConditional = { diff --git a/ts/src/main/scala/org/scalablytyped/converter/internal/ts/parser/TsParser.scala b/ts/src/main/scala/org/scalablytyped/converter/internal/ts/parser/TsParser.scala index 52d6ddbe9a..1d524a903f 100644 --- a/ts/src/main/scala/org/scalablytyped/converter/internal/ts/parser/TsParser.scala +++ b/ts/src/main/scala/org/scalablytyped/converter/internal/ts/parser/TsParser.scala @@ -446,7 +446,7 @@ class TsParser(path: Option[(os.Path, Int)]) extends StdTokenParsers with Parser ((tsIdent <~ "is") ~ tsType ^^ TsTypeIs | comments ~ tsMembers ^^ TsTypeObject | tsTypeFunction - | "new" ~> tsTypeFunction ^^ TsTypeConstructor + | ("abstract".isDefined <~ "new") ~ tsTypeFunction ^^ TsTypeConstructor | "unique" ~> "symbol" ~> success(TsTypeRef(NoComments, TsQIdent.symbol, Empty)) | "typeof" ~> qualifiedIdent ^^ TsTypeQuery | tsTypeTuple diff --git a/ts/src/main/scala/org/scalablytyped/converter/internal/ts/transforms/ExtractClasses.scala b/ts/src/main/scala/org/scalablytyped/converter/internal/ts/transforms/ExtractClasses.scala index 0d2b4cd011..f68d625df7 100644 --- a/ts/src/main/scala/org/scalablytyped/converter/internal/ts/transforms/ExtractClasses.scala +++ b/ts/src/main/scala/org/scalablytyped/converter/internal/ts/transforms/ExtractClasses.scala @@ -188,8 +188,8 @@ object ExtractClasses extends TransformLeaveMembers { } FollowAliases(scope)(tpe) match { - case TsTypeIntersect(types) => types.flatMap(findCtors(scope, loopDetector)) - case TsTypeConstructor(TsTypeFunction(sig)) => IArray(sig) + case TsTypeIntersect(types) => types.flatMap(findCtors(scope, loopDetector)) + case TsTypeConstructor(_, TsTypeFunction(sig)) => IArray(sig) case tr: TsTypeRef => loopDetector.including(tr, scope) match { case Left(()) => Empty diff --git a/ts/src/main/scala/org/scalablytyped/converter/internal/ts/transforms/ResolveTypeQueries.scala b/ts/src/main/scala/org/scalablytyped/converter/internal/ts/transforms/ResolveTypeQueries.scala index c81f3e79e1..2c4c91b734 100644 --- a/ts/src/main/scala/org/scalablytyped/converter/internal/ts/transforms/ResolveTypeQueries.scala +++ b/ts/src/main/scala/org/scalablytyped/converter/internal/ts/transforms/ResolveTypeQueries.scala @@ -123,6 +123,7 @@ object ResolveTypeQueries extends TransformMembers with TransformLeaveClassMembe object RewrittenClass { def asTypeCtor(cls: TsDeclClass, cs: Comments, params: IArray[TsFunParam]) = TsTypeConstructor( + isAbstract = false, TsTypeFunction( TsFunSig( comments = cs,