mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
fix(55994): Type-check Import Attributes in static imports (#56034)
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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",
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 =>
|
||||
|
||||
@@ -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:
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user