Skip to content

Commit cf3af3f

Browse files
authored
Properly propagate ObjectFlags.NonInferrableType, clean up non-inferrable code paths (#49887)
1 parent 4902860 commit cf3af3f

9 files changed

+637
-39
lines changed

src/compiler/checker.ts

Lines changed: 42 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -785,11 +785,10 @@ namespace ts {
785785
const errorTypes = new Map<string, Type>();
786786

787787
const anyType = createIntrinsicType(TypeFlags.Any, "any");
788-
const autoType = createIntrinsicType(TypeFlags.Any, "any");
788+
const autoType = createIntrinsicType(TypeFlags.Any, "any", ObjectFlags.NonInferrableType);
789789
const wildcardType = createIntrinsicType(TypeFlags.Any, "any");
790790
const errorType = createIntrinsicType(TypeFlags.Any, "error");
791791
const unresolvedType = createIntrinsicType(TypeFlags.Any, "unresolved");
792-
const nonInferrableAnyType = createIntrinsicType(TypeFlags.Any, "any", ObjectFlags.ContainsWideningType);
793792
const intrinsicMarkerType = createIntrinsicType(TypeFlags.Any, "intrinsic");
794793
const unknownType = createIntrinsicType(TypeFlags.Unknown, "unknown");
795794
const nonNullUnknownType = createIntrinsicType(TypeFlags.Unknown, "unknown");
@@ -818,8 +817,7 @@ namespace ts {
818817
const esSymbolType = createIntrinsicType(TypeFlags.ESSymbol, "symbol");
819818
const voidType = createIntrinsicType(TypeFlags.Void, "void");
820819
const neverType = createIntrinsicType(TypeFlags.Never, "never");
821-
const silentNeverType = createIntrinsicType(TypeFlags.Never, "never");
822-
const nonInferrableType = createIntrinsicType(TypeFlags.Never, "never", ObjectFlags.NonInferrableType);
820+
const silentNeverType = createIntrinsicType(TypeFlags.Never, "never", ObjectFlags.NonInferrableType);
823821
const implicitNeverType = createIntrinsicType(TypeFlags.Never, "never");
824822
const unreachableNeverType = createIntrinsicType(TypeFlags.Never, "never");
825823
const nonPrimitiveType = createIntrinsicType(TypeFlags.NonPrimitive, "object");
@@ -9456,11 +9454,7 @@ namespace ts {
94569454
if (reportErrors && !declarationBelongsToPrivateAmbientMember(element)) {
94579455
reportImplicitAny(element, anyType);
94589456
}
9459-
// When we're including the pattern in the type (an indication we're obtaining a contextual type), we
9460-
// use the non-inferrable any type. Inference will never directly infer this type, but it is possible
9461-
// to infer a type that contains it, e.g. for a binding pattern like [foo] or { foo }. In such cases,
9462-
// widening of the binding pattern type substitutes a regular any for the non-inferrable any.
9463-
return includePatternInType ? nonInferrableAnyType : anyType;
9457+
return anyType;
94649458
}
94659459

94669460
// Return the type implied by an object binding pattern
@@ -13499,12 +13493,12 @@ namespace ts {
1349913493

1350013494
// This function is used to propagate certain flags when creating new object type references and union types.
1350113495
// It is only necessary to do so if a constituent type might be the undefined type, the null type, the type
13502-
// of an object literal or the anyFunctionType. This is because there are operations in the type checker
13496+
// of an object literal or a non-inferrable type. This is because there are operations in the type checker
1350313497
// that care about the presence of such types at arbitrary depth in a containing type.
13504-
function getPropagatingFlagsOfTypes(types: readonly Type[], excludeKinds: TypeFlags): ObjectFlags {
13498+
function getPropagatingFlagsOfTypes(types: readonly Type[], excludeKinds?: TypeFlags): ObjectFlags {
1350513499
let result: ObjectFlags = 0;
1350613500
for (const type of types) {
13507-
if (!(type.flags & excludeKinds)) {
13501+
if (excludeKinds === undefined || !(type.flags & excludeKinds)) {
1350813502
result |= getObjectFlags(type);
1350913503
}
1351013504
}
@@ -13517,7 +13511,7 @@ namespace ts {
1351713511
if (!type) {
1351813512
type = createObjectType(ObjectFlags.Reference, target.symbol) as TypeReference;
1351913513
target.instantiations.set(id, type);
13520-
type.objectFlags |= typeArguments ? getPropagatingFlagsOfTypes(typeArguments, /*excludeKinds*/ 0) : 0;
13514+
type.objectFlags |= typeArguments ? getPropagatingFlagsOfTypes(typeArguments) : 0;
1352113515
type.target = target;
1352213516
type.resolvedTypeArguments = typeArguments;
1352313517
}
@@ -17196,6 +17190,7 @@ namespace ts {
1719617190
result.mapper = mapper;
1719717191
result.aliasSymbol = aliasSymbol || type.aliasSymbol;
1719817192
result.aliasTypeArguments = aliasSymbol ? aliasTypeArguments : instantiateTypes(type.aliasTypeArguments, mapper);
17193+
result.objectFlags |= result.aliasTypeArguments ? getPropagatingFlagsOfTypes(result.aliasTypeArguments) : 0;
1719917194
return result;
1720017195
}
1720117196

@@ -22422,10 +22417,13 @@ namespace ts {
2242222417
propagationType = savePropagationType;
2242322418
return;
2242422419
}
22425-
if (source.aliasSymbol && source.aliasTypeArguments && source.aliasSymbol === target.aliasSymbol) {
22426-
// Source and target are types originating in the same generic type alias declaration.
22427-
// Simply infer from source type arguments to target type arguments.
22428-
inferFromTypeArguments(source.aliasTypeArguments, target.aliasTypeArguments!, getAliasVariances(source.aliasSymbol));
22420+
if (source.aliasSymbol && source.aliasSymbol === target.aliasSymbol) {
22421+
if (source.aliasTypeArguments) {
22422+
// Source and target are types originating in the same generic type alias declaration.
22423+
// Simply infer from source type arguments to target type arguments.
22424+
inferFromTypeArguments(source.aliasTypeArguments, target.aliasTypeArguments!, getAliasVariances(source.aliasSymbol));
22425+
}
22426+
// And if there weren't any type arguments, there's no reason to run inference as the types must be the same.
2242922427
return;
2243022428
}
2243122429
if (source === target && source.flags & TypeFlags.UnionOrIntersection) {
@@ -22481,18 +22479,26 @@ namespace ts {
2248122479
target = getActualTypeVariable(target);
2248222480
}
2248322481
if (target.flags & TypeFlags.TypeVariable) {
22484-
// If target is a type parameter, make an inference, unless the source type contains
22485-
// the anyFunctionType (the wildcard type that's used to avoid contextually typing functions).
22486-
// Because the anyFunctionType is internal, it should not be exposed to the user by adding
22487-
// it as an inference candidate. Hopefully, a better candidate will come along that does
22488-
// not contain anyFunctionType when we come back to this argument for its second round
22489-
// of inference. Also, we exclude inferences for silentNeverType (which is used as a wildcard
22490-
// when constructing types from type parameters that had no inference candidates).
22491-
if (source === nonInferrableAnyType || source === silentNeverType || (priority & InferencePriority.ReturnType && (source === autoType || source === autoArrayType)) || isFromInferenceBlockedSource(source)) {
22482+
// Skip inference if the source is "blocked", which is used by the language service to
22483+
// prevent inference on nodes currently being edited.
22484+
if (isFromInferenceBlockedSource(source)) {
2249222485
return;
2249322486
}
2249422487
const inference = getInferenceInfoForType(target);
2249522488
if (inference) {
22489+
// If target is a type parameter, make an inference, unless the source type contains
22490+
// a "non-inferrable" type. Types with this flag set are markers used to prevent inference.
22491+
//
22492+
// For example:
22493+
// - anyFunctionType is a wildcard type that's used to avoid contextually typing functions;
22494+
// it's internal, so should not be exposed to the user by adding it as a candidate.
22495+
// - autoType (and autoArrayType) is a special "any" used in control flow; like anyFunctionType,
22496+
// it's internal and should not be observable.
22497+
// - silentNeverType is returned by getInferredType when instantiating a generic function for
22498+
// inference (and a type variable has no mapping).
22499+
//
22500+
// This flag is infectious; if we produce Box<never> (where never is silentNeverType), Box<never> is
22501+
// also non-inferrable.
2249622502
if (getObjectFlags(source) & ObjectFlags.NonInferrableType) {
2249722503
return;
2249822504
}
@@ -23043,21 +23049,18 @@ namespace ts {
2304323049
const sourceLen = sourceSignatures.length;
2304423050
const targetLen = targetSignatures.length;
2304523051
const len = sourceLen < targetLen ? sourceLen : targetLen;
23046-
const skipParameters = !!(getObjectFlags(source) & ObjectFlags.NonInferrableType);
2304723052
for (let i = 0; i < len; i++) {
23048-
inferFromSignature(getBaseSignature(sourceSignatures[sourceLen - len + i]), getErasedSignature(targetSignatures[targetLen - len + i]), skipParameters);
23053+
inferFromSignature(getBaseSignature(sourceSignatures[sourceLen - len + i]), getErasedSignature(targetSignatures[targetLen - len + i]));
2304923054
}
2305023055
}
2305123056

23052-
function inferFromSignature(source: Signature, target: Signature, skipParameters: boolean) {
23053-
if (!skipParameters) {
23054-
const saveBivariant = bivariant;
23055-
const kind = target.declaration ? target.declaration.kind : SyntaxKind.Unknown;
23056-
// Once we descend into a bivariant signature we remain bivariant for all nested inferences
23057-
bivariant = bivariant || kind === SyntaxKind.MethodDeclaration || kind === SyntaxKind.MethodSignature || kind === SyntaxKind.Constructor;
23058-
applyToParameterTypes(source, target, inferFromContravariantTypes);
23059-
bivariant = saveBivariant;
23060-
}
23057+
function inferFromSignature(source: Signature, target: Signature) {
23058+
const saveBivariant = bivariant;
23059+
const kind = target.declaration ? target.declaration.kind : SyntaxKind.Unknown;
23060+
// Once we descend into a bivariant signature we remain bivariant for all nested inferences
23061+
bivariant = bivariant || kind === SyntaxKind.MethodDeclaration || kind === SyntaxKind.MethodSignature || kind === SyntaxKind.Constructor;
23062+
applyToParameterTypes(source, target, inferFromContravariantTypes);
23063+
bivariant = saveBivariant;
2306123064
applyToReturnTypes(source, target, inferFromTypes);
2306223065
}
2306323066

@@ -31271,7 +31274,7 @@ namespace ts {
3127131274
// returns a function type, we choose to defer processing. This narrowly permits function composition
3127231275
// operators to flow inferences through return types, but otherwise processes calls right away. We
3127331276
// use the resolvingSignature singleton to indicate that we deferred processing. This result will be
31274-
// propagated out and eventually turned into nonInferrableType (a type that is assignable to anything and
31277+
// propagated out and eventually turned into silentNeverType (a type that is assignable to anything and
3127531278
// from which we never make inferences).
3127631279
if (checkMode & CheckMode.SkipGenericFunctions && !node.typeArguments && callSignatures.some(isGenericFunctionReturningFunction)) {
3127731280
skippedGenericFunction(node, checkMode);
@@ -31906,8 +31909,8 @@ namespace ts {
3190631909
const signature = getResolvedSignature(node, /*candidatesOutArray*/ undefined, checkMode);
3190731910
if (signature === resolvingSignature) {
3190831911
// CheckMode.SkipGenericFunctions is enabled and this is a call to a generic function that
31909-
// returns a function type. We defer checking and return nonInferrableType.
31910-
return nonInferrableType;
31912+
// returns a function type. We defer checking and return silentNeverType.
31913+
return silentNeverType;
3191131914
}
3191231915

3191331916
checkDeprecatedSignature(signature, node);
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//// [nonInferrableTypePropagation1.ts]
2+
type Op<I, O> = (thing: Thing<I>) => Thing<O>;
3+
type Thing<T> = {
4+
value: T;
5+
pipe<A, B>(
6+
opA: Op<T, A>,
7+
opB: Op<A, B>,
8+
): Thing<B>;
9+
};
10+
type Box<V> = { data: V };
11+
12+
declare const thing: Thing<number>;
13+
14+
declare function map<T, R>(project: (value: T) => R): Op<T, R>;
15+
declare function tap<T>(next: (value: T) => void): Op<T, T>;
16+
declare function box<V>(data: V): Box<V>;
17+
declare function createAndUnbox<V>(factory: () => Thing<V | Box<V>>): Thing<V>;
18+
declare function log(value: any): void;
19+
20+
const result1 = createAndUnbox(() => thing.pipe(
21+
map((data) => box(data)),
22+
tap((v) => log(v)),
23+
));
24+
25+
const result2 = createAndUnbox(() => thing.pipe(
26+
tap((v) => log(v)),
27+
map((data) => box(data)),
28+
));
29+
30+
31+
//// [nonInferrableTypePropagation1.js]
32+
"use strict";
33+
var result1 = createAndUnbox(function () { return thing.pipe(map(function (data) { return box(data); }), tap(function (v) { return log(v); })); });
34+
var result2 = createAndUnbox(function () { return thing.pipe(tap(function (v) { return log(v); }), map(function (data) { return box(data); })); });

0 commit comments

Comments
 (0)