@@ -1366,13 +1366,15 @@ const enum IntrinsicTypeKind {
13661366 Lowercase,
13671367 Capitalize,
13681368 Uncapitalize,
1369+ NoInfer,
13691370}
13701371
13711372const intrinsicTypeKinds: ReadonlyMap<string, IntrinsicTypeKind> = new Map(Object.entries({
13721373 Uppercase: IntrinsicTypeKind.Uppercase,
13731374 Lowercase: IntrinsicTypeKind.Lowercase,
13741375 Capitalize: IntrinsicTypeKind.Capitalize,
13751376 Uncapitalize: IntrinsicTypeKind.Uncapitalize,
1377+ NoInfer: IntrinsicTypeKind.NoInfer,
13761378}));
13771379
13781380const SymbolLinks = class implements SymbolLinks {
@@ -6749,7 +6751,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
67496751 return visitAndTransformType(type, type => conditionalTypeToTypeNode(type as ConditionalType));
67506752 }
67516753 if (type.flags & TypeFlags.Substitution) {
6752- return typeToTypeNodeHelper((type as SubstitutionType).baseType, context);
6754+ const typeNode = typeToTypeNodeHelper((type as SubstitutionType).baseType, context);
6755+ const noInferSymbol = isNoInferType(type) && getGlobalTypeSymbol("NoInfer" as __String, /*reportErrors*/ false);
6756+ return noInferSymbol ? symbolToTypeNode(noInferSymbol, context, SymbolFlags.Type, [typeNode]) : typeNode;
67536757 }
67546758
67556759 return Debug.fail("Should be unreachable.");
@@ -15975,8 +15979,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1597515979
1597615980 function getTypeAliasInstantiation(symbol: Symbol, typeArguments: readonly Type[] | undefined, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]): Type {
1597715981 const type = getDeclaredTypeOfSymbol(symbol);
15978- if (type === intrinsicMarkerType && intrinsicTypeKinds.has(symbol.escapedName as string) && typeArguments && typeArguments.length === 1) {
15979- return getStringMappingType(symbol, typeArguments[0]);
15982+ if (type === intrinsicMarkerType) {
15983+ const typeKind = intrinsicTypeKinds.get(symbol.escapedName as string);
15984+ if (typeKind !== undefined && typeArguments && typeArguments.length === 1) {
15985+ return typeKind === IntrinsicTypeKind.NoInfer ? getNoInferType(typeArguments[0]) : getStringMappingType(symbol, typeArguments[0]);
15986+ }
1598015987 }
1598115988 const links = getSymbolLinks(symbol);
1598215989 const typeParameters = links.typeParameters!;
@@ -16158,10 +16165,32 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1615816165 return links.resolvedJSDocType;
1615916166 }
1616016167
16168+ function getNoInferType(type: Type) {
16169+ return isNoInferTargetType(type) ? getOrCreateSubstitutionType(type, unknownType) : type;
16170+ }
16171+
16172+ function isNoInferTargetType(type: Type): boolean {
16173+ // This is effectively a more conservative and predictable form of couldContainTypeVariables. We want to
16174+ // preserve NoInfer<T> only for types that could contain type variables, but we don't want to exhaustively
16175+ // examine all object type members.
16176+ return !!(type.flags & TypeFlags.UnionOrIntersection && some((type as UnionOrIntersectionType).types, isNoInferTargetType) ||
16177+ type.flags & TypeFlags.Substitution && !isNoInferType(type) && isNoInferTargetType((type as SubstitutionType).baseType) ||
16178+ type.flags & TypeFlags.Object && !isEmptyAnonymousObjectType(type) ||
16179+ type.flags & (TypeFlags.Instantiable & ~TypeFlags.Substitution) && !isPatternLiteralType(type));
16180+ }
16181+
16182+ function isNoInferType(type: Type) {
16183+ // A NoInfer<T> type is represented as a substitution type with a TypeFlags.Unknown constraint.
16184+ return !!(type.flags & TypeFlags.Substitution && (type as SubstitutionType).constraint.flags & TypeFlags.Unknown);
16185+ }
16186+
1616116187 function getSubstitutionType(baseType: Type, constraint: Type) {
16162- if (constraint.flags & TypeFlags.AnyOrUnknown || constraint === baseType || baseType.flags & TypeFlags.Any) {
16163- return baseType;
16164- }
16188+ return constraint.flags & TypeFlags.AnyOrUnknown || constraint === baseType || baseType.flags & TypeFlags.Any ?
16189+ baseType :
16190+ getOrCreateSubstitutionType(baseType, constraint);
16191+ }
16192+
16193+ function getOrCreateSubstitutionType(baseType: Type, constraint: Type) {
1616516194 const id = `${getTypeId(baseType)}>${getTypeId(constraint)}`;
1616616195 const cached = substitutionTypes.get(id);
1616716196 if (cached) {
@@ -16175,7 +16204,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1617516204 }
1617616205
1617716206 function getSubstitutionIntersection(substitutionType: SubstitutionType) {
16178- return getIntersectionType([substitutionType.constraint, substitutionType.baseType]);
16207+ return isNoInferType(substitutionType) ? substitutionType.baseType : getIntersectionType([substitutionType.constraint, substitutionType.baseType]);
1617916208 }
1618016209
1618116210 function isUnaryTupleTypeNode(node: TypeNode) {
@@ -17853,7 +17882,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1785317882
1785417883 function getIndexType(type: Type, indexFlags = defaultIndexFlags): Type {
1785517884 type = getReducedType(type);
17856- return shouldDeferIndexType(type, indexFlags) ? getIndexTypeForGenericType(type as InstantiableType | UnionOrIntersectionType, indexFlags) :
17885+ return isNoInferType(type) ? getNoInferType(getIndexType((type as SubstitutionType).baseType, indexFlags)) :
17886+ shouldDeferIndexType(type, indexFlags) ? getIndexTypeForGenericType(type as InstantiableType | UnionOrIntersectionType, indexFlags) :
1785717887 type.flags & TypeFlags.Union ? getIntersectionType(map((type as UnionType).types, t => getIndexType(t, indexFlags))) :
1785817888 type.flags & TypeFlags.Intersection ? getUnionType(map((type as IntersectionType).types, t => getIndexType(t, indexFlags))) :
1785917889 getObjectFlags(type) & ObjectFlags.Mapped ? getIndexTypeForMappedType(type as MappedType, indexFlags) :
@@ -19941,6 +19971,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1994119971 }
1994219972 if (flags & TypeFlags.Substitution) {
1994319973 const newBaseType = instantiateType((type as SubstitutionType).baseType, mapper);
19974+ if (isNoInferType(type)) {
19975+ return getNoInferType(newBaseType);
19976+ }
1994419977 const newConstraint = instantiateType((type as SubstitutionType).constraint, mapper);
1994519978 // A substitution type originates in the true branch of a conditional type and can be resolved
1994619979 // to just the base type in the same cases as the conditional type resolves to its true branch
@@ -25459,7 +25492,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2545925492 inferFromTypes(originalSource, originalTarget);
2546025493
2546125494 function inferFromTypes(source: Type, target: Type): void {
25462- if (!couldContainTypeVariables(target)) {
25495+ if (!couldContainTypeVariables(target) || isNoInferType(target) ) {
2546325496 return;
2546425497 }
2546525498 if (source === wildcardType || source === blockedStringType) {
@@ -25532,6 +25565,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2553225565 }
2553325566 }
2553425567 if (target.flags & (TypeFlags.IndexedAccess | TypeFlags.Substitution)) {
25568+ if (isNoInferType(target)) {
25569+ return;
25570+ }
2553525571 target = getActualTypeVariable(target);
2553625572 }
2553725573 if (target.flags & TypeFlags.TypeVariable) {
0 commit comments