diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index ec544ea0950d0..53ef034e6af9b 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -528,6 +528,7 @@ namespace ts {
         let deferredGlobalExcludeSymbol: Symbol;
         let deferredGlobalPickSymbol: Symbol;
         let deferredGlobalBigIntType: ObjectType;
+        let deferredGlobalIfTypeSymbol: Symbol;
 
         const allPotentiallyUnusedIdentifiers = createMap<PotentiallyUnusedIdentifier[]>(); // key is file name
 
@@ -3993,7 +3994,8 @@ namespace ts {
                 const defaultParameter = getDefaultFromTypeParameter(type);
                 const defaultParameterNode = defaultParameter && typeToTypeNodeHelper(defaultParameter, context);
                 context.flags = savedContextFlags;
-                return createTypeParameterDeclaration(name, constraintNode, defaultParameterNode);
+                const uniformityConstraint = getUniformityConstraintDeclaration(type);
+                return createTypeParameterDeclaration(name, constraintNode, defaultParameterNode, uniformityConstraint);
             }
 
             function typeParameterToDeclaration(type: TypeParameter, context: NodeBuilderContext, constraint = getConstraintOfTypeParameter(type)): TypeParameterDeclaration {
@@ -8525,6 +8527,11 @@ namespace ts {
             return decl && getEffectiveConstraintOfTypeParameter(decl);
         }
 
+        function getUniformityConstraintDeclaration(type: TypeParameter) {
+            const decl = type.symbol && getDeclarationOfKind<TypeParameterDeclaration>(type.symbol, SyntaxKind.TypeParameter);
+            return decl && decl.uniformityConstraint ? decl.uniformityConstraint : 0;
+        }
+
         function getInferredTypeParameterConstraint(typeParameter: TypeParameter) {
             let inferences: Type[] | undefined;
             if (typeParameter.symbol) {
@@ -8585,6 +8592,13 @@ namespace ts {
             return typeParameter.constraint === noConstraintType ? undefined : typeParameter.constraint;
         }
 
+        function getUniformityConstraintFromTypeParameter(typeParameter: TypeParameter): UniformityFlags {
+            if (typeParameter.uniformityConstraint === undefined) {
+                typeParameter.uniformityConstraint = getUniformityConstraintDeclaration(typeParameter);
+            }
+            return typeParameter.uniformityConstraint;
+        }
+
         function getParentSymbolOfTypeParameter(typeParameter: TypeParameter): Symbol | undefined {
             const tp = getDeclarationOfKind<TypeParameterDeclaration>(typeParameter.symbol, SyntaxKind.TypeParameter)!;
             const host = isJSDocTemplateTag(tp.parent) ? getHostSignatureFromJSDoc(tp.parent) : tp.parent;
@@ -9108,6 +9122,10 @@ namespace ts {
             return deferredGlobalPickSymbol || (deferredGlobalPickSymbol = getGlobalSymbol("Pick" as __String, SymbolFlags.TypeAlias, Diagnostics.Cannot_find_global_type_0)!); // TODO: GH#18217
         }
 
+        function getGlobalIfTypeSymbol(): Symbol {
+            return deferredGlobalIfTypeSymbol || (deferredGlobalIfTypeSymbol = getGlobalSymbol("If" as __String, SymbolFlags.TypeAlias, Diagnostics.Cannot_find_global_type_0)!); // TODO: GH#18217
+        }
+
         function getGlobalBigIntType(reportErrors: boolean) {
             return deferredGlobalBigIntType || (deferredGlobalBigIntType = getGlobalType("BigInt" as __String, /*arity*/ 0, reportErrors)) || emptyObjectType;
         }
@@ -10177,9 +10195,18 @@ namespace ts {
                 // types with type parameters mapped to the wildcard type, the most permissive instantiations
                 // possible (the wildcard type is assignable to and from all types). If those are not related,
                 // then no instantiations will be and we can just return the false branch type.
-                if (!isTypeAssignableTo(getPermissiveInstantiation(checkType), getPermissiveInstantiation(inferredExtendsType))) {
-                    return falseType;
+                if (isTypeNarrowableUnderUniformity(checkType, UniformityFlags.Equality | UniformityFlags.TypeOf)) {
+                    if (!isTypeAssignableTo(getUniformInstantiation(checkType), getPermissiveInstantiation(inferredExtendsType))) {
+                        return falseType;
+                    }
                 }
+                else {
+                    if (!isTypeAssignableTo(getPermissiveInstantiation(checkType), getPermissiveInstantiation(inferredExtendsType))) {
+                        return falseType;
+                    }
+                }
+
+
                 // Return trueType for a definitely true extends check. We check instantiations of the two
                 // types with type parameters mapped to their restrictive form, i.e. a form of the type parameter
                 // that has no constraint. This ensures that, for example, the type
@@ -10812,6 +10839,10 @@ namespace ts {
             return type.flags & TypeFlags.TypeParameter ? wildcardType : type;
         }
 
+        function unknownMapper(type: Type) {
+            return type.flags & TypeFlags.TypeParameter ? unknownType : type;
+        }
+
         function getRestrictiveTypeParameter(tp: TypeParameter) {
             return tp.constraint === unknownType ? tp : tp.restrictiveInstantiation || (
                 tp.restrictiveInstantiation = createTypeParameter(tp.symbol),
@@ -11190,6 +11221,11 @@ namespace ts {
                 type.permissiveInstantiation || (type.permissiveInstantiation = instantiateType(type, permissiveMapper));
         }
 
+        function getUniformInstantiation(type: Type) {
+            return type.flags & (TypeFlags.Primitive | TypeFlags.AnyOrUnknown | TypeFlags.Never) ? type :
+                type.permissiveInstantiation || (type.permissiveInstantiation = instantiateType(type, unknownMapper));
+        }
+
         function getRestrictiveInstantiation(type: Type) {
             if (type.flags & (TypeFlags.Primitive | TypeFlags.AnyOrUnknown | TypeFlags.Never)) {
                 return type;
@@ -13909,6 +13945,16 @@ namespace ts {
             return value.base10Value === "0";
         }
 
+        function isTypeNarrowableUnderUniformity(type: Type, kind: UniformityFlags): boolean {
+            if (type.flags & TypeFlags.TypeParameter) {
+                return (getUniformityConstraintFromTypeParameter(<TypeParameter>type) & kind) !== 0;
+            }
+            if (type.flags & TypeFlags.Intersection) {
+                return (<IntersectionType>type).types.some(t => isTypeNarrowableUnderUniformity(t, kind));
+            }
+            return false;
+        }
+
         function getFalsyFlagsOfTypes(types: Type[]): TypeFlags {
             let result: TypeFlags = 0;
             for (const t of types) {
@@ -15043,6 +15089,12 @@ namespace ts {
                 inference.inferredType = inferredType;
 
                 const constraint = getConstraintOfTypeParameter(inference.typeParameter);
+                const uniformityConstraint = getUniformityConstraintFromTypeParameter(inference.typeParameter);
+                if (uniformityConstraint) {
+                    if (!isUniformType(inferredType, uniformityConstraint)) {
+                        error(/*location*/ undefined, Diagnostics.Type_0_does_not_satisfy_uniformity_constraint_of_type_1_Values_of_type_0_do_not_behave_identically_under_typeof, typeToString(inferredType), typeToString(inference.typeParameter));
+                    }
+                }
                 if (constraint) {
                     context.flags |= InferenceFlags.NoFixing;
                     const instantiatedConstraint = instantiateType(constraint, context);
@@ -16247,7 +16299,8 @@ namespace ts {
                         if (containsMatchingReferenceDiscriminant(reference, left) || containsMatchingReferenceDiscriminant(reference, right)) {
                             return declaredType;
                         }
-                        break;
+                        return narrowTypeByUniformEquality(type, operator, left, right, assumeTrue);
+
                     case SyntaxKind.InstanceOfKeyword:
                         return narrowTypeByInstanceof(type, expr, assumeTrue);
                     case SyntaxKind.InKeyword:
@@ -16262,6 +16315,45 @@ namespace ts {
                 return type;
             }
 
+            function narrowTypeByUniformEquality(type: Type, operator: SyntaxKind, left: Expression, right: Expression, assumeTrue: boolean): Type {
+                if ((operator === SyntaxKind.ExclamationEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken)) {
+                    assumeTrue = !assumeTrue;
+                }
+                if (!assumeTrue || operator === SyntaxKind.EqualsEqualsToken) {
+                    return type;
+                }
+                const leftType = getTypeOfExpression(left);
+                const rightType = getTypeOfExpression(right);
+                const leftIsNarrowable = isTypeNarrowableUnderUniformity(leftType, UniformityFlags.Equality | UniformityFlags.TypeOf);
+                if (leftIsNarrowable && isUnitType(rightType)) {
+                    if (leftType === type) {
+                        return getIntersectionType([type, rightType]);
+                    }
+                    if (type.flags & TypeFlags.Conditional) {
+                        const cond = <ConditionalType>type;
+                        const subst = getIntersectionType([leftType, rightType]);
+                        const mapper = createTypeMapper([<TypeParameter>leftType], [subst]);
+                        const narrowedMapper = combineTypeMappers(cond.mapper, mapper);
+                        return instantiateType(cond, narrowedMapper);
+                    }
+                    return type;
+                }
+                const rightIsNarrowable = isTypeNarrowableUnderUniformity(rightType, UniformityFlags.Equality | UniformityFlags.TypeOf);
+                if (rightIsNarrowable && isUnitType(leftType)) {
+                    if (rightType === type) {
+                        return getIntersectionType([type, leftType]);
+                    }
+                    if (type.flags & TypeFlags.Conditional) {
+                        const cond = <ConditionalType>type;
+                        const subst = getIntersectionType([rightType, leftType]);
+                        const mapper = createTypeMapper([<TypeParameter>rightType], [subst]);
+                        const narrowedMapper = combineTypeMappers(cond.mapper, mapper);
+                        return instantiateType(cond, narrowedMapper);
+                    }
+                }
+                return type;
+            }
+
             function narrowTypeByEquality(type: Type, operator: SyntaxKind, value: Expression, assumeTrue: boolean): Type {
                 if (type.flags & TypeFlags.Any) {
                     return type;
@@ -16314,7 +16406,34 @@ namespace ts {
                     if (containsMatchingReference(reference, target)) {
                         return declaredType;
                     }
-                    return type;
+                    if (!isIdentifier(target)) {
+                        return type;
+                    }
+                    const targetType = getTypeOfExpression(target);
+                    const isNarrowableUnderUniformity = isTypeNarrowableUnderUniformity(targetType, UniformityFlags.TypeOf);
+                    if (!isNarrowableUnderUniformity) {
+                        return type;
+                    }
+                    if (type.flags & TypeFlags.Conditional) {
+                        if ((operator === SyntaxKind.ExclamationEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken)) {
+                            assumeTrue = !assumeTrue;
+                        }
+                        if (!assumeTrue || operator === SyntaxKind.EqualsEqualsToken) {
+                            return type;
+                        }
+                        const cond = <ConditionalType>type;
+                        const substType = literal.text === "function" ? globalFunctionType : typeofTypesByName.get(literal.text);
+                        if (!substType) {
+                            return type;
+                        }
+                        const subst = getIntersectionType([targetType, substType]);
+                        const mapper = createTypeMapper([<TypeParameter>targetType], [subst]);
+                        const narrowedMapper = combineTypeMappers(cond.mapper, mapper);
+                        return instantiateType(cond, narrowedMapper);
+                    }
+                    if (targetType !== type) {
+                        return declaredType;
+                    }
                 }
                 if (operator === SyntaxKind.ExclamationEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) {
                     assumeTrue = !assumeTrue;
@@ -16348,6 +16467,10 @@ namespace ts {
                                 return getIntersectionType([type, targetType]);
                             }
                         }
+                        if (isTypeNarrowableUnderUniformity(type, UniformityFlags.TypeOf)) {
+                            const narrowed = getIntersectionType([type, targetType]);
+                            return (narrowed.flags & TypeFlags.Intersection) && isEmptyIntersectionType(<IntersectionType>narrowed) ? neverType : narrowed;
+                        }
                     }
                     return type;
                 }
@@ -20270,6 +20393,22 @@ namespace ts {
                 createTupleType(append(types.slice(0, spreadIndex), getUnionType(types.slice(spreadIndex))), spreadIndex, /*hasRestElement*/ true);
         }
 
+        function isUniformType(type: Type, kind: UniformityFlags): boolean {
+            if (kind & UniformityFlags.Equality) {
+                return isUnitType(type);
+            }
+            else if (kind & UniformityFlags.TypeOf) {
+                // jw todo: more cases
+                if (isUnitType(type) || (type.flags & TypeFlags.Primitive)) {
+                    return true;
+                }
+                if (type.flags & TypeFlags.TypeParameter) {
+                    return !!getUniformityConstraintFromTypeParameter(<TypeParameter>type);
+                }
+            }
+            return false;
+        }
+
         function checkTypeArguments(signature: Signature, typeArgumentNodes: ReadonlyArray<TypeNode>, reportErrors: boolean, headMessage?: DiagnosticMessage): Type[] | undefined {
             const isJavascript = isInJSFile(signature.declaration);
             const typeParameters = signature.typeParameters!;
@@ -20278,6 +20417,12 @@ namespace ts {
             for (let i = 0; i < typeArgumentNodes.length; i++) {
                 Debug.assert(typeParameters[i] !== undefined, "Should not call checkTypeArguments with too many type arguments");
                 const constraint = getConstraintOfTypeParameter(typeParameters[i]);
+                const uniformityConstraint = getUniformityConstraintFromTypeParameter(typeParameters[i]);
+                if (uniformityConstraint) {
+                    if (!isUniformType(typeArgumentTypes[i], uniformityConstraint)) {
+                        error(typeArgumentNodes[i], Diagnostics.Type_0_does_not_satisfy_uniformity_constraint_of_type_1_Values_of_type_0_do_not_behave_identically_under_typeof, typeToString(typeArgumentTypes[i]), typeToString(typeParameters[i]));
+                    }
+                }
                 if (constraint) {
                     const errorInfo = reportErrors && headMessage ? (() => chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Type_0_does_not_satisfy_the_constraint_1)) : undefined;
                     const typeArgumentHeadMessage = headMessage || Diagnostics.Type_0_does_not_satisfy_the_constraint_1;
@@ -23300,6 +23445,19 @@ namespace ts {
             checkTruthinessExpression(node.condition);
             const type1 = checkExpression(node.whenTrue, checkMode);
             const type2 = checkExpression(node.whenFalse, checkMode);
+            if (isBinaryExpression(node.condition) && node.condition.left.kind === SyntaxKind.TypeOfExpression && isStringLiteralLike(node.condition.right)) {
+                const extendsType = typeofTypesByName.get(node.condition.right.text);
+                if (extendsType !== undefined) {
+                    const typeofTest = getTypeOfExpression((<TypeOfExpression>node.condition.left).expression);
+                    if ((typeofTest.flags & TypeFlags.TypeVariable) && getUniformityConstraintFromTypeParameter(<TypeParameter>typeofTest)) {
+                        const ifTypeAlias = getGlobalIfTypeSymbol();
+                        if (!ifTypeAlias) {
+                            return errorType;
+                        }
+                        return getTypeAliasInstantiation(ifTypeAlias, [typeofTest, extendsType, type1, type2]);
+                    }
+                }
+            }
             return getUnionType([type1, type2], UnionReduction.Subtype);
         }
 
@@ -24353,6 +24511,16 @@ namespace ts {
             let result = true;
             for (let i = 0; i < typeParameters.length; i++) {
                 const constraint = getConstraintOfTypeParameter(typeParameters[i]);
+                const uniformityConstraint = getUniformityConstraintFromTypeParameter(typeParameters[i]);
+                if (uniformityConstraint) {
+                    if (!typeArguments) {
+                        typeArguments = getEffectiveTypeArguments(node, typeParameters);
+                        mapper = createTypeMapper(typeParameters, typeArguments);
+                    }
+                    if (!isUniformType(typeArguments[i], uniformityConstraint)) {
+                        error(node.typeArguments && node.typeArguments[i], Diagnostics.Type_0_does_not_satisfy_uniformity_constraint_of_type_1_Values_of_type_0_do_not_behave_identically_under_typeof, typeToString(typeArguments[i]), typeToString(typeParameters[i]));
+                    }
+                }
                 if (constraint) {
                     if (!typeArguments) {
                         typeArguments = getEffectiveTypeArguments(node, typeParameters);
diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json
index 53094bf188852..06bbbf43c130b 100644
--- a/src/compiler/diagnosticMessages.json
+++ b/src/compiler/diagnosticMessages.json
@@ -2593,7 +2593,10 @@
         "category": "Error",
         "code": 2751
     },
-
+    "Type '{0}' does not satisfy uniformity constraint of type '{1}'. Values of type '{0}' do not behave identically under typeof": {
+        "category": "Error",
+        "code": 2752
+    },
     "Import declaration '{0}' is using private name '{1}'.": {
         "category": "Error",
         "code": 4000
diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts
index e55e768bab31f..a2d87a7bfd3fb 100644
--- a/src/compiler/emitter.ts
+++ b/src/compiler/emitter.ts
@@ -1637,6 +1637,12 @@ namespace ts {
 
         function emitTypeParameter(node: TypeParameterDeclaration) {
             emit(node.name);
+            if (node.uniformityConstraint && node.uniformityConstraint & UniformityFlags.TypeOf) {
+                writePunctuation("!");
+            }
+            else if (node.uniformityConstraint && node.uniformityConstraint & UniformityFlags.Equality) {
+                writePunctuation("~");
+            }
             if (node.constraint) {
                 writeSpace();
                 writeKeyword("extends");
diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts
index a3bffb785a8fc..ded8151624151 100644
--- a/src/compiler/factory.ts
+++ b/src/compiler/factory.ts
@@ -300,19 +300,21 @@ namespace ts {
 
     // Signature elements
 
-    export function createTypeParameterDeclaration(name: string | Identifier, constraint?: TypeNode, defaultType?: TypeNode) {
+    export function createTypeParameterDeclaration(name: string | Identifier, constraint?: TypeNode, defaultType?: TypeNode, uniformityConstraint?: UniformityFlags) {
         const node = createSynthesizedNode(SyntaxKind.TypeParameter) as TypeParameterDeclaration;
         node.name = asName(name);
         node.constraint = constraint;
         node.default = defaultType;
+        node.uniformityConstraint = uniformityConstraint;
         return node;
     }
 
-    export function updateTypeParameterDeclaration(node: TypeParameterDeclaration, name: Identifier, constraint: TypeNode | undefined, defaultType: TypeNode | undefined) {
+    export function updateTypeParameterDeclaration(node: TypeParameterDeclaration, name: Identifier, constraint: TypeNode | undefined, defaultType: TypeNode | undefined, uniformityConstraint?: UniformityFlags) {
         return node.name !== name
             || node.constraint !== constraint
             || node.default !== defaultType
-            ? updateNode(createTypeParameterDeclaration(name, constraint, defaultType), node)
+            || node.uniformityConstraint !== uniformityConstraint
+            ? updateNode(createTypeParameterDeclaration(name, constraint, defaultType, uniformityConstraint), node)
             : node;
     }
 
diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index 1564b78ca61d2..73fd0d89a8411 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -3,7 +3,7 @@ namespace ts {
         None = 0,
         Yield = 1 << 0,
         Await = 1 << 1,
-        Type  = 1 << 2,
+        Type = 1 << 2,
         IgnoreMissingOpenBrace = 1 << 4,
         JSDoc = 1 << 5,
     }
@@ -2415,6 +2415,9 @@ namespace ts {
         function parseTypeParameter(): TypeParameterDeclaration {
             const node = <TypeParameterDeclaration>createNode(SyntaxKind.TypeParameter);
             node.name = parseIdentifier();
+            node.uniformityConstraint = 0;
+            node.uniformityConstraint |= parseOptional(SyntaxKind.ExclamationToken) ? UniformityFlags.TypeOf : 0;
+            node.uniformityConstraint |= parseOptional(SyntaxKind.TildeToken) ? UniformityFlags.Equality : 0;
             if (parseOptional(SyntaxKind.ExtendsKeyword)) {
                 // It's not uncommon for people to write improper constraints to a generic.  If the
                 // user writes a constraint that is an expression and not an actual type, then parse
diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts
index 1f88bfb9e4f34..ee1d93866efe5 100644
--- a/src/compiler/transformers/declarations.ts
+++ b/src/compiler/transformers/declarations.ts
@@ -847,7 +847,7 @@ namespace ts {
                     }
                     case SyntaxKind.TypeParameter: {
                         if (isPrivateMethodTypeParameter(input) && (input.default || input.constraint)) {
-                            return cleanup(updateTypeParameterDeclaration(input, input.name, /*constraint*/ undefined, /*defaultType*/ undefined));
+                            return cleanup(updateTypeParameterDeclaration(input, input.name, /*constraint*/ undefined, /*defaultType*/ undefined, /*uniformityConstraint*/ undefined));
                         }
                         return cleanup(visitEachChild(input, visitDeclarationSubtree, context));
                     }
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index 20fab57c36b18..f6942b287dffb 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -852,6 +852,8 @@ namespace ts {
 
         // For error recovery purposes.
         expression?: Expression;
+
+        uniformityConstraint?: UniformityFlags;
     }
 
     export interface SignatureDeclarationBase extends NamedDeclaration, JSDocContainer {
@@ -4041,6 +4043,11 @@ namespace ts {
     export interface EnumType extends Type {
     }
 
+    export const enum UniformityFlags {
+        TypeOf = 1 << 0,
+        Equality = 1 << 1,
+    }
+
     export const enum ObjectFlags {
         Class            = 1 << 0,  // Class
         Interface        = 1 << 1,  // Interface
@@ -4272,6 +4279,8 @@ namespace ts {
         isThisType?: boolean;
         /* @internal */
         resolvedDefaultType?: Type;
+        /* @internal */
+        uniformityConstraint?: UniformityFlags;
     }
 
     // Indexed access types (TypeFlags.IndexedAccess)
diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts
index 0d4d0d9482d6a..91bb2777dc59e 100644
--- a/src/compiler/visitor.ts
+++ b/src/compiler/visitor.ts
@@ -237,7 +237,8 @@ namespace ts {
                 return updateTypeParameterDeclaration(<TypeParameterDeclaration>node,
                     visitNode((<TypeParameterDeclaration>node).name, visitor, isIdentifier),
                     visitNode((<TypeParameterDeclaration>node).constraint, visitor, isTypeNode),
-                    visitNode((<TypeParameterDeclaration>node).default, visitor, isTypeNode));
+                    visitNode((<TypeParameterDeclaration>node).default, visitor, isTypeNode),
+                    (<TypeParameterDeclaration>node).uniformityConstraint);
 
             case SyntaxKind.Parameter:
                 return updateParameter(<ParameterDeclaration>node,
diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts
index 16f1a37ae83fa..1d7452b8c140f 100644
--- a/src/harness/fourslash.ts
+++ b/src/harness/fourslash.ts
@@ -4504,6 +4504,7 @@ namespace FourSlashInterface {
             typeEntry("Required"),
             typeEntry("Readonly"),
             typeEntry("Pick"),
+            typeEntry("If"),
             typeEntry("Record"),
             typeEntry("Exclude"),
             typeEntry("Extract"),
diff --git a/src/lib/es5.d.ts b/src/lib/es5.d.ts
index 4112f5a2ae7d7..489c55d7e9dc5 100644
--- a/src/lib/es5.d.ts
+++ b/src/lib/es5.d.ts
@@ -1426,6 +1426,8 @@ type Pick<T, K extends keyof T> = {
     [P in K]: T[P];
 };
 
+type If<C, E, T, F> = [C] extends [E] ? T : F;
+
 /**
  * Construct a type with a set of properties K of type T
  */
diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts
index e99261be3f3b5..04e8cc2db8080 100644
--- a/tests/baselines/reference/api/tsserverlibrary.d.ts
+++ b/tests/baselines/reference/api/tsserverlibrary.d.ts
@@ -563,6 +563,7 @@ declare namespace ts {
         constraint?: TypeNode;
         default?: TypeNode;
         expression?: Expression;
+        uniformityConstraint?: UniformityFlags;
     }
     interface SignatureDeclarationBase extends NamedDeclaration, JSDocContainer {
         kind: SignatureDeclaration["kind"];
@@ -2277,6 +2278,10 @@ declare namespace ts {
     }
     interface EnumType extends Type {
     }
+    enum UniformityFlags {
+        TypeOf = 1,
+        Equality = 2
+    }
     enum ObjectFlags {
         Class = 1,
         Interface = 2,
@@ -3757,8 +3762,8 @@ declare namespace ts {
     function updateQualifiedName(node: QualifiedName, left: EntityName, right: Identifier): QualifiedName;
     function createComputedPropertyName(expression: Expression): ComputedPropertyName;
     function updateComputedPropertyName(node: ComputedPropertyName, expression: Expression): ComputedPropertyName;
-    function createTypeParameterDeclaration(name: string | Identifier, constraint?: TypeNode, defaultType?: TypeNode): TypeParameterDeclaration;
-    function updateTypeParameterDeclaration(node: TypeParameterDeclaration, name: Identifier, constraint: TypeNode | undefined, defaultType: TypeNode | undefined): TypeParameterDeclaration;
+    function createTypeParameterDeclaration(name: string | Identifier, constraint?: TypeNode, defaultType?: TypeNode, uniformityConstraint?: UniformityFlags): TypeParameterDeclaration;
+    function updateTypeParameterDeclaration(node: TypeParameterDeclaration, name: Identifier, constraint: TypeNode | undefined, defaultType: TypeNode | undefined, uniformityConstraint?: UniformityFlags): TypeParameterDeclaration;
     function createParameter(decorators: ReadonlyArray<Decorator> | undefined, modifiers: ReadonlyArray<Modifier> | undefined, dotDotDotToken: DotDotDotToken | undefined, name: string | BindingName, questionToken?: QuestionToken, type?: TypeNode, initializer?: Expression): ParameterDeclaration;
     function updateParameter(node: ParameterDeclaration, decorators: ReadonlyArray<Decorator> | undefined, modifiers: ReadonlyArray<Modifier> | undefined, dotDotDotToken: DotDotDotToken | undefined, name: string | BindingName, questionToken: QuestionToken | undefined, type: TypeNode | undefined, initializer: Expression | undefined): ParameterDeclaration;
     function createDecorator(expression: Expression): Decorator;
diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts
index d7b494960537c..75a519b37f972 100644
--- a/tests/baselines/reference/api/typescript.d.ts
+++ b/tests/baselines/reference/api/typescript.d.ts
@@ -563,6 +563,7 @@ declare namespace ts {
         constraint?: TypeNode;
         default?: TypeNode;
         expression?: Expression;
+        uniformityConstraint?: UniformityFlags;
     }
     interface SignatureDeclarationBase extends NamedDeclaration, JSDocContainer {
         kind: SignatureDeclaration["kind"];
@@ -2277,6 +2278,10 @@ declare namespace ts {
     }
     interface EnumType extends Type {
     }
+    enum UniformityFlags {
+        TypeOf = 1,
+        Equality = 2
+    }
     enum ObjectFlags {
         Class = 1,
         Interface = 2,
@@ -3757,8 +3762,8 @@ declare namespace ts {
     function updateQualifiedName(node: QualifiedName, left: EntityName, right: Identifier): QualifiedName;
     function createComputedPropertyName(expression: Expression): ComputedPropertyName;
     function updateComputedPropertyName(node: ComputedPropertyName, expression: Expression): ComputedPropertyName;
-    function createTypeParameterDeclaration(name: string | Identifier, constraint?: TypeNode, defaultType?: TypeNode): TypeParameterDeclaration;
-    function updateTypeParameterDeclaration(node: TypeParameterDeclaration, name: Identifier, constraint: TypeNode | undefined, defaultType: TypeNode | undefined): TypeParameterDeclaration;
+    function createTypeParameterDeclaration(name: string | Identifier, constraint?: TypeNode, defaultType?: TypeNode, uniformityConstraint?: UniformityFlags): TypeParameterDeclaration;
+    function updateTypeParameterDeclaration(node: TypeParameterDeclaration, name: Identifier, constraint: TypeNode | undefined, defaultType: TypeNode | undefined, uniformityConstraint?: UniformityFlags): TypeParameterDeclaration;
     function createParameter(decorators: ReadonlyArray<Decorator> | undefined, modifiers: ReadonlyArray<Modifier> | undefined, dotDotDotToken: DotDotDotToken | undefined, name: string | BindingName, questionToken?: QuestionToken, type?: TypeNode, initializer?: Expression): ParameterDeclaration;
     function updateParameter(node: ParameterDeclaration, decorators: ReadonlyArray<Decorator> | undefined, modifiers: ReadonlyArray<Modifier> | undefined, dotDotDotToken: DotDotDotToken | undefined, name: string | BindingName, questionToken: QuestionToken | undefined, type: TypeNode | undefined, initializer: Expression | undefined): ParameterDeclaration;
     function createDecorator(expression: Expression): Decorator;
diff --git a/tests/baselines/reference/conditionalTypes1.errors.txt b/tests/baselines/reference/conditionalTypes1.errors.txt
index ec35f89161589..647c96af646ab 100644
--- a/tests/baselines/reference/conditionalTypes1.errors.txt
+++ b/tests/baselines/reference/conditionalTypes1.errors.txt
@@ -290,10 +290,10 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(288,43): error TS
     type T38<T> = [T] extends [{ a: string }] ? [T] extends [{ b: number }] ? T35<T> : never : never;
     
     type Extends<T, U> = T extends U ? true : false;
-    type If<C extends boolean, T, F> = C extends true ? T : F;
-    type Not<C extends boolean> = If<C, false, true>;
-    type And<A extends boolean, B extends boolean> = If<A, B, false>;
-    type Or<A extends boolean, B extends boolean> = If<A, true, B>;
+    type IfC<C extends boolean, T, F> = C extends true ? T : F;
+    type Not<C extends boolean> = IfC<C, false, true>;
+    type And<A extends boolean, B extends boolean> = IfC<A, B, false>;
+    type Or<A extends boolean, B extends boolean> = IfC<A, true, B>;
     
     type IsString<T> = Extends<T, string>;
     
@@ -420,9 +420,9 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(288,43): error TS
     
     function f50() {
         type Eq<T, U> = T extends U ? U extends T ? true : false : false;
-        type If<S, T, U> = S extends false ? U : T;
-        type Omit<T extends object> = { [P in keyof T]: If<Eq<T[P], never>, never, P>; }[keyof T];
-        type Omit2<T extends object, U = never> = { [P in keyof T]: If<Eq<T[P], U>, never, P>; }[keyof T];
+        type IfC<S, T, U> = S extends false ? U : T;
+        type Omit<T extends object> = { [P in keyof T]: IfC<Eq<T[P], never>, never, P>; }[keyof T];
+        type Omit2<T extends object, U = never> = { [P in keyof T]: IfC<Eq<T[P], U>, never, P>; }[keyof T];
         type A = Omit<{ a: void; b: never; }>;  // 'a'
         type B = Omit2<{ a: void; b: never; }>;  // 'a'
     }
diff --git a/tests/baselines/reference/conditionalTypes1.js b/tests/baselines/reference/conditionalTypes1.js
index b592bd33d10e8..faed16a136ac5 100644
--- a/tests/baselines/reference/conditionalTypes1.js
+++ b/tests/baselines/reference/conditionalTypes1.js
@@ -167,10 +167,10 @@ type T37<T> = T extends { b: number } ? T extends { a: string } ? T35<T> : never
 type T38<T> = [T] extends [{ a: string }] ? [T] extends [{ b: number }] ? T35<T> : never : never;
 
 type Extends<T, U> = T extends U ? true : false;
-type If<C extends boolean, T, F> = C extends true ? T : F;
-type Not<C extends boolean> = If<C, false, true>;
-type And<A extends boolean, B extends boolean> = If<A, B, false>;
-type Or<A extends boolean, B extends boolean> = If<A, true, B>;
+type IfC<C extends boolean, T, F> = C extends true ? T : F;
+type Not<C extends boolean> = IfC<C, false, true>;
+type And<A extends boolean, B extends boolean> = IfC<A, B, false>;
+type Or<A extends boolean, B extends boolean> = IfC<A, true, B>;
 
 type IsString<T> = Extends<T, string>;
 
@@ -292,9 +292,9 @@ const f45 = <U>(value: T95<U>): T94<U> => value;  // Error
 
 function f50() {
     type Eq<T, U> = T extends U ? U extends T ? true : false : false;
-    type If<S, T, U> = S extends false ? U : T;
-    type Omit<T extends object> = { [P in keyof T]: If<Eq<T[P], never>, never, P>; }[keyof T];
-    type Omit2<T extends object, U = never> = { [P in keyof T]: If<Eq<T[P], U>, never, P>; }[keyof T];
+    type IfC<S, T, U> = S extends false ? U : T;
+    type Omit<T extends object> = { [P in keyof T]: IfC<Eq<T[P], never>, never, P>; }[keyof T];
+    type Omit2<T extends object, U = never> = { [P in keyof T]: IfC<Eq<T[P], U>, never, P>; }[keyof T];
     type A = Omit<{ a: void; b: never; }>;  // 'a'
     type B = Omit2<{ a: void; b: never; }>;  // 'a'
 }
@@ -591,10 +591,10 @@ declare type T38<T> = [T] extends [{
     b: number;
 }] ? T35<T> : never : never;
 declare type Extends<T, U> = T extends U ? true : false;
-declare type If<C extends boolean, T, F> = C extends true ? T : F;
-declare type Not<C extends boolean> = If<C, false, true>;
-declare type And<A extends boolean, B extends boolean> = If<A, B, false>;
-declare type Or<A extends boolean, B extends boolean> = If<A, true, B>;
+declare type IfC<C extends boolean, T, F> = C extends true ? T : F;
+declare type Not<C extends boolean> = IfC<C, false, true>;
+declare type And<A extends boolean, B extends boolean> = IfC<A, B, false>;
+declare type Or<A extends boolean, B extends boolean> = IfC<A, true, B>;
 declare type IsString<T> = Extends<T, string>;
 declare type Q1 = IsString<number>;
 declare type Q2 = IsString<"abc">;
diff --git a/tests/baselines/reference/conditionalTypes1.symbols b/tests/baselines/reference/conditionalTypes1.symbols
index e5c6ae3d6c7f7..e4c9d215a2f08 100644
--- a/tests/baselines/reference/conditionalTypes1.symbols
+++ b/tests/baselines/reference/conditionalTypes1.symbols
@@ -656,142 +656,142 @@ type Extends<T, U> = T extends U ? true : false;
 >T : Symbol(T, Decl(conditionalTypes1.ts, 167, 13))
 >U : Symbol(U, Decl(conditionalTypes1.ts, 167, 15))
 
-type If<C extends boolean, T, F> = C extends true ? T : F;
->If : Symbol(If, Decl(conditionalTypes1.ts, 167, 48))
->C : Symbol(C, Decl(conditionalTypes1.ts, 168, 8))
->T : Symbol(T, Decl(conditionalTypes1.ts, 168, 26))
->F : Symbol(F, Decl(conditionalTypes1.ts, 168, 29))
->C : Symbol(C, Decl(conditionalTypes1.ts, 168, 8))
->T : Symbol(T, Decl(conditionalTypes1.ts, 168, 26))
->F : Symbol(F, Decl(conditionalTypes1.ts, 168, 29))
-
-type Not<C extends boolean> = If<C, false, true>;
->Not : Symbol(Not, Decl(conditionalTypes1.ts, 168, 58))
+type IfC<C extends boolean, T, F> = C extends true ? T : F;
+>IfC : Symbol(IfC, Decl(conditionalTypes1.ts, 167, 48))
+>C : Symbol(C, Decl(conditionalTypes1.ts, 168, 9))
+>T : Symbol(T, Decl(conditionalTypes1.ts, 168, 27))
+>F : Symbol(F, Decl(conditionalTypes1.ts, 168, 30))
+>C : Symbol(C, Decl(conditionalTypes1.ts, 168, 9))
+>T : Symbol(T, Decl(conditionalTypes1.ts, 168, 27))
+>F : Symbol(F, Decl(conditionalTypes1.ts, 168, 30))
+
+type Not<C extends boolean> = IfC<C, false, true>;
+>Not : Symbol(Not, Decl(conditionalTypes1.ts, 168, 59))
 >C : Symbol(C, Decl(conditionalTypes1.ts, 169, 9))
->If : Symbol(If, Decl(conditionalTypes1.ts, 167, 48))
+>IfC : Symbol(IfC, Decl(conditionalTypes1.ts, 167, 48))
 >C : Symbol(C, Decl(conditionalTypes1.ts, 169, 9))
 
-type And<A extends boolean, B extends boolean> = If<A, B, false>;
->And : Symbol(And, Decl(conditionalTypes1.ts, 169, 49))
+type And<A extends boolean, B extends boolean> = IfC<A, B, false>;
+>And : Symbol(And, Decl(conditionalTypes1.ts, 169, 50))
 >A : Symbol(A, Decl(conditionalTypes1.ts, 170, 9))
 >B : Symbol(B, Decl(conditionalTypes1.ts, 170, 27))
->If : Symbol(If, Decl(conditionalTypes1.ts, 167, 48))
+>IfC : Symbol(IfC, Decl(conditionalTypes1.ts, 167, 48))
 >A : Symbol(A, Decl(conditionalTypes1.ts, 170, 9))
 >B : Symbol(B, Decl(conditionalTypes1.ts, 170, 27))
 
-type Or<A extends boolean, B extends boolean> = If<A, true, B>;
->Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 65))
+type Or<A extends boolean, B extends boolean> = IfC<A, true, B>;
+>Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 66))
 >A : Symbol(A, Decl(conditionalTypes1.ts, 171, 8))
 >B : Symbol(B, Decl(conditionalTypes1.ts, 171, 26))
->If : Symbol(If, Decl(conditionalTypes1.ts, 167, 48))
+>IfC : Symbol(IfC, Decl(conditionalTypes1.ts, 167, 48))
 >A : Symbol(A, Decl(conditionalTypes1.ts, 171, 8))
 >B : Symbol(B, Decl(conditionalTypes1.ts, 171, 26))
 
 type IsString<T> = Extends<T, string>;
->IsString : Symbol(IsString, Decl(conditionalTypes1.ts, 171, 63))
+>IsString : Symbol(IsString, Decl(conditionalTypes1.ts, 171, 64))
 >T : Symbol(T, Decl(conditionalTypes1.ts, 173, 14))
 >Extends : Symbol(Extends, Decl(conditionalTypes1.ts, 165, 97))
 >T : Symbol(T, Decl(conditionalTypes1.ts, 173, 14))
 
 type Q1 = IsString<number>;  // false
 >Q1 : Symbol(Q1, Decl(conditionalTypes1.ts, 173, 38))
->IsString : Symbol(IsString, Decl(conditionalTypes1.ts, 171, 63))
+>IsString : Symbol(IsString, Decl(conditionalTypes1.ts, 171, 64))
 
 type Q2 = IsString<"abc">;  // true
 >Q2 : Symbol(Q2, Decl(conditionalTypes1.ts, 175, 27))
->IsString : Symbol(IsString, Decl(conditionalTypes1.ts, 171, 63))
+>IsString : Symbol(IsString, Decl(conditionalTypes1.ts, 171, 64))
 
 type Q3 = IsString<any>;  // boolean
 >Q3 : Symbol(Q3, Decl(conditionalTypes1.ts, 176, 26))
->IsString : Symbol(IsString, Decl(conditionalTypes1.ts, 171, 63))
+>IsString : Symbol(IsString, Decl(conditionalTypes1.ts, 171, 64))
 
 type Q4 = IsString<never>;  // never
 >Q4 : Symbol(Q4, Decl(conditionalTypes1.ts, 177, 24))
->IsString : Symbol(IsString, Decl(conditionalTypes1.ts, 171, 63))
+>IsString : Symbol(IsString, Decl(conditionalTypes1.ts, 171, 64))
 
 type N1 = Not<false>;  // true
 >N1 : Symbol(N1, Decl(conditionalTypes1.ts, 178, 26))
->Not : Symbol(Not, Decl(conditionalTypes1.ts, 168, 58))
+>Not : Symbol(Not, Decl(conditionalTypes1.ts, 168, 59))
 
 type N2 = Not<true>;  // false
 >N2 : Symbol(N2, Decl(conditionalTypes1.ts, 180, 21))
->Not : Symbol(Not, Decl(conditionalTypes1.ts, 168, 58))
+>Not : Symbol(Not, Decl(conditionalTypes1.ts, 168, 59))
 
 type N3 = Not<boolean>;  // boolean
 >N3 : Symbol(N3, Decl(conditionalTypes1.ts, 181, 20))
->Not : Symbol(Not, Decl(conditionalTypes1.ts, 168, 58))
+>Not : Symbol(Not, Decl(conditionalTypes1.ts, 168, 59))
 
 type A1 = And<false, false>;  // false
 >A1 : Symbol(A1, Decl(conditionalTypes1.ts, 182, 23))
->And : Symbol(And, Decl(conditionalTypes1.ts, 169, 49))
+>And : Symbol(And, Decl(conditionalTypes1.ts, 169, 50))
 
 type A2 = And<false, true>;  // false
 >A2 : Symbol(A2, Decl(conditionalTypes1.ts, 184, 28))
->And : Symbol(And, Decl(conditionalTypes1.ts, 169, 49))
+>And : Symbol(And, Decl(conditionalTypes1.ts, 169, 50))
 
 type A3 = And<true, false>;  // false
 >A3 : Symbol(A3, Decl(conditionalTypes1.ts, 185, 27))
->And : Symbol(And, Decl(conditionalTypes1.ts, 169, 49))
+>And : Symbol(And, Decl(conditionalTypes1.ts, 169, 50))
 
 type A4 = And<true, true>;  // true
 >A4 : Symbol(A4, Decl(conditionalTypes1.ts, 186, 27))
->And : Symbol(And, Decl(conditionalTypes1.ts, 169, 49))
+>And : Symbol(And, Decl(conditionalTypes1.ts, 169, 50))
 
 type A5 = And<boolean, false>;  // false
 >A5 : Symbol(A5, Decl(conditionalTypes1.ts, 187, 26))
->And : Symbol(And, Decl(conditionalTypes1.ts, 169, 49))
+>And : Symbol(And, Decl(conditionalTypes1.ts, 169, 50))
 
 type A6 = And<false, boolean>;  // false
 >A6 : Symbol(A6, Decl(conditionalTypes1.ts, 188, 30))
->And : Symbol(And, Decl(conditionalTypes1.ts, 169, 49))
+>And : Symbol(And, Decl(conditionalTypes1.ts, 169, 50))
 
 type A7 = And<boolean, true>;  // boolean
 >A7 : Symbol(A7, Decl(conditionalTypes1.ts, 189, 30))
->And : Symbol(And, Decl(conditionalTypes1.ts, 169, 49))
+>And : Symbol(And, Decl(conditionalTypes1.ts, 169, 50))
 
 type A8 = And<true, boolean>;  // boolean
 >A8 : Symbol(A8, Decl(conditionalTypes1.ts, 190, 29))
->And : Symbol(And, Decl(conditionalTypes1.ts, 169, 49))
+>And : Symbol(And, Decl(conditionalTypes1.ts, 169, 50))
 
 type A9 = And<boolean, boolean>;  // boolean
 >A9 : Symbol(A9, Decl(conditionalTypes1.ts, 191, 29))
->And : Symbol(And, Decl(conditionalTypes1.ts, 169, 49))
+>And : Symbol(And, Decl(conditionalTypes1.ts, 169, 50))
 
 type O1 = Or<false, false>;  // false
 >O1 : Symbol(O1, Decl(conditionalTypes1.ts, 192, 32))
->Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 65))
+>Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 66))
 
 type O2 = Or<false, true>;  // true
 >O2 : Symbol(O2, Decl(conditionalTypes1.ts, 194, 27))
->Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 65))
+>Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 66))
 
 type O3 = Or<true, false>;  // true
 >O3 : Symbol(O3, Decl(conditionalTypes1.ts, 195, 26))
->Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 65))
+>Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 66))
 
 type O4 = Or<true, true>;  // true
 >O4 : Symbol(O4, Decl(conditionalTypes1.ts, 196, 26))
->Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 65))
+>Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 66))
 
 type O5 = Or<boolean, false>;  // boolean
 >O5 : Symbol(O5, Decl(conditionalTypes1.ts, 197, 25))
->Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 65))
+>Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 66))
 
 type O6 = Or<false, boolean>;  // boolean
 >O6 : Symbol(O6, Decl(conditionalTypes1.ts, 198, 29))
->Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 65))
+>Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 66))
 
 type O7 = Or<boolean, true>;  // true
 >O7 : Symbol(O7, Decl(conditionalTypes1.ts, 199, 29))
->Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 65))
+>Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 66))
 
 type O8 = Or<true, boolean>;  // true
 >O8 : Symbol(O8, Decl(conditionalTypes1.ts, 200, 28))
->Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 65))
+>Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 66))
 
 type O9 = Or<boolean, boolean>;  // boolean
 >O9 : Symbol(O9, Decl(conditionalTypes1.ts, 201, 28))
->Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 65))
+>Or : Symbol(Or, Decl(conditionalTypes1.ts, 170, 66))
 
 type T40 = never extends never ? true : false;  // true
 >T40 : Symbol(T40, Decl(conditionalTypes1.ts, 202, 31))
@@ -1136,34 +1136,34 @@ function f50() {
 >U : Symbol(U, Decl(conditionalTypes1.ts, 292, 14))
 >T : Symbol(T, Decl(conditionalTypes1.ts, 292, 12))
 
-    type If<S, T, U> = S extends false ? U : T;
->If : Symbol(If, Decl(conditionalTypes1.ts, 292, 69))
->S : Symbol(S, Decl(conditionalTypes1.ts, 293, 12))
->T : Symbol(T, Decl(conditionalTypes1.ts, 293, 14))
->U : Symbol(U, Decl(conditionalTypes1.ts, 293, 17))
->S : Symbol(S, Decl(conditionalTypes1.ts, 293, 12))
->U : Symbol(U, Decl(conditionalTypes1.ts, 293, 17))
->T : Symbol(T, Decl(conditionalTypes1.ts, 293, 14))
-
-    type Omit<T extends object> = { [P in keyof T]: If<Eq<T[P], never>, never, P>; }[keyof T];
->Omit : Symbol(Omit, Decl(conditionalTypes1.ts, 293, 47))
+    type IfC<S, T, U> = S extends false ? U : T;
+>IfC : Symbol(IfC, Decl(conditionalTypes1.ts, 292, 69))
+>S : Symbol(S, Decl(conditionalTypes1.ts, 293, 13))
+>T : Symbol(T, Decl(conditionalTypes1.ts, 293, 15))
+>U : Symbol(U, Decl(conditionalTypes1.ts, 293, 18))
+>S : Symbol(S, Decl(conditionalTypes1.ts, 293, 13))
+>U : Symbol(U, Decl(conditionalTypes1.ts, 293, 18))
+>T : Symbol(T, Decl(conditionalTypes1.ts, 293, 15))
+
+    type Omit<T extends object> = { [P in keyof T]: IfC<Eq<T[P], never>, never, P>; }[keyof T];
+>Omit : Symbol(Omit, Decl(conditionalTypes1.ts, 293, 48))
 >T : Symbol(T, Decl(conditionalTypes1.ts, 294, 14))
 >P : Symbol(P, Decl(conditionalTypes1.ts, 294, 37))
 >T : Symbol(T, Decl(conditionalTypes1.ts, 294, 14))
->If : Symbol(If, Decl(conditionalTypes1.ts, 292, 69))
+>IfC : Symbol(IfC, Decl(conditionalTypes1.ts, 292, 69))
 >Eq : Symbol(Eq, Decl(conditionalTypes1.ts, 291, 16))
 >T : Symbol(T, Decl(conditionalTypes1.ts, 294, 14))
 >P : Symbol(P, Decl(conditionalTypes1.ts, 294, 37))
 >P : Symbol(P, Decl(conditionalTypes1.ts, 294, 37))
 >T : Symbol(T, Decl(conditionalTypes1.ts, 294, 14))
 
-    type Omit2<T extends object, U = never> = { [P in keyof T]: If<Eq<T[P], U>, never, P>; }[keyof T];
->Omit2 : Symbol(Omit2, Decl(conditionalTypes1.ts, 294, 94))
+    type Omit2<T extends object, U = never> = { [P in keyof T]: IfC<Eq<T[P], U>, never, P>; }[keyof T];
+>Omit2 : Symbol(Omit2, Decl(conditionalTypes1.ts, 294, 95))
 >T : Symbol(T, Decl(conditionalTypes1.ts, 295, 15))
 >U : Symbol(U, Decl(conditionalTypes1.ts, 295, 32))
 >P : Symbol(P, Decl(conditionalTypes1.ts, 295, 49))
 >T : Symbol(T, Decl(conditionalTypes1.ts, 295, 15))
->If : Symbol(If, Decl(conditionalTypes1.ts, 292, 69))
+>IfC : Symbol(IfC, Decl(conditionalTypes1.ts, 292, 69))
 >Eq : Symbol(Eq, Decl(conditionalTypes1.ts, 291, 16))
 >T : Symbol(T, Decl(conditionalTypes1.ts, 295, 15))
 >P : Symbol(P, Decl(conditionalTypes1.ts, 295, 49))
@@ -1172,14 +1172,14 @@ function f50() {
 >T : Symbol(T, Decl(conditionalTypes1.ts, 295, 15))
 
     type A = Omit<{ a: void; b: never; }>;  // 'a'
->A : Symbol(A, Decl(conditionalTypes1.ts, 295, 102))
->Omit : Symbol(Omit, Decl(conditionalTypes1.ts, 293, 47))
+>A : Symbol(A, Decl(conditionalTypes1.ts, 295, 103))
+>Omit : Symbol(Omit, Decl(conditionalTypes1.ts, 293, 48))
 >a : Symbol(a, Decl(conditionalTypes1.ts, 296, 19))
 >b : Symbol(b, Decl(conditionalTypes1.ts, 296, 28))
 
     type B = Omit2<{ a: void; b: never; }>;  // 'a'
 >B : Symbol(B, Decl(conditionalTypes1.ts, 296, 42))
->Omit2 : Symbol(Omit2, Decl(conditionalTypes1.ts, 294, 94))
+>Omit2 : Symbol(Omit2, Decl(conditionalTypes1.ts, 294, 95))
 >a : Symbol(a, Decl(conditionalTypes1.ts, 297, 20))
 >b : Symbol(b, Decl(conditionalTypes1.ts, 297, 29))
 }
diff --git a/tests/baselines/reference/conditionalTypes1.types b/tests/baselines/reference/conditionalTypes1.types
index 8cca94f0a1021..3d3fbfaec4fbb 100644
--- a/tests/baselines/reference/conditionalTypes1.types
+++ b/tests/baselines/reference/conditionalTypes1.types
@@ -518,21 +518,21 @@ type Extends<T, U> = T extends U ? true : false;
 >true : true
 >false : false
 
-type If<C extends boolean, T, F> = C extends true ? T : F;
->If : If<C, T, F>
+type IfC<C extends boolean, T, F> = C extends true ? T : F;
+>IfC : IfC<C, T, F>
 >true : true
 
-type Not<C extends boolean> = If<C, false, true>;
->Not : If<C, false, true>
+type Not<C extends boolean> = IfC<C, false, true>;
+>Not : IfC<C, false, true>
 >false : false
 >true : true
 
-type And<A extends boolean, B extends boolean> = If<A, B, false>;
->And : If<A, B, false>
+type And<A extends boolean, B extends boolean> = IfC<A, B, false>;
+>And : IfC<A, B, false>
 >false : false
 
-type Or<A extends boolean, B extends boolean> = If<A, true, B>;
->Or : If<A, true, B>
+type Or<A extends boolean, B extends boolean> = IfC<A, true, B>;
+>Or : IfC<A, true, B>
 >true : true
 
 type IsString<T> = Extends<T, string>;
@@ -909,14 +909,14 @@ function f50() {
 >false : false
 >false : false
 
-    type If<S, T, U> = S extends false ? U : T;
->If : S extends false ? U : T
+    type IfC<S, T, U> = S extends false ? U : T;
+>IfC : S extends false ? U : T
 >false : false
 
-    type Omit<T extends object> = { [P in keyof T]: If<Eq<T[P], never>, never, P>; }[keyof T];
+    type Omit<T extends object> = { [P in keyof T]: IfC<Eq<T[P], never>, never, P>; }[keyof T];
 >Omit : { [P in keyof T]: (T[P] extends never ? never : false) extends false ? P : never; }[keyof T]
 
-    type Omit2<T extends object, U = never> = { [P in keyof T]: If<Eq<T[P], U>, never, P>; }[keyof T];
+    type Omit2<T extends object, U = never> = { [P in keyof T]: IfC<Eq<T[P], U>, never, P>; }[keyof T];
 >Omit2 : { [P in keyof T]: (T[P] extends U ? U extends T[P] ? true : false : false) extends false ? P : never; }[keyof T]
 
     type A = Omit<{ a: void; b: never; }>;  // 'a'
diff --git a/tests/cases/conformance/types/conditional/conditionalTypes1.ts b/tests/cases/conformance/types/conditional/conditionalTypes1.ts
index 2e8690a228b45..91ccfec356e3b 100644
--- a/tests/cases/conformance/types/conditional/conditionalTypes1.ts
+++ b/tests/cases/conformance/types/conditional/conditionalTypes1.ts
@@ -169,10 +169,10 @@ type T37<T> = T extends { b: number } ? T extends { a: string } ? T35<T> : never
 type T38<T> = [T] extends [{ a: string }] ? [T] extends [{ b: number }] ? T35<T> : never : never;
 
 type Extends<T, U> = T extends U ? true : false;
-type If<C extends boolean, T, F> = C extends true ? T : F;
-type Not<C extends boolean> = If<C, false, true>;
-type And<A extends boolean, B extends boolean> = If<A, B, false>;
-type Or<A extends boolean, B extends boolean> = If<A, true, B>;
+type IfC<C extends boolean, T, F> = C extends true ? T : F;
+type Not<C extends boolean> = IfC<C, false, true>;
+type And<A extends boolean, B extends boolean> = IfC<A, B, false>;
+type Or<A extends boolean, B extends boolean> = IfC<A, true, B>;
 
 type IsString<T> = Extends<T, string>;
 
@@ -294,9 +294,9 @@ const f45 = <U>(value: T95<U>): T94<U> => value;  // Error
 
 function f50() {
     type Eq<T, U> = T extends U ? U extends T ? true : false : false;
-    type If<S, T, U> = S extends false ? U : T;
-    type Omit<T extends object> = { [P in keyof T]: If<Eq<T[P], never>, never, P>; }[keyof T];
-    type Omit2<T extends object, U = never> = { [P in keyof T]: If<Eq<T[P], U>, never, P>; }[keyof T];
+    type IfC<S, T, U> = S extends false ? U : T;
+    type Omit<T extends object> = { [P in keyof T]: IfC<Eq<T[P], never>, never, P>; }[keyof T];
+    type Omit2<T extends object, U = never> = { [P in keyof T]: IfC<Eq<T[P], U>, never, P>; }[keyof T];
     type A = Omit<{ a: void; b: never; }>;  // 'a'
     type B = Omit2<{ a: void; b: never; }>;  // 'a'
 }