diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4f80bcb7834..5bc244600b9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -325,6 +325,7 @@ import { getModeForUsageLocation, getModifiers, getModuleInstanceState, + getNameFromImportAttribute, getNameFromIndexInfo, getNameOfDeclaration, getNameOfExpando, @@ -407,6 +408,7 @@ import { IdentifierTypePredicate, idText, IfStatement, + ImportAttribute, ImportAttributes, ImportCall, ImportClause, @@ -553,6 +555,7 @@ import { isIdentifierTypePredicate, isIdentifierTypeReference, isIfStatement, + isImportAttributes, isImportCall, isImportClause, isImportDeclaration, @@ -2179,6 +2182,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { var deferredGlobalImportMetaType: ObjectType; var deferredGlobalImportMetaExpressionType: ObjectType; var deferredGlobalImportCallOptionsType: ObjectType | undefined; + var deferredGlobalImportAttributesType: ObjectType | undefined; var deferredGlobalDisposableType: ObjectType | undefined; var deferredGlobalAsyncDisposableType: ObjectType | undefined; var deferredGlobalExtractSymbol: Symbol | undefined; @@ -11555,6 +11559,25 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return widenTypeForVariableLikeDeclaration(getTypeForVariableLikeDeclaration(declaration, /*includeOptionality*/ true, CheckMode.Normal), declaration, reportErrors); } + function getTypeFromImportAttributes(node: ImportAttributes): Type { + const links = getNodeLinks(node); + if (!links.resolvedType) { + const symbol = createSymbol(SymbolFlags.ObjectLiteral, InternalSymbolName.ImportAttributes); + const members = createSymbolTable(); + forEach(node.elements, attr => { + const member = createSymbol(SymbolFlags.Property, getNameFromImportAttribute(attr)); + member.parent = symbol; + member.links.type = checkImportAttribute(attr); + member.links.target = member; + members.set(member.escapedName, member); + }); + const type = createAnonymousType(symbol, members, emptyArray, emptyArray, emptyArray); + type.objectFlags |= ObjectFlags.ObjectLiteral | ObjectFlags.NonInferrableType; + links.resolvedType = type; + } + return links.resolvedType; + } + function isGlobalSymbolConstructor(node: Node) { const symbol = getSymbolOfNode(node); const globalSymbol = getGlobalESSymbolConstructorTypeSymbol(/*reportErrors*/ false); @@ -16417,6 +16440,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return (deferredGlobalImportCallOptionsType ||= getGlobalType("ImportCallOptions" as __String, /*arity*/ 0, reportErrors)) || emptyObjectType; } + function getGlobalImportAttributesType(reportErrors: boolean) { + return (deferredGlobalImportAttributesType ||= getGlobalType("ImportAttributes" as __String, /*arity*/ 0, reportErrors)) || emptyObjectType; + } + function getGlobalESSymbolConstructorSymbol(reportErrors: boolean): Symbol | undefined { return deferredGlobalESSymbolConstructorSymbol ||= getGlobalValueSymbol("Symbol" as __String, reportErrors); } @@ -30904,6 +30931,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { case SyntaxKind.JsxOpeningElement: case SyntaxKind.JsxSelfClosingElement: return getContextualJsxElementAttributesType(parent as JsxOpeningLikeElement, contextFlags); + case SyntaxKind.ImportAttribute: + return getContextualImportAttributeType(parent as ImportAttribute); } return undefined; } @@ -30950,6 +30979,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } + function getContextualImportAttributeType(node: ImportAttribute) { + return getTypeOfPropertyOfContextualType(getGlobalImportAttributesType(/*reportErrors*/ false), getNameFromImportAttribute(node)); + } + function getContextualJsxElementAttributesType(node: JsxOpeningLikeElement, contextFlags: ContextFlags | undefined) { if (isJsxOpeningElement(node) && contextFlags !== ContextFlags.Completions) { const index = findContextualNode(node.parent, /*includeCaches*/ !contextFlags); @@ -45991,6 +46024,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function checkImportAttributes(declaration: ImportDeclaration | ExportDeclaration) { const node = declaration.attributes; if (node) { + const importAttributesType = getGlobalImportAttributesType(/*reportErrors*/ true); + if (importAttributesType !== emptyObjectType) { + checkTypeAssignableTo(getTypeFromImportAttributes(node), getNullableType(importAttributesType, TypeFlags.Undefined), node); + } + const validForTypeAttributes = isExclusivelyTypeOnlyImportOrExport(declaration); const override = getResolutionModeOverride(node, validForTypeAttributes ? grammarErrorOnNode : undefined); const isImportAttributes = declaration.attributes.token === SyntaxKind.WithKeyword; @@ -46020,6 +46058,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } + function checkImportAttribute(node: ImportAttribute) { + return getRegularTypeOfLiteralType(checkExpressionCached(node.value)); + } + function checkImportDeclaration(node: ImportDeclaration) { if (checkGrammarModuleElementContext(node, isInJSFile(node) ? Diagnostics.An_import_declaration_can_only_be_used_at_the_top_level_of_a_module : Diagnostics.An_import_declaration_can_only_be_used_at_the_top_level_of_a_namespace_or_module)) { // If we hit an import declaration in an illegal context, just bail out to avoid cascading errors. @@ -47700,6 +47742,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return checkMetaPropertyKeyword(node.parent); } + if (isImportAttributes(node)) { + return getGlobalImportAttributesType(/*reportErrors*/ false); + } + return errorType; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 23873de4824..e25a8d0bceb 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5971,6 +5971,7 @@ export const enum InternalSymbolName { Default = "default", // Default export symbol (technically not wholly internal, but included here for usability) This = "this", InstantiationExpression = "__instantiationExpression", // Instantiation expressions + ImportAttributes = "__importAttributes", } /** diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 36fb67860a3..ba51391f719 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -220,6 +220,7 @@ import { idText, IfStatement, ignoredPaths, + ImportAttribute, ImportCall, ImportClause, ImportDeclaration, @@ -10656,3 +10657,8 @@ export function replaceFirstStar(s: string, replacement: string): string { // Attempt to defeat this analysis by indirectly calling the method. return stringReplace.call(s, "*", replacement); } + +/** @internal */ +export function getNameFromImportAttribute(node: ImportAttribute) { + return isIdentifier(node.name) ? node.name.escapedText : escapeLeadingUnderscores(node.name.text); +} diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index 2842fd6896a..7d9d3c6c9f9 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -2448,7 +2448,13 @@ export class TestState { const annotations = this.annotateContentWithTooltips( result, "completions", - item => item.optionalReplacementSpan, + item => { + if (item.optionalReplacementSpan) { + const { start, length } = item.optionalReplacementSpan; + return start && length === 0 ? { start, length: 1 } : item.optionalReplacementSpan; + } + return undefined; + }, item => item.entries?.flatMap( entry => diff --git a/src/services/completions.ts b/src/services/completions.ts index 155d048562b..234a4072c76 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -95,6 +95,7 @@ import { getLineStartPositionForPosition, getLocalSymbolForExportDefault, getModifiers, + getNameFromImportAttribute, getNameOfDeclaration, getNameTable, getNewLineCharacter, @@ -169,6 +170,7 @@ import { isIdentifier, isIdentifierText, isImportableFile, + isImportAttributes, isImportDeclaration, isImportEqualsDeclaration, isImportKeyword, @@ -3768,6 +3770,7 @@ function getCompletionData( || tryGetObjectLikeCompletionSymbols() || tryGetImportCompletionSymbols() || tryGetImportOrExportClauseCompletionSymbols() + || tryGetImportAttributesCompletionSymbols() || tryGetLocalNamedExportCompletionSymbols() || tryGetConstructorCompletion() || tryGetClassLikeCompletionSymbols() @@ -4455,6 +4458,21 @@ function getCompletionData( return GlobalsSearch.Success; } + /** + * import { x } from "foo" with { | } + */ + function tryGetImportAttributesCompletionSymbols(): GlobalsSearch { + if (contextToken === undefined) return GlobalsSearch.Continue; + + const importAttributes = contextToken.kind === SyntaxKind.OpenBraceToken || contextToken.kind === SyntaxKind.CommaToken ? tryCast(contextToken.parent, isImportAttributes) : + contextToken.kind === SyntaxKind.ColonToken ? tryCast(contextToken.parent.parent, isImportAttributes) : undefined; + if (importAttributes === undefined) return GlobalsSearch.Continue; + + const existing = new Set(importAttributes.elements.map(getNameFromImportAttribute)); + symbols = filter(typeChecker.getTypeAtLocation(importAttributes).getApparentProperties(), attr => !existing.has(attr.escapedName)); + return GlobalsSearch.Success; + } + /** * Adds local declarations for completions in named exports: * diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index fe1a414ef6f..6cd050855d3 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -7076,6 +7076,7 @@ declare namespace ts { Default = "default", This = "this", InstantiationExpression = "__instantiationExpression", + ImportAttributes = "__importAttributes", } /** * This represents a string whose leading underscore have been escaped by adding extra leading underscores. diff --git a/tests/baselines/reference/completionListForImportAttributes.baseline b/tests/baselines/reference/completionListForImportAttributes.baseline new file mode 100644 index 00000000000..2629823d733 --- /dev/null +++ b/tests/baselines/reference/completionListForImportAttributes.baseline @@ -0,0 +1,334 @@ +// === Completions === +=== /tests/cases/fourslash/./b.ts === +// declare global { +// interface ImportAttributes { +// type: "json", +// "resolution-mode": "import" +// } +// } +// const str = "hello"; +// +// import * as t1 from "./a" with { }; +// ^ +// | ---------------------------------------------------------------------- +// | (property) ImportAttributes["resolution-mode"]: "import" +// | (property) ImportAttributes.type: "json" +// | ---------------------------------------------------------------------- +// import * as t2 from "./a" with { type: "" }; +// ^ +// | ---------------------------------------------------------------------- +// | json +// | ---------------------------------------------------------------------- +// import * as t3 from "./a" with { type: "json", }; +// ^ +// | ---------------------------------------------------------------------- +// | (property) ImportAttributes["resolution-mode"]: "import" +// | ---------------------------------------------------------------------- +// import * as t4 from "./a" with { type: }; +// ^ +// | ---------------------------------------------------------------------- +// | "json" +// | (property) ImportAttributes["resolution-mode"]: "import" +// | ---------------------------------------------------------------------- + +[ + { + "marker": { + "fileName": "/tests/cases/fourslash/./b.ts", + "position": 171, + "name": "1" + }, + "item": { + "flags": 0, + "isGlobalCompletion": false, + "isMemberCompletion": false, + "isNewIdentifierLocation": false, + "entries": [ + { + "name": "resolution-mode", + "kind": "property", + "kindModifiers": "declare", + "sortText": "11", + "displayParts": [ + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "property", + "kind": "text" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "ImportAttributes", + "kind": "interfaceName" + }, + { + "text": "[", + "kind": "punctuation" + }, + { + "text": "\"resolution-mode\"", + "kind": "stringLiteral" + }, + { + "text": "]", + "kind": "punctuation" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "\"import\"", + "kind": "stringLiteral" + } + ], + "documentation": [] + }, + { + "name": "type", + "kind": "property", + "kindModifiers": "declare", + "sortText": "11", + "displayParts": [ + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "property", + "kind": "text" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "ImportAttributes", + "kind": "interfaceName" + }, + { + "text": ".", + "kind": "punctuation" + }, + { + "text": "type", + "kind": "propertyName" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "\"json\"", + "kind": "stringLiteral" + } + ], + "documentation": [] + } + ] + } + }, + { + "marker": { + "fileName": "/tests/cases/fourslash/./b.ts", + "position": 215, + "name": "2" + }, + "item": { + "isGlobalCompletion": false, + "isMemberCompletion": false, + "isNewIdentifierLocation": false, + "optionalReplacementSpan": { + "start": 215, + "length": 0 + }, + "entries": [ + { + "name": "json", + "kindModifiers": "", + "kind": "string", + "sortText": "11", + "replacementSpan": { + "start": 215, + "length": 0 + }, + "displayParts": [ + { + "text": "json", + "kind": "text" + } + ] + } + ] + } + }, + { + "marker": { + "fileName": "/tests/cases/fourslash/./b.ts", + "position": 267, + "name": "3" + }, + "item": { + "flags": 0, + "isGlobalCompletion": false, + "isMemberCompletion": false, + "isNewIdentifierLocation": false, + "entries": [ + { + "name": "resolution-mode", + "kind": "property", + "kindModifiers": "declare", + "sortText": "11", + "displayParts": [ + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "property", + "kind": "text" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "ImportAttributes", + "kind": "interfaceName" + }, + { + "text": "[", + "kind": "punctuation" + }, + { + "text": "\"resolution-mode\"", + "kind": "stringLiteral" + }, + { + "text": "]", + "kind": "punctuation" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "\"import\"", + "kind": "stringLiteral" + } + ], + "documentation": [] + } + ] + } + }, + { + "marker": { + "fileName": "/tests/cases/fourslash/./b.ts", + "position": 310, + "name": "4" + }, + "item": { + "flags": 0, + "isGlobalCompletion": false, + "isMemberCompletion": false, + "isNewIdentifierLocation": false, + "entries": [ + { + "name": "\"json\"", + "kind": "string", + "kindModifiers": "", + "sortText": "11", + "displayParts": [ + { + "text": "\"json\"", + "kind": "stringLiteral" + } + ] + }, + { + "name": "resolution-mode", + "kind": "property", + "kindModifiers": "declare", + "sortText": "11", + "displayParts": [ + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "property", + "kind": "text" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "ImportAttributes", + "kind": "interfaceName" + }, + { + "text": "[", + "kind": "punctuation" + }, + { + "text": "\"resolution-mode\"", + "kind": "stringLiteral" + }, + { + "text": "]", + "kind": "punctuation" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "\"import\"", + "kind": "stringLiteral" + } + ], + "documentation": [] + } + ] + } + } +] \ No newline at end of file diff --git a/tests/baselines/reference/completionPropertyFromConstraint.baseline b/tests/baselines/reference/completionPropertyFromConstraint.baseline index 210d913dabe..a31f3e0646c 100644 --- a/tests/baselines/reference/completionPropertyFromConstraint.baseline +++ b/tests/baselines/reference/completionPropertyFromConstraint.baseline @@ -17,7 +17,7 @@ // createStyles({ // x: { // '': '' -// +// ^ // | ---------------------------------------------------------------------- // | (property) alignContent?: string // | (property) alignItems?: string diff --git a/tests/baselines/reference/importAssertionNonstring.errors.txt b/tests/baselines/reference/importAssertionNonstring.errors.txt index bb55e2a13bc..81a06f27808 100644 --- a/tests/baselines/reference/importAssertionNonstring.errors.txt +++ b/tests/baselines/reference/importAssertionNonstring.errors.txt @@ -1,13 +1,29 @@ +mod.mts(1,37): error TS2322: Type '{ field: 0; }' is not assignable to type 'ImportAttributes'. + Property 'field' is incompatible with index signature. + Type 'number' is not assignable to type 'string'. mod.mts(1,52): error TS2837: Import assertion values must be string literal expressions. mod.mts(3,52): error TS2837: Import assertion values must be string literal expressions. +mod.mts(5,37): error TS2322: Type '{ field: RegExp; }' is not assignable to type 'ImportAttributes'. + Property 'field' is incompatible with index signature. + Type 'RegExp' is not assignable to type 'string'. mod.mts(5,52): error TS2837: Import assertion values must be string literal expressions. +mod.mts(7,37): error TS2322: Type '{ field: string[]; }' is not assignable to type 'ImportAttributes'. + Property 'field' is incompatible with index signature. + Type 'string[]' is not assignable to type 'string'. mod.mts(7,52): error TS2837: Import assertion values must be string literal expressions. +mod.mts(9,37): error TS2322: Type '{ field: { a: number; }; }' is not assignable to type 'ImportAttributes'. + Property 'field' is incompatible with index signature. + Type '{ a: number; }' is not assignable to type 'string'. mod.mts(9,52): error TS2837: Import assertion values must be string literal expressions. mod.mts(11,66): error TS2837: Import assertion values must be string literal expressions. -==== mod.mts (6 errors) ==== +==== mod.mts (10 errors) ==== import * as thing1 from "./mod.mjs" assert {field: 0}; + ~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type '{ field: 0; }' is not assignable to type 'ImportAttributes'. +!!! error TS2322: Property 'field' is incompatible with index signature. +!!! error TS2322: Type 'number' is not assignable to type 'string'. ~ !!! error TS2837: Import assertion values must be string literal expressions. @@ -16,14 +32,26 @@ mod.mts(11,66): error TS2837: Import assertion values must be string literal exp !!! error TS2837: Import assertion values must be string literal expressions. import * as thing3 from "./mod.mjs" assert {field: /a/g}; + ~~~~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type '{ field: RegExp; }' is not assignable to type 'ImportAttributes'. +!!! error TS2322: Property 'field' is incompatible with index signature. +!!! error TS2322: Type 'RegExp' is not assignable to type 'string'. ~~~~ !!! error TS2837: Import assertion values must be string literal expressions. import * as thing4 from "./mod.mjs" assert {field: ["a"]}; + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type '{ field: string[]; }' is not assignable to type 'ImportAttributes'. +!!! error TS2322: Property 'field' is incompatible with index signature. +!!! error TS2322: Type 'string[]' is not assignable to type 'string'. ~~~~~ !!! error TS2837: Import assertion values must be string literal expressions. import * as thing5 from "./mod.mjs" assert {field: { a: 0 }}; + ~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type '{ field: { a: number; }; }' is not assignable to type 'ImportAttributes'. +!!! error TS2322: Property 'field' is incompatible with index signature. +!!! error TS2322: Type '{ a: number; }' is not assignable to type 'string'. ~~~~~~~~ !!! error TS2837: Import assertion values must be string literal expressions. diff --git a/tests/baselines/reference/importAttributes6.errors.txt b/tests/baselines/reference/importAttributes6.errors.txt index 18fa0ba6b69..f2f160a4bd0 100644 --- a/tests/baselines/reference/importAttributes6.errors.txt +++ b/tests/baselines/reference/importAttributes6.errors.txt @@ -1,25 +1,53 @@ +mod.mts(1,37): error TS2322: Type '{ field: 0; }' is not assignable to type 'ImportAttributes'. + Property 'field' is incompatible with index signature. + Type 'number' is not assignable to type 'string'. mod.mts(1,51): error TS2858: Import attribute values must be string literal expressions. mod.mts(2,51): error TS2858: Import attribute values must be string literal expressions. +mod.mts(3,37): error TS2322: Type '{ field: RegExp; }' is not assignable to type 'ImportAttributes'. + Property 'field' is incompatible with index signature. + Type 'RegExp' is not assignable to type 'string'. mod.mts(3,51): error TS2858: Import attribute values must be string literal expressions. +mod.mts(4,37): error TS2322: Type '{ field: string[]; }' is not assignable to type 'ImportAttributes'. + Property 'field' is incompatible with index signature. + Type 'string[]' is not assignable to type 'string'. mod.mts(4,51): error TS2858: Import attribute values must be string literal expressions. +mod.mts(5,37): error TS2322: Type '{ field: { a: number; }; }' is not assignable to type 'ImportAttributes'. + Property 'field' is incompatible with index signature. + Type '{ a: number; }' is not assignable to type 'string'. mod.mts(5,51): error TS2858: Import attribute values must be string literal expressions. mod.mts(6,65): error TS2858: Import attribute values must be string literal expressions. -==== mod.mts (6 errors) ==== +==== mod.mts (10 errors) ==== import * as thing1 from "./mod.mjs" with { field: 0 }; + ~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type '{ field: 0; }' is not assignable to type 'ImportAttributes'. +!!! error TS2322: Property 'field' is incompatible with index signature. +!!! error TS2322: Type 'number' is not assignable to type 'string'. ~ !!! error TS2858: Import attribute values must be string literal expressions. import * as thing2 from "./mod.mjs" with { field: `a` }; ~~~ !!! error TS2858: Import attribute values must be string literal expressions. import * as thing3 from "./mod.mjs" with { field: /a/g }; + ~~~~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type '{ field: RegExp; }' is not assignable to type 'ImportAttributes'. +!!! error TS2322: Property 'field' is incompatible with index signature. +!!! error TS2322: Type 'RegExp' is not assignable to type 'string'. ~~~~ !!! error TS2858: Import attribute values must be string literal expressions. import * as thing4 from "./mod.mjs" with { field: ["a"] }; + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type '{ field: string[]; }' is not assignable to type 'ImportAttributes'. +!!! error TS2322: Property 'field' is incompatible with index signature. +!!! error TS2322: Type 'string[]' is not assignable to type 'string'. ~~~~~ !!! error TS2858: Import attribute values must be string literal expressions. import * as thing5 from "./mod.mjs" with { field: { a: 0 } }; + ~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type '{ field: { a: number; }; }' is not assignable to type 'ImportAttributes'. +!!! error TS2322: Property 'field' is incompatible with index signature. +!!! error TS2322: Type '{ a: number; }' is not assignable to type 'string'. ~~~~~~~~ !!! error TS2858: Import attribute values must be string literal expressions. import * as thing6 from "./mod.mjs" with { type: "json", field: 0..toString() }; diff --git a/tests/baselines/reference/importAttributes9.errors.txt b/tests/baselines/reference/importAttributes9.errors.txt new file mode 100644 index 00000000000..59006e6b71e --- /dev/null +++ b/tests/baselines/reference/importAttributes9.errors.txt @@ -0,0 +1,41 @@ +b.ts(7,27): error TS2322: Type '{ type: "not-json"; }' is not assignable to type 'ImportAttributes'. + Types of property 'type' are incompatible. + Type '"not-json"' is not assignable to type '"json"'. +b.ts(11,25): error TS2322: Type '{ with: { type: "not-json"; }; }' is not assignable to type 'ImportCallOptions'. + The types of 'with.type' are incompatible between these types. + Type '"not-json"' is not assignable to type '"json"'. + + +==== ./a.ts (0 errors) ==== + export default {}; + +==== ./b.ts (2 errors) ==== + declare global { + interface ImportAttributes { + type: "json" + } + } + + import * as ns from "./a" with { type: "not-json" }; + ~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type '{ type: "not-json"; }' is not assignable to type 'ImportAttributes'. +!!! error TS2322: Types of property 'type' are incompatible. +!!! error TS2322: Type '"not-json"' is not assignable to type '"json"'. + void ns; + + async function f() { + await import("./a", { + ~ + with: { + ~~~~~~~~~~~~~~~ + type: "not-json", + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + }, + ~~~~~~~~~~ + }); + ~~~~~ +!!! error TS2322: Type '{ with: { type: "not-json"; }; }' is not assignable to type 'ImportCallOptions'. +!!! error TS2322: The types of 'with.type' are incompatible between these types. +!!! error TS2322: Type '"not-json"' is not assignable to type '"json"'. + } + \ No newline at end of file diff --git a/tests/baselines/reference/importAttributes9.js b/tests/baselines/reference/importAttributes9.js new file mode 100644 index 00000000000..10e5230e2b6 --- /dev/null +++ b/tests/baselines/reference/importAttributes9.js @@ -0,0 +1,36 @@ +//// [tests/cases/conformance/importAttributes/importAttributes9.ts] //// + +//// [a.ts] +export default {}; + +//// [b.ts] +declare global { + interface ImportAttributes { + type: "json" + } +} + +import * as ns from "./a" with { type: "not-json" }; +void ns; + +async function f() { + await import("./a", { + with: { + type: "not-json", + }, + }); +} + + +//// [a.js] +export default {}; +//// [b.js] +import * as ns from "./a" with { type: "not-json" }; +void ns; +async function f() { + await import("./a", { + with: { + type: "not-json", + }, + }); +} diff --git a/tests/baselines/reference/importAttributes9.symbols b/tests/baselines/reference/importAttributes9.symbols new file mode 100644 index 00000000000..bb0975afa8d --- /dev/null +++ b/tests/baselines/reference/importAttributes9.symbols @@ -0,0 +1,40 @@ +//// [tests/cases/conformance/importAttributes/importAttributes9.ts] //// + +=== ./a.ts === + +export default {}; + +=== ./b.ts === +declare global { +>global : Symbol(global, Decl(b.ts, 0, 0)) + + interface ImportAttributes { +>ImportAttributes : Symbol(ImportAttributes, Decl(lib.es5.d.ts, --, --), Decl(b.ts, 0, 16)) + + type: "json" +>type : Symbol(ImportAttributes.type, Decl(b.ts, 1, 33)) + } +} + +import * as ns from "./a" with { type: "not-json" }; +>ns : Symbol(ns, Decl(b.ts, 6, 6)) + +void ns; +>ns : Symbol(ns, Decl(b.ts, 6, 6)) + +async function f() { +>f : Symbol(f, Decl(b.ts, 7, 8)) + + await import("./a", { +>"./a" : Symbol(ns, Decl(a.ts, 0, 0)) + + with: { +>with : Symbol(with, Decl(b.ts, 10, 25)) + + type: "not-json", +>type : Symbol(type, Decl(b.ts, 11, 15)) + + }, + }); +} + diff --git a/tests/baselines/reference/importAttributes9.types b/tests/baselines/reference/importAttributes9.types new file mode 100644 index 00000000000..c17e4150005 --- /dev/null +++ b/tests/baselines/reference/importAttributes9.types @@ -0,0 +1,45 @@ +//// [tests/cases/conformance/importAttributes/importAttributes9.ts] //// + +=== ./a.ts === +export default {}; +>{} : {} + +=== ./b.ts === +declare global { +>global : any + + interface ImportAttributes { + type: "json" +>type : "json" + } +} + +import * as ns from "./a" with { type: "not-json" }; +>ns : typeof ns +>type : any + +void ns; +>void ns : undefined +>ns : typeof ns + +async function f() { +>f : () => Promise + + await import("./a", { +>await import("./a", { with: { type: "not-json", }, }) : typeof ns +>import("./a", { with: { type: "not-json", }, }) : Promise +>"./a" : "./a" +>{ with: { type: "not-json", }, } : { with: { type: "not-json"; }; } + + with: { +>with : { type: "not-json"; } +>{ type: "not-json", } : { type: "not-json"; } + + type: "not-json", +>type : "not-json" +>"not-json" : "not-json" + + }, + }); +} + diff --git a/tests/cases/conformance/importAttributes/importAttributes9.ts b/tests/cases/conformance/importAttributes/importAttributes9.ts new file mode 100644 index 00000000000..9b713865927 --- /dev/null +++ b/tests/cases/conformance/importAttributes/importAttributes9.ts @@ -0,0 +1,22 @@ +// @module: esnext +// @target: esnext +// @filename: ./a.ts +export default {}; + +// @filename: ./b.ts +declare global { + interface ImportAttributes { + type: "json" + } +} + +import * as ns from "./a" with { type: "not-json" }; +void ns; + +async function f() { + await import("./a", { + with: { + type: "not-json", + }, + }); +} diff --git a/tests/cases/fourslash/completionListForImportAttributes.ts b/tests/cases/fourslash/completionListForImportAttributes.ts new file mode 100644 index 00000000000..f96c4c9d27f --- /dev/null +++ b/tests/cases/fourslash/completionListForImportAttributes.ts @@ -0,0 +1,22 @@ +/// + +// @module: esnext +// @target: esnext +// @filename: ./a.ts +////export default {}; + +// @filename: ./b.ts +////declare global { +//// interface ImportAttributes { +//// type: "json", +//// "resolution-mode": "import" +//// } +////} +////const str = "hello"; +//// +////import * as t1 from "./a" with { /*1*/ }; +////import * as t2 from "./a" with { type: "/*2*/" }; +////import * as t3 from "./a" with { type: "json", /*3*/ }; +////import * as t4 from "./a" with { type: /*4*/ }; + +verify.baselineCompletions();