Skip to content

Commit 58e847a

Browse files
authored
Add assignability rule relaxing the assignability of partial mapped types (microsoft#30112)
* Add assignability rule relaxing the assignability of partial mapped types * Update comment
1 parent 45a6cb7 commit 58e847a

File tree

5 files changed

+85
-8
lines changed

5 files changed

+85
-8
lines changed

src/compiler/checker.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12725,16 +12725,26 @@ namespace ts {
1272512725
(<IndexedAccessType>template).indexType === getTypeParameterFromMappedType(target)) {
1272612726
return Ternary.True;
1272712727
}
12728-
// 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.
12729-
if (!isGenericMappedType(source) && isRelatedTo(getConstraintTypeFromMappedType(target), getIndexType(source))) {
12730-
const indexedAccessType = getIndexedAccessType(source, getTypeParameterFromMappedType(target));
12731-
const templateType = getTemplateTypeFromMappedType(target);
12732-
if (result = isRelatedTo(indexedAccessType, templateType, reportErrors)) {
12733-
return result;
12728+
if (!isGenericMappedType(source)) {
12729+
const targetConstraint = getConstraintTypeFromMappedType(target);
12730+
const sourceKeys = getIndexType(source);
12731+
const hasOptionalUnionKeys = modifiers & MappedTypeModifiers.IncludeOptional && targetConstraint.flags & TypeFlags.Union;
12732+
const filteredByApplicability = hasOptionalUnionKeys ? filterType(targetConstraint, t => !!isRelatedTo(t, sourceKeys)) : undefined;
12733+
// 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.
12734+
// 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.
12735+
if (hasOptionalUnionKeys
12736+
? !(filteredByApplicability!.flags & TypeFlags.Never)
12737+
: isRelatedTo(targetConstraint, sourceKeys)) {
12738+
const indexingType = hasOptionalUnionKeys ? filteredByApplicability! : getTypeParameterFromMappedType(target);
12739+
const indexedAccessType = getIndexedAccessType(source, indexingType);
12740+
const templateType = getTemplateTypeFromMappedType(target);
12741+
if (result = isRelatedTo(indexedAccessType, templateType, reportErrors)) {
12742+
return result;
12743+
}
1273412744
}
12745+
originalErrorInfo = errorInfo;
12746+
errorInfo = saveErrorInfo;
1273512747
}
12736-
originalErrorInfo = errorInfo;
12737-
errorInfo = saveErrorInfo;
1273812748
}
1273912749
}
1274012750

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//// [mappedTypePartialNonHomomorphicBaseConstraint.ts]
2+
export type Errors<D> = { readonly [K in keyof D | "base"]?: string[] };
3+
4+
class Model<D> {
5+
getErrors(): Errors<D> {
6+
return { base: ["some base error"] };
7+
}
8+
}
9+
10+
11+
//// [mappedTypePartialNonHomomorphicBaseConstraint.js]
12+
"use strict";
13+
exports.__esModule = true;
14+
var Model = /** @class */ (function () {
15+
function Model() {
16+
}
17+
Model.prototype.getErrors = function () {
18+
return { base: ["some base error"] };
19+
};
20+
return Model;
21+
}());
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
=== tests/cases/compiler/mappedTypePartialNonHomomorphicBaseConstraint.ts ===
2+
export type Errors<D> = { readonly [K in keyof D | "base"]?: string[] };
3+
>Errors : Symbol(Errors, Decl(mappedTypePartialNonHomomorphicBaseConstraint.ts, 0, 0))
4+
>D : Symbol(D, Decl(mappedTypePartialNonHomomorphicBaseConstraint.ts, 0, 19))
5+
>K : Symbol(K, Decl(mappedTypePartialNonHomomorphicBaseConstraint.ts, 0, 36))
6+
>D : Symbol(D, Decl(mappedTypePartialNonHomomorphicBaseConstraint.ts, 0, 19))
7+
8+
class Model<D> {
9+
>Model : Symbol(Model, Decl(mappedTypePartialNonHomomorphicBaseConstraint.ts, 0, 72))
10+
>D : Symbol(D, Decl(mappedTypePartialNonHomomorphicBaseConstraint.ts, 2, 12))
11+
12+
getErrors(): Errors<D> {
13+
>getErrors : Symbol(Model.getErrors, Decl(mappedTypePartialNonHomomorphicBaseConstraint.ts, 2, 16))
14+
>Errors : Symbol(Errors, Decl(mappedTypePartialNonHomomorphicBaseConstraint.ts, 0, 0))
15+
>D : Symbol(D, Decl(mappedTypePartialNonHomomorphicBaseConstraint.ts, 2, 12))
16+
17+
return { base: ["some base error"] };
18+
>base : Symbol(base, Decl(mappedTypePartialNonHomomorphicBaseConstraint.ts, 4, 12))
19+
}
20+
}
21+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
=== tests/cases/compiler/mappedTypePartialNonHomomorphicBaseConstraint.ts ===
2+
export type Errors<D> = { readonly [K in keyof D | "base"]?: string[] };
3+
>Errors : Errors<D>
4+
5+
class Model<D> {
6+
>Model : Model<D>
7+
8+
getErrors(): Errors<D> {
9+
>getErrors : () => Errors<D>
10+
11+
return { base: ["some base error"] };
12+
>{ base: ["some base error"] } : { base: string[]; }
13+
>base : string[]
14+
>["some base error"] : string[]
15+
>"some base error" : "some base error"
16+
}
17+
}
18+
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export type Errors<D> = { readonly [K in keyof D | "base"]?: string[] };
2+
3+
class Model<D> {
4+
getErrors(): Errors<D> {
5+
return { base: ["some base error"] };
6+
}
7+
}

0 commit comments

Comments
 (0)