-
Notifications
You must be signed in to change notification settings - Fork 12.9k
Allow type comparison when target is generic mapped type with key remapping #45700
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -18585,39 +18585,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; | ||||||||||||||||||
Comment on lines
+18635
to
+18639
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||
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; | ||||||||||||||||||
} | ||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
tests/cases/conformance/types/mapped/mappedTypeAsClauseRelationships.ts(12,9): error TS2322: Type 'T' is not assignable to type 'Modify<T>'. | ||
tests/cases/conformance/types/mapped/mappedTypeAsClauseRelationships.ts(23,9): error TS2322: Type 'T' is not assignable to type 'FilterExclOpt<T>'. | ||
tests/cases/conformance/types/mapped/mappedTypeAsClauseRelationships.ts(24,9): error TS2322: Type 'T' is not assignable to type 'ModifyExclOpt<T>'. | ||
|
||
|
||
==== tests/cases/conformance/types/mapped/mappedTypeAsClauseRelationships.ts (3 errors) ==== | ||
// From original issue #45212: | ||
type Methods<T> = { [P in keyof T as T[P] extends Function ? P : never]: T[P] }; | ||
type H<T> = T[keyof Methods<T>]; // Ok | ||
|
||
// `Filter<T>` only filters out some keys of `T`. | ||
type Filter<T> = { [P in keyof T as T[P] extends Function ? P : never]: T[P] }; | ||
// `Modify<T>` might modify some keys of `T`. | ||
type Modify<T> = { [P in keyof T as P extends string? `bool${P}`: P]: T[P] }; | ||
|
||
function fun<T>(val: T) { | ||
let x: Filter<T> = val; // Ok | ||
let y: Modify<T> = val; // Error | ||
~ | ||
!!! error TS2322: Type 'T' is not assignable to type 'Modify<T>'. | ||
} | ||
|
||
type FilterInclOpt<T> = { [P in keyof T as T[P] extends Function ? P : never]+?: T[P] }; | ||
type ModifyInclOpt<T> = { [P in keyof T as P extends string? `bool${P}`: never ]+?: T[P] }; | ||
type FilterExclOpt<T> = { [P in keyof T as T[P] extends Function ? P : never]-?: T[P] }; | ||
type ModifyExclOpt<T> = { [P in keyof T as P extends string? `bool${P}`: never ]-?: T[P] }; | ||
|
||
function fun2<T>(val: T) { | ||
let x: FilterInclOpt<T> = val; // Ok | ||
let y: ModifyInclOpt<T> = val; // Ok | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My initial reaction to this one was that this is wrong because interface Foo {
x?: string;
y?: string;
z?: string;
}
function f<T>(x: T) { // What if `T` is `{ x: number }`?
const _: Foo = x;
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I tried to follow the already existing logic and reuse everything that's already there, so there might be inconsistencies that were already there or that result from the kind of types that are in key remapping clauses. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Optional properties are a pretty big soundness hole, unfortunately. The unconstrained generic one is actually one I have a PR up to remove - #33570 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This exact discussed case has changed behavior in 51b346d#diff-25f367dc11e40edc9febb9d6f088e13f9c7c8086bb8b9f85aed321f0903bc46eR31-R33 One of my PRs bring back original behavior (somewhat accidentally, I wasn't aiming for it), see here Which one is correct? 😅 |
||
let z: FilterExclOpt<T> = val; // Error | ||
~ | ||
!!! error TS2322: Type 'T' is not assignable to type 'FilterExclOpt<T>'. | ||
let w: ModifyExclOpt<T> = val; // Error | ||
~ | ||
!!! error TS2322: Type 'T' is not assignable to type 'ModifyExclOpt<T>'. | ||
} | ||
|
||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
//// [mappedTypeAsClauseRelationships.ts] | ||
// From original issue #45212: | ||
type Methods<T> = { [P in keyof T as T[P] extends Function ? P : never]: T[P] }; | ||
type H<T> = T[keyof Methods<T>]; // Ok | ||
|
||
// `Filter<T>` only filters out some keys of `T`. | ||
type Filter<T> = { [P in keyof T as T[P] extends Function ? P : never]: T[P] }; | ||
// `Modify<T>` might modify some keys of `T`. | ||
type Modify<T> = { [P in keyof T as P extends string? `bool${P}`: P]: T[P] }; | ||
|
||
function fun<T>(val: T) { | ||
let x: Filter<T> = val; // Ok | ||
let y: Modify<T> = val; // Error | ||
} | ||
|
||
type FilterInclOpt<T> = { [P in keyof T as T[P] extends Function ? P : never]+?: T[P] }; | ||
type ModifyInclOpt<T> = { [P in keyof T as P extends string? `bool${P}`: never ]+?: T[P] }; | ||
type FilterExclOpt<T> = { [P in keyof T as T[P] extends Function ? P : never]-?: T[P] }; | ||
type ModifyExclOpt<T> = { [P in keyof T as P extends string? `bool${P}`: never ]-?: T[P] }; | ||
|
||
function fun2<T>(val: T) { | ||
let x: FilterInclOpt<T> = val; // Ok | ||
let y: ModifyInclOpt<T> = val; // Ok | ||
let z: FilterExclOpt<T> = val; // Error | ||
let w: ModifyExclOpt<T> = val; // Error | ||
} | ||
|
||
|
||
|
||
|
||
//// [mappedTypeAsClauseRelationships.js] | ||
function fun(val) { | ||
var x = val; // Ok | ||
var y = val; // Error | ||
} | ||
function fun2(val) { | ||
var x = val; // Ok | ||
var y = val; // Ok | ||
var z = val; // Error | ||
var w = val; // Error | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
=== tests/cases/conformance/types/mapped/mappedTypeAsClauseRelationships.ts === | ||
// From original issue #45212: | ||
type Methods<T> = { [P in keyof T as T[P] extends Function ? P : never]: T[P] }; | ||
>Methods : Symbol(Methods, Decl(mappedTypeAsClauseRelationships.ts, 0, 0)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 1, 13)) | ||
>P : Symbol(P, Decl(mappedTypeAsClauseRelationships.ts, 1, 21)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 1, 13)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 1, 13)) | ||
>P : Symbol(P, Decl(mappedTypeAsClauseRelationships.ts, 1, 21)) | ||
>Function : Symbol(Function, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) | ||
>P : Symbol(P, Decl(mappedTypeAsClauseRelationships.ts, 1, 21)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 1, 13)) | ||
>P : Symbol(P, Decl(mappedTypeAsClauseRelationships.ts, 1, 21)) | ||
|
||
type H<T> = T[keyof Methods<T>]; // Ok | ||
>H : Symbol(H, Decl(mappedTypeAsClauseRelationships.ts, 1, 80)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 2, 7)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 2, 7)) | ||
>Methods : Symbol(Methods, Decl(mappedTypeAsClauseRelationships.ts, 0, 0)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 2, 7)) | ||
|
||
// `Filter<T>` only filters out some keys of `T`. | ||
type Filter<T> = { [P in keyof T as T[P] extends Function ? P : never]: T[P] }; | ||
>Filter : Symbol(Filter, Decl(mappedTypeAsClauseRelationships.ts, 2, 32)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 5, 12)) | ||
>P : Symbol(P, Decl(mappedTypeAsClauseRelationships.ts, 5, 20)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 5, 12)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 5, 12)) | ||
>P : Symbol(P, Decl(mappedTypeAsClauseRelationships.ts, 5, 20)) | ||
>Function : Symbol(Function, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) | ||
>P : Symbol(P, Decl(mappedTypeAsClauseRelationships.ts, 5, 20)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 5, 12)) | ||
>P : Symbol(P, Decl(mappedTypeAsClauseRelationships.ts, 5, 20)) | ||
|
||
// `Modify<T>` might modify some keys of `T`. | ||
type Modify<T> = { [P in keyof T as P extends string? `bool${P}`: P]: T[P] }; | ||
>Modify : Symbol(Modify, Decl(mappedTypeAsClauseRelationships.ts, 5, 79)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 7, 12)) | ||
>P : Symbol(P, Decl(mappedTypeAsClauseRelationships.ts, 7, 20)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 7, 12)) | ||
>P : Symbol(P, Decl(mappedTypeAsClauseRelationships.ts, 7, 20)) | ||
>P : Symbol(P, Decl(mappedTypeAsClauseRelationships.ts, 7, 20)) | ||
>P : Symbol(P, Decl(mappedTypeAsClauseRelationships.ts, 7, 20)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 7, 12)) | ||
>P : Symbol(P, Decl(mappedTypeAsClauseRelationships.ts, 7, 20)) | ||
|
||
function fun<T>(val: T) { | ||
>fun : Symbol(fun, Decl(mappedTypeAsClauseRelationships.ts, 7, 77)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 9, 13)) | ||
>val : Symbol(val, Decl(mappedTypeAsClauseRelationships.ts, 9, 16)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 9, 13)) | ||
|
||
let x: Filter<T> = val; // Ok | ||
>x : Symbol(x, Decl(mappedTypeAsClauseRelationships.ts, 10, 7)) | ||
>Filter : Symbol(Filter, Decl(mappedTypeAsClauseRelationships.ts, 2, 32)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 9, 13)) | ||
>val : Symbol(val, Decl(mappedTypeAsClauseRelationships.ts, 9, 16)) | ||
|
||
let y: Modify<T> = val; // Error | ||
>y : Symbol(y, Decl(mappedTypeAsClauseRelationships.ts, 11, 7)) | ||
>Modify : Symbol(Modify, Decl(mappedTypeAsClauseRelationships.ts, 5, 79)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 9, 13)) | ||
>val : Symbol(val, Decl(mappedTypeAsClauseRelationships.ts, 9, 16)) | ||
} | ||
|
||
type FilterInclOpt<T> = { [P in keyof T as T[P] extends Function ? P : never]+?: T[P] }; | ||
>FilterInclOpt : Symbol(FilterInclOpt, Decl(mappedTypeAsClauseRelationships.ts, 12, 1)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 14, 19)) | ||
>P : Symbol(P, Decl(mappedTypeAsClauseRelationships.ts, 14, 27)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 14, 19)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 14, 19)) | ||
>P : Symbol(P, Decl(mappedTypeAsClauseRelationships.ts, 14, 27)) | ||
>Function : Symbol(Function, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) | ||
>P : Symbol(P, Decl(mappedTypeAsClauseRelationships.ts, 14, 27)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 14, 19)) | ||
>P : Symbol(P, Decl(mappedTypeAsClauseRelationships.ts, 14, 27)) | ||
|
||
type ModifyInclOpt<T> = { [P in keyof T as P extends string? `bool${P}`: never ]+?: T[P] }; | ||
>ModifyInclOpt : Symbol(ModifyInclOpt, Decl(mappedTypeAsClauseRelationships.ts, 14, 88)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 15, 19)) | ||
>P : Symbol(P, Decl(mappedTypeAsClauseRelationships.ts, 15, 27)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 15, 19)) | ||
>P : Symbol(P, Decl(mappedTypeAsClauseRelationships.ts, 15, 27)) | ||
>P : Symbol(P, Decl(mappedTypeAsClauseRelationships.ts, 15, 27)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 15, 19)) | ||
>P : Symbol(P, Decl(mappedTypeAsClauseRelationships.ts, 15, 27)) | ||
|
||
type FilterExclOpt<T> = { [P in keyof T as T[P] extends Function ? P : never]-?: T[P] }; | ||
>FilterExclOpt : Symbol(FilterExclOpt, Decl(mappedTypeAsClauseRelationships.ts, 15, 91)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 16, 19)) | ||
>P : Symbol(P, Decl(mappedTypeAsClauseRelationships.ts, 16, 27)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 16, 19)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 16, 19)) | ||
>P : Symbol(P, Decl(mappedTypeAsClauseRelationships.ts, 16, 27)) | ||
>Function : Symbol(Function, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) | ||
>P : Symbol(P, Decl(mappedTypeAsClauseRelationships.ts, 16, 27)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 16, 19)) | ||
>P : Symbol(P, Decl(mappedTypeAsClauseRelationships.ts, 16, 27)) | ||
|
||
type ModifyExclOpt<T> = { [P in keyof T as P extends string? `bool${P}`: never ]-?: T[P] }; | ||
>ModifyExclOpt : Symbol(ModifyExclOpt, Decl(mappedTypeAsClauseRelationships.ts, 16, 88)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 17, 19)) | ||
>P : Symbol(P, Decl(mappedTypeAsClauseRelationships.ts, 17, 27)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 17, 19)) | ||
>P : Symbol(P, Decl(mappedTypeAsClauseRelationships.ts, 17, 27)) | ||
>P : Symbol(P, Decl(mappedTypeAsClauseRelationships.ts, 17, 27)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 17, 19)) | ||
>P : Symbol(P, Decl(mappedTypeAsClauseRelationships.ts, 17, 27)) | ||
|
||
function fun2<T>(val: T) { | ||
>fun2 : Symbol(fun2, Decl(mappedTypeAsClauseRelationships.ts, 17, 91)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 19, 14)) | ||
>val : Symbol(val, Decl(mappedTypeAsClauseRelationships.ts, 19, 17)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 19, 14)) | ||
|
||
let x: FilterInclOpt<T> = val; // Ok | ||
>x : Symbol(x, Decl(mappedTypeAsClauseRelationships.ts, 20, 7)) | ||
>FilterInclOpt : Symbol(FilterInclOpt, Decl(mappedTypeAsClauseRelationships.ts, 12, 1)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 19, 14)) | ||
>val : Symbol(val, Decl(mappedTypeAsClauseRelationships.ts, 19, 17)) | ||
|
||
let y: ModifyInclOpt<T> = val; // Ok | ||
>y : Symbol(y, Decl(mappedTypeAsClauseRelationships.ts, 21, 7)) | ||
>ModifyInclOpt : Symbol(ModifyInclOpt, Decl(mappedTypeAsClauseRelationships.ts, 14, 88)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 19, 14)) | ||
>val : Symbol(val, Decl(mappedTypeAsClauseRelationships.ts, 19, 17)) | ||
|
||
let z: FilterExclOpt<T> = val; // Error | ||
>z : Symbol(z, Decl(mappedTypeAsClauseRelationships.ts, 22, 7)) | ||
>FilterExclOpt : Symbol(FilterExclOpt, Decl(mappedTypeAsClauseRelationships.ts, 15, 91)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 19, 14)) | ||
>val : Symbol(val, Decl(mappedTypeAsClauseRelationships.ts, 19, 17)) | ||
|
||
let w: ModifyExclOpt<T> = val; // Error | ||
>w : Symbol(w, Decl(mappedTypeAsClauseRelationships.ts, 23, 7)) | ||
>ModifyExclOpt : Symbol(ModifyExclOpt, Decl(mappedTypeAsClauseRelationships.ts, 16, 88)) | ||
>T : Symbol(T, Decl(mappedTypeAsClauseRelationships.ts, 19, 14)) | ||
>val : Symbol(val, Decl(mappedTypeAsClauseRelationships.ts, 19, 17)) | ||
} | ||
|
||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not entirely sure this won't cause issues for the case where the target mapped type has an include optional modifier.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, to be clear here, in the
keysRemapped
case, we're skipping thegetIntersectionType
calls with thetypeParameter
, simply because the keys may or may not actually contain the remapping type parameter (eg, every key may just be mapped tostring
) - thetargetKeys
will already contain it if so. I think this is correct - it's just not necessarily clear. In the case where there's noas
clause, we intersect the type parameter and the (filtered) keys so we get a generic property key - whereas when the keys are remapped, we don't need to do so as the remapping overrides that.