From 72a1808d40d665b5e84d5c3c537047ef04c7ca17 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Wed, 16 Jun 2021 08:41:39 -0700 Subject: [PATCH 1/4] Initial cut Pushing to a PR for performance testing. This looks extremely slow. --- src/compiler/checker.ts | 113 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 110 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c3b1cd4258bc4..72b5c5e33c4f3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -365,6 +365,93 @@ namespace ts { const argumentsSymbol = createSymbol(SymbolFlags.Property, "arguments" as __String); const requireSymbol = createSymbol(SymbolFlags.Property, "require" as __String); + const fail = (name: string) => () => Debug.fail("Unit kind expected for " + name) + // boolean, bigint, number, string, symbol [literal], boolean, bigint, number, string, symbol, object, union (recursive?!), ..., unknown, never, null, void, undefined, any + const ordre: Array<[TypeFlags, (t1: Type, t2: Type) => Comparison]> = [ + [TypeFlags.BooleanLiteral, t1 => t1 === trueType ? Comparison.LessThan : Comparison.GreaterThan], + [TypeFlags.BigIntLiteral, (t1, t2) => (t1 as BigIntLiteralType).value < (t2 as BigIntLiteralType).value ? Comparison.LessThan : Comparison.GreaterThan], + [TypeFlags.EnumLiteral, (t1, t2) => { + Debug.assert(t1.flags & (TypeFlags.Union | TypeFlags.StringLiteral | TypeFlags.NumberLiteral)) + Debug.assert(t2.flags & (TypeFlags.Union | TypeFlags.StringLiteral | TypeFlags.NumberLiteral)) + if (t1.flags & TypeFlags.Union) { + if (t2.flags & TypeFlags.Union) { + return t1.symbol.escapedName < t2.symbol.escapedName ? Comparison.LessThan : Comparison.GreaterThan + } + return Comparison.LessThan + } + else if (t1.flags & TypeFlags.NumberLiteral) { + if (t2.flags & TypeFlags.Union) + return Comparison.GreaterThan + else if (t2.flags & TypeFlags.StringLiteral) + return Comparison.LessThan + else + return (t1 as NumberLiteralType).value < (t2 as NumberLiteralType).value ? Comparison.LessThan : Comparison.GreaterThan + } + else if (t1.flags & TypeFlags.StringLiteral) { + if (t2.flags & (TypeFlags.Union | TypeFlags.NumberLiteral)) + return Comparison.GreaterThan + else + return (t1 as StringLiteralType).value < (t2 as StringLiteralType).value ? Comparison.LessThan : Comparison.GreaterThan + } + Debug.fail("I was lied to!") + }], + [TypeFlags.NumberLiteral, (t1, t2) => (t1 as NumberLiteralType).value < (t2 as NumberLiteralType).value ? Comparison.LessThan : Comparison.GreaterThan], + [TypeFlags.StringLiteral, (t1, t2) => (t1 as StringLiteralType).value < (t2 as StringLiteralType).value ? Comparison.LessThan : Comparison.GreaterThan], + [TypeFlags.TemplateLiteral, (t1, t2) => (t1 as TemplateLiteralType).texts[0] < (t2 as TemplateLiteralType).texts[0] ? Comparison.LessThan : Comparison.GreaterThan], + [TypeFlags.UniqueESSymbol, (t1, t2) => (t1 as UniqueESSymbolType).escapedName < (t2 as UniqueESSymbolType).escapedName ? Comparison.LessThan : Comparison.GreaterThan], + [TypeFlags.Boolean, fail("boolean")], + [TypeFlags.BigInt, fail("bigint")], + [TypeFlags.Number, fail("number")], + [TypeFlags.String, fail("string")], + [TypeFlags.ESSymbol, fail("essymbol")], + [TypeFlags.Enum, (t1, t2) => { + Debug.assert(t1.symbol && t2.symbol) + return t1.symbol.escapedName < t2.symbol.escapedName ? Comparison.LessThan : Comparison.GreaterThan + }], + [TypeFlags.NonPrimitive, fail("object")], + [TypeFlags.Object, () => Comparison.EqualTo], // TODO: Lots more work to be done here + // Class = 1 << 0, // Class + // Interface = 1 << 1, // Interface + // Reference = 1 << 2, // Generic type reference + // Tuple = 1 << 3, // Synthesized generic tuple type + // Anonymous = 1 << 4, // Anonymous + // Mapped = 1 << 5, // Mapped + // Instantiated = 1 << 6, // Instantiated anonymous or mapped type + // ObjectLiteral = 1 << 7, // Originates in an object literal + // EvolvingArray = 1 << 8, // Evolving array type + // ObjectLiteralPatternWithComputedProperties = 1 << 9, // Object literal pattern with computed properties + // ReverseMapped = 1 << 10, // Object contains a property from a reverse-mapped type + // JsxAttributes = 1 << 11, // Jsx attributes type + // MarkerType = 1 << 12, // Marker type used for variance probing + // JSLiteral = 1 << 13, // Object type declared in JS - disables errors on read/write of nonexisting members + // FreshLiteral = 1 << 14, // Fresh object literal + // ArrayLiteral = 1 << 15, // Originates in an array literal + // /* @internal */ + // PrimitiveUnion = 1 << 16, // Union of only primitive types + // /* @internal */ + // ContainsWideningType = 1 << 17, // Type is or contains undefined or null widening type + // /* @internal */ + // ContainsObjectOrArrayLiteral = 1 << 18, // Type is or contains object literal type + // /* @internal */ + // NonInferrableType = 1 << 19, // Type is or contains anyFunctionType or silentNeverType + // /* @internal */ + // CouldContainTypeVariablesComputed = 1 << 20, // CouldContainTypeVariables flag has been computed + // /* @internal */ + // CouldContainTypeVariables = 1 << 21, // Type could contain a type variable + + [TypeFlags.Union, () => Comparison.EqualTo], // TODO: Recur? Should this even be possible? + [TypeFlags.Intersection, () => Comparison.EqualTo], // TODO: Recur? + // TODO: Everything else + // TemplateLiteral = 1 << 27, // Template literal type + [TypeFlags.Index | TypeFlags.IndexedAccess | TypeFlags.Conditional | TypeFlags.Substitution | TypeFlags.TypeParameter | TypeFlags.StringMapping, () => Comparison.EqualTo], + // TODO: Unit types (and unit kinds) should Debug.fail in their functions. + [TypeFlags.Never, () => Comparison.EqualTo], + [TypeFlags.Unknown, () => Comparison.EqualTo], + [TypeFlags.Null, () => Comparison.EqualTo], + [TypeFlags.Void, () => Comparison.EqualTo], + [TypeFlags.Undefined, () => Comparison.EqualTo], + [TypeFlags.Any, () => Comparison.EqualTo], + ] /** This will be set during calls to `getResolvedSignature` where services determines an apparent number of arguments greater than what is actually provided. */ let apparentArgumentCount: number | undefined; @@ -4710,11 +4797,11 @@ namespace ts { type = (type as UnionType).origin!; } if (type.flags & (TypeFlags.Union | TypeFlags.Intersection)) { - const types = type.flags & TypeFlags.Union ? formatUnionTypes((type as UnionType).types) : (type as IntersectionType).types; + const types = type.flags & TypeFlags.Union ? formatUnionTypes((type as UnionType).types) : (type as IntersectionType).types.slice(); if (length(types) === 1) { return typeToTypeNodeHelper(types[0], context); } - const typeNodes = mapToTypeNodes(types, context, /*isBareList*/ true); + const typeNodes = mapToTypeNodes(types.sort(typeDisplayComparison), context, /*isBareList*/ true); if (typeNodes && typeNodes.length > 0) { return type.flags & TypeFlags.Union ? factory.createUnionTypeNode(typeNodes) : factory.createIntersectionTypeNode(typeNodes); } @@ -7766,7 +7853,27 @@ namespace ts { } if (flags & TypeFlags.Null) result.push(nullType); if (flags & TypeFlags.Undefined) result.push(undefinedType); - return result || types; + return result; + } + + function typeDisplayComparison(t1: Type, t2: Type) { + Debug.assert(t1 !== t2, "shouldn't display the same type twice (or have a union/intersection with duped types)") + let prev: TypeFlags = 0 + for (const [kind, predicate] of ordre) { + if (t1.flags & kind) { + if (t2.flags & prev) { + return Comparison.LessThan + } + else if (t2.flags & kind) { + return predicate(t1, t2) + } + else { + return Comparison.GreaterThan + } + } + prev |= kind + } + return Comparison.EqualTo } function visibilityToString(flags: ModifierFlags): string | undefined { From 43fe78f7705c8556b9e7ec89be5f65243a8fd627 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Mon, 21 Jun 2021 11:35:43 -0700 Subject: [PATCH 2/4] full version is done --- src/compiler/checker.ts | 152 ++++++++++++++++++++++++++++------------ src/compiler/types.ts | 2 +- 2 files changed, 109 insertions(+), 45 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3725c3f320bbd..9787372ee87d9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -393,7 +393,7 @@ namespace ts { else return (t1 as StringLiteralType).value < (t2 as StringLiteralType).value ? Comparison.LessThan : Comparison.GreaterThan } - Debug.fail("I was lied to!") + Debug.fail("bad enum flags combination") }], [TypeFlags.NumberLiteral, (t1, t2) => (t1 as NumberLiteralType).value < (t2 as NumberLiteralType).value ? Comparison.LessThan : Comparison.GreaterThan], [TypeFlags.StringLiteral, (t1, t2) => (t1 as StringLiteralType).value < (t2 as StringLiteralType).value ? Comparison.LessThan : Comparison.GreaterThan], @@ -409,48 +409,52 @@ namespace ts { return t1.symbol.escapedName < t2.symbol.escapedName ? Comparison.LessThan : Comparison.GreaterThan }], [TypeFlags.NonPrimitive, fail("object")], - [TypeFlags.Object, () => Comparison.EqualTo], // TODO: Lots more work to be done here - // Class = 1 << 0, // Class - // Interface = 1 << 1, // Interface - // Reference = 1 << 2, // Generic type reference - // Tuple = 1 << 3, // Synthesized generic tuple type - // Anonymous = 1 << 4, // Anonymous - // Mapped = 1 << 5, // Mapped - // Instantiated = 1 << 6, // Instantiated anonymous or mapped type - // ObjectLiteral = 1 << 7, // Originates in an object literal - // EvolvingArray = 1 << 8, // Evolving array type - // ObjectLiteralPatternWithComputedProperties = 1 << 9, // Object literal pattern with computed properties - // ReverseMapped = 1 << 10, // Object contains a property from a reverse-mapped type - // JsxAttributes = 1 << 11, // Jsx attributes type - // MarkerType = 1 << 12, // Marker type used for variance probing - // JSLiteral = 1 << 13, // Object type declared in JS - disables errors on read/write of nonexisting members - // FreshLiteral = 1 << 14, // Fresh object literal - // ArrayLiteral = 1 << 15, // Originates in an array literal - // /* @internal */ - // PrimitiveUnion = 1 << 16, // Union of only primitive types - // /* @internal */ - // ContainsWideningType = 1 << 17, // Type is or contains undefined or null widening type - // /* @internal */ - // ContainsObjectOrArrayLiteral = 1 << 18, // Type is or contains object literal type - // /* @internal */ - // NonInferrableType = 1 << 19, // Type is or contains anyFunctionType or silentNeverType - // /* @internal */ - // CouldContainTypeVariablesComputed = 1 << 20, // CouldContainTypeVariables flag has been computed - // /* @internal */ - // CouldContainTypeVariables = 1 << 21, // Type could contain a type variable - - [TypeFlags.Union, () => Comparison.EqualTo], // TODO: Recur? Should this even be possible? - [TypeFlags.Intersection, () => Comparison.EqualTo], // TODO: Recur? - // TODO: Everything else - // TemplateLiteral = 1 << 27, // Template literal type - [TypeFlags.Index | TypeFlags.IndexedAccess | TypeFlags.Conditional | TypeFlags.Substitution | TypeFlags.TypeParameter | TypeFlags.StringMapping, () => Comparison.EqualTo], - // TODO: Unit types (and unit kinds) should Debug.fail in their functions. - [TypeFlags.Never, () => Comparison.EqualTo], - [TypeFlags.Unknown, () => Comparison.EqualTo], - [TypeFlags.Null, () => Comparison.EqualTo], - [TypeFlags.Void, () => Comparison.EqualTo], - [TypeFlags.Undefined, () => Comparison.EqualTo], - [TypeFlags.Any, () => Comparison.EqualTo], + [TypeFlags.Object, objectTypeDisplayComparison], + [TypeFlags.Union, (t1, t2) => typeDisplayComparison((t1 as UnionType).types[0], (t2 as UnionType).types[0])], + [TypeFlags.Intersection, (t1, t2) => typeDisplayComparison((t1 as IntersectionType).types[0], (t2 as IntersectionType).types[0])], + [TypeFlags.IndexedAccess, (t1, t2) => typeDisplayComparison((t1 as IndexedAccessType).objectType, (t2 as IndexedAccessType).objectType)], + [TypeFlags.Index, (t1, t2) => typeDisplayComparison((t1 as IndexType).type,(t2 as IndexType).type)], + [TypeFlags.Conditional, (t1, t2) => typeDisplayComparison((t1 as ConditionalType).checkType, (t2 as ConditionalType).checkType)], + [TypeFlags.TypeParameter, (t1, t2) => t1.symbol.escapedName < t2.symbol.escapedName ? Comparison.LessThan : Comparison.GreaterThan], + [TypeFlags.Substitution, (t1, t2) => typeDisplayComparison((t1 as SubstitutionType).baseType, (t2 as SubstitutionType).baseType)], + [TypeFlags.StringMapping, (t1,t2) => { + const k1 = intrinsicTypeKinds.get(t1.symbol.escapedName as string) + const k2 = intrinsicTypeKinds.get(t2.symbol.escapedName as string) + switch (k1) { + case IntrinsicTypeKind.Capitalize: + if (k2 === IntrinsicTypeKind.Capitalize) { + return typeDisplayComparison((t1 as StringMappingType).type, (t2 as StringMappingType).type) + } + else + return Comparison.LessThan + case IntrinsicTypeKind.Lowercase: + if (k2 === IntrinsicTypeKind.Capitalize) + return Comparison.GreaterThan + else if (k2 === IntrinsicTypeKind.Lowercase) + return typeDisplayComparison((t1 as StringMappingType).type, (t2 as StringMappingType).type) + else + return Comparison.LessThan + case IntrinsicTypeKind.Uncapitalize: + if (k2 === IntrinsicTypeKind.Capitalize || k2 === IntrinsicTypeKind.Lowercase) + return Comparison.GreaterThan + else if (k2 === IntrinsicTypeKind.Uncapitalize) + return typeDisplayComparison((t1 as StringMappingType).type, (t2 as StringMappingType).type) + else + return Comparison.LessThan + case IntrinsicTypeKind.Uppercase: + if (k2 === IntrinsicTypeKind.Uppercase) + return typeDisplayComparison((t1 as StringMappingType).type, (t2 as StringMappingType).type) + else + return Comparison.GreaterThan + } + return Comparison.EqualTo + }], + [TypeFlags.Never, fail("never")], + [TypeFlags.Unknown, fail("unknown")], + [TypeFlags.Null, fail("null")], + [TypeFlags.Void, fail("void")], + [TypeFlags.Undefined, fail("undefined")], + [TypeFlags.Any, fail("any")], ] /** This will be set during calls to `getResolvedSignature` where services determines an apparent number of arguments greater than what is actually provided. */ @@ -7858,7 +7862,7 @@ namespace ts { } function typeDisplayComparison(t1: Type, t2: Type) { - Debug.assert(t1 !== t2, "shouldn't display the same type twice (or have a union/intersection with duped types)") + if (t1 === t2) return Comparison.EqualTo let prev: TypeFlags = 0 for (const [kind, predicate] of ordre) { if (t1.flags & kind) { @@ -7877,6 +7881,66 @@ namespace ts { return Comparison.EqualTo } + /** + * Named before unnamed, sort names alphabetically + * Tuples after non-tuples, sort tuples by length, or by first type if they have the same length. (no change if both have length 0) + * (TODO: Should take named tuples' names into account) + * Everything else sorts by number of properties, then by name of the first property + */ + function objectTypeDisplayComparison(originalt1: ObjectType, originalt2: ObjectType) { + if (originalt1 === originalt2) return Comparison.EqualTo + const t1 = getObjectFlags(originalt1) & ObjectFlags.Reference ? (originalt1 as TypeReference).target : originalt1 + const t2 = getObjectFlags(originalt2) & ObjectFlags.Reference ? (originalt2 as TypeReference).target : originalt2 + const name1 = t1.symbol?.escapedName + const name2 = t2.symbol?.escapedName + if (name1 !== undefined && name2 !== undefined) + return name1 < name2 ? Comparison.LessThan : Comparison.GreaterThan + else if (name1 === undefined && name2 !== undefined) + return Comparison.GreaterThan + else if (name1 !== undefined && name2 === undefined) + return Comparison.LessThan + const f1 = getObjectFlags(t1) + const f2 = getObjectFlags(t2) + if (f1 & ObjectFlags.Tuple && f2 & ObjectFlags.Tuple) { + // number of properties, then recur on first property (if length non-zero) + const length1 = getTypeReferenceArity(originalt1 as TypeReference) + const length2 = getTypeReferenceArity(originalt2 as TypeReference) + if (length1 === length2) { + const args1 = getTypeArguments(originalt1 as TypeReference) + const args2 = getTypeArguments(originalt2 as TypeReference) + if (args1.length && args2.length) { + return typeDisplayComparison(args1[0], args1[0]) + } + else + return args1.length ? Comparison.LessThan : Comparison.GreaterThan + + } + else + return length1 < length2 ? Comparison.LessThan : Comparison.GreaterThan + } + else if (f1 & ObjectFlags.Tuple) + return Comparison.GreaterThan + else if (f2 & ObjectFlags.Tuple) + return Comparison.LessThan + + const pcount1 = t1.properties?.length ?? 0 + const pcount2 = t2.properties?.length ?? 0 + if (pcount1 === 0 && pcount2 === 0) // ??? + return Comparison.EqualTo + else if (pcount1 === 0) + return Comparison.LessThan + else if (pcount2 === 0) + return Comparison.GreaterThan + const firstp1 = t1.properties![0].escapedName + const firstp2 = t2.properties![0].escapedName + if (firstp1 < firstp2) + return Comparison.LessThan + if (firstp2 > firstp1) + return Comparison.GreaterThan + else + return t1.properties!.length < t2.properties!.length ? Comparison.LessThan : Comparison.GreaterThan + } + function visibilityToString(flags: ModifierFlags): string | undefined { if (flags === ModifierFlags.Private) { return "private"; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 5e4a66dc61328..6bc41dc480d64 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5595,7 +5595,7 @@ namespace ts { } export interface StringMappingType extends InstantiableType { - symbol: Symbol; + symbol: Symbol; // TODO: Not needed, inherited from Type type: Type; } From 8de10a5c3ca61cd6316c9a285dde1b441069a040 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Mon, 21 Jun 2021 13:29:07 -0700 Subject: [PATCH 3/4] update a few fourslash tests --- tests/cases/fourslash/quickInfoCanBeTruncated.ts | 2 +- tests/cases/fourslash/server/quickinfoWrongComment.ts | 2 +- tests/cases/fourslash/thisPredicateFunctionQuickInfo01.ts | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/cases/fourslash/quickInfoCanBeTruncated.ts b/tests/cases/fourslash/quickInfoCanBeTruncated.ts index 0ee7ebba1e74c..e659e25986afb 100644 --- a/tests/cases/fourslash/quickInfoCanBeTruncated.ts +++ b/tests/cases/fourslash/quickInfoCanBeTruncated.ts @@ -515,7 +515,7 @@ goTo.marker("1"); verify.quickInfoIs(`type A = keyof Foo`); goTo.marker("2"); -verify.quickInfoIs(`type Less = "_1" | "_2" | "_3" | "_4" | "_5" | "_6" | "_7" | "_8" | "_9" | "_10" | "_11" | "_12" | "_13" | "_14" | "_15" | "_16" | "_17" | "_18" | "_19" | "_20" | "_21" | "_22" | "_23" | "_24" | "_25" | ... 473 more ... | "_499"`); +verify.quickInfoIs(`type Less = "_1" | "_10" | "_100" | "_101" | "_102" | "_103" | "_104" | "_105" | "_106" | "_107" | "_108" | "_109" | "_11" | "_110" | "_111" | "_112" | "_113" | "_114" | "_115" | "_116" | "_117" | ... 477 more ... | "_99"`); goTo.marker("3"); verify.signatureHelp({ marker: "3", diff --git a/tests/cases/fourslash/server/quickinfoWrongComment.ts b/tests/cases/fourslash/server/quickinfoWrongComment.ts index 9f96d2c665aea..a36322db1f50d 100644 --- a/tests/cases/fourslash/server/quickinfoWrongComment.ts +++ b/tests/cases/fourslash/server/quickinfoWrongComment.ts @@ -15,4 +15,4 @@ //// f.colour/*1*/ goTo.marker("1") -verify.quickInfoIs("(property) colour: \"red\" | \"green\" | \"yellow\"", "The colour") \ No newline at end of file +verify.quickInfoIs("(property) colour: \"green\" | \"red\" | \"yellow\"", "The colour") diff --git a/tests/cases/fourslash/thisPredicateFunctionQuickInfo01.ts b/tests/cases/fourslash/thisPredicateFunctionQuickInfo01.ts index 153e5aa639066..b5b1a7ce41d77 100644 --- a/tests/cases/fourslash/thisPredicateFunctionQuickInfo01.ts +++ b/tests/cases/fourslash/thisPredicateFunctionQuickInfo01.ts @@ -43,11 +43,11 @@ verify.quickInfos({ 1: "(method) FileSystemObject.isFile(): this is Item", 2: "(method) FileSystemObject.isDirectory(): this is Directory", - 3: "(method) FileSystemObject.isNetworked(): this is Networked & this", + 3: "(method) FileSystemObject.isNetworked(): this is this & Networked", 4: "(method) FileSystemObject.isFile(): this is Item", - 5: "(method) FileSystemObject.isNetworked(): this is Networked & Item", + 5: "(method) FileSystemObject.isNetworked(): this is Item & Networked", 6: "(method) FileSystemObject.isDirectory(): this is Directory", - 7: "(method) FileSystemObject.isNetworked(): this is Networked & Directory", - 8: "(method) FileSystemObject.isNetworked(): this is Networked & FileSystemObject" + 7: "(method) FileSystemObject.isNetworked(): this is Directory & Networked", + 8: "(method) FileSystemObject.isNetworked(): this is FileSystemObject & Networked" }); From af6068d7892fe7439448f41d982dca0416a2bd3e Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Tue, 22 Jun 2021 07:30:22 -0700 Subject: [PATCH 4/4] stop sorting intersections --- src/compiler/checker.ts | 6 +++--- tests/cases/fourslash/thisPredicateFunctionQuickInfo01.ts | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9787372ee87d9..99ffcc5ce448d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4802,11 +4802,11 @@ namespace ts { type = (type as UnionType).origin!; } if (type.flags & (TypeFlags.Union | TypeFlags.Intersection)) { - const types = type.flags & TypeFlags.Union ? formatUnionTypes((type as UnionType).types) : (type as IntersectionType).types.slice(); + const types = type.flags & TypeFlags.Union ? formatUnionTypes((type as UnionType).types) : (type as IntersectionType).types; if (length(types) === 1) { return typeToTypeNodeHelper(types[0], context); } - const typeNodes = mapToTypeNodes(types.sort(typeDisplayComparison), context, /*isBareList*/ true); + const typeNodes = mapToTypeNodes(types, context, /*isBareList*/ true); if (typeNodes && typeNodes.length > 0) { return type.flags & TypeFlags.Union ? factory.createUnionTypeNode(typeNodes) : factory.createIntersectionTypeNode(typeNodes); } @@ -7858,7 +7858,7 @@ namespace ts { } if (flags & TypeFlags.Null) result.push(nullType); if (flags & TypeFlags.Undefined) result.push(undefinedType); - return result; + return result.sort(typeDisplayComparison); } function typeDisplayComparison(t1: Type, t2: Type) { diff --git a/tests/cases/fourslash/thisPredicateFunctionQuickInfo01.ts b/tests/cases/fourslash/thisPredicateFunctionQuickInfo01.ts index b5b1a7ce41d77..153e5aa639066 100644 --- a/tests/cases/fourslash/thisPredicateFunctionQuickInfo01.ts +++ b/tests/cases/fourslash/thisPredicateFunctionQuickInfo01.ts @@ -43,11 +43,11 @@ verify.quickInfos({ 1: "(method) FileSystemObject.isFile(): this is Item", 2: "(method) FileSystemObject.isDirectory(): this is Directory", - 3: "(method) FileSystemObject.isNetworked(): this is this & Networked", + 3: "(method) FileSystemObject.isNetworked(): this is Networked & this", 4: "(method) FileSystemObject.isFile(): this is Item", - 5: "(method) FileSystemObject.isNetworked(): this is Item & Networked", + 5: "(method) FileSystemObject.isNetworked(): this is Networked & Item", 6: "(method) FileSystemObject.isDirectory(): this is Directory", - 7: "(method) FileSystemObject.isNetworked(): this is Directory & Networked", - 8: "(method) FileSystemObject.isNetworked(): this is FileSystemObject & Networked" + 7: "(method) FileSystemObject.isNetworked(): this is Networked & Directory", + 8: "(method) FileSystemObject.isNetworked(): this is Networked & FileSystemObject" });