From d656487fca5985734f6d7b91cff907c8e94e393c Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 1 Feb 2023 09:09:55 -0800 Subject: [PATCH] Permit instantiation expressions in `typeof import(...)` (#52532) --- src/compiler/checker.ts | 11 ++--- src/compiler/diagnosticMessages.json | 4 -- src/compiler/types.ts | 2 +- .../getSupportedCodeFixes-can-be-proxied.js | 3 -- .../typeofImportInstantiationExpression.js | 28 +++++++++++ ...ypeofImportInstantiationExpression.symbols | 49 +++++++++++++++++++ .../typeofImportInstantiationExpression.types | 38 ++++++++++++++ .../typeofImportInstantiationExpression.ts | 16 ++++++ 8 files changed, 137 insertions(+), 14 deletions(-) create mode 100644 tests/baselines/reference/typeofImportInstantiationExpression.js create mode 100644 tests/baselines/reference/typeofImportInstantiationExpression.symbols create mode 100644 tests/baselines/reference/typeofImportInstantiationExpression.types create mode 100644 tests/cases/compiler/typeofImportInstantiationExpression.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1b38ca72a46..2e539ee75a3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -17748,11 +17748,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function getTypeFromImportTypeNode(node: ImportTypeNode): Type { const links = getNodeLinks(node); if (!links.resolvedType) { - if (node.isTypeOf && node.typeArguments) { // Only the non-typeof form can make use of type arguments - error(node, Diagnostics.Type_arguments_cannot_be_used_here); - links.resolvedSymbol = unknownSymbol; - return links.resolvedType = errorType; - } if (!isLiteralImportTypeNode(node)) { error(node.argument, Diagnostics.String_literal_expected); links.resolvedSymbol = unknownSymbol; @@ -17815,7 +17810,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const resolvedSymbol = resolveSymbol(symbol); links.resolvedSymbol = resolvedSymbol; if (meaning === SymbolFlags.Value) { - return getTypeOfSymbol(symbol); // intentionally doesn't use resolved symbol so type is cached as expected on the alias + return getInstantiationExpressionType(getTypeOfSymbol(symbol), node); // intentionally doesn't use resolved symbol so type is cached as expected on the alias } else { const type = tryGetDeclaredTypeOfSymbol(resolvedSymbol); // call this first to ensure typeParameters is populated (if applicable) @@ -34055,6 +34050,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const exprType = node.kind === SyntaxKind.ExpressionWithTypeArguments ? checkExpression(node.expression) : isThisIdentifier(node.exprName) ? checkThisExpression(node.exprName) : checkExpression(node.exprName); + return getInstantiationExpressionType(exprType, node); + } + + function getInstantiationExpressionType(exprType: Type, node: NodeWithTypeArguments) { const typeArguments = node.typeArguments; if (exprType === silentNeverType || isErrorType(exprType) || !some(typeArguments)) { return exprType; diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index a566b30383f..43b2fed2d9a 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1076,10 +1076,6 @@ "category": "Error", "code": 1341 }, - "Type arguments cannot be used here.": { - "category": "Error", - "code": 1342 - }, "The 'import.meta' meta-property is only allowed when the '--module' option is 'es2020', 'es2022', 'esnext', 'system', 'node16', or 'nodenext'.": { "category": "Error", "code": 1343 diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 0a43afe978e..544b804e865 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -6409,7 +6409,7 @@ export interface AnonymousType extends ObjectType { /** @internal */ export interface InstantiationExpressionType extends AnonymousType { - node: ExpressionWithTypeArguments | TypeQueryNode; + node: NodeWithTypeArguments; } /** @internal */ diff --git a/tests/baselines/reference/tsserver/plugins/getSupportedCodeFixes-can-be-proxied.js b/tests/baselines/reference/tsserver/plugins/getSupportedCodeFixes-can-be-proxied.js index 9a2640baa41..189d354018d 100644 --- a/tests/baselines/reference/tsserver/plugins/getSupportedCodeFixes-can-be-proxied.js +++ b/tests/baselines/reference/tsserver/plugins/getSupportedCodeFixes-can-be-proxied.js @@ -664,7 +664,6 @@ Info 32 [00:01:13.000] response: "1339", "1340", "1341", - "1342", "1343", "1344", "1345", @@ -2013,7 +2012,6 @@ Info 38 [00:01:19.000] response: "1339", "1340", "1341", - "1342", "1343", "1344", "1345", @@ -3274,7 +3272,6 @@ Info 40 [00:01:21.000] response: "1339", "1340", "1341", - "1342", "1343", "1344", "1345", diff --git a/tests/baselines/reference/typeofImportInstantiationExpression.js b/tests/baselines/reference/typeofImportInstantiationExpression.js new file mode 100644 index 00000000000..a14ad91153d --- /dev/null +++ b/tests/baselines/reference/typeofImportInstantiationExpression.js @@ -0,0 +1,28 @@ +//// [tests/cases/compiler/typeofImportInstantiationExpression.ts] //// + +//// [input.ts] +// @strict + +// Repro from #52248 + +interface Arg = Record> { + "__is_argument__"?: true; + meta?: T; + params?: Params; +} + +export function myFunction = Record>(arg: Arg) { return (arg.params || {}) as U } + +//// [main.ts] +type T1 = typeof import('./input.js').myFunction; +type T2 = typeof import('./input.js').myFunction; + + +//// [input.js] +"use strict"; +// @strict +Object.defineProperty(exports, "__esModule", { value: true }); +exports.myFunction = void 0; +function myFunction(arg) { return (arg.params || {}); } +exports.myFunction = myFunction; +//// [main.js] diff --git a/tests/baselines/reference/typeofImportInstantiationExpression.symbols b/tests/baselines/reference/typeofImportInstantiationExpression.symbols new file mode 100644 index 00000000000..bbbbb8f70a1 --- /dev/null +++ b/tests/baselines/reference/typeofImportInstantiationExpression.symbols @@ -0,0 +1,49 @@ +=== tests/cases/compiler/input.ts === +// @strict + +// Repro from #52248 + +interface Arg = Record> { +>Arg : Symbol(Arg, Decl(input.ts, 0, 0)) +>T : Symbol(T, Decl(input.ts, 4, 14)) +>Params : Symbol(Params, Decl(input.ts, 4, 22)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) + + "__is_argument__"?: true; +>"__is_argument__" : Symbol(Arg["__is_argument__"], Decl(input.ts, 4, 82)) + + meta?: T; +>meta : Symbol(Arg.meta, Decl(input.ts, 5, 29)) +>T : Symbol(T, Decl(input.ts, 4, 14)) + + params?: Params; +>params : Symbol(Arg.params, Decl(input.ts, 6, 13)) +>Params : Symbol(Params, Decl(input.ts, 4, 22)) +} + +export function myFunction = Record>(arg: Arg) { return (arg.params || {}) as U } +>myFunction : Symbol(myFunction, Decl(input.ts, 8, 1)) +>T : Symbol(T, Decl(input.ts, 10, 27)) +>U : Symbol(U, Decl(input.ts, 10, 35)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>arg : Symbol(arg, Decl(input.ts, 10, 89)) +>Arg : Symbol(Arg, Decl(input.ts, 0, 0)) +>T : Symbol(T, Decl(input.ts, 10, 27)) +>U : Symbol(U, Decl(input.ts, 10, 35)) +>arg.params : Symbol(Arg.params, Decl(input.ts, 6, 13)) +>arg : Symbol(arg, Decl(input.ts, 10, 89)) +>params : Symbol(Arg.params, Decl(input.ts, 6, 13)) +>U : Symbol(U, Decl(input.ts, 10, 35)) + +=== tests/cases/compiler/main.ts === +type T1 = typeof import('./input.js').myFunction; +>T1 : Symbol(T1, Decl(main.ts, 0, 0)) +>myFunction : Symbol(myFunction, Decl(input.ts, 8, 1)) + +type T2 = typeof import('./input.js').myFunction; +>T2 : Symbol(T2, Decl(main.ts, 0, 49)) +>myFunction : Symbol(myFunction, Decl(input.ts, 8, 1)) +>slug : Symbol(slug, Decl(main.ts, 1, 55)) + diff --git a/tests/baselines/reference/typeofImportInstantiationExpression.types b/tests/baselines/reference/typeofImportInstantiationExpression.types new file mode 100644 index 00000000000..74561c37ef2 --- /dev/null +++ b/tests/baselines/reference/typeofImportInstantiationExpression.types @@ -0,0 +1,38 @@ +=== tests/cases/compiler/input.ts === +// @strict + +// Repro from #52248 + +interface Arg = Record> { + "__is_argument__"?: true; +>"__is_argument__" : true +>true : true + + meta?: T; +>meta : T + + params?: Params; +>params : Params +} + +export function myFunction = Record>(arg: Arg) { return (arg.params || {}) as U } +>myFunction : = Record>(arg: Arg) => U +>arg : Arg +>(arg.params || {}) as U : U +>(arg.params || {}) : U | {} +>arg.params || {} : U | {} +>arg.params : U +>arg : Arg +>params : U +>{} : {} + +=== tests/cases/compiler/main.ts === +type T1 = typeof import('./input.js').myFunction; +>T1 : = Record>(arg: Arg) => U +>myFunction : error + +type T2 = typeof import('./input.js').myFunction; +>T2 : (arg: Arg) => { slug: 'hello'; } +>myFunction : error +>slug : "hello" + diff --git a/tests/cases/compiler/typeofImportInstantiationExpression.ts b/tests/cases/compiler/typeofImportInstantiationExpression.ts new file mode 100644 index 00000000000..69a57d97b12 --- /dev/null +++ b/tests/cases/compiler/typeofImportInstantiationExpression.ts @@ -0,0 +1,16 @@ +// @strict + +// Repro from #52248 + +// @filename: input.ts +interface Arg = Record> { + "__is_argument__"?: true; + meta?: T; + params?: Params; +} + +export function myFunction = Record>(arg: Arg) { return (arg.params || {}) as U } + +// @filename: main.ts +type T1 = typeof import('./input.js').myFunction; +type T2 = typeof import('./input.js').myFunction;