mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-16 15:45:27 -05:00
Handle 'keyof' for generic tuple types (#39218)
* Handle keyof T where T is generic tuple type * Add tests * Accept new baselines * Address CR feedback * Accept new baselines
This commit is contained in:
@@ -10195,7 +10195,8 @@ namespace ts {
|
||||
return type;
|
||||
}
|
||||
if (type.flags & TypeFlags.Index) {
|
||||
return getIndexType(getApparentType((<IndexType>type).type));
|
||||
const t = getApparentType((<IndexType>type).type);
|
||||
return isGenericTupleType(t) ? getKnownKeysOfTupleType(t) : getIndexType(t);
|
||||
}
|
||||
if (type.flags & TypeFlags.Conditional) {
|
||||
if ((<ConditionalType>type).root.isDistributive) {
|
||||
@@ -10520,9 +10521,6 @@ namespace ts {
|
||||
return indexedAccess;
|
||||
}
|
||||
}
|
||||
if (isGenericTupleType(type.objectType)) {
|
||||
return getIndexTypeOfType(type.objectType, IndexKind.Number);
|
||||
}
|
||||
const objectConstraint = getSimplifiedTypeOrConstraint(type.objectType);
|
||||
if (objectConstraint && objectConstraint !== type.objectType) {
|
||||
return getIndexedAccessTypeOrUndefined(objectConstraint, type.indexType);
|
||||
@@ -10711,9 +10709,6 @@ namespace ts {
|
||||
return keyofConstraintType;
|
||||
}
|
||||
if (t.flags & TypeFlags.IndexedAccess) {
|
||||
if (isGenericTupleType((<IndexedAccessType>t).objectType)) {
|
||||
return getIndexTypeOfType((<IndexedAccessType>t).objectType, IndexKind.Number);
|
||||
}
|
||||
const baseObjectType = getBaseConstraint((<IndexedAccessType>t).objectType);
|
||||
const baseIndexType = getBaseConstraint((<IndexedAccessType>t).indexType);
|
||||
const baseIndexedAccess = baseObjectType && baseIndexType && getIndexedAccessTypeOrUndefined(baseObjectType, baseIndexType);
|
||||
@@ -12647,6 +12642,11 @@ namespace ts {
|
||||
/*readonly*/ false, target.labeledElementDeclarations && target.labeledElementDeclarations.slice(index, endIndex));
|
||||
}
|
||||
|
||||
function getKnownKeysOfTupleType(type: TupleTypeReference) {
|
||||
return getUnionType(append(arrayOf(type.target.fixedLength, i => getLiteralType("" + i)),
|
||||
getIndexType(type.target.readonly ? globalReadonlyArrayType : globalArrayType)));
|
||||
}
|
||||
|
||||
function getTypeFromOptionalTypeNode(node: OptionalTypeNode): Type {
|
||||
const type = getTypeFromTypeNode(node.type);
|
||||
return strictNullChecks ? getOptionalType(type) : type;
|
||||
@@ -16831,11 +16831,11 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
// For a generic type T, [...T] is assignable to T, T is assignable to readonly [...T], and T is assignable
|
||||
// to [...T] when T is constrained to a mutable array or tuple type.
|
||||
if (isSingleElementGenericTupleType(source) && getTypeArguments(source)[0] === target && !source.target.readonly ||
|
||||
isSingleElementGenericTupleType(target) && getTypeArguments(target)[0] === source && (target.target.readonly || isMutableArrayOrTuple(getBaseConstraintOfType(source) || source))) {
|
||||
return Ternary.True;
|
||||
// For a generic type T and a type U that is assignable to T, [...U] is assignable to T, U is assignable to readonly [...T],
|
||||
// and U is assignable to [...T] when U is constrained to a mutable array or tuple type.
|
||||
if (isSingleElementGenericTupleType(source) && !source.target.readonly && (result = isRelatedTo(getTypeArguments(source)[0], target)) ||
|
||||
isSingleElementGenericTupleType(target) && (target.target.readonly || isMutableArrayOrTuple(getBaseConstraintOfType(source) || source)) && (result = isRelatedTo(source, getTypeArguments(target)[0]))) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (target.flags & TypeFlags.TypeParameter) {
|
||||
@@ -16851,22 +16851,32 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
else if (target.flags & TypeFlags.Index) {
|
||||
const targetType = (target as IndexType).type;
|
||||
// A keyof S is related to a keyof T if T is related to S.
|
||||
if (source.flags & TypeFlags.Index) {
|
||||
if (result = isRelatedTo((<IndexType>target).type, (<IndexType>source).type, /*reportErrors*/ false)) {
|
||||
if (result = isRelatedTo(targetType, (<IndexType>source).type, /*reportErrors*/ false)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
// A type S is assignable to keyof T if S is assignable to keyof C, where C is the
|
||||
// simplified form of T or, if T doesn't simplify, the constraint of T.
|
||||
const constraint = getSimplifiedTypeOrConstraint((<IndexType>target).type);
|
||||
if (constraint) {
|
||||
// We require Ternary.True here such that circular constraints don't cause
|
||||
// false positives. For example, given 'T extends { [K in keyof T]: string }',
|
||||
// 'keyof T' has itself as its constraint and produces a Ternary.Maybe when
|
||||
// related to other types.
|
||||
if (isRelatedTo(source, getIndexType(constraint, (target as IndexType).stringsOnly), reportErrors) === Ternary.True) {
|
||||
return Ternary.True;
|
||||
if (isTupleType(targetType)) {
|
||||
// An index type can have a tuple type target when the tuple type contains variadic elements.
|
||||
// Check if the source is related to the known keys of the tuple type.
|
||||
if (result = isRelatedTo(source, getKnownKeysOfTupleType(targetType), reportErrors)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// A type S is assignable to keyof T if S is assignable to keyof C, where C is the
|
||||
// simplified form of T or, if T doesn't simplify, the constraint of T.
|
||||
const constraint = getSimplifiedTypeOrConstraint(targetType);
|
||||
if (constraint) {
|
||||
// We require Ternary.True here such that circular constraints don't cause
|
||||
// false positives. For example, given 'T extends { [K in keyof T]: string }',
|
||||
// 'keyof T' has itself as its constraint and produces a Ternary.Maybe when
|
||||
// related to other types.
|
||||
if (isRelatedTo(source, getIndexType(constraint, (target as IndexType).stringsOnly), reportErrors) === Ternary.True) {
|
||||
return Ternary.True;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user