From 8bdf5d98285d3c068880e1a887b6e478ff1c105a Mon Sep 17 00:00:00 2001 From: Brian Ng Date: Thu, 1 Oct 2020 14:22:57 -0500 Subject: [PATCH 1/2] Support TypeScript mapped type 'as' clauses --- Makefile | 2 +- .../src/generators/typescript.js | 10 +- .../typescript/types-mapped-as/input.js | 11 + .../typescript/types-mapped-as/output.js | 3 + .../src/plugins/typescript/index.js | 5 + packages/babel-parser/src/types.js | 1 + .../types/mapped-as-invalid/input.ts | 1 + .../types/mapped-as-invalid/options.json | 7 + .../typescript/types/mapped-as/input.ts | 11 + .../typescript/types/mapped-as/output.json | 307 ++++++++++++++++++ .../babel-types/src/definitions/typescript.js | 3 +- 11 files changed, 358 insertions(+), 3 deletions(-) create mode 100644 packages/babel-generator/test/fixtures/typescript/types-mapped-as/input.js create mode 100644 packages/babel-generator/test/fixtures/typescript/types-mapped-as/output.js create mode 100644 packages/babel-parser/test/fixtures/typescript/types/mapped-as-invalid/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/types/mapped-as-invalid/options.json create mode 100644 packages/babel-parser/test/fixtures/typescript/types/mapped-as/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/types/mapped-as/output.json diff --git a/Makefile b/Makefile index 6537e089b8f7..2063f2445c7c 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ FLOW_COMMIT = a1f9a4c709dcebb27a5084acf47755fbae699c25 TEST262_COMMIT = 058adfed86b1d4129996faaf50a85ea55379a66a -TYPESCRIPT_COMMIT = d779a190535e52896cfe5100101173c00b6b8625 +TYPESCRIPT_COMMIT = da8633212023517630de5f3620a23736b63234b1 FORCE_PUBLISH = "@babel/runtime,@babel/runtime-corejs2,@babel/runtime-corejs3,@babel/standalone" diff --git a/packages/babel-generator/src/generators/typescript.js b/packages/babel-generator/src/generators/typescript.js index dbdb5bce7fb4..fb52fec4676e 100644 --- a/packages/babel-generator/src/generators/typescript.js +++ b/packages/babel-generator/src/generators/typescript.js @@ -330,7 +330,7 @@ export function TSIndexedAccessType(node) { } export function TSMappedType(node) { - const { readonly, typeParameter, optional } = node; + const { nameType, optional, readonly, typeParameter } = node; this.token("{"); this.space(); if (readonly) { @@ -345,6 +345,14 @@ export function TSMappedType(node) { this.word("in"); this.space(); this.print(typeParameter.constraint, typeParameter); + + if (nameType) { + this.space(); + this.word("as"); + this.space(); + this.print(nameType, node); + } + this.token("]"); if (optional) { diff --git a/packages/babel-generator/test/fixtures/typescript/types-mapped-as/input.js b/packages/babel-generator/test/fixtures/typescript/types-mapped-as/input.js new file mode 100644 index 000000000000..3d2123649d0d --- /dev/null +++ b/packages/babel-generator/test/fixtures/typescript/types-mapped-as/input.js @@ -0,0 +1,11 @@ +type MappedTypeWithNewKeys = { + [K in keyof T as NewKeyType]: T[K] +}; + +type RemoveKindField = { + [K in keyof T as Exclude]: T[K] +}; + +type PickByValueType = { + [K in keyof T as T[K] extends U ? K : never]: T[K] +}; diff --git a/packages/babel-generator/test/fixtures/typescript/types-mapped-as/output.js b/packages/babel-generator/test/fixtures/typescript/types-mapped-as/output.js new file mode 100644 index 000000000000..561075b6b066 --- /dev/null +++ b/packages/babel-generator/test/fixtures/typescript/types-mapped-as/output.js @@ -0,0 +1,3 @@ +type MappedTypeWithNewKeys = { [K in keyof T as NewKeyType]: T[K] }; +type RemoveKindField = { [K in keyof T as Exclude]: T[K] }; +type PickByValueType = { [K in keyof T as T[K] extends U ? K : never]: T[K] }; \ No newline at end of file diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 1b9b7f1f3e83..037bde8b53ae 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -621,6 +621,11 @@ export default (superClass: Class): Class => this.expect(tt.bracketL); node.typeParameter = this.tsParseMappedTypeParameter(); + + if (this.eatContextual("as")) { + node.nameType = this.tsParseType(); + } + this.expect(tt.bracketR); if (this.match(tt.plusMin)) { diff --git a/packages/babel-parser/src/types.js b/packages/babel-parser/src/types.js index 6d77c5c0d00d..914b4793392d 100644 --- a/packages/babel-parser/src/types.js +++ b/packages/babel-parser/src/types.js @@ -1339,6 +1339,7 @@ export type TsMappedType = TsTypeBase & { typeParameter: TsTypeParameter, optional?: true | "+" | "-", typeAnnotation: ?TsType, + nameType: ?TsType, }; export type TsLiteralType = TsTypeBase & { diff --git a/packages/babel-parser/test/fixtures/typescript/types/mapped-as-invalid/input.ts b/packages/babel-parser/test/fixtures/typescript/types/mapped-as-invalid/input.ts new file mode 100644 index 000000000000..b97e9e2cd83d --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/types/mapped-as-invalid/input.ts @@ -0,0 +1 @@ +type Foo = { [K in keyof T as]: T[K] }; diff --git a/packages/babel-parser/test/fixtures/typescript/types/mapped-as-invalid/options.json b/packages/babel-parser/test/fixtures/typescript/types/mapped-as-invalid/options.json new file mode 100644 index 000000000000..07de165bf59e --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/types/mapped-as-invalid/options.json @@ -0,0 +1,7 @@ +{ + "sourceType": "module", + "plugins": [ + "typescript" + ], + "throws": "Unexpected token (1:32)" +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/types/mapped-as/input.ts b/packages/babel-parser/test/fixtures/typescript/types/mapped-as/input.ts new file mode 100644 index 000000000000..3d2123649d0d --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/types/mapped-as/input.ts @@ -0,0 +1,11 @@ +type MappedTypeWithNewKeys = { + [K in keyof T as NewKeyType]: T[K] +}; + +type RemoveKindField = { + [K in keyof T as Exclude]: T[K] +}; + +type PickByValueType = { + [K in keyof T as T[K] extends U ? K : never]: T[K] +}; diff --git a/packages/babel-parser/test/fixtures/typescript/types/mapped-as/output.json b/packages/babel-parser/test/fixtures/typescript/types/mapped-as/output.json new file mode 100644 index 000000000000..3b8d16fbe15a --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/types/mapped-as/output.json @@ -0,0 +1,307 @@ +{ + "type": "File", + "start":0,"end":238,"loc":{"start":{"line":1,"column":0},"end":{"line":11,"column":2}}, + "program": { + "type": "Program", + "start":0,"end":238,"loc":{"start":{"line":1,"column":0},"end":{"line":11,"column":2}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "TSTypeAliasDeclaration", + "start":0,"end":73,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":2}}, + "id": { + "type": "Identifier", + "start":5,"end":26,"loc":{"start":{"line":1,"column":5},"end":{"line":1,"column":26},"identifierName":"MappedTypeWithNewKeys"}, + "name": "MappedTypeWithNewKeys" + }, + "typeParameters": { + "type": "TSTypeParameterDeclaration", + "start":26,"end":29,"loc":{"start":{"line":1,"column":26},"end":{"line":1,"column":29}}, + "params": [ + { + "type": "TSTypeParameter", + "start":27,"end":28,"loc":{"start":{"line":1,"column":27},"end":{"line":1,"column":28}}, + "name": "T" + } + ] + }, + "typeAnnotation": { + "type": "TSMappedType", + "start":32,"end":72,"loc":{"start":{"line":1,"column":32},"end":{"line":3,"column":1}}, + "typeParameter": { + "type": "TSTypeParameter", + "start":37,"end":49,"loc":{"start":{"line":2,"column":3},"end":{"line":2,"column":15}}, + "name": "K", + "constraint": { + "type": "TSTypeOperator", + "start":42,"end":49,"loc":{"start":{"line":2,"column":8},"end":{"line":2,"column":15}}, + "operator": "keyof", + "typeAnnotation": { + "type": "TSTypeReference", + "start":48,"end":49,"loc":{"start":{"line":2,"column":14},"end":{"line":2,"column":15}}, + "typeName": { + "type": "Identifier", + "start":48,"end":49,"loc":{"start":{"line":2,"column":14},"end":{"line":2,"column":15},"identifierName":"T"}, + "name": "T" + } + } + } + }, + "nameType": { + "type": "TSTypeReference", + "start":53,"end":63,"loc":{"start":{"line":2,"column":19},"end":{"line":2,"column":29}}, + "typeName": { + "type": "Identifier", + "start":53,"end":63,"loc":{"start":{"line":2,"column":19},"end":{"line":2,"column":29},"identifierName":"NewKeyType"}, + "name": "NewKeyType" + } + }, + "typeAnnotation": { + "type": "TSIndexedAccessType", + "start":66,"end":70,"loc":{"start":{"line":2,"column":32},"end":{"line":2,"column":36}}, + "objectType": { + "type": "TSTypeReference", + "start":66,"end":67,"loc":{"start":{"line":2,"column":32},"end":{"line":2,"column":33}}, + "typeName": { + "type": "Identifier", + "start":66,"end":67,"loc":{"start":{"line":2,"column":32},"end":{"line":2,"column":33},"identifierName":"T"}, + "name": "T" + } + }, + "indexType": { + "type": "TSTypeReference", + "start":68,"end":69,"loc":{"start":{"line":2,"column":34},"end":{"line":2,"column":35}}, + "typeName": { + "type": "Identifier", + "start":68,"end":69,"loc":{"start":{"line":2,"column":34},"end":{"line":2,"column":35},"identifierName":"K"}, + "name": "K" + } + } + } + } + }, + { + "type": "TSTypeAliasDeclaration", + "start":75,"end":150,"loc":{"start":{"line":5,"column":0},"end":{"line":7,"column":2}}, + "id": { + "type": "Identifier", + "start":80,"end":95,"loc":{"start":{"line":5,"column":5},"end":{"line":5,"column":20},"identifierName":"RemoveKindField"}, + "name": "RemoveKindField" + }, + "typeParameters": { + "type": "TSTypeParameterDeclaration", + "start":95,"end":98,"loc":{"start":{"line":5,"column":20},"end":{"line":5,"column":23}}, + "params": [ + { + "type": "TSTypeParameter", + "start":96,"end":97,"loc":{"start":{"line":5,"column":21},"end":{"line":5,"column":22}}, + "name": "T" + } + ] + }, + "typeAnnotation": { + "type": "TSMappedType", + "start":101,"end":149,"loc":{"start":{"line":5,"column":26},"end":{"line":7,"column":1}}, + "typeParameter": { + "type": "TSTypeParameter", + "start":106,"end":118,"loc":{"start":{"line":6,"column":3},"end":{"line":6,"column":15}}, + "name": "K", + "constraint": { + "type": "TSTypeOperator", + "start":111,"end":118,"loc":{"start":{"line":6,"column":8},"end":{"line":6,"column":15}}, + "operator": "keyof", + "typeAnnotation": { + "type": "TSTypeReference", + "start":117,"end":118,"loc":{"start":{"line":6,"column":14},"end":{"line":6,"column":15}}, + "typeName": { + "type": "Identifier", + "start":117,"end":118,"loc":{"start":{"line":6,"column":14},"end":{"line":6,"column":15},"identifierName":"T"}, + "name": "T" + } + } + } + }, + "nameType": { + "type": "TSTypeReference", + "start":122,"end":140,"loc":{"start":{"line":6,"column":19},"end":{"line":6,"column":37}}, + "typeName": { + "type": "Identifier", + "start":122,"end":129,"loc":{"start":{"line":6,"column":19},"end":{"line":6,"column":26},"identifierName":"Exclude"}, + "name": "Exclude" + }, + "typeParameters": { + "type": "TSTypeParameterInstantiation", + "start":129,"end":140,"loc":{"start":{"line":6,"column":26},"end":{"line":6,"column":37}}, + "params": [ + { + "type": "TSTypeReference", + "start":130,"end":131,"loc":{"start":{"line":6,"column":27},"end":{"line":6,"column":28}}, + "typeName": { + "type": "Identifier", + "start":130,"end":131,"loc":{"start":{"line":6,"column":27},"end":{"line":6,"column":28},"identifierName":"K"}, + "name": "K" + } + }, + { + "type": "TSLiteralType", + "start":133,"end":139,"loc":{"start":{"line":6,"column":30},"end":{"line":6,"column":36}}, + "literal": { + "type": "StringLiteral", + "start":133,"end":139,"loc":{"start":{"line":6,"column":30},"end":{"line":6,"column":36}}, + "extra": { + "rawValue": "kind", + "raw": "\"kind\"" + }, + "value": "kind" + } + } + ] + } + }, + "typeAnnotation": { + "type": "TSIndexedAccessType", + "start":143,"end":147,"loc":{"start":{"line":6,"column":40},"end":{"line":6,"column":44}}, + "objectType": { + "type": "TSTypeReference", + "start":143,"end":144,"loc":{"start":{"line":6,"column":40},"end":{"line":6,"column":41}}, + "typeName": { + "type": "Identifier", + "start":143,"end":144,"loc":{"start":{"line":6,"column":40},"end":{"line":6,"column":41},"identifierName":"T"}, + "name": "T" + } + }, + "indexType": { + "type": "TSTypeReference", + "start":145,"end":146,"loc":{"start":{"line":6,"column":42},"end":{"line":6,"column":43}}, + "typeName": { + "type": "Identifier", + "start":145,"end":146,"loc":{"start":{"line":6,"column":42},"end":{"line":6,"column":43},"identifierName":"K"}, + "name": "K" + } + } + } + } + }, + { + "type": "TSTypeAliasDeclaration", + "start":152,"end":238,"loc":{"start":{"line":9,"column":0},"end":{"line":11,"column":2}}, + "id": { + "type": "Identifier", + "start":157,"end":172,"loc":{"start":{"line":9,"column":5},"end":{"line":9,"column":20},"identifierName":"PickByValueType"}, + "name": "PickByValueType" + }, + "typeParameters": { + "type": "TSTypeParameterDeclaration", + "start":172,"end":178,"loc":{"start":{"line":9,"column":20},"end":{"line":9,"column":26}}, + "params": [ + { + "type": "TSTypeParameter", + "start":173,"end":174,"loc":{"start":{"line":9,"column":21},"end":{"line":9,"column":22}}, + "name": "T" + }, + { + "type": "TSTypeParameter", + "start":176,"end":177,"loc":{"start":{"line":9,"column":24},"end":{"line":9,"column":25}}, + "name": "U" + } + ] + }, + "typeAnnotation": { + "type": "TSMappedType", + "start":181,"end":237,"loc":{"start":{"line":9,"column":29},"end":{"line":11,"column":1}}, + "typeParameter": { + "type": "TSTypeParameter", + "start":186,"end":198,"loc":{"start":{"line":10,"column":3},"end":{"line":10,"column":15}}, + "name": "K", + "constraint": { + "type": "TSTypeOperator", + "start":191,"end":198,"loc":{"start":{"line":10,"column":8},"end":{"line":10,"column":15}}, + "operator": "keyof", + "typeAnnotation": { + "type": "TSTypeReference", + "start":197,"end":198,"loc":{"start":{"line":10,"column":14},"end":{"line":10,"column":15}}, + "typeName": { + "type": "Identifier", + "start":197,"end":198,"loc":{"start":{"line":10,"column":14},"end":{"line":10,"column":15},"identifierName":"T"}, + "name": "T" + } + } + } + }, + "nameType": { + "type": "TSConditionalType", + "start":202,"end":228,"loc":{"start":{"line":10,"column":19},"end":{"line":10,"column":45}}, + "checkType": { + "type": "TSIndexedAccessType", + "start":202,"end":206,"loc":{"start":{"line":10,"column":19},"end":{"line":10,"column":23}}, + "objectType": { + "type": "TSTypeReference", + "start":202,"end":203,"loc":{"start":{"line":10,"column":19},"end":{"line":10,"column":20}}, + "typeName": { + "type": "Identifier", + "start":202,"end":203,"loc":{"start":{"line":10,"column":19},"end":{"line":10,"column":20},"identifierName":"T"}, + "name": "T" + } + }, + "indexType": { + "type": "TSTypeReference", + "start":204,"end":205,"loc":{"start":{"line":10,"column":21},"end":{"line":10,"column":22}}, + "typeName": { + "type": "Identifier", + "start":204,"end":205,"loc":{"start":{"line":10,"column":21},"end":{"line":10,"column":22},"identifierName":"K"}, + "name": "K" + } + } + }, + "extendsType": { + "type": "TSTypeReference", + "start":215,"end":216,"loc":{"start":{"line":10,"column":32},"end":{"line":10,"column":33}}, + "typeName": { + "type": "Identifier", + "start":215,"end":216,"loc":{"start":{"line":10,"column":32},"end":{"line":10,"column":33},"identifierName":"U"}, + "name": "U" + } + }, + "trueType": { + "type": "TSTypeReference", + "start":219,"end":220,"loc":{"start":{"line":10,"column":36},"end":{"line":10,"column":37}}, + "typeName": { + "type": "Identifier", + "start":219,"end":220,"loc":{"start":{"line":10,"column":36},"end":{"line":10,"column":37},"identifierName":"K"}, + "name": "K" + } + }, + "falseType": { + "type": "TSNeverKeyword", + "start":223,"end":228,"loc":{"start":{"line":10,"column":40},"end":{"line":10,"column":45}} + } + }, + "typeAnnotation": { + "type": "TSIndexedAccessType", + "start":231,"end":235,"loc":{"start":{"line":10,"column":48},"end":{"line":10,"column":52}}, + "objectType": { + "type": "TSTypeReference", + "start":231,"end":232,"loc":{"start":{"line":10,"column":48},"end":{"line":10,"column":49}}, + "typeName": { + "type": "Identifier", + "start":231,"end":232,"loc":{"start":{"line":10,"column":48},"end":{"line":10,"column":49},"identifierName":"T"}, + "name": "T" + } + }, + "indexType": { + "type": "TSTypeReference", + "start":233,"end":234,"loc":{"start":{"line":10,"column":50},"end":{"line":10,"column":51}}, + "typeName": { + "type": "Identifier", + "start":233,"end":234,"loc":{"start":{"line":10,"column":50},"end":{"line":10,"column":51},"identifierName":"K"}, + "name": "K" + } + } + } + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-types/src/definitions/typescript.js b/packages/babel-types/src/definitions/typescript.js index 05e2e6f7e5f0..263af3b6504b 100644 --- a/packages/babel-types/src/definitions/typescript.js +++ b/packages/babel-types/src/definitions/typescript.js @@ -305,12 +305,13 @@ defineType("TSIndexedAccessType", { defineType("TSMappedType", { aliases: ["TSType"], - visitor: ["typeParameter", "typeAnnotation"], + visitor: ["typeParameter", "typeAnnotation", "nameType"], fields: { readonly: validateOptional(bool), typeParameter: validateType("TSTypeParameter"), optional: validateOptional(bool), typeAnnotation: validateOptionalType("TSType"), + nameType: validateOptionalType("TSType"), }, }); From 63217afcd05ce6bb82a92a01049caff3a8802bd8 Mon Sep 17 00:00:00 2001 From: Brian Ng Date: Fri, 2 Oct 2020 10:54:43 -0500 Subject: [PATCH 2/2] null --- packages/babel-parser/src/plugins/typescript/index.js | 5 +---- .../test/fixtures/typescript/types/mapped/output.json | 4 ++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 037bde8b53ae..b6c2b936dae3 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -621,10 +621,7 @@ export default (superClass: Class): Class => this.expect(tt.bracketL); node.typeParameter = this.tsParseMappedTypeParameter(); - - if (this.eatContextual("as")) { - node.nameType = this.tsParseType(); - } + node.nameType = this.eatContextual("as") ? this.tsParseType() : null; this.expect(tt.bracketR); diff --git a/packages/babel-parser/test/fixtures/typescript/types/mapped/output.json b/packages/babel-parser/test/fixtures/typescript/types/mapped/output.json index 7b27bddbec7a..47f82fab7476 100644 --- a/packages/babel-parser/test/fixtures/typescript/types/mapped/output.json +++ b/packages/babel-parser/test/fixtures/typescript/types/mapped/output.json @@ -33,6 +33,7 @@ "start":18,"end":24,"loc":{"start":{"line":1,"column":18},"end":{"line":1,"column":24}} } }, + "nameType": null, "typeAnnotation": { "type": "TSNumberKeyword", "start":27,"end":33,"loc":{"start":{"line":1,"column":27},"end":{"line":1,"column":33}} @@ -72,6 +73,7 @@ "start":65,"end":71,"loc":{"start":{"line":2,"column":27},"end":{"line":2,"column":33}} } }, + "nameType": null, "optional": true, "typeAnnotation": { "type": "TSNumberKeyword", @@ -112,6 +114,7 @@ "start":114,"end":120,"loc":{"start":{"line":3,"column":28},"end":{"line":3,"column":34}} } }, + "nameType": null, "optional": "+", "typeAnnotation": { "type": "TSNumberKeyword", @@ -152,6 +155,7 @@ "start":164,"end":170,"loc":{"start":{"line":4,"column":28},"end":{"line":4,"column":34}} } }, + "nameType": null, "optional": "-", "typeAnnotation": { "type": "TSNumberKeyword",