mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
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
This commit is contained in:
+23
-32
@@ -14902,40 +14902,35 @@ namespace ts {
|
||||
return !!(type.flags & TypeFlags.TemplateLiteral) && every((type as TemplateLiteralType).types, isPatternLiteralPlaceholderType);
|
||||
}
|
||||
|
||||
function isGenericType(type: Type): boolean {
|
||||
return !!getGenericObjectFlags(type);
|
||||
}
|
||||
|
||||
function isGenericObjectType(type: Type): boolean {
|
||||
if (type.flags & TypeFlags.UnionOrIntersection) {
|
||||
if (!((type as UnionOrIntersectionType).objectFlags & ObjectFlags.IsGenericObjectTypeComputed)) {
|
||||
(type as UnionOrIntersectionType).objectFlags |= ObjectFlags.IsGenericObjectTypeComputed |
|
||||
(some((type as UnionOrIntersectionType).types, isGenericObjectType) ? ObjectFlags.IsGenericObjectType : 0);
|
||||
}
|
||||
return !!((type as UnionOrIntersectionType).objectFlags & ObjectFlags.IsGenericObjectType);
|
||||
}
|
||||
if (type.flags & TypeFlags.Substitution) {
|
||||
if (!((type as SubstitutionType).objectFlags & ObjectFlags.IsGenericObjectTypeComputed)) {
|
||||
(type as SubstitutionType).objectFlags |= ObjectFlags.IsGenericObjectTypeComputed |
|
||||
(isGenericObjectType((type as SubstitutionType).substitute) || isGenericObjectType((type as SubstitutionType).baseType) ? ObjectFlags.IsGenericObjectType : 0);
|
||||
}
|
||||
return !!((type as SubstitutionType).objectFlags & ObjectFlags.IsGenericObjectType);
|
||||
}
|
||||
return !!(type.flags & TypeFlags.InstantiableNonPrimitive) || isGenericMappedType(type) || isGenericTupleType(type);
|
||||
return !!(getGenericObjectFlags(type) & ObjectFlags.IsGenericObjectType);
|
||||
}
|
||||
|
||||
function isGenericIndexType(type: Type): boolean {
|
||||
return !!(getGenericObjectFlags(type) & ObjectFlags.IsGenericIndexType);
|
||||
}
|
||||
|
||||
function getGenericObjectFlags(type: Type): ObjectFlags {
|
||||
if (type.flags & TypeFlags.UnionOrIntersection) {
|
||||
if (!((type as UnionOrIntersectionType).objectFlags & ObjectFlags.IsGenericIndexTypeComputed)) {
|
||||
(type as UnionOrIntersectionType).objectFlags |= ObjectFlags.IsGenericIndexTypeComputed |
|
||||
(some((type as UnionOrIntersectionType).types, isGenericIndexType) ? ObjectFlags.IsGenericIndexType : 0);
|
||||
if (!((type as UnionOrIntersectionType).objectFlags & ObjectFlags.IsGenericTypeComputed)) {
|
||||
(type as UnionOrIntersectionType).objectFlags |= ObjectFlags.IsGenericTypeComputed |
|
||||
reduceLeft((type as UnionOrIntersectionType).types, (flags, t) => flags | getGenericObjectFlags(t), 0);
|
||||
}
|
||||
return !!((type as UnionOrIntersectionType).objectFlags & ObjectFlags.IsGenericIndexType);
|
||||
return (type as UnionOrIntersectionType).objectFlags & ObjectFlags.IsGenericType;
|
||||
}
|
||||
if (type.flags & TypeFlags.Substitution) {
|
||||
if (!((type as SubstitutionType).objectFlags & ObjectFlags.IsGenericIndexTypeComputed)) {
|
||||
(type as SubstitutionType).objectFlags |= ObjectFlags.IsGenericIndexTypeComputed |
|
||||
(isGenericIndexType((type as SubstitutionType).substitute) || isGenericIndexType((type as SubstitutionType).baseType) ? ObjectFlags.IsGenericIndexType : 0);
|
||||
if (!((type as SubstitutionType).objectFlags & ObjectFlags.IsGenericTypeComputed)) {
|
||||
(type as SubstitutionType).objectFlags |= ObjectFlags.IsGenericTypeComputed |
|
||||
getGenericObjectFlags((type as SubstitutionType).substitute) | getGenericObjectFlags((type as SubstitutionType).baseType);
|
||||
}
|
||||
return !!((type as SubstitutionType).objectFlags & ObjectFlags.IsGenericIndexType);
|
||||
return (type as SubstitutionType).objectFlags & ObjectFlags.IsGenericType;
|
||||
}
|
||||
return !!(type.flags & (TypeFlags.InstantiableNonPrimitive | TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping)) && !isPatternLiteralType(type);
|
||||
return (type.flags & TypeFlags.InstantiableNonPrimitive || isGenericMappedType(type) || isGenericTupleType(type) ? ObjectFlags.IsGenericObjectType : 0) |
|
||||
(type.flags & (TypeFlags.InstantiableNonPrimitive | TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping) && !isPatternLiteralType(type) ? ObjectFlags.IsGenericIndexType : 0);
|
||||
}
|
||||
|
||||
function isThisTypeParameter(type: Type): boolean {
|
||||
@@ -15212,7 +15207,7 @@ namespace ts {
|
||||
while (true) {
|
||||
const isUnwrapped = isTypicalNondistributiveConditional(root);
|
||||
const checkType = instantiateType(unwrapNondistributiveConditionalTuple(root, getActualTypeVariable(root.checkType)), mapper);
|
||||
const checkTypeInstantiable = isGenericObjectType(checkType) || isGenericIndexType(checkType);
|
||||
const checkTypeInstantiable = isGenericType(checkType);
|
||||
const extendsType = instantiateType(unwrapNondistributiveConditionalTuple(root, root.extendsType), mapper);
|
||||
if (checkType === wildcardType || extendsType === wildcardType) {
|
||||
return wildcardType;
|
||||
@@ -15234,7 +15229,7 @@ namespace ts {
|
||||
// Instantiate the extends type including inferences for 'infer T' type parameters
|
||||
const inferredExtendsType = combinedMapper ? instantiateType(unwrapNondistributiveConditionalTuple(root, root.extendsType), combinedMapper) : extendsType;
|
||||
// We attempt to resolve the conditional type only when the check and extends types are non-generic
|
||||
if (!checkTypeInstantiable && !isGenericObjectType(inferredExtendsType) && !isGenericIndexType(inferredExtendsType)) {
|
||||
if (!checkTypeInstantiable && !isGenericType(inferredExtendsType)) {
|
||||
// Return falseType for a definitely false extends check. We check an instantiations of the two
|
||||
// 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,
|
||||
@@ -24269,17 +24264,13 @@ namespace ts {
|
||||
return !!(type.flags & TypeFlags.Instantiable && !maybeTypeOfKind(getBaseConstraintOrType(type), TypeFlags.Nullable));
|
||||
}
|
||||
|
||||
function containsGenericType(type: Type): boolean {
|
||||
return !!(type.flags & TypeFlags.Instantiable || type.flags & TypeFlags.UnionOrIntersection && some((type as UnionOrIntersectionType).types, containsGenericType));
|
||||
}
|
||||
|
||||
function hasNonBindingPatternContextualTypeWithNoGenericTypes(node: Node) {
|
||||
// Computing the contextual type for a child of a JSX element involves resolving the type of the
|
||||
// element's tag name, so we exclude that here to avoid circularities.
|
||||
const contextualType = (isIdentifier(node) || isPropertyAccessExpression(node) || isElementAccessExpression(node)) &&
|
||||
!((isJsxOpeningElement(node.parent) || isJsxSelfClosingElement(node.parent)) && node.parent.tagName === node) &&
|
||||
getContextualType(node, ContextFlags.SkipBindingPatterns);
|
||||
return contextualType && !someType(contextualType, containsGenericType);
|
||||
return contextualType && !isGenericType(contextualType);
|
||||
}
|
||||
|
||||
function getNarrowableTypeForReference(type: Type, reference: Node, checkMode?: CheckMode) {
|
||||
@@ -41659,7 +41650,7 @@ namespace ts {
|
||||
return grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_must_have_a_type_annotation);
|
||||
}
|
||||
const type = getTypeFromTypeNode(parameter.type);
|
||||
if (someType(type, t => !!(t.flags & TypeFlags.StringOrNumberLiteralOrUnique)) || isGenericIndexType(type) || isGenericObjectType(type)) {
|
||||
if (someType(type, t => !!(t.flags & TypeFlags.StringOrNumberLiteralOrUnique)) || isGenericType(type)) {
|
||||
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);
|
||||
}
|
||||
if (!everyType(type, isValidIndexKeyType)) {
|
||||
|
||||
@@ -5266,23 +5266,23 @@ namespace ts {
|
||||
|
||||
// Flags that require TypeFlags.UnionOrIntersection or TypeFlags.Substitution
|
||||
/* @internal */
|
||||
IsGenericObjectTypeComputed = 1 << 22, // IsGenericObjectType flag has been computed
|
||||
IsGenericTypeComputed = 1 << 22, // IsGenericObjectType flag has been computed
|
||||
/* @internal */
|
||||
IsGenericObjectType = 1 << 23, // Union or intersection contains generic object type
|
||||
/* @internal */
|
||||
IsGenericIndexTypeComputed = 1 << 24, // IsGenericIndexType flag has been computed
|
||||
IsGenericIndexType = 1 << 24, // Union or intersection contains generic index type
|
||||
/* @internal */
|
||||
IsGenericIndexType = 1 << 25, // Union or intersection contains generic index type
|
||||
IsGenericType = IsGenericObjectType | IsGenericIndexType,
|
||||
|
||||
// Flags that require TypeFlags.Union
|
||||
/* @internal */
|
||||
ContainsIntersections = 1 << 26, // Union contains intersections
|
||||
ContainsIntersections = 1 << 25, // Union contains intersections
|
||||
|
||||
// Flags that require TypeFlags.Intersection
|
||||
/* @internal */
|
||||
IsNeverIntersectionComputed = 1 << 26, // IsNeverLike flag has been computed
|
||||
IsNeverIntersectionComputed = 1 << 25, // IsNeverLike flag has been computed
|
||||
/* @internal */
|
||||
IsNeverIntersection = 1 << 27, // Intersection reduces to never
|
||||
IsNeverIntersection = 1 << 26, // Intersection reduces to never
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
|
||||
@@ -204,4 +204,20 @@ tests/cases/conformance/controlFlow/controlFlowGenericTypes.ts(168,9): error TS2
|
||||
iSpec[null! as keyof PublicSpec];
|
||||
}
|
||||
}
|
||||
|
||||
// Repros from #45145
|
||||
|
||||
function f10<T extends { a: string } | undefined>(x: T, y: Partial<T>) {
|
||||
y = x;
|
||||
}
|
||||
|
||||
type SqlInsertSet<T> = T extends undefined ? object : { [P in keyof T]: unknown };
|
||||
|
||||
class SqlTable<T> {
|
||||
protected validateRow(_row: Partial<SqlInsertSet<T>>): void {
|
||||
}
|
||||
public insertRow(row: SqlInsertSet<T>) {
|
||||
this.validateRow(row);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,6 +174,22 @@ class TableBaseEnum<
|
||||
iSpec[null! as keyof PublicSpec];
|
||||
}
|
||||
}
|
||||
|
||||
// Repros from #45145
|
||||
|
||||
function f10<T extends { a: string } | undefined>(x: T, y: Partial<T>) {
|
||||
y = x;
|
||||
}
|
||||
|
||||
type SqlInsertSet<T> = T extends undefined ? object : { [P in keyof T]: unknown };
|
||||
|
||||
class SqlTable<T> {
|
||||
protected validateRow(_row: Partial<SqlInsertSet<T>>): void {
|
||||
}
|
||||
public insertRow(row: SqlInsertSet<T>) {
|
||||
this.validateRow(row);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//// [controlFlowGenericTypes.js]
|
||||
@@ -313,3 +329,17 @@ var TableBaseEnum = /** @class */ (function () {
|
||||
};
|
||||
return TableBaseEnum;
|
||||
}());
|
||||
// Repros from #45145
|
||||
function f10(x, y) {
|
||||
y = x;
|
||||
}
|
||||
var SqlTable = /** @class */ (function () {
|
||||
function SqlTable() {
|
||||
}
|
||||
SqlTable.prototype.validateRow = function (_row) {
|
||||
};
|
||||
SqlTable.prototype.insertRow = function (row) {
|
||||
this.validateRow(row);
|
||||
};
|
||||
return SqlTable;
|
||||
}());
|
||||
|
||||
@@ -525,3 +525,52 @@ class TableBaseEnum<
|
||||
}
|
||||
}
|
||||
|
||||
// Repros from #45145
|
||||
|
||||
function f10<T extends { a: string } | undefined>(x: T, y: Partial<T>) {
|
||||
>f10 : Symbol(f10, Decl(controlFlowGenericTypes.ts, 174, 1))
|
||||
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 178, 13))
|
||||
>a : Symbol(a, Decl(controlFlowGenericTypes.ts, 178, 24))
|
||||
>x : Symbol(x, Decl(controlFlowGenericTypes.ts, 178, 50))
|
||||
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 178, 13))
|
||||
>y : Symbol(y, Decl(controlFlowGenericTypes.ts, 178, 55))
|
||||
>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --))
|
||||
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 178, 13))
|
||||
|
||||
y = x;
|
||||
>y : Symbol(y, Decl(controlFlowGenericTypes.ts, 178, 55))
|
||||
>x : Symbol(x, Decl(controlFlowGenericTypes.ts, 178, 50))
|
||||
}
|
||||
|
||||
type SqlInsertSet<T> = T extends undefined ? object : { [P in keyof T]: unknown };
|
||||
>SqlInsertSet : Symbol(SqlInsertSet, Decl(controlFlowGenericTypes.ts, 180, 1))
|
||||
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 182, 18))
|
||||
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 182, 18))
|
||||
>P : Symbol(P, Decl(controlFlowGenericTypes.ts, 182, 57))
|
||||
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 182, 18))
|
||||
|
||||
class SqlTable<T> {
|
||||
>SqlTable : Symbol(SqlTable, Decl(controlFlowGenericTypes.ts, 182, 82))
|
||||
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 184, 15))
|
||||
|
||||
protected validateRow(_row: Partial<SqlInsertSet<T>>): void {
|
||||
>validateRow : Symbol(SqlTable.validateRow, Decl(controlFlowGenericTypes.ts, 184, 19))
|
||||
>_row : Symbol(_row, Decl(controlFlowGenericTypes.ts, 185, 26))
|
||||
>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --))
|
||||
>SqlInsertSet : Symbol(SqlInsertSet, Decl(controlFlowGenericTypes.ts, 180, 1))
|
||||
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 184, 15))
|
||||
}
|
||||
public insertRow(row: SqlInsertSet<T>) {
|
||||
>insertRow : Symbol(SqlTable.insertRow, Decl(controlFlowGenericTypes.ts, 186, 5))
|
||||
>row : Symbol(row, Decl(controlFlowGenericTypes.ts, 187, 21))
|
||||
>SqlInsertSet : Symbol(SqlInsertSet, Decl(controlFlowGenericTypes.ts, 180, 1))
|
||||
>T : Symbol(T, Decl(controlFlowGenericTypes.ts, 184, 15))
|
||||
|
||||
this.validateRow(row);
|
||||
>this.validateRow : Symbol(SqlTable.validateRow, Decl(controlFlowGenericTypes.ts, 184, 19))
|
||||
>this : Symbol(SqlTable, Decl(controlFlowGenericTypes.ts, 182, 82))
|
||||
>validateRow : Symbol(SqlTable.validateRow, Decl(controlFlowGenericTypes.ts, 184, 19))
|
||||
>row : Symbol(row, Decl(controlFlowGenericTypes.ts, 187, 21))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -505,3 +505,40 @@ class TableBaseEnum<
|
||||
}
|
||||
}
|
||||
|
||||
// Repros from #45145
|
||||
|
||||
function f10<T extends { a: string } | undefined>(x: T, y: Partial<T>) {
|
||||
>f10 : <T extends { a: string; } | undefined>(x: T, y: Partial<T>) => void
|
||||
>a : string
|
||||
>x : T
|
||||
>y : Partial<T>
|
||||
|
||||
y = x;
|
||||
>y = x : T
|
||||
>y : Partial<T>
|
||||
>x : T
|
||||
}
|
||||
|
||||
type SqlInsertSet<T> = T extends undefined ? object : { [P in keyof T]: unknown };
|
||||
>SqlInsertSet : SqlInsertSet<T>
|
||||
|
||||
class SqlTable<T> {
|
||||
>SqlTable : SqlTable<T>
|
||||
|
||||
protected validateRow(_row: Partial<SqlInsertSet<T>>): void {
|
||||
>validateRow : (_row: Partial<SqlInsertSet<T>>) => void
|
||||
>_row : Partial<SqlInsertSet<T>>
|
||||
}
|
||||
public insertRow(row: SqlInsertSet<T>) {
|
||||
>insertRow : (row: SqlInsertSet<T>) => void
|
||||
>row : SqlInsertSet<T>
|
||||
|
||||
this.validateRow(row);
|
||||
>this.validateRow(row) : void
|
||||
>this.validateRow : (_row: Partial<SqlInsertSet<T>>) => void
|
||||
>this : this
|
||||
>validateRow : (_row: Partial<SqlInsertSet<T>>) => void
|
||||
>row : SqlInsertSet<T>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -175,3 +175,19 @@ class TableBaseEnum<
|
||||
iSpec[null! as keyof PublicSpec];
|
||||
}
|
||||
}
|
||||
|
||||
// Repros from #45145
|
||||
|
||||
function f10<T extends { a: string } | undefined>(x: T, y: Partial<T>) {
|
||||
y = x;
|
||||
}
|
||||
|
||||
type SqlInsertSet<T> = T extends undefined ? object : { [P in keyof T]: unknown };
|
||||
|
||||
class SqlTable<T> {
|
||||
protected validateRow(_row: Partial<SqlInsertSet<T>>): void {
|
||||
}
|
||||
public insertRow(row: SqlInsertSet<T>) {
|
||||
this.validateRow(row);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user