Skip to content

Fix generic mapped type relationships #19564

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

Merged
merged 3 commits into from
Oct 30, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 18 additions & 20 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9454,32 +9454,30 @@ namespace ts {
}
}
}
else if (isGenericMappedType(target) && !isGenericMappedType(source) && getConstraintTypeFromMappedType(<MappedType>target) === getIndexType(source)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a branch lower down that checks whether target is a generic mapped type. Can this be performed there?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No because that particular section of code is only entered if source is an object or intersection type.

// A source type T is related to a target type { [P in keyof T]: X } if T[P] is related to X.
const indexedAccessType = getIndexedAccessType(source, getTypeParameterFromMappedType(<MappedType>target));
const templateType = getTemplateTypeFromMappedType(<MappedType>target);
if (result = isRelatedTo(indexedAccessType, templateType, reportErrors)) {
errorInfo = saveErrorInfo;
return result;
}
}

if (source.flags & TypeFlags.TypeParameter) {
// A source type T is related to a target type { [P in keyof T]: X } if T[P] is related to X.
if (getObjectFlags(target) & ObjectFlags.Mapped && getConstraintTypeFromMappedType(<MappedType>target) === getIndexType(source)) {
const indexedAccessType = getIndexedAccessType(source, getTypeParameterFromMappedType(<MappedType>target));
const templateType = getTemplateTypeFromMappedType(<MappedType>target);
if (result = isRelatedTo(indexedAccessType, templateType, reportErrors)) {
let constraint = getConstraintOfTypeParameter(<TypeParameter>source);
// A type parameter with no constraint is not related to the non-primitive object type.
if (constraint || !(target.flags & TypeFlags.NonPrimitive)) {
if (!constraint || constraint.flags & TypeFlags.Any) {
constraint = emptyObjectType;
}
// Report constraint errors only if the constraint is not the empty object type
const reportConstraintErrors = reportErrors && constraint !== emptyObjectType;
if (result = isRelatedTo(constraint, target, reportConstraintErrors)) {
errorInfo = saveErrorInfo;
return result;
}
}
else {
let constraint = getConstraintOfTypeParameter(<TypeParameter>source);
// A type parameter with no constraint is not related to the non-primitive object type.
if (constraint || !(target.flags & TypeFlags.NonPrimitive)) {
if (!constraint || constraint.flags & TypeFlags.Any) {
constraint = emptyObjectType;
}
// Report constraint errors only if the constraint is not the empty object type
const reportConstraintErrors = reportErrors && constraint !== emptyObjectType;
if (result = isRelatedTo(constraint, target, reportConstraintErrors)) {
errorInfo = saveErrorInfo;
return result;
}
}
}
}
else if (source.flags & TypeFlags.IndexedAccess) {
// A type S[K] is related to a type T if A[K] is related to T, where K is string-like and
Expand Down
12 changes: 12 additions & 0 deletions tests/baselines/reference/mappedTypeRelationships.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -460,4 +460,16 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(168,5): error TS
!!! error TS2322: Type 'T[string]' is not assignable to type 'U[string]'.
!!! error TS2322: Type 'T' is not assignable to type 'U'.
}

function f80<T>(t: T): Partial<T> {
return t;
}

function f81<T, K extends keyof T>(t: T, k: K): Partial<T[K]> {
return t[k];
}

function f82<T, K1 extends keyof T, K2 extends keyof T[K1]>(t: T, k1: K1, k2: K2): Partial<T[K1][K2]> {
return t[k1][k2];
}

24 changes: 24 additions & 0 deletions tests/baselines/reference/mappedTypeRelationships.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,18 @@ function f76<T, U extends T, K extends keyof T>(x: { [P in K]: T[P] }, y: { [P i
x = y;
y = x; // Error
}

function f80<T>(t: T): Partial<T> {
return t;
}

function f81<T, K extends keyof T>(t: T, k: K): Partial<T[K]> {
return t[k];
}

function f82<T, K1 extends keyof T, K2 extends keyof T[K1]>(t: T, k1: K1, k2: K2): Partial<T[K1][K2]> {
return t[k1][k2];
}


//// [mappedTypeRelationships.js]
Expand Down Expand Up @@ -289,6 +301,15 @@ function f76(x, y) {
x = y;
y = x; // Error
}
function f80(t) {
return t;
}
function f81(t, k) {
return t[k];
}
function f82(t, k1, k2) {
return t[k1][k2];
}


//// [mappedTypeRelationships.d.ts]
Expand Down Expand Up @@ -369,3 +390,6 @@ declare function f76<T, U extends T, K extends keyof T>(x: {
}, y: {
[P in K]: U[P];
}): void;
declare function f80<T>(t: T): Partial<T>;
declare function f81<T, K extends keyof T>(t: T, k: K): Partial<T[K]>;
declare function f82<T, K1 extends keyof T, K2 extends keyof T[K1]>(t: T, k1: K1, k2: K2): Partial<T[K1][K2]>;
55 changes: 55 additions & 0 deletions tests/baselines/reference/mappedTypeRelationships.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -750,3 +750,58 @@ function f76<T, U extends T, K extends keyof T>(x: { [P in K]: T[P] }, y: { [P i
>x : Symbol(x, Decl(mappedTypeRelationships.ts, 165, 48))
}

function f80<T>(t: T): Partial<T> {
>f80 : Symbol(f80, Decl(mappedTypeRelationships.ts, 168, 1))
>T : Symbol(T, Decl(mappedTypeRelationships.ts, 170, 13))
>t : Symbol(t, Decl(mappedTypeRelationships.ts, 170, 16))
>T : Symbol(T, Decl(mappedTypeRelationships.ts, 170, 13))
>Partial : Symbol(Partial, Decl(lib.d.ts, --, --))
>T : Symbol(T, Decl(mappedTypeRelationships.ts, 170, 13))

return t;
>t : Symbol(t, Decl(mappedTypeRelationships.ts, 170, 16))
}

function f81<T, K extends keyof T>(t: T, k: K): Partial<T[K]> {
>f81 : Symbol(f81, Decl(mappedTypeRelationships.ts, 172, 1))
>T : Symbol(T, Decl(mappedTypeRelationships.ts, 174, 13))
>K : Symbol(K, Decl(mappedTypeRelationships.ts, 174, 15))
>T : Symbol(T, Decl(mappedTypeRelationships.ts, 174, 13))
>t : Symbol(t, Decl(mappedTypeRelationships.ts, 174, 35))
>T : Symbol(T, Decl(mappedTypeRelationships.ts, 174, 13))
>k : Symbol(k, Decl(mappedTypeRelationships.ts, 174, 40))
>K : Symbol(K, Decl(mappedTypeRelationships.ts, 174, 15))
>Partial : Symbol(Partial, Decl(lib.d.ts, --, --))
>T : Symbol(T, Decl(mappedTypeRelationships.ts, 174, 13))
>K : Symbol(K, Decl(mappedTypeRelationships.ts, 174, 15))

return t[k];
>t : Symbol(t, Decl(mappedTypeRelationships.ts, 174, 35))
>k : Symbol(k, Decl(mappedTypeRelationships.ts, 174, 40))
}

function f82<T, K1 extends keyof T, K2 extends keyof T[K1]>(t: T, k1: K1, k2: K2): Partial<T[K1][K2]> {
>f82 : Symbol(f82, Decl(mappedTypeRelationships.ts, 176, 1))
>T : Symbol(T, Decl(mappedTypeRelationships.ts, 178, 13))
>K1 : Symbol(K1, Decl(mappedTypeRelationships.ts, 178, 15))
>T : Symbol(T, Decl(mappedTypeRelationships.ts, 178, 13))
>K2 : Symbol(K2, Decl(mappedTypeRelationships.ts, 178, 35))
>T : Symbol(T, Decl(mappedTypeRelationships.ts, 178, 13))
>K1 : Symbol(K1, Decl(mappedTypeRelationships.ts, 178, 15))
>t : Symbol(t, Decl(mappedTypeRelationships.ts, 178, 60))
>T : Symbol(T, Decl(mappedTypeRelationships.ts, 178, 13))
>k1 : Symbol(k1, Decl(mappedTypeRelationships.ts, 178, 65))
>K1 : Symbol(K1, Decl(mappedTypeRelationships.ts, 178, 15))
>k2 : Symbol(k2, Decl(mappedTypeRelationships.ts, 178, 73))
>K2 : Symbol(K2, Decl(mappedTypeRelationships.ts, 178, 35))
>Partial : Symbol(Partial, Decl(lib.d.ts, --, --))
>T : Symbol(T, Decl(mappedTypeRelationships.ts, 178, 13))
>K1 : Symbol(K1, Decl(mappedTypeRelationships.ts, 178, 15))
>K2 : Symbol(K2, Decl(mappedTypeRelationships.ts, 178, 35))

return t[k1][k2];
>t : Symbol(t, Decl(mappedTypeRelationships.ts, 178, 60))
>k1 : Symbol(k1, Decl(mappedTypeRelationships.ts, 178, 65))
>k2 : Symbol(k2, Decl(mappedTypeRelationships.ts, 178, 73))
}

58 changes: 58 additions & 0 deletions tests/baselines/reference/mappedTypeRelationships.types
Original file line number Diff line number Diff line change
Expand Up @@ -856,3 +856,61 @@ function f76<T, U extends T, K extends keyof T>(x: { [P in K]: T[P] }, y: { [P i
>x : { [P in K]: T[P]; }
}

function f80<T>(t: T): Partial<T> {
>f80 : <T>(t: T) => Partial<T>
>T : T
>t : T
>T : T
>Partial : Partial<T>
>T : T

return t;
>t : T
}

function f81<T, K extends keyof T>(t: T, k: K): Partial<T[K]> {
>f81 : <T, K extends keyof T>(t: T, k: K) => Partial<T[K]>
>T : T
>K : K
>T : T
>t : T
>T : T
>k : K
>K : K
>Partial : Partial<T>
>T : T
>K : K

return t[k];
>t[k] : T[K]
>t : T
>k : K
}

function f82<T, K1 extends keyof T, K2 extends keyof T[K1]>(t: T, k1: K1, k2: K2): Partial<T[K1][K2]> {
>f82 : <T, K1 extends keyof T, K2 extends keyof T[K1]>(t: T, k1: K1, k2: K2) => Partial<T[K1][K2]>
>T : T
>K1 : K1
>T : T
>K2 : K2
>T : T
>K1 : K1
>t : T
>T : T
>k1 : K1
>K1 : K1
>k2 : K2
>K2 : K2
>Partial : Partial<T>
>T : T
>K1 : K1
>K2 : K2

return t[k1][k2];
>t[k1][k2] : T[K1][K2]
>t[k1] : T[K1]
>t : T
>k1 : K1
>k2 : K2
}

12 changes: 12 additions & 0 deletions tests/cases/conformance/types/mapped/mappedTypeRelationships.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,15 @@ function f76<T, U extends T, K extends keyof T>(x: { [P in K]: T[P] }, y: { [P i
x = y;
y = x; // Error
}

function f80<T>(t: T): Partial<T> {
return t;
}

function f81<T, K extends keyof T>(t: T, k: K): Partial<T[K]> {
return t[k];
}

function f82<T, K1 extends keyof T, K2 extends keyof T[K1]>(t: T, k1: K1, k2: K2): Partial<T[K1][K2]> {
return t[k1][k2];
}