diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index beb75fd16d2dc..c230d876668a0 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13174,8 +13174,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // and T as the template type. const typeParameter = getTypeParameterFromMappedType(type); const constraintType = getConstraintTypeFromMappedType(type); - const nameType = getNameTypeFromMappedType(type.target as MappedType || type); - const isFilteringMappedType = nameType && isTypeAssignableTo(nameType, typeParameter); + const mappedType = (type.target as MappedType) || type; + const nameType = getNameTypeFromMappedType(mappedType); + const shouldLinkPropDeclarations = !nameType || isFilteringMappedType(mappedType); const templateType = getTemplateTypeFromMappedType(type.target as MappedType || type); const modifiersType = getApparentType(getModifiersTypeFromMappedType(type)); // The 'T' in 'keyof T' const templateModifiers = getMappedTypeModifiers(type); @@ -13222,7 +13223,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { prop.links.keyType = keyType; if (modifiersProp) { prop.links.syntheticOrigin = modifiersProp; - prop.declarations = !nameType || isFilteringMappedType ? modifiersProp.declarations : undefined; + prop.declarations = shouldLinkPropDeclarations ? modifiersProp.declarations : undefined; } members.set(propName, prop); } @@ -13355,6 +13356,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return false; } + function isFilteringMappedType(type: MappedType): boolean { + const nameType = getNameTypeFromMappedType(type); + return !!nameType && isTypeAssignableTo(nameType, getTypeParameterFromMappedType(type)); + } + function resolveStructuredTypeMembers(type: StructuredType): ResolvedType { if (!(type as ResolvedType).members) { if (type.flags & TypeFlags.Object) { @@ -17473,8 +17479,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // K is generic and N is assignable to P, instantiate E using a mapper that substitutes the index type for P. // For example, for an index access { [P in K]: Box }[X], we construct the type Box. if (isGenericMappedType(objectType)) { - const nameType = getNameTypeFromMappedType(objectType); - if (!nameType || isTypeAssignableTo(nameType, getTypeParameterFromMappedType(objectType))) { + if (!getNameTypeFromMappedType(objectType) || isFilteringMappedType(objectType)) { return type[cache] = mapType(substituteIndexedMappedType(objectType, type.indexType), t => getSimplifiedType(t, writing)); } } diff --git a/tests/cases/fourslash/goToDefinition_filteringGenericMappedType.ts b/tests/cases/fourslash/goToDefinition_filteringGenericMappedType.ts new file mode 100644 index 0000000000000..95a8ec67ced4c --- /dev/null +++ b/tests/cases/fourslash/goToDefinition_filteringGenericMappedType.ts @@ -0,0 +1,25 @@ +/// + +//// const obj = { +//// get /*def*/id() { +//// return 1; +//// }, +//// name: "test", +//// }; +//// +//// type Omit2 = { +//// [K in keyof T as Exclude]: T[K]; +//// }; +//// +//// declare function omit2( +//// obj: O, +//// mask: Mask +//// ): Omit2; +//// +//// const obj2 = omit2(obj, { +//// name: true, +//// }); +//// +//// obj2.[|/*ref*/id|]; + +verify.goToDefinition("ref", "def");