Skip to content

Commit 3bd5079

Browse files
authored
Fix check for generic types in control flow analysis (#45148)
* Fix check in hasNonBindingPatternContextualTypeWithNoGenericTypes * Add regression tests * Accept new baselines * Compute both ObjectFlags.IsGenericXXXType flags in one go
1 parent 54898de commit 3bd5079

File tree

7 files changed

+177
-38
lines changed

7 files changed

+177
-38
lines changed

src/compiler/checker.ts

Lines changed: 23 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -14902,40 +14902,35 @@ namespace ts {
1490214902
return !!(type.flags & TypeFlags.TemplateLiteral) && every((type as TemplateLiteralType).types, isPatternLiteralPlaceholderType);
1490314903
}
1490414904

14905+
function isGenericType(type: Type): boolean {
14906+
return !!getGenericObjectFlags(type);
14907+
}
14908+
1490514909
function isGenericObjectType(type: Type): boolean {
14906-
if (type.flags & TypeFlags.UnionOrIntersection) {
14907-
if (!((type as UnionOrIntersectionType).objectFlags & ObjectFlags.IsGenericObjectTypeComputed)) {
14908-
(type as UnionOrIntersectionType).objectFlags |= ObjectFlags.IsGenericObjectTypeComputed |
14909-
(some((type as UnionOrIntersectionType).types, isGenericObjectType) ? ObjectFlags.IsGenericObjectType : 0);
14910-
}
14911-
return !!((type as UnionOrIntersectionType).objectFlags & ObjectFlags.IsGenericObjectType);
14912-
}
14913-
if (type.flags & TypeFlags.Substitution) {
14914-
if (!((type as SubstitutionType).objectFlags & ObjectFlags.IsGenericObjectTypeComputed)) {
14915-
(type as SubstitutionType).objectFlags |= ObjectFlags.IsGenericObjectTypeComputed |
14916-
(isGenericObjectType((type as SubstitutionType).substitute) || isGenericObjectType((type as SubstitutionType).baseType) ? ObjectFlags.IsGenericObjectType : 0);
14917-
}
14918-
return !!((type as SubstitutionType).objectFlags & ObjectFlags.IsGenericObjectType);
14919-
}
14920-
return !!(type.flags & TypeFlags.InstantiableNonPrimitive) || isGenericMappedType(type) || isGenericTupleType(type);
14910+
return !!(getGenericObjectFlags(type) & ObjectFlags.IsGenericObjectType);
1492114911
}
1492214912

1492314913
function isGenericIndexType(type: Type): boolean {
14914+
return !!(getGenericObjectFlags(type) & ObjectFlags.IsGenericIndexType);
14915+
}
14916+
14917+
function getGenericObjectFlags(type: Type): ObjectFlags {
1492414918
if (type.flags & TypeFlags.UnionOrIntersection) {
14925-
if (!((type as UnionOrIntersectionType).objectFlags & ObjectFlags.IsGenericIndexTypeComputed)) {
14926-
(type as UnionOrIntersectionType).objectFlags |= ObjectFlags.IsGenericIndexTypeComputed |
14927-
(some((type as UnionOrIntersectionType).types, isGenericIndexType) ? ObjectFlags.IsGenericIndexType : 0);
14919+
if (!((type as UnionOrIntersectionType).objectFlags & ObjectFlags.IsGenericTypeComputed)) {
14920+
(type as UnionOrIntersectionType).objectFlags |= ObjectFlags.IsGenericTypeComputed |
14921+
reduceLeft((type as UnionOrIntersectionType).types, (flags, t) => flags | getGenericObjectFlags(t), 0);
1492814922
}
14929-
return !!((type as UnionOrIntersectionType).objectFlags & ObjectFlags.IsGenericIndexType);
14923+
return (type as UnionOrIntersectionType).objectFlags & ObjectFlags.IsGenericType;
1493014924
}
1493114925
if (type.flags & TypeFlags.Substitution) {
14932-
if (!((type as SubstitutionType).objectFlags & ObjectFlags.IsGenericIndexTypeComputed)) {
14933-
(type as SubstitutionType).objectFlags |= ObjectFlags.IsGenericIndexTypeComputed |
14934-
(isGenericIndexType((type as SubstitutionType).substitute) || isGenericIndexType((type as SubstitutionType).baseType) ? ObjectFlags.IsGenericIndexType : 0);
14926+
if (!((type as SubstitutionType).objectFlags & ObjectFlags.IsGenericTypeComputed)) {
14927+
(type as SubstitutionType).objectFlags |= ObjectFlags.IsGenericTypeComputed |
14928+
getGenericObjectFlags((type as SubstitutionType).substitute) | getGenericObjectFlags((type as SubstitutionType).baseType);
1493514929
}
14936-
return !!((type as SubstitutionType).objectFlags & ObjectFlags.IsGenericIndexType);
14930+
return (type as SubstitutionType).objectFlags & ObjectFlags.IsGenericType;
1493714931
}
14938-
return !!(type.flags & (TypeFlags.InstantiableNonPrimitive | TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping)) && !isPatternLiteralType(type);
14932+
return (type.flags & TypeFlags.InstantiableNonPrimitive || isGenericMappedType(type) || isGenericTupleType(type) ? ObjectFlags.IsGenericObjectType : 0) |
14933+
(type.flags & (TypeFlags.InstantiableNonPrimitive | TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping) && !isPatternLiteralType(type) ? ObjectFlags.IsGenericIndexType : 0);
1493914934
}
1494014935

1494114936
function isThisTypeParameter(type: Type): boolean {
@@ -15212,7 +15207,7 @@ namespace ts {
1521215207
while (true) {
1521315208
const isUnwrapped = isTypicalNondistributiveConditional(root);
1521415209
const checkType = instantiateType(unwrapNondistributiveConditionalTuple(root, getActualTypeVariable(root.checkType)), mapper);
15215-
const checkTypeInstantiable = isGenericObjectType(checkType) || isGenericIndexType(checkType);
15210+
const checkTypeInstantiable = isGenericType(checkType);
1521615211
const extendsType = instantiateType(unwrapNondistributiveConditionalTuple(root, root.extendsType), mapper);
1521715212
if (checkType === wildcardType || extendsType === wildcardType) {
1521815213
return wildcardType;
@@ -15234,7 +15229,7 @@ namespace ts {
1523415229
// Instantiate the extends type including inferences for 'infer T' type parameters
1523515230
const inferredExtendsType = combinedMapper ? instantiateType(unwrapNondistributiveConditionalTuple(root, root.extendsType), combinedMapper) : extendsType;
1523615231
// We attempt to resolve the conditional type only when the check and extends types are non-generic
15237-
if (!checkTypeInstantiable && !isGenericObjectType(inferredExtendsType) && !isGenericIndexType(inferredExtendsType)) {
15232+
if (!checkTypeInstantiable && !isGenericType(inferredExtendsType)) {
1523815233
// Return falseType for a definitely false extends check. We check an instantiations of the two
1523915234
// types with type parameters mapped to the wildcard type, the most permissive instantiations
1524015235
// possible (the wildcard type is assignable to and from all types). If those are not related,
@@ -24269,17 +24264,13 @@ namespace ts {
2426924264
return !!(type.flags & TypeFlags.Instantiable && !maybeTypeOfKind(getBaseConstraintOrType(type), TypeFlags.Nullable));
2427024265
}
2427124266

24272-
function containsGenericType(type: Type): boolean {
24273-
return !!(type.flags & TypeFlags.Instantiable || type.flags & TypeFlags.UnionOrIntersection && some((type as UnionOrIntersectionType).types, containsGenericType));
24274-
}
24275-
2427624267
function hasNonBindingPatternContextualTypeWithNoGenericTypes(node: Node) {
2427724268
// Computing the contextual type for a child of a JSX element involves resolving the type of the
2427824269
// element's tag name, so we exclude that here to avoid circularities.
2427924270
const contextualType = (isIdentifier(node) || isPropertyAccessExpression(node) || isElementAccessExpression(node)) &&
2428024271
!((isJsxOpeningElement(node.parent) || isJsxSelfClosingElement(node.parent)) && node.parent.tagName === node) &&
2428124272
getContextualType(node, ContextFlags.SkipBindingPatterns);
24282-
return contextualType && !someType(contextualType, containsGenericType);
24273+
return contextualType && !isGenericType(contextualType);
2428324274
}
2428424275

2428524276
function getNarrowableTypeForReference(type: Type, reference: Node, checkMode?: CheckMode) {
@@ -41659,7 +41650,7 @@ namespace ts {
4165941650
return grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_must_have_a_type_annotation);
4166041651
}
4166141652
const type = getTypeFromTypeNode(parameter.type);
41662-
if (someType(type, t => !!(t.flags & TypeFlags.StringOrNumberLiteralOrUnique)) || isGenericIndexType(type) || isGenericObjectType(type)) {
41653+
if (someType(type, t => !!(t.flags & TypeFlags.StringOrNumberLiteralOrUnique)) || isGenericType(type)) {
4166341654
return grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_type_cannot_be_a_literal_type_or_generic_type_Consider_using_a_mapped_object_type_instead);
4166441655
}
4166541656
if (!everyType(type, isValidIndexKeyType)) {

src/compiler/types.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5266,23 +5266,23 @@ namespace ts {
52665266

52675267
// Flags that require TypeFlags.UnionOrIntersection or TypeFlags.Substitution
52685268
/* @internal */
5269-
IsGenericObjectTypeComputed = 1 << 22, // IsGenericObjectType flag has been computed
5269+
IsGenericTypeComputed = 1 << 22, // IsGenericObjectType flag has been computed
52705270
/* @internal */
52715271
IsGenericObjectType = 1 << 23, // Union or intersection contains generic object type
52725272
/* @internal */
5273-
IsGenericIndexTypeComputed = 1 << 24, // IsGenericIndexType flag has been computed
5273+
IsGenericIndexType = 1 << 24, // Union or intersection contains generic index type
52745274
/* @internal */
5275-
IsGenericIndexType = 1 << 25, // Union or intersection contains generic index type
5275+
IsGenericType = IsGenericObjectType | IsGenericIndexType,
52765276

52775277
// Flags that require TypeFlags.Union
52785278
/* @internal */
5279-
ContainsIntersections = 1 << 26, // Union contains intersections
5279+
ContainsIntersections = 1 << 25, // Union contains intersections
52805280

52815281
// Flags that require TypeFlags.Intersection
52825282
/* @internal */
5283-
IsNeverIntersectionComputed = 1 << 26, // IsNeverLike flag has been computed
5283+
IsNeverIntersectionComputed = 1 << 25, // IsNeverLike flag has been computed
52845284
/* @internal */
5285-
IsNeverIntersection = 1 << 27, // Intersection reduces to never
5285+
IsNeverIntersection = 1 << 26, // Intersection reduces to never
52865286
}
52875287

52885288
/* @internal */

tests/baselines/reference/controlFlowGenericTypes.errors.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,4 +204,20 @@ tests/cases/conformance/controlFlow/controlFlowGenericTypes.ts(168,9): error TS2
204204
iSpec[null! as keyof PublicSpec];
205205
}
206206
}
207+
208+
// Repros from #45145
209+
210+
function f10<T extends { a: string } | undefined>(x: T, y: Partial<T>) {
211+
y = x;
212+
}
213+
214+
type SqlInsertSet<T> = T extends undefined ? object : { [P in keyof T]: unknown };
215+
216+
class SqlTable<T> {
217+
protected validateRow(_row: Partial<SqlInsertSet<T>>): void {
218+
}
219+
public insertRow(row: SqlInsertSet<T>) {
220+
this.validateRow(row);
221+
}
222+
}
207223

tests/baselines/reference/controlFlowGenericTypes.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,22 @@ class TableBaseEnum<
174174
iSpec[null! as keyof PublicSpec];
175175
}
176176
}
177+
178+
// Repros from #45145
179+
180+
function f10<T extends { a: string } | undefined>(x: T, y: Partial<T>) {
181+
y = x;
182+
}
183+
184+
type SqlInsertSet<T> = T extends undefined ? object : { [P in keyof T]: unknown };
185+
186+
class SqlTable<T> {
187+
protected validateRow(_row: Partial<SqlInsertSet<T>>): void {
188+
}
189+
public insertRow(row: SqlInsertSet<T>) {
190+
this.validateRow(row);
191+
}
192+
}
177193

178194

179195
//// [controlFlowGenericTypes.js]
@@ -313,3 +329,17 @@ var TableBaseEnum = /** @class */ (function () {
313329
};
314330
return TableBaseEnum;
315331
}());
332+
// Repros from #45145
333+
function f10(x, y) {
334+
y = x;
335+
}
336+
var SqlTable = /** @class */ (function () {
337+
function SqlTable() {
338+
}
339+
SqlTable.prototype.validateRow = function (_row) {
340+
};
341+
SqlTable.prototype.insertRow = function (row) {
342+
this.validateRow(row);
343+
};
344+
return SqlTable;
345+
}());

tests/baselines/reference/controlFlowGenericTypes.symbols

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,3 +525,52 @@ class TableBaseEnum<
525525
}
526526
}
527527

528+
// Repros from #45145
529+
530+
function f10<T extends { a: string } | undefined>(x: T, y: Partial<T>) {
531+
>f10 : Symbol(f10, Decl(controlFlowGenericTypes.ts, 174, 1))
532+
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 178, 13))
533+
>a : Symbol(a, Decl(controlFlowGenericTypes.ts, 178, 24))
534+
>x : Symbol(x, Decl(controlFlowGenericTypes.ts, 178, 50))
535+
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 178, 13))
536+
>y : Symbol(y, Decl(controlFlowGenericTypes.ts, 178, 55))
537+
>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --))
538+
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 178, 13))
539+
540+
y = x;
541+
>y : Symbol(y, Decl(controlFlowGenericTypes.ts, 178, 55))
542+
>x : Symbol(x, Decl(controlFlowGenericTypes.ts, 178, 50))
543+
}
544+
545+
type SqlInsertSet<T> = T extends undefined ? object : { [P in keyof T]: unknown };
546+
>SqlInsertSet : Symbol(SqlInsertSet, Decl(controlFlowGenericTypes.ts, 180, 1))
547+
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 182, 18))
548+
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 182, 18))
549+
>P : Symbol(P, Decl(controlFlowGenericTypes.ts, 182, 57))
550+
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 182, 18))
551+
552+
class SqlTable<T> {
553+
>SqlTable : Symbol(SqlTable, Decl(controlFlowGenericTypes.ts, 182, 82))
554+
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 184, 15))
555+
556+
protected validateRow(_row: Partial<SqlInsertSet<T>>): void {
557+
>validateRow : Symbol(SqlTable.validateRow, Decl(controlFlowGenericTypes.ts, 184, 19))
558+
>_row : Symbol(_row, Decl(controlFlowGenericTypes.ts, 185, 26))
559+
>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --))
560+
>SqlInsertSet : Symbol(SqlInsertSet, Decl(controlFlowGenericTypes.ts, 180, 1))
561+
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 184, 15))
562+
}
563+
public insertRow(row: SqlInsertSet<T>) {
564+
>insertRow : Symbol(SqlTable.insertRow, Decl(controlFlowGenericTypes.ts, 186, 5))
565+
>row : Symbol(row, Decl(controlFlowGenericTypes.ts, 187, 21))
566+
>SqlInsertSet : Symbol(SqlInsertSet, Decl(controlFlowGenericTypes.ts, 180, 1))
567+
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 184, 15))
568+
569+
this.validateRow(row);
570+
>this.validateRow : Symbol(SqlTable.validateRow, Decl(controlFlowGenericTypes.ts, 184, 19))
571+
>this : Symbol(SqlTable, Decl(controlFlowGenericTypes.ts, 182, 82))
572+
>validateRow : Symbol(SqlTable.validateRow, Decl(controlFlowGenericTypes.ts, 184, 19))
573+
>row : Symbol(row, Decl(controlFlowGenericTypes.ts, 187, 21))
574+
}
575+
}
576+

tests/baselines/reference/controlFlowGenericTypes.types

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,3 +505,40 @@ class TableBaseEnum<
505505
}
506506
}
507507

508+
// Repros from #45145
509+
510+
function f10<T extends { a: string } | undefined>(x: T, y: Partial<T>) {
511+
>f10 : <T extends { a: string; } | undefined>(x: T, y: Partial<T>) => void
512+
>a : string
513+
>x : T
514+
>y : Partial<T>
515+
516+
y = x;
517+
>y = x : T
518+
>y : Partial<T>
519+
>x : T
520+
}
521+
522+
type SqlInsertSet<T> = T extends undefined ? object : { [P in keyof T]: unknown };
523+
>SqlInsertSet : SqlInsertSet<T>
524+
525+
class SqlTable<T> {
526+
>SqlTable : SqlTable<T>
527+
528+
protected validateRow(_row: Partial<SqlInsertSet<T>>): void {
529+
>validateRow : (_row: Partial<SqlInsertSet<T>>) => void
530+
>_row : Partial<SqlInsertSet<T>>
531+
}
532+
public insertRow(row: SqlInsertSet<T>) {
533+
>insertRow : (row: SqlInsertSet<T>) => void
534+
>row : SqlInsertSet<T>
535+
536+
this.validateRow(row);
537+
>this.validateRow(row) : void
538+
>this.validateRow : (_row: Partial<SqlInsertSet<T>>) => void
539+
>this : this
540+
>validateRow : (_row: Partial<SqlInsertSet<T>>) => void
541+
>row : SqlInsertSet<T>
542+
}
543+
}
544+

tests/cases/conformance/controlFlow/controlFlowGenericTypes.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,3 +175,19 @@ class TableBaseEnum<
175175
iSpec[null! as keyof PublicSpec];
176176
}
177177
}
178+
179+
// Repros from #45145
180+
181+
function f10<T extends { a: string } | undefined>(x: T, y: Partial<T>) {
182+
y = x;
183+
}
184+
185+
type SqlInsertSet<T> = T extends undefined ? object : { [P in keyof T]: unknown };
186+
187+
class SqlTable<T> {
188+
protected validateRow(_row: Partial<SqlInsertSet<T>>): void {
189+
}
190+
public insertRow(row: SqlInsertSet<T>) {
191+
this.validateRow(row);
192+
}
193+
}

0 commit comments

Comments
 (0)