Make special intersections order-independent (#52782)

This commit is contained in:
Mateusz Burzyński 2023-03-08 00:39:14 +01:00 committed by GitHub
parent 3f4d16a25e
commit 88adf8014b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 25 additions and 9 deletions

View File

@ -16766,12 +16766,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return reduceLeft(types, (n, t) => n + getConstituentCount(t), 0);
}
function areIntersectedTypesAvoidingPrimitiveReduction(t1: Type, t2: Type) {
return !!(t1.flags & (TypeFlags.String | TypeFlags.Number | TypeFlags.BigInt)) && t2 === emptyTypeLiteralType;
}
function getTypeFromIntersectionTypeNode(node: IntersectionTypeNode): Type {
const links = getNodeLinks(node);
if (!links.resolvedType) {
const aliasSymbol = getAliasSymbolForTypeNode(node);
const types = map(node.types, getTypeFromTypeNode);
const noSupertypeReduction = types.length === 2 && !!(types[0].flags & (TypeFlags.String | TypeFlags.Number | TypeFlags.BigInt)) && types[1] === emptyTypeLiteralType;
const noSupertypeReduction = types.length === 2 && (areIntersectedTypesAvoidingPrimitiveReduction(types[0], types[1]) || areIntersectedTypesAvoidingPrimitiveReduction(types[1], types[0]));
links.resolvedType = getIntersectionType(types, aliasSymbol, getTypeArgumentsForAliasSymbol(aliasSymbol), noSupertypeReduction);
}
return links.resolvedType;

View File

@ -2165,6 +2165,10 @@ export function isStringOrRegularExpressionOrTemplateLiteral(kind: SyntaxKind):
return false;
}
function areIntersectedTypesAvoidingStringReduction(checker: TypeChecker, t1: Type, t2: Type) {
return !!(t1.flags & TypeFlags.String) && checker.isEmptyAnonymousObjectType(t2);
}
/** @internal */
export function isStringAndEmptyAnonymousObjectIntersection(type: Type) {
if (!type.isIntersection()) {
@ -2172,8 +2176,8 @@ export function isStringAndEmptyAnonymousObjectIntersection(type: Type) {
}
const { types, checker } = type;
return types.length === 2
&& (types[0].flags & TypeFlags.String) && checker.isEmptyAnonymousObjectType(types[1]);
return types.length === 2 &&
(areIntersectedTypesAvoidingStringReduction(checker, types[0], types[1]) || areIntersectedTypesAvoidingStringReduction(checker, types[1], types[0]));
}
/** @internal */

View File

@ -6,7 +6,7 @@ tests/cases/conformance/types/unknown/unknownControlFlow.ts(293,5): error TS2345
==== tests/cases/conformance/types/unknown/unknownControlFlow.ts (5 errors) ====
type T01 = {} & string; // string
type T01 = {} & string; // {} & string
type T02 = {} & 'a'; // 'a'
type T03 = {} & object; // object
type T04 = {} & { x: number }; // { x: number }

View File

@ -1,5 +1,5 @@
//// [unknownControlFlow.ts]
type T01 = {} & string; // string
type T01 = {} & string; // {} & string
type T02 = {} & 'a'; // 'a'
type T03 = {} & object; // object
type T04 = {} & { x: number }; // { x: number }

View File

@ -1,5 +1,5 @@
=== tests/cases/conformance/types/unknown/unknownControlFlow.ts ===
type T01 = {} & string; // string
type T01 = {} & string; // {} & string
>T01 : Symbol(T01, Decl(unknownControlFlow.ts, 0, 0))
type T02 = {} & 'a'; // 'a'

View File

@ -1,6 +1,6 @@
=== tests/cases/conformance/types/unknown/unknownControlFlow.ts ===
type T01 = {} & string; // string
>T01 : string
type T01 = {} & string; // {} & string
>T01 : {} & string
type T02 = {} & 'a'; // 'a'
>T02 : "a"

View File

@ -1,7 +1,7 @@
// @strict: true
// @declaration: true
type T01 = {} & string; // string
type T01 = {} & string; // {} & string
type T02 = {} & 'a'; // 'a'
type T03 = {} & object; // object
type T04 = {} & { x: number }; // { x: number }

View File

@ -0,0 +1,8 @@
/// <reference path="fourslash.ts" />
//
//// declare function a(arg: 'test' | (string & {})): void
//// a('/*1*/')
//// declare function b(arg: 'test' | ({} & string)): void
//// b('/*2*/')
verify.completions({ marker: ["1", "2"], exact: ["test"] });