Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Improve intersection reduction and CFA for truthy, equality, and typeof checks#49119

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
ahejlsberg merged 32 commits intomainfromimproveIntersectionReduction
May 27, 2022
Merged
Changes from1 commit
Commits
Show all changes
32 commits
Select commitHold shift + click to select a range
ce23b8d
Improve reduction of intersection types
ahejlsbergMay 5, 2022
ffe147e
Accept new baselines
ahejlsbergMay 5, 2022
a95de48
Merge branch 'main' into improveIntersectionReduction
ahejlsbergMay 13, 2022
29edef1
Improve CFA for truthy, equality, and typeof checks
ahejlsbergMay 15, 2022
39326d7
Accept new baselines
ahejlsbergMay 15, 2022
d0b7ec8
Remove special case for Function type
ahejlsbergMay 15, 2022
a25388d
Merge branch 'main' into improveIntersectionReduction
ahejlsbergMay 15, 2022
95c3c56
Don't reduce intersections of form {...} & object
ahejlsbergMay 15, 2022
986963c
Accept new baselines
ahejlsbergMay 15, 2022
c400181
Anything is assignable to unknown-like union
ahejlsbergMay 16, 2022
f1ebcdd
Accept new baselines
ahejlsbergMay 16, 2022
73c91fd
Tweak subtype check
ahejlsbergMay 16, 2022
722c462
Recombine unknown type from unknown-like union in more cases
ahejlsbergMay 17, 2022
5e1111a
Display union origin only if it is shorter than union itself
ahejlsbergMay 18, 2022
5c579a4
Accept new baselines
ahejlsbergMay 18, 2022
8913cf0
Add tests
ahejlsbergMay 18, 2022
dbce210
Only attach origin type when it is shorter than union itself
ahejlsbergMay 19, 2022
1913c94
Specially preserve string & {}, number & {}, bigint & {}
ahejlsbergMay 20, 2022
8c7a38b
Accept new baselines
ahejlsbergMay 20, 2022
6f18e90
Add additional tests
ahejlsbergMay 20, 2022
e70bf7c
Fix getNormalizedType and getNarrowableTypeForReference for intersect…
ahejlsbergMay 20, 2022
7a62983
Switch NonNullable<T> to use T & {}
ahejlsbergMay 20, 2022
c35cba7
Accept new baselines
ahejlsbergMay 20, 2022
a0c0929
Use NonNullable<T> in place of anonymous T & {}
ahejlsbergMay 20, 2022
eff3be3
Accept new baselines
ahejlsbergMay 20, 2022
d3eb84f
Add fourslash test
ahejlsbergMay 24, 2022
954e80a
More fourslash tests
ahejlsbergMay 24, 2022
1d9b53f
Fix getFalsyFlags handling of intersections
ahejlsbergMay 26, 2022
3815934
Accept new baselines
ahejlsbergMay 26, 2022
bfa3a90
Add constraint to compareProperties type parameter
ahejlsbergMay 26, 2022
9801667
Unconstrained type parameter not assignable to {} with strictNullChecks
ahejlsbergMay 26, 2022
51da289
Accept new baselines
ahejlsbergMay 26, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
PrevPrevious commit
NextNext commit
Improve CFA for truthy, equality, and typeof checks
  • Loading branch information
@ahejlsberg
ahejlsberg committedMay 15, 2022
commit29edef115cc190ab5df15e11fb50a52e0a8a8e7c
79 changes: 48 additions & 31 deletionssrc/compiler/checker.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -848,6 +848,8 @@ namespace ts {
emptyTypeLiteralSymbol.members = createSymbolTable();
const emptyTypeLiteralType = createAnonymousType(emptyTypeLiteralSymbol, emptySymbols, emptyArray, emptyArray, emptyArray);

const unknownUnionType = strictNullChecks ? getUnionType([undefinedType, nullType, createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, emptyArray)]) : unknownType;

const emptyGenericType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, emptyArray) as ObjectType as GenericType;
emptyGenericType.instantiations = new Map<string, TypeReference>();

Expand DownExpand Up@@ -14835,6 +14837,7 @@ namespace ts {
t.flags & TypeFlags.Number && includes & TypeFlags.NumberLiteral ||
t.flags & TypeFlags.BigInt && includes & TypeFlags.BigIntLiteral ||
t.flags & TypeFlags.ESSymbol && includes & TypeFlags.UniqueESSymbol ||
t.flags & TypeFlags.Void && includes & TypeFlags.Undefined ||
t.flags & TypeFlags.NonPrimitive && includes & TypeFlags.Object ||
isEmptyAnonymousObjectType(t) && includes & TypeFlags.DefinitelyNonNullable;
if (remove) {
Expand DownExpand Up@@ -14998,6 +15001,7 @@ namespace ts {
includes & TypeFlags.Number && includes & TypeFlags.NumberLiteral ||
includes & TypeFlags.BigInt && includes & TypeFlags.BigIntLiteral ||
includes & TypeFlags.ESSymbol && includes & TypeFlags.UniqueESSymbol ||
includes & TypeFlags.Void && includes & TypeFlags.Undefined ||
includes & TypeFlags.NonPrimitive && includes & TypeFlags.Object ||
includes & TypeFlags.IncludesEmptyObject && includes & TypeFlags.DefinitelyNonNullable) {
removeRedundantSupertypes(typeSet, includes);
Expand DownExpand Up@@ -18183,7 +18187,7 @@ namespace ts {
// Since unions and intersections may reduce to `never`, we exclude them here.
if (s & TypeFlags.Undefined && (!strictNullChecks && !(t & TypeFlags.UnionOrIntersection) || t & (TypeFlags.Undefined | TypeFlags.Void))) return true;
if (s & TypeFlags.Null && (!strictNullChecks && !(t & TypeFlags.UnionOrIntersection) || t & TypeFlags.Null)) return true;
if (s & TypeFlags.Object && t & TypeFlags.NonPrimitive) return true;
if (s & TypeFlags.Object && t & TypeFlags.NonPrimitive && !(relation === strictSubtypeRelation && isEmptyAnonymousObjectType(source))) return true;
if (relation === assignableRelation || relation === comparableRelation) {
if (s & TypeFlags.Any) return true;
// Type number or any numeric literal type is assignable to any numeric enum type or any
Expand DownExpand Up@@ -23505,6 +23509,24 @@ namespace ts {
return filterType(type, t => (getTypeFacts(t) & include) !== 0);
}

function getIntersectionWithFacts(type: Type, facts: TypeFacts) {
const reduced = getTypeWithFacts(strictNullChecks && type.flags & TypeFlags.Unknown ? unknownUnionType : type, facts);
if (strictNullChecks) {
switch (facts) {
case TypeFacts.NEUndefined:
const emptyOrNull = maybeTypeOfKind(reduced, TypeFlags.Null) ? emptyObjectType : getUnionType([emptyObjectType, nullType]);
return mapType(reduced, t => getTypeFacts(t) & TypeFacts.EQUndefined ? getIntersectionType([t, getTypeFacts(t) & TypeFacts.EQNull ? emptyOrNull : emptyObjectType]): t);
case TypeFacts.NENull:
const emptyOrUndefined = maybeTypeOfKind(reduced, TypeFlags.Undefined) ? emptyObjectType : getUnionType([emptyObjectType, undefinedType]);
return mapType(reduced, t => getTypeFacts(t) & TypeFacts.EQNull ? getIntersectionType([t, getTypeFacts(t) & TypeFacts.EQUndefined ? emptyOrUndefined : emptyObjectType]): t);
case TypeFacts.NEUndefinedOrNull:
case TypeFacts.Truthy:
return mapType(reduced, t => getTypeFacts(t) & TypeFacts.EQUndefinedOrNull ? getIntersectionType([t, emptyObjectType]): t);
}
}
return reduced;
}

function getTypeWithDefault(type: Type, defaultExpression: Expression) {
return defaultExpression ?
getUnionType([getNonUndefinedType(type), getTypeOfExpression(defaultExpression)]) :
Expand DownExpand Up@@ -24637,6 +24659,9 @@ namespace ts {
return getEvolvingArrayType(getUnionType(map(types, getElementTypeOfEvolvingArrayType)));
}
const result = getUnionType(sameMap(types, finalizeEvolvingArrayType), subtypeReduction);
if (result === unknownUnionType) {
return unknownType;
}
if (result !== declaredType && result.flags & declaredType.flags & TypeFlags.Union && arraysEqual((result as UnionType).types, (declaredType as UnionType).types)) {
return declaredType;
}
Expand DownExpand Up@@ -24744,8 +24769,7 @@ namespace ts {

function narrowTypeByTruthiness(type: Type, expr: Expression, assumeTrue: boolean): Type {
if (isMatchingReference(reference, expr)) {
return type.flags & TypeFlags.Unknown && assumeTrue ? nonNullUnknownType :
getTypeWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy);
return getIntersectionWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy);
}
if (strictNullChecks && assumeTrue && optionalChainContainsReference(expr, reference)) {
type = getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull);
Expand DownExpand Up@@ -24905,9 +24929,6 @@ namespace ts {
assumeTrue = !assumeTrue;
}
const valueType = getTypeOfExpression(value);
if (assumeTrue && (type.flags & TypeFlags.Unknown) && (operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsToken) && (valueType.flags & TypeFlags.Null)) {
return getUnionType([nullType, undefinedType]);
}
if ((type.flags & TypeFlags.Unknown) && assumeTrue && (operator === SyntaxKind.EqualsEqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken)) {
if (valueType.flags & (TypeFlags.Primitive | TypeFlags.NonPrimitive)) {
return valueType;
Expand All@@ -24927,7 +24948,7 @@ namespace ts {
valueType.flags & TypeFlags.Null ?
assumeTrue ? TypeFacts.EQNull : TypeFacts.NENull :
assumeTrue ? TypeFacts.EQUndefined : TypeFacts.NEUndefined;
returntype.flags & TypeFlags.Unknown && facts & (TypeFacts.NENull | TypeFacts.NEUndefinedOrNull) ? nonNullUnknownType : getTypeWithFacts(type, facts);
returngetIntersectionWithFacts(type, facts);
}
if (assumeTrue) {
const filterFn: (t: Type) => boolean = operator === SyntaxKind.EqualsEqualsToken ?
Expand DownExpand Up@@ -24956,17 +24977,11 @@ namespace ts {
if (type.flags & TypeFlags.Any && literal.text === "function") {
return type;
}
if (assumeTrue && type.flags & TypeFlags.Unknown && literal.text === "object") {
// The non-null unknown type is used to track whether a previous narrowing operation has removed the null type
// from the unknown type. For example, the expression `x && typeof x === 'object'` first narrows x to the non-null
// unknown type, and then narrows that to the non-primitive type.
return type === nonNullUnknownType ? nonPrimitiveType : getUnionType([nonPrimitiveType, nullType]);
}
const facts = assumeTrue ?
typeofEQFacts.get(literal.text) || TypeFacts.TypeofEQHostObject :
typeofNEFacts.get(literal.text) || TypeFacts.TypeofNEHostObject;
const impliedType = getImpliedTypeFromTypeofGuard(type, literal.text);
return getTypeWithFacts(assumeTrue && impliedType ?mapType(type,narrowUnionMemberByTypeof(impliedType)) : type, facts);
return getTypeWithFacts(assumeTrue && impliedType ?narrowTypeByImpliedType(type, impliedType) : type, facts);
}

function narrowTypeBySwitchOptionalChainContainment(type: Type, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number, clauseCheck: (type: Type) => boolean) {
Expand DownExpand Up@@ -25019,10 +25034,10 @@ namespace ts {

function getImpliedTypeFromTypeofGuard(type: Type, text: string) {
switch (text) {
case "object":
return type.flags & TypeFlags.Any ? type : getUnionType([nullType, nonPrimitiveType]);
case "function":
return type.flags & TypeFlags.Any ? type : globalFunctionType;
case "object":
return type.flags & TypeFlags.Unknown ? getUnionType([nonPrimitiveType, nullType]) : type;
default:
return typeofTypesByName.get(text);
}
Expand All@@ -25034,22 +25049,24 @@ namespace ts {
// the guard. For example: narrowing `{} | undefined` by `"boolean"` should produce the type `boolean`, not
// the filtered type `{}`. For this reason we narrow constituents of the union individually, in addition to
// filtering by type-facts.
functionnarrowUnionMemberByTypeof(candidate: Type) {
return (type: Type) => {
if (isTypeSubtypeOf(type,candidate)) {
return type;
}
if (isTypeSubtypeOf(candidate,type)) {
returncandidate;
functionnarrowTypeByImpliedType(type: Type,candidate: Type) {
if (type.flags & TypeFlags.AnyOrUnknown) {
returncandidate;
}
return mapType(type, t => {
if (isTypeRelatedTo(t,candidate,strictSubtypeRelation)) {
returnt;
}
if (type.flags & TypeFlags.Instantiable) {
const constraint = getBaseConstraintOfType(type) || anyType;
if (isTypeSubtypeOf(candidate, constraint)) {
return getIntersectionType([type, candidate]);
return mapType(candidate, c => {
if (!areTypesComparable(t, c)) {
return neverType;
}
}
return type;
};
if ((c.flags & TypeFlags.Primitive || c === globalFunctionType) && t.flags & TypeFlags.Object && !isEmptyAnonymousObjectType(t)) {
return isTypeSubtypeOf(c, t) ? c : neverType;
}
return getIntersectionType([t, c]);
});
});
}

function narrowBySwitchOnTypeOf(type: Type, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number): Type {
Expand DownExpand Up@@ -25109,7 +25126,7 @@ namespace ts {
because it is caught in the first clause.
*/
const impliedType = getTypeWithFacts(getUnionType(clauseWitnesses.map(text => getImpliedTypeFromTypeofGuard(type, text) || type)), switchFacts);
return getTypeWithFacts(mapType(type,narrowUnionMemberByTypeof(impliedType)), switchFacts);
return getTypeWithFacts(narrowTypeByImpliedType(type, impliedType), switchFacts);
}

function isMatchingConstructorReference(expr: Expression) {
Expand Down

[8]ページ先頭

©2009-2025 Movatter.jp