mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-16 15:45:27 -05:00
Allow type comparison when target is generic mapped type with key remapping (#45700)
* Add case for type related to for generic mapped type with as clause target * Clean up code and add baselines * cleanup
This commit is contained in:
committed by
GitHub
parent
617251f2e0
commit
4f5bbd00e6
@@ -18625,39 +18625,60 @@ namespace ts {
|
||||
originalErrorInfo = undefined;
|
||||
}
|
||||
}
|
||||
else if (isGenericMappedType(target) && !target.declaration.nameType) {
|
||||
// A source type T is related to a target type { [P in X]: T[P] }
|
||||
const template = getTemplateTypeFromMappedType(target);
|
||||
else if (isGenericMappedType(target)) {
|
||||
// Check if source type `S` is related to target type `{ [P in Q]: T }` or `{ [P in Q as R]: T}`.
|
||||
const keysRemapped = !!target.declaration.nameType;
|
||||
const templateType = getTemplateTypeFromMappedType(target);
|
||||
const modifiers = getMappedTypeModifiers(target);
|
||||
if (!(modifiers & MappedTypeModifiers.ExcludeOptional)) {
|
||||
if (template.flags & TypeFlags.IndexedAccess && (template as IndexedAccessType).objectType === source &&
|
||||
(template as IndexedAccessType).indexType === getTypeParameterFromMappedType(target)) {
|
||||
// If the mapped type has shape `{ [P in Q]: T[P] }`,
|
||||
// source `S` is related to target if `T` = `S`, i.e. `S` is related to `{ [P in Q]: S[P] }`.
|
||||
if (!keysRemapped && templateType.flags & TypeFlags.IndexedAccess && (templateType as IndexedAccessType).objectType === source &&
|
||||
(templateType as IndexedAccessType).indexType === getTypeParameterFromMappedType(target)) {
|
||||
return Ternary.True;
|
||||
}
|
||||
if (!isGenericMappedType(source)) {
|
||||
const targetConstraint = getConstraintTypeFromMappedType(target);
|
||||
// If target has shape `{ [P in Q as R]: T}`, then its keys have type `R`.
|
||||
// If target has shape `{ [P in Q]: T }`, then its keys have type `Q`.
|
||||
const targetKeys = keysRemapped ? getNameTypeFromMappedType(target)! : getConstraintTypeFromMappedType(target);
|
||||
// Type of the keys of source type `S`, i.e. `keyof S`.
|
||||
const sourceKeys = getIndexType(source, /*stringsOnly*/ undefined, /*noIndexSignatures*/ true);
|
||||
const includeOptional = modifiers & MappedTypeModifiers.IncludeOptional;
|
||||
const filteredByApplicability = includeOptional ? intersectTypes(targetConstraint, sourceKeys) : undefined;
|
||||
// A source type T is related to a target type { [P in Q]: X } if Q is related to keyof T and T[Q] is related to X.
|
||||
// A source type T is related to a target type { [P in Q]?: X } if some constituent Q' of Q is related to keyof T and T[Q'] is related to X.
|
||||
const filteredByApplicability = includeOptional ? intersectTypes(targetKeys, sourceKeys) : undefined;
|
||||
// A source type `S` is related to a target type `{ [P in Q]: T }` if `Q` is related to `keyof S` and `S[Q]` is related to `T`.
|
||||
// A source type `S` is related to a target type `{ [P in Q as R]: T }` if `R` is related to `keyof S` and `S[R]` is related to `T.
|
||||
// A source type `S` is related to a target type `{ [P in Q]?: T }` if some constituent `Q'` of `Q` is related to `keyof S` and `S[Q']` is related to `T`.
|
||||
// A source type `S` is related to a target type `{ [P in Q as R]?: T }` if some constituent `R'` of `R` is related to `keyof S` and `S[R']` is related to `T`.
|
||||
if (includeOptional
|
||||
? !(filteredByApplicability!.flags & TypeFlags.Never)
|
||||
: isRelatedTo(targetConstraint, sourceKeys)) {
|
||||
const templateType = getTemplateTypeFromMappedType(target);
|
||||
: isRelatedTo(targetKeys, sourceKeys)) {
|
||||
const typeParameter = getTypeParameterFromMappedType(target);
|
||||
|
||||
// Fastpath: When the template has the form Obj[P] where P is the mapped type parameter, directly compare `source` with `Obj`
|
||||
// to avoid creating the (potentially very large) number of new intermediate types made by manufacturing `source[P]`
|
||||
// Fastpath: When the template type has the form `Obj[P]` where `P` is the mapped type parameter, directly compare source `S` with `Obj`
|
||||
// to avoid creating the (potentially very large) number of new intermediate types made by manufacturing `S[P]`.
|
||||
const nonNullComponent = extractTypesOfKind(templateType, ~TypeFlags.Nullable);
|
||||
if (nonNullComponent.flags & TypeFlags.IndexedAccess && (nonNullComponent as IndexedAccessType).indexType === typeParameter) {
|
||||
if (!keysRemapped && nonNullComponent.flags & TypeFlags.IndexedAccess && (nonNullComponent as IndexedAccessType).indexType === typeParameter) {
|
||||
if (result = isRelatedTo(source, (nonNullComponent as IndexedAccessType).objectType, reportErrors)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else {
|
||||
const indexingType = filteredByApplicability ? getIntersectionType([filteredByApplicability, typeParameter]) : typeParameter;
|
||||
// We need to compare the type of a property on the source type `S` to the type of the same property on the target type,
|
||||
// so we need to construct an indexing type representing a property, and then use indexing type to index the source type for comparison.
|
||||
|
||||
// If the target type has shape `{ [P in Q]: T }`, then a property of the target has type `P`.
|
||||
// If the target type has shape `{ [P in Q]?: T }`, then a property of the target has type `P`,
|
||||
// but the property is optional, so we only want to compare properties `P` that are common between `keyof S` and `Q`.
|
||||
// If the target type has shape `{ [P in Q as R]: T }`, then a property of the target has type `R`.
|
||||
// If the target type has shape `{ [P in Q as R]?: T }`, then a property of the target has type `R`,
|
||||
// but the property is optional, so we only want to compare properties `R` that are common between `keyof S` and `R`.
|
||||
const indexingType = keysRemapped
|
||||
? (filteredByApplicability || targetKeys)
|
||||
: filteredByApplicability
|
||||
? getIntersectionType([filteredByApplicability, typeParameter])
|
||||
: typeParameter;
|
||||
const indexedAccessType = getIndexedAccessType(source, indexingType);
|
||||
// Compare `S[indexingType]` to `T`, where `T` is the type of a property of the target type.
|
||||
if (result = isRelatedTo(indexedAccessType, templateType, reportErrors)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user