From 58e847abb50b18b1ab97653b6c5c4f11f72e8e8e Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Fri, 8 Mar 2019 14:01:27 -0800 Subject: [PATCH] Add assignability rule relaxing the assignability of partial mapped types (#30112) * Add assignability rule relaxing the assignability of partial mapped types * Update comment --- src/compiler/checker.ts | 26 +++++++++++++------ ...TypePartialNonHomomorphicBaseConstraint.js | 21 +++++++++++++++ ...artialNonHomomorphicBaseConstraint.symbols | 21 +++++++++++++++ ...ePartialNonHomomorphicBaseConstraint.types | 18 +++++++++++++ ...TypePartialNonHomomorphicBaseConstraint.ts | 7 +++++ 5 files changed, 85 insertions(+), 8 deletions(-) create mode 100644 tests/baselines/reference/mappedTypePartialNonHomomorphicBaseConstraint.js create mode 100644 tests/baselines/reference/mappedTypePartialNonHomomorphicBaseConstraint.symbols create mode 100644 tests/baselines/reference/mappedTypePartialNonHomomorphicBaseConstraint.types create mode 100644 tests/cases/compiler/mappedTypePartialNonHomomorphicBaseConstraint.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c856351c8973f..ec9e181c05211 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -12725,16 +12725,26 @@ namespace ts { (template).indexType === getTypeParameterFromMappedType(target)) { return Ternary.True; } - // 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. - if (!isGenericMappedType(source) && isRelatedTo(getConstraintTypeFromMappedType(target), getIndexType(source))) { - const indexedAccessType = getIndexedAccessType(source, getTypeParameterFromMappedType(target)); - const templateType = getTemplateTypeFromMappedType(target); - if (result = isRelatedTo(indexedAccessType, templateType, reportErrors)) { - return result; + if (!isGenericMappedType(source)) { + const targetConstraint = getConstraintTypeFromMappedType(target); + const sourceKeys = getIndexType(source); + const hasOptionalUnionKeys = modifiers & MappedTypeModifiers.IncludeOptional && targetConstraint.flags & TypeFlags.Union; + const filteredByApplicability = hasOptionalUnionKeys ? filterType(targetConstraint, t => !!isRelatedTo(t, 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. + if (hasOptionalUnionKeys + ? !(filteredByApplicability!.flags & TypeFlags.Never) + : isRelatedTo(targetConstraint, sourceKeys)) { + const indexingType = hasOptionalUnionKeys ? filteredByApplicability! : getTypeParameterFromMappedType(target); + const indexedAccessType = getIndexedAccessType(source, indexingType); + const templateType = getTemplateTypeFromMappedType(target); + if (result = isRelatedTo(indexedAccessType, templateType, reportErrors)) { + return result; + } } + originalErrorInfo = errorInfo; + errorInfo = saveErrorInfo; } - originalErrorInfo = errorInfo; - errorInfo = saveErrorInfo; } } diff --git a/tests/baselines/reference/mappedTypePartialNonHomomorphicBaseConstraint.js b/tests/baselines/reference/mappedTypePartialNonHomomorphicBaseConstraint.js new file mode 100644 index 0000000000000..438260336ac97 --- /dev/null +++ b/tests/baselines/reference/mappedTypePartialNonHomomorphicBaseConstraint.js @@ -0,0 +1,21 @@ +//// [mappedTypePartialNonHomomorphicBaseConstraint.ts] +export type Errors = { readonly [K in keyof D | "base"]?: string[] }; + +class Model { + getErrors(): Errors { + return { base: ["some base error"] }; + } +} + + +//// [mappedTypePartialNonHomomorphicBaseConstraint.js] +"use strict"; +exports.__esModule = true; +var Model = /** @class */ (function () { + function Model() { + } + Model.prototype.getErrors = function () { + return { base: ["some base error"] }; + }; + return Model; +}()); diff --git a/tests/baselines/reference/mappedTypePartialNonHomomorphicBaseConstraint.symbols b/tests/baselines/reference/mappedTypePartialNonHomomorphicBaseConstraint.symbols new file mode 100644 index 0000000000000..1baa841cf0ff6 --- /dev/null +++ b/tests/baselines/reference/mappedTypePartialNonHomomorphicBaseConstraint.symbols @@ -0,0 +1,21 @@ +=== tests/cases/compiler/mappedTypePartialNonHomomorphicBaseConstraint.ts === +export type Errors = { readonly [K in keyof D | "base"]?: string[] }; +>Errors : Symbol(Errors, Decl(mappedTypePartialNonHomomorphicBaseConstraint.ts, 0, 0)) +>D : Symbol(D, Decl(mappedTypePartialNonHomomorphicBaseConstraint.ts, 0, 19)) +>K : Symbol(K, Decl(mappedTypePartialNonHomomorphicBaseConstraint.ts, 0, 36)) +>D : Symbol(D, Decl(mappedTypePartialNonHomomorphicBaseConstraint.ts, 0, 19)) + +class Model { +>Model : Symbol(Model, Decl(mappedTypePartialNonHomomorphicBaseConstraint.ts, 0, 72)) +>D : Symbol(D, Decl(mappedTypePartialNonHomomorphicBaseConstraint.ts, 2, 12)) + + getErrors(): Errors { +>getErrors : Symbol(Model.getErrors, Decl(mappedTypePartialNonHomomorphicBaseConstraint.ts, 2, 16)) +>Errors : Symbol(Errors, Decl(mappedTypePartialNonHomomorphicBaseConstraint.ts, 0, 0)) +>D : Symbol(D, Decl(mappedTypePartialNonHomomorphicBaseConstraint.ts, 2, 12)) + + return { base: ["some base error"] }; +>base : Symbol(base, Decl(mappedTypePartialNonHomomorphicBaseConstraint.ts, 4, 12)) + } +} + diff --git a/tests/baselines/reference/mappedTypePartialNonHomomorphicBaseConstraint.types b/tests/baselines/reference/mappedTypePartialNonHomomorphicBaseConstraint.types new file mode 100644 index 0000000000000..8d07b70c88cb9 --- /dev/null +++ b/tests/baselines/reference/mappedTypePartialNonHomomorphicBaseConstraint.types @@ -0,0 +1,18 @@ +=== tests/cases/compiler/mappedTypePartialNonHomomorphicBaseConstraint.ts === +export type Errors = { readonly [K in keyof D | "base"]?: string[] }; +>Errors : Errors + +class Model { +>Model : Model + + getErrors(): Errors { +>getErrors : () => Errors + + return { base: ["some base error"] }; +>{ base: ["some base error"] } : { base: string[]; } +>base : string[] +>["some base error"] : string[] +>"some base error" : "some base error" + } +} + diff --git a/tests/cases/compiler/mappedTypePartialNonHomomorphicBaseConstraint.ts b/tests/cases/compiler/mappedTypePartialNonHomomorphicBaseConstraint.ts new file mode 100644 index 0000000000000..dd490e252b78b --- /dev/null +++ b/tests/cases/compiler/mappedTypePartialNonHomomorphicBaseConstraint.ts @@ -0,0 +1,7 @@ +export type Errors = { readonly [K in keyof D | "base"]?: string[] }; + +class Model { + getErrors(): Errors { + return { base: ["some base error"] }; + } +}