From 5a85fca0cd9db17b95e80068f17467b0c7f04fe5 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 26 Jul 2017 15:19:17 -0700 Subject: [PATCH 1/4] Properly check mapped type constituents / Fix generic mapped type display --- src/compiler/checker.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2ff45ee0b737d..ec9867fa9b769 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2558,10 +2558,8 @@ namespace ts { } function createTypeNodeFromObjectType(type: ObjectType): TypeNode { - if (type.objectFlags & ObjectFlags.Mapped) { - if (getConstraintTypeFromMappedType(type).flags & (TypeFlags.TypeParameter | TypeFlags.Index)) { - return createMappedTypeNodeFromType(type); - } + if (isGenericMappedType(type)) { + return createMappedTypeNodeFromType(type); } const resolved = resolveStructuredTypeMembers(type); @@ -3464,11 +3462,9 @@ namespace ts { } function writeLiteralType(type: ObjectType, flags: TypeFormatFlags) { - if (type.objectFlags & ObjectFlags.Mapped) { - if (getConstraintTypeFromMappedType(type).flags & (TypeFlags.TypeParameter | TypeFlags.Index)) { - writeMappedType(type); - return; - } + if (isGenericMappedType(type)) { + writeMappedType(type); + return; } const resolved = resolveStructuredTypeMembers(type); @@ -18641,6 +18637,8 @@ namespace ts { } function checkIndexedAccessType(node: IndexedAccessTypeNode) { + checkSourceElement(node.objectType); + checkSourceElement(node.indexType); checkIndexedAccessIndexType(getTypeFromIndexedAccessTypeNode(node), node); } From 62ddc99a4948d81a713bb9997dd6ddef4c460dea Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 27 Jul 2017 09:35:43 -0700 Subject: [PATCH 2/4] Accept new baselines --- .../reference/anyIndexedAccessArrayNoException.errors.txt | 5 ++++- .../reference/keyofAndIndexedAccessErrors.errors.txt | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/baselines/reference/anyIndexedAccessArrayNoException.errors.txt b/tests/baselines/reference/anyIndexedAccessArrayNoException.errors.txt index e207468122bc0..01c5bd6b2e2c0 100644 --- a/tests/baselines/reference/anyIndexedAccessArrayNoException.errors.txt +++ b/tests/baselines/reference/anyIndexedAccessArrayNoException.errors.txt @@ -1,8 +1,11 @@ +tests/cases/compiler/anyIndexedAccessArrayNoException.ts(1,12): error TS1122: A tuple type element list cannot be empty. tests/cases/compiler/anyIndexedAccessArrayNoException.ts(1,12): error TS2538: Type '[]' cannot be used as an index type. -==== tests/cases/compiler/anyIndexedAccessArrayNoException.ts (1 errors) ==== +==== tests/cases/compiler/anyIndexedAccessArrayNoException.ts (2 errors) ==== var x: any[[]]; ~~ +!!! error TS1122: A tuple type element list cannot be empty. + ~~ !!! error TS2538: Type '[]' cannot be used as an index type. \ No newline at end of file diff --git a/tests/baselines/reference/keyofAndIndexedAccessErrors.errors.txt b/tests/baselines/reference/keyofAndIndexedAccessErrors.errors.txt index 1e88e2abc4341..0634c3419e014 100644 --- a/tests/baselines/reference/keyofAndIndexedAccessErrors.errors.txt +++ b/tests/baselines/reference/keyofAndIndexedAccessErrors.errors.txt @@ -15,6 +15,7 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(35,21): error tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(36,21): error TS2538: Type 'boolean' cannot be used as an index type. tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(41,31): error TS2538: Type 'boolean' cannot be used as an index type. tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(46,16): error TS2538: Type 'boolean' cannot be used as an index type. +tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(49,12): error TS1122: A tuple type element list cannot be empty. tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(63,33): error TS2345: Argument of type '"size"' is not assignable to parameter of type '"name" | "width" | "height" | "visible"'. tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(64,33): error TS2345: Argument of type '"name" | "size"' is not assignable to parameter of type '"name" | "width" | "height" | "visible"'. Type '"size"' is not assignable to type '"name" | "width" | "height" | "visible"'. @@ -28,7 +29,7 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(76,5): error tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(77,5): error TS2322: Type 'keyof (T & U)' is not assignable to type 'keyof (T | U)'. -==== tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts (24 errors) ==== +==== tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts (25 errors) ==== class Shape { name: string; width: number; @@ -112,6 +113,8 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(77,5): error type T60 = {}["toString"]; type T61 = []["toString"]; + ~~ +!!! error TS1122: A tuple type element list cannot be empty. declare let cond: boolean; From b6ec9512076a30f2c635cb58e59282ea6df6ca5b Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 27 Jul 2017 09:50:57 -0700 Subject: [PATCH 3/4] Add missing check in getIndexedAccessForMappedType --- src/compiler/checker.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ec9867fa9b769..05b06860ecfbc 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7573,10 +7573,16 @@ namespace ts { } function getIndexedAccessForMappedType(type: MappedType, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode) { - const accessExpression = accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression ? accessNode : undefined; - if (accessExpression && isAssignmentTarget(accessExpression) && type.declaration.readonlyToken) { - error(accessExpression, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(type)); - return unknownType; + if (accessNode) { + // Check if the index type is assignable to 'keyof T' for the object type. + if (!isTypeAssignableTo(indexType, getIndexType(type))) { + error(accessNode, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(indexType), typeToString(type)); + return unknownType; + } + if (accessNode.kind === SyntaxKind.ElementAccessExpression && isAssignmentTarget(accessNode) && type.declaration.readonlyToken) { + error(accessNode, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(type)); + return unknownType; + } } const mapper = createTypeMapper([getTypeParameterFromMappedType(type)], [indexType]); const templateMapper = type.mapper ? combineTypeMappers(type.mapper, mapper) : mapper; From 9e900942b5080d61bbd23e3dd09d60d922a781a1 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 27 Jul 2017 09:51:17 -0700 Subject: [PATCH 4/4] Add regression tests --- .../reference/mappedTypeErrors2.errors.txt | 35 +++++++++++++ .../baselines/reference/mappedTypeErrors2.js | 49 +++++++++++++++++++ .../types/mapped/mappedTypeErrors2.ts | 22 +++++++++ 3 files changed, 106 insertions(+) create mode 100644 tests/baselines/reference/mappedTypeErrors2.errors.txt create mode 100644 tests/baselines/reference/mappedTypeErrors2.js create mode 100644 tests/cases/conformance/types/mapped/mappedTypeErrors2.ts diff --git a/tests/baselines/reference/mappedTypeErrors2.errors.txt b/tests/baselines/reference/mappedTypeErrors2.errors.txt new file mode 100644 index 0000000000000..18bc7cc723962 --- /dev/null +++ b/tests/baselines/reference/mappedTypeErrors2.errors.txt @@ -0,0 +1,35 @@ +tests/cases/conformance/types/mapped/mappedTypeErrors2.ts(9,30): error TS2536: Type 'K' cannot be used to index type 'T1'. +tests/cases/conformance/types/mapped/mappedTypeErrors2.ts(13,30): error TS2536: Type 'K' cannot be used to index type 'T3'. +tests/cases/conformance/types/mapped/mappedTypeErrors2.ts(15,47): error TS2536: Type 'S' cannot be used to index type 'AB'. +tests/cases/conformance/types/mapped/mappedTypeErrors2.ts(17,49): error TS2536: Type 'L' cannot be used to index type '{ [key in AB[S]]: true; }'. + + +==== tests/cases/conformance/types/mapped/mappedTypeErrors2.ts (4 errors) ==== + // Repros from #17238 + + type AB = { + a: 'a' + b: 'a' + }; + + type T1 = { [key in AB[K]]: true }; + type T2 = T1[K]; // Error + ~~~~~~~~ +!!! error TS2536: Type 'K' cannot be used to index type 'T1'. + + type R = AB[keyof AB]; // "a" + type T3 = { [key in R]: true }; + type T4 = T3[K] // Error + ~~~~~ +!!! error TS2536: Type 'K' cannot be used to index type 'T3'. + + type T5 = {[key in AB[S]]: true}[S]; // Error + ~~~~~ +!!! error TS2536: Type 'S' cannot be used to index type 'AB'. + + type T6 = {[key in AB[S]]: true}[L]; // Error + ~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2536: Type 'L' cannot be used to index type '{ [key in AB[S]]: true; }'. + + type T7 = {[key in AB[S]]: true}[L]; + \ No newline at end of file diff --git a/tests/baselines/reference/mappedTypeErrors2.js b/tests/baselines/reference/mappedTypeErrors2.js new file mode 100644 index 0000000000000..d9fc6b0bc723a --- /dev/null +++ b/tests/baselines/reference/mappedTypeErrors2.js @@ -0,0 +1,49 @@ +//// [mappedTypeErrors2.ts] +// Repros from #17238 + +type AB = { + a: 'a' + b: 'a' +}; + +type T1 = { [key in AB[K]]: true }; +type T2 = T1[K]; // Error + +type R = AB[keyof AB]; // "a" +type T3 = { [key in R]: true }; +type T4 = T3[K] // Error + +type T5 = {[key in AB[S]]: true}[S]; // Error + +type T6 = {[key in AB[S]]: true}[L]; // Error + +type T7 = {[key in AB[S]]: true}[L]; + + +//// [mappedTypeErrors2.js] +// Repros from #17238 + + +//// [mappedTypeErrors2.d.ts] +declare type AB = { + a: 'a'; + b: 'a'; +}; +declare type T1 = { + [key in AB[K]]: true; +}; +declare type T2 = T1[K]; +declare type R = AB[keyof AB]; +declare type T3 = { + [key in R]: true; +}; +declare type T4 = T3[K]; +declare type T5 = { + [key in AB[S]]: true; +}[S]; +declare type T6 = { + [key in AB[S]]: true; +}[L]; +declare type T7 = { + [key in AB[S]]: true; +}[L]; diff --git a/tests/cases/conformance/types/mapped/mappedTypeErrors2.ts b/tests/cases/conformance/types/mapped/mappedTypeErrors2.ts new file mode 100644 index 0000000000000..100a93eb159a2 --- /dev/null +++ b/tests/cases/conformance/types/mapped/mappedTypeErrors2.ts @@ -0,0 +1,22 @@ +// @strictNullChecks: true +// @declaration: true + +// Repros from #17238 + +type AB = { + a: 'a' + b: 'a' +}; + +type T1 = { [key in AB[K]]: true }; +type T2 = T1[K]; // Error + +type R = AB[keyof AB]; // "a" +type T3 = { [key in R]: true }; +type T4 = T3[K] // Error + +type T5 = {[key in AB[S]]: true}[S]; // Error + +type T6 = {[key in AB[S]]: true}[L]; // Error + +type T7 = {[key in AB[S]]: true}[L];