mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-16 15:45:27 -05:00
Merge pull request #17404 from Microsoft/use-type-param-constraints-for-computed-prop-types
Use type parameter constraints for computed property types
This commit is contained in:
@@ -7556,11 +7556,11 @@ namespace ts {
|
||||
return getTypeOfSymbol(prop);
|
||||
}
|
||||
}
|
||||
if (isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbol)) {
|
||||
if (!(indexType.flags & TypeFlags.Nullable) && isTypeAssignableToKind(indexType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbol)) {
|
||||
if (isTypeAny(objectType)) {
|
||||
return anyType;
|
||||
}
|
||||
const indexInfo = isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.NumberLike) && getIndexInfoOfType(objectType, IndexKind.Number) ||
|
||||
const indexInfo = isTypeAssignableToKind(indexType, TypeFlags.NumberLike) && getIndexInfoOfType(objectType, IndexKind.Number) ||
|
||||
getIndexInfoOfType(objectType, IndexKind.String) ||
|
||||
undefined;
|
||||
if (indexInfo) {
|
||||
@@ -11384,7 +11384,7 @@ namespace ts {
|
||||
(<BinaryExpression>parent.parent).operatorToken.kind === SyntaxKind.EqualsToken &&
|
||||
(<BinaryExpression>parent.parent).left === parent &&
|
||||
!isAssignmentTarget(parent.parent) &&
|
||||
isTypeAnyOrAllConstituentTypesHaveKind(getTypeOfExpression((<ElementAccessExpression>parent).argumentExpression), TypeFlags.NumberLike | TypeFlags.Undefined);
|
||||
isTypeAssignableToKind(getTypeOfExpression((<ElementAccessExpression>parent).argumentExpression), TypeFlags.NumberLike);
|
||||
return isLengthPushOrUnshift || isElementAssignment;
|
||||
}
|
||||
|
||||
@@ -11556,7 +11556,7 @@ namespace ts {
|
||||
}
|
||||
else {
|
||||
const indexType = getTypeOfExpression((<ElementAccessExpression>(<BinaryExpression>node).left).argumentExpression);
|
||||
if (isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.NumberLike | TypeFlags.Undefined)) {
|
||||
if (isTypeAssignableToKind(indexType, TypeFlags.NumberLike)) {
|
||||
evolvedType = addEvolvingArrayElementType(evolvedType, (<BinaryExpression>node).right);
|
||||
}
|
||||
}
|
||||
@@ -13398,11 +13398,7 @@ namespace ts {
|
||||
function isNumericComputedName(name: ComputedPropertyName): boolean {
|
||||
// It seems odd to consider an expression of type Any to result in a numeric name,
|
||||
// but this behavior is consistent with checkIndexedAccess
|
||||
return isTypeAnyOrAllConstituentTypesHaveKind(checkComputedPropertyName(name), TypeFlags.NumberLike);
|
||||
}
|
||||
|
||||
function isTypeAnyOrAllConstituentTypesHaveKind(type: Type, kind: TypeFlags): boolean {
|
||||
return isTypeAny(type) || isTypeOfKind(type, kind);
|
||||
return isTypeAssignableToKind(checkComputedPropertyName(name), TypeFlags.NumberLike);
|
||||
}
|
||||
|
||||
function isInfinityOrNaNString(name: string | __String): boolean {
|
||||
@@ -13438,10 +13434,11 @@ namespace ts {
|
||||
const links = getNodeLinks(node.expression);
|
||||
if (!links.resolvedType) {
|
||||
links.resolvedType = checkExpression(node.expression);
|
||||
|
||||
// This will allow types number, string, symbol or any. It will also allow enums, the unknown
|
||||
// type, and any union of these types (like string | number).
|
||||
if (!isTypeAnyOrAllConstituentTypesHaveKind(links.resolvedType, TypeFlags.NumberLike | TypeFlags.StringLike | TypeFlags.ESSymbol)) {
|
||||
if (links.resolvedType.flags & TypeFlags.Nullable ||
|
||||
!isTypeAssignableToKind(links.resolvedType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbol) &&
|
||||
!isTypeAssignableTo(links.resolvedType, getUnionType([stringType, numberType, esSymbolType]))) {
|
||||
error(node, Diagnostics.A_computed_property_name_must_be_of_type_string_number_symbol_or_any);
|
||||
}
|
||||
else {
|
||||
@@ -15546,7 +15543,7 @@ namespace ts {
|
||||
|
||||
case SyntaxKind.ComputedPropertyName:
|
||||
const nameType = checkComputedPropertyName(element.name);
|
||||
if (isTypeOfKind(nameType, TypeFlags.ESSymbol)) {
|
||||
if (isTypeAssignableToKind(nameType, TypeFlags.ESSymbol)) {
|
||||
return nameType;
|
||||
}
|
||||
else {
|
||||
@@ -16936,7 +16933,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function checkArithmeticOperandType(operand: Node, type: Type, diagnostic: DiagnosticMessage): boolean {
|
||||
if (!isTypeAnyOrAllConstituentTypesHaveKind(type, TypeFlags.NumberLike)) {
|
||||
if (!isTypeAssignableToKind(type, TypeFlags.NumberLike)) {
|
||||
error(operand, diagnostic);
|
||||
return false;
|
||||
}
|
||||
@@ -17114,31 +17111,22 @@ namespace ts {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return true if type is of the given kind. A union type is of a given kind if all constituent types
|
||||
// are of the given kind. An intersection type is of a given kind if at least one constituent type is
|
||||
// of the given kind.
|
||||
function isTypeOfKind(type: Type, kind: TypeFlags): boolean {
|
||||
if (type.flags & kind) {
|
||||
function isTypeAssignableToKind(source: Type, kind: TypeFlags, strict?: boolean): boolean {
|
||||
if (source.flags & kind) {
|
||||
return true;
|
||||
}
|
||||
if (type.flags & TypeFlags.Union) {
|
||||
const types = (<UnionOrIntersectionType>type).types;
|
||||
for (const t of types) {
|
||||
if (!isTypeOfKind(t, kind)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
if (strict && source.flags & (TypeFlags.Any | TypeFlags.Void | TypeFlags.Undefined | TypeFlags.Null)) {
|
||||
return false;
|
||||
}
|
||||
if (type.flags & TypeFlags.Intersection) {
|
||||
const types = (<UnionOrIntersectionType>type).types;
|
||||
for (const t of types) {
|
||||
if (isTypeOfKind(t, kind)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return (kind & TypeFlags.NumberLike && isTypeAssignableTo(source, numberType)) ||
|
||||
(kind & TypeFlags.StringLike && isTypeAssignableTo(source, stringType)) ||
|
||||
(kind & TypeFlags.BooleanLike && isTypeAssignableTo(source, booleanType)) ||
|
||||
(kind & TypeFlags.Void && isTypeAssignableTo(source, voidType)) ||
|
||||
(kind & TypeFlags.Never && isTypeAssignableTo(source, neverType)) ||
|
||||
(kind & TypeFlags.Null && isTypeAssignableTo(source, nullType)) ||
|
||||
(kind & TypeFlags.Undefined && isTypeAssignableTo(source, undefinedType)) ||
|
||||
(kind & TypeFlags.ESSymbol && isTypeAssignableTo(source, esSymbolType)) ||
|
||||
(kind & TypeFlags.NonPrimitive && isTypeAssignableTo(source, nonPrimitiveType));
|
||||
}
|
||||
|
||||
function isConstEnumObjectType(type: Type): boolean {
|
||||
@@ -17158,7 +17146,7 @@ namespace ts {
|
||||
// and the right operand to be of type Any, a subtype of the 'Function' interface type, or have a call or construct signature.
|
||||
// The result is always of the Boolean primitive type.
|
||||
// NOTE: do not raise error if leftType is unknown as related error was already reported
|
||||
if (isTypeOfKind(leftType, TypeFlags.Primitive)) {
|
||||
if (!isTypeAny(leftType) && isTypeAssignableToKind(leftType, TypeFlags.Primitive)) {
|
||||
error(left, Diagnostics.The_left_hand_side_of_an_instanceof_expression_must_be_of_type_any_an_object_type_or_a_type_parameter);
|
||||
}
|
||||
// NOTE: do not raise error if right is unknown as related error was already reported
|
||||
@@ -17181,10 +17169,10 @@ namespace ts {
|
||||
// The in operator requires the left operand to be of type Any, the String primitive type, or the Number primitive type,
|
||||
// and the right operand to be of type Any, an object type, or a type parameter type.
|
||||
// The result is always of the Boolean primitive type.
|
||||
if (!(isTypeComparableTo(leftType, stringType) || isTypeOfKind(leftType, TypeFlags.NumberLike | TypeFlags.ESSymbol))) {
|
||||
if (!(isTypeComparableTo(leftType, stringType) || isTypeAssignableToKind(leftType, TypeFlags.NumberLike | TypeFlags.ESSymbol))) {
|
||||
error(left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_of_type_any_string_number_or_symbol);
|
||||
}
|
||||
if (!isTypeAnyOrAllConstituentTypesHaveKind(rightType, TypeFlags.Object | TypeFlags.TypeVariable | TypeFlags.NonPrimitive)) {
|
||||
if (!isTypeAssignableToKind(rightType, TypeFlags.NonPrimitive | TypeFlags.TypeVariable)) {
|
||||
error(right, Diagnostics.The_right_hand_side_of_an_in_expression_must_be_of_type_any_an_object_type_or_a_type_parameter);
|
||||
}
|
||||
return booleanType;
|
||||
@@ -17493,32 +17481,30 @@ namespace ts {
|
||||
return silentNeverType;
|
||||
}
|
||||
|
||||
if (!isTypeOfKind(leftType, TypeFlags.Any | TypeFlags.StringLike) && !isTypeOfKind(rightType, TypeFlags.Any | TypeFlags.StringLike)) {
|
||||
if (!isTypeAssignableToKind(leftType, TypeFlags.StringLike) && !isTypeAssignableToKind(rightType, TypeFlags.StringLike)) {
|
||||
leftType = checkNonNullType(leftType, left);
|
||||
rightType = checkNonNullType(rightType, right);
|
||||
}
|
||||
|
||||
let resultType: Type;
|
||||
if (isTypeOfKind(leftType, TypeFlags.NumberLike) && isTypeOfKind(rightType, TypeFlags.NumberLike)) {
|
||||
if (isTypeAssignableToKind(leftType, TypeFlags.NumberLike, /*strict*/ true) && isTypeAssignableToKind(rightType, TypeFlags.NumberLike, /*strict*/ true)) {
|
||||
// Operands of an enum type are treated as having the primitive type Number.
|
||||
// If both operands are of the Number primitive type, the result is of the Number primitive type.
|
||||
resultType = numberType;
|
||||
}
|
||||
else {
|
||||
if (isTypeOfKind(leftType, TypeFlags.StringLike) || isTypeOfKind(rightType, TypeFlags.StringLike)) {
|
||||
else if (isTypeAssignableToKind(leftType, TypeFlags.StringLike, /*strict*/ true) || isTypeAssignableToKind(rightType, TypeFlags.StringLike, /*strict*/ true)) {
|
||||
// If one or both operands are of the String primitive type, the result is of the String primitive type.
|
||||
resultType = stringType;
|
||||
}
|
||||
else if (isTypeAny(leftType) || isTypeAny(rightType)) {
|
||||
// Otherwise, the result is of type Any.
|
||||
// NOTE: unknown type here denotes error type. Old compiler treated this case as any type so do we.
|
||||
resultType = leftType === unknownType || rightType === unknownType ? unknownType : anyType;
|
||||
}
|
||||
}
|
||||
else if (isTypeAny(leftType) || isTypeAny(rightType)) {
|
||||
// Otherwise, the result is of type Any.
|
||||
// NOTE: unknown type here denotes error type. Old compiler treated this case as any type so do we.
|
||||
resultType = leftType === unknownType || rightType === unknownType ? unknownType : anyType;
|
||||
}
|
||||
|
||||
// Symbols are not allowed at all in arithmetic expressions
|
||||
if (resultType && !checkForDisallowedESSymbolOperand(operator)) {
|
||||
return resultType;
|
||||
}
|
||||
// Symbols are not allowed at all in arithmetic expressions
|
||||
if (resultType && !checkForDisallowedESSymbolOperand(operator)) {
|
||||
return resultType;
|
||||
}
|
||||
|
||||
if (!resultType) {
|
||||
@@ -18732,7 +18718,7 @@ namespace ts {
|
||||
}
|
||||
// Check if we're indexing with a numeric type and the object type is a generic
|
||||
// type with a constraint that has a numeric index signature.
|
||||
if (maybeTypeOfKind(objectType, TypeFlags.TypeVariable) && isTypeOfKind(indexType, TypeFlags.NumberLike)) {
|
||||
if (maybeTypeOfKind(objectType, TypeFlags.TypeVariable) && isTypeAssignableToKind(indexType, TypeFlags.NumberLike)) {
|
||||
const constraint = getBaseConstraintOfType(objectType);
|
||||
if (constraint && getIndexInfoOfType(constraint, IndexKind.Number)) {
|
||||
return type;
|
||||
@@ -20439,7 +20425,7 @@ namespace ts {
|
||||
|
||||
// unknownType is returned i.e. if node.expression is identifier whose name cannot be resolved
|
||||
// in this case error about missing name is already reported - do not report extra one
|
||||
if (!isTypeAnyOrAllConstituentTypesHaveKind(rightType, TypeFlags.Object | TypeFlags.TypeVariable | TypeFlags.NonPrimitive)) {
|
||||
if (!isTypeAssignableToKind(rightType, TypeFlags.NonPrimitive | TypeFlags.TypeVariable)) {
|
||||
error(node.expression, Diagnostics.The_right_hand_side_of_a_for_in_statement_must_be_of_type_any_an_object_type_or_a_type_parameter);
|
||||
}
|
||||
|
||||
@@ -23426,22 +23412,22 @@ namespace ts {
|
||||
else if (type.flags & TypeFlags.Any) {
|
||||
return TypeReferenceSerializationKind.ObjectType;
|
||||
}
|
||||
else if (isTypeOfKind(type, TypeFlags.Void | TypeFlags.Nullable | TypeFlags.Never)) {
|
||||
else if (isTypeAssignableToKind(type, TypeFlags.Void | TypeFlags.Nullable | TypeFlags.Never)) {
|
||||
return TypeReferenceSerializationKind.VoidNullableOrNeverType;
|
||||
}
|
||||
else if (isTypeOfKind(type, TypeFlags.BooleanLike)) {
|
||||
else if (isTypeAssignableToKind(type, TypeFlags.BooleanLike)) {
|
||||
return TypeReferenceSerializationKind.BooleanType;
|
||||
}
|
||||
else if (isTypeOfKind(type, TypeFlags.NumberLike)) {
|
||||
else if (isTypeAssignableToKind(type, TypeFlags.NumberLike)) {
|
||||
return TypeReferenceSerializationKind.NumberLikeType;
|
||||
}
|
||||
else if (isTypeOfKind(type, TypeFlags.StringLike)) {
|
||||
else if (isTypeAssignableToKind(type, TypeFlags.StringLike)) {
|
||||
return TypeReferenceSerializationKind.StringLikeType;
|
||||
}
|
||||
else if (isTupleType(type)) {
|
||||
return TypeReferenceSerializationKind.ArrayLikeType;
|
||||
}
|
||||
else if (isTypeOfKind(type, TypeFlags.ESSymbol)) {
|
||||
else if (isTypeAssignableToKind(type, TypeFlags.ESSymbol)) {
|
||||
return TypeReferenceSerializationKind.ESSymbolType;
|
||||
}
|
||||
else if (isFunctionType(type)) {
|
||||
|
||||
Reference in New Issue
Block a user