;
return list.some(property => {
@@ -11517,6 +11815,11 @@ namespace ts {
}
function getConstraintFromIndexedAccess(type: IndexedAccessType) {
+ if (isMappedTypeGenericIndexedAccess(type)) {
+ // For indexed access types of the form { [P in K]: E }[X], where K is non-generic and X is generic,
+ // we substitute an instantiation of E where P is replaced with X.
+ return substituteIndexedMappedType(type.objectType as MappedType, type.indexType);
+ }
const indexConstraint = getSimplifiedTypeOrConstraint(type.indexType);
if (indexConstraint && indexConstraint !== type.indexType) {
const indexedAccess = getIndexedAccessTypeOrUndefined(type.objectType, indexConstraint, type.accessFlags);
@@ -11730,6 +12033,11 @@ namespace ts {
return constraint ? getStringMappingType((t as StringMappingType).symbol, constraint) : stringType;
}
if (t.flags & TypeFlags.IndexedAccess) {
+ if (isMappedTypeGenericIndexedAccess(t)) {
+ // For indexed access types of the form { [P in K]: E }[X], where K is non-generic and X is generic,
+ // we substitute an instantiation of E where P is replaced with X.
+ return getBaseConstraint(substituteIndexedMappedType((t as IndexedAccessType).objectType as MappedType, (t as IndexedAccessType).indexType));
+ }
const baseObjectType = getBaseConstraint((t as IndexedAccessType).objectType);
const baseIndexType = getBaseConstraint((t as IndexedAccessType).indexType);
const baseIndexedAccess = baseObjectType && baseIndexType && getIndexedAccessTypeOrUndefined(baseObjectType, baseIndexType, (t as IndexedAccessType).accessFlags);
@@ -11812,13 +12120,18 @@ namespace ts {
return type;
}
+ function isMappedTypeGenericIndexedAccess(type: Type) {
+ return type.flags & TypeFlags.IndexedAccess && getObjectFlags((type as IndexedAccessType).objectType) & ObjectFlags.Mapped &&
+ !isGenericMappedType((type as IndexedAccessType).objectType) && isGenericIndexType((type as IndexedAccessType).indexType);
+ }
+
/**
* For a type parameter, return the base constraint of the type parameter. For the string, number,
* boolean, and symbol primitive types, return the corresponding object types. Otherwise return the
* type itself.
*/
function getApparentType(type: Type): Type {
- const t = type.flags & TypeFlags.Instantiable ? getBaseConstraintOfType(type) || unknownType : type;
+ const t = !(type.flags & TypeFlags.Instantiable) ? type : getBaseConstraintOfType(type) || unknownType;
return getObjectFlags(t) & ObjectFlags.Mapped ? getApparentTypeOfMappedType(t as MappedType) :
t.flags & TypeFlags.Intersection ? getApparentTypeOfIntersectionType(t as IntersectionType) :
t.flags & TypeFlags.StringLike ? globalStringType :
@@ -11852,7 +12165,7 @@ namespace ts {
let mergedInstantiations = false;
for (const current of containingType.types) {
const type = getApparentType(current);
- if (!(type === errorType || type.flags & TypeFlags.Never)) {
+ if (!(isErrorType(type) || type.flags & TypeFlags.Never)) {
const prop = getPropertyOfType(type, name, skipObjectFunctionPropertyAugment);
const modifiers = prop ? getDeclarationModifierFlagsFromSymbol(prop) : 0;
if (prop) {
@@ -11942,6 +12255,7 @@ namespace ts {
let firstType: Type | undefined;
let nameType: Type | undefined;
const propTypes: Type[] = [];
+ let writeTypes: Type[] | undefined;
let firstValueDeclaration: Declaration | undefined;
let hasNonUniformValueDeclaration = false;
for (const prop of props) {
@@ -11957,10 +12271,14 @@ namespace ts {
firstType = type;
nameType = getSymbolLinks(prop).nameType;
}
+ const writeType = getWriteTypeOfSymbol(prop);
+ if (writeTypes || writeType !== type) {
+ writeTypes = append(!writeTypes ? propTypes.slice() : writeTypes, writeType);
+ }
else if (type !== firstType) {
checkFlags |= CheckFlags.HasNonUniformType;
}
- if (isLiteralType(type)) {
+ if (isLiteralType(type) || isPatternLiteralType(type)) {
checkFlags |= CheckFlags.HasLiteralType;
}
if (type.flags & TypeFlags.Never) {
@@ -11987,9 +12305,13 @@ namespace ts {
result.checkFlags |= CheckFlags.DeferredType;
result.deferralParent = containingType;
result.deferralConstituents = propTypes;
+ result.deferralWriteConstituents = writeTypes;
}
else {
result.type = isUnion ? getUnionType(propTypes) : getIntersectionType(propTypes);
+ if (writeTypes) {
+ result.writeType = isUnion ? getUnionType(writeTypes) : getIntersectionType(writeTypes);
+ }
}
return result;
}
@@ -12456,6 +12778,13 @@ namespace ts {
return typeTag?.typeExpression && getSingleCallSignature(getTypeFromTypeNode(typeTag.typeExpression));
}
+ function getParameterTypeOfTypeTag(func: FunctionLikeDeclaration, parameter: ParameterDeclaration) {
+ const signature = getSignatureOfTypeTag(func);
+ if (!signature) return undefined;
+ const pos = func.parameters.indexOf(parameter);
+ return parameter.dotDotDotToken ? getRestTypeAtPosition(signature, pos) : getTypeAtPosition(signature, pos);
+ }
+
function getReturnTypeOfTypeTag(node: SignatureDeclaration | JSDocSignature) {
const signature = getSignatureOfTypeTag(node);
return signature && getReturnTypeOfSignature(signature);
@@ -12477,7 +12806,7 @@ namespace ts {
if (!node) return false;
switch (node.kind) {
case SyntaxKind.Identifier:
- return (node as Identifier).escapedText === argumentsSymbol.escapedName && getResolvedSymbol(node as Identifier) === argumentsSymbol;
+ return (node as Identifier).escapedText === argumentsSymbol.escapedName && getReferencedValueSymbol(node as Identifier) === argumentsSymbol;
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.MethodDeclaration:
@@ -12490,6 +12819,9 @@ namespace ts {
case SyntaxKind.ElementAccessExpression:
return traverse((node as PropertyAccessExpression | ElementAccessExpression).expression);
+ case SyntaxKind.PropertyAssignment:
+ return traverse((node as PropertyAssignment).initializer);
+
default:
return !nodeStartsNewLexicalEnvironment(node) && !isPartOfTypeNode(node) && !!forEachChild(node, traverse);
}
@@ -12745,8 +13077,11 @@ namespace ts {
// object type literal or interface (using the new keyword). Each way of declaring a constructor
// will result in a different declaration kind.
if (!signature.isolatedSignatureType) {
- const kind = signature.declaration ? signature.declaration.kind : SyntaxKind.Unknown;
- const isConstructor = kind === SyntaxKind.Constructor || kind === SyntaxKind.ConstructSignature || kind === SyntaxKind.ConstructorType;
+ const kind = signature.declaration?.kind;
+
+ // If declaration is undefined, it is likely to be the signature of the default constructor.
+ const isConstructor = kind === undefined || kind === SyntaxKind.Constructor || kind === SyntaxKind.ConstructSignature || kind === SyntaxKind.ConstructorType;
+
const type = createObjectType(ObjectFlags.Anonymous);
type.members = emptySymbols;
type.properties = emptyArray;
@@ -12889,7 +13224,7 @@ namespace ts {
}
else {
let type = getTypeFromTypeNode(constraintDeclaration);
- if (type.flags & TypeFlags.Any && type !== errorType) { // Allow errorType to propegate to keep downstream errors suppressed
+ if (type.flags & TypeFlags.Any && !isErrorType(type)) { // Allow errorType to propegate to keep downstream errors suppressed
// use keyofConstraintType as the base constraint for mapped type key constraints (unknown isn;t assignable to that, but `any` was),
// use unknown otherwise
type = constraintDeclaration.parent.parent.kind === SyntaxKind.MappedType ? keyofConstraintType : unknownType;
@@ -13084,6 +13419,18 @@ namespace ts {
* declared type. Instantiations are cached using the type identities of the type arguments as the key.
*/
function getTypeFromTypeAliasReference(node: NodeWithTypeArguments, symbol: Symbol): Type {
+ if (getCheckFlags(symbol) & CheckFlags.Unresolved) {
+ const typeArguments = typeArgumentsFromTypeReferenceNode(node);
+ const id = getAliasId(symbol, typeArguments);
+ let errorType = errorTypes.get(id);
+ if (!errorType) {
+ errorType = createIntrinsicType(TypeFlags.Any, "error");
+ errorType.aliasSymbol = symbol;
+ errorType.aliasTypeArguments = typeArguments;
+ errorTypes.set(id, errorType);
+ }
+ return errorType;
+ }
const type = getDeclaredTypeOfSymbol(symbol);
const typeParameters = getSymbolLinks(symbol).typeParameters;
if (typeParameters) {
@@ -13132,12 +13479,39 @@ namespace ts {
return undefined;
}
- function resolveTypeReferenceName(typeReferenceName: EntityNameExpression | EntityName | undefined, meaning: SymbolFlags, ignoreErrors?: boolean) {
- if (!typeReferenceName) {
+ function getSymbolPath(symbol: Symbol): string {
+ return symbol.parent ? `${getSymbolPath(symbol.parent)}.${symbol.escapedName}` : symbol.escapedName as string;
+ }
+
+ function getUnresolvedSymbolForEntityName(name: EntityNameOrEntityNameExpression) {
+ const identifier = name.kind === SyntaxKind.QualifiedName ? name.right :
+ name.kind === SyntaxKind.PropertyAccessExpression ? name.name :
+ name;
+ const text = identifier.escapedText;
+ if (text) {
+ const parentSymbol = name.kind === SyntaxKind.QualifiedName ? getUnresolvedSymbolForEntityName(name.left) :
+ name.kind === SyntaxKind.PropertyAccessExpression ? getUnresolvedSymbolForEntityName(name.expression) :
+ undefined;
+ const path = parentSymbol ? `${getSymbolPath(parentSymbol)}.${text}` : text as string;
+ let result = unresolvedSymbols.get(path);
+ if (!result) {
+ unresolvedSymbols.set(path, result = createSymbol(SymbolFlags.TypeAlias, text, CheckFlags.Unresolved));
+ result.parent = parentSymbol;
+ result.declaredType = unresolvedType;
+ }
+ return result;
+ }
+ return unknownSymbol;
+ }
+
+ function resolveTypeReferenceName(typeReference: TypeReferenceType, meaning: SymbolFlags, ignoreErrors?: boolean) {
+ const name = getTypeReferenceName(typeReference);
+ if (!name) {
return unknownSymbol;
}
-
- return resolveEntityName(typeReferenceName, meaning, ignoreErrors) || unknownSymbol;
+ const symbol = resolveEntityName(name, meaning, ignoreErrors);
+ return symbol && symbol !== unknownSymbol ? symbol :
+ ignoreErrors ? unknownSymbol : getUnresolvedSymbolForEntityName(name);
}
function getTypeReferenceType(node: NodeWithTypeArguments, symbol: Symbol): Type {
@@ -13163,7 +13537,7 @@ namespace ts {
}
else {
// Resolve the type reference as a Type for the purpose of reporting errors.
- resolveTypeReferenceName(getTypeReferenceName(node), SymbolFlags.Type);
+ resolveTypeReferenceName(node, SymbolFlags.Type);
return getTypeOfSymbol(symbol);
}
}
@@ -13213,7 +13587,7 @@ namespace ts {
function getImpliedConstraint(type: Type, checkNode: TypeNode, extendsNode: TypeNode): Type | undefined {
return isUnaryTupleTypeNode(checkNode) && isUnaryTupleTypeNode(extendsNode) ? getImpliedConstraint(type, (checkNode as TupleTypeNode).elements[0], (extendsNode as TupleTypeNode).elements[0]) :
- getActualTypeVariable(getTypeFromTypeNode(checkNode)) === type ? getTypeFromTypeNode(extendsNode) :
+ getActualTypeVariable(getTypeFromTypeNode(checkNode)) === getActualTypeVariable(type) ? getTypeFromTypeNode(extendsNode) :
undefined;
}
@@ -13317,18 +13691,18 @@ namespace ts {
if (isJSDocTypeReference(node)) {
type = getIntendedTypeFromJSDocTypeReference(node);
if (!type) {
- symbol = resolveTypeReferenceName(getTypeReferenceName(node), meaning, /*ignoreErrors*/ true);
+ symbol = resolveTypeReferenceName(node, meaning, /*ignoreErrors*/ true);
if (symbol === unknownSymbol) {
- symbol = resolveTypeReferenceName(getTypeReferenceName(node), meaning | SymbolFlags.Value);
+ symbol = resolveTypeReferenceName(node, meaning | SymbolFlags.Value);
}
else {
- resolveTypeReferenceName(getTypeReferenceName(node), meaning); // Resolve again to mark errors, if any
+ resolveTypeReferenceName(node, meaning); // Resolve again to mark errors, if any
}
type = getTypeReferenceType(node, symbol);
}
}
if (!type) {
- symbol = resolveTypeReferenceName(getTypeReferenceName(node), meaning);
+ symbol = resolveTypeReferenceName(node, meaning);
type = getTypeReferenceType(node, symbol);
}
// Cache both the resolved symbol and the resolved type. The resolved symbol is needed when we check the
@@ -13350,7 +13724,7 @@ namespace ts {
// The expression is processed as an identifier expression (section 4.3)
// or property access expression(section 4.10),
// the widened type(section 3.9) of which becomes the result.
- const type = isThisIdentifier(node.exprName) ? checkThisExpression(node.exprName) : checkExpression(node.exprName);
+ const type = checkExpressionWithTypeArguments(node);
links.resolvedType = getRegularTypeOfLiteralType(getWidenedType(type));
}
return links.resolvedType;
@@ -13395,28 +13769,48 @@ namespace ts {
return getGlobalSymbol(name, SymbolFlags.Type, reportErrors ? Diagnostics.Cannot_find_global_type_0 : undefined);
}
- function getGlobalSymbol(name: __String, meaning: SymbolFlags, diagnostic: DiagnosticMessage | undefined): Symbol | undefined {
- // Don't track references for global symbols anyway, so value if `isReference` is arbitrary
- return resolveName(undefined, name, meaning, diagnostic, name, /*isUse*/ false);
+ function getGlobalTypeAliasSymbol(name: __String, arity: number, reportErrors: boolean): Symbol | undefined {
+ const symbol = getGlobalSymbol(name, SymbolFlags.Type, reportErrors ? Diagnostics.Cannot_find_global_type_0 : undefined);
+ if (symbol) {
+ // Resolve the declared type of the symbol. This resolves type parameters for the type
+ // alias so that we can check arity.
+ getDeclaredTypeOfSymbol(symbol);
+ if (length(getSymbolLinks(symbol).typeParameters) !== arity) {
+ const decl = symbol.declarations && find(symbol.declarations, isTypeAliasDeclaration);
+ error(decl, Diagnostics.Global_type_0_must_have_1_type_parameter_s, symbolName(symbol), arity);
+ return undefined;
+ }
+ }
+ return symbol;
}
- function getGlobalType(name: __String, arity: 0, reportErrors: boolean): ObjectType;
- function getGlobalType(name: __String, arity: number, reportErrors: boolean): GenericType;
+ function getGlobalSymbol(name: __String, meaning: SymbolFlags, diagnostic: DiagnosticMessage | undefined): Symbol | undefined {
+ // Don't track references for global symbols anyway, so value if `isReference` is arbitrary
+ return resolveName(undefined, name, meaning, diagnostic, name, /*isUse*/ false, /*excludeGlobals*/ false, /*getSpellingSuggestions*/ false);
+ }
+
+ function getGlobalType(name: __String, arity: 0, reportErrors: true): ObjectType;
+ function getGlobalType(name: __String, arity: 0, reportErrors: boolean): ObjectType | undefined;
+ function getGlobalType(name: __String, arity: number, reportErrors: true): GenericType;
+ function getGlobalType(name: __String, arity: number, reportErrors: boolean): GenericType | undefined;
function getGlobalType(name: __String, arity: number, reportErrors: boolean): ObjectType | undefined {
const symbol = getGlobalTypeSymbol(name, reportErrors);
return symbol || reportErrors ? getTypeOfGlobalSymbol(symbol, arity) : undefined;
}
function getGlobalTypedPropertyDescriptorType() {
- return deferredGlobalTypedPropertyDescriptorType || (deferredGlobalTypedPropertyDescriptorType = getGlobalType("TypedPropertyDescriptor" as __String, /*arity*/ 1, /*reportErrors*/ true)) || emptyGenericType;
+ // We always report an error, so store a result in the event we could not resolve the symbol to prevent reporting it multiple times
+ return deferredGlobalTypedPropertyDescriptorType ||= getGlobalType("TypedPropertyDescriptor" as __String, /*arity*/ 1, /*reportErrors*/ true) || emptyGenericType;
}
function getGlobalTemplateStringsArrayType() {
- return deferredGlobalTemplateStringsArrayType || (deferredGlobalTemplateStringsArrayType = getGlobalType("TemplateStringsArray" as __String, /*arity*/ 0, /*reportErrors*/ true)) || emptyObjectType;
+ // We always report an error, so store a result in the event we could not resolve the symbol to prevent reporting it multiple times
+ return deferredGlobalTemplateStringsArrayType ||= getGlobalType("TemplateStringsArray" as __String, /*arity*/ 0, /*reportErrors*/ true) || emptyObjectType;
}
function getGlobalImportMetaType() {
- return deferredGlobalImportMetaType || (deferredGlobalImportMetaType = getGlobalType("ImportMeta" as __String, /*arity*/ 0, /*reportErrors*/ true)) || emptyObjectType;
+ // We always report an error, so store a result in the event we could not resolve the symbol to prevent reporting it multiple times
+ return deferredGlobalImportMetaType ||= getGlobalType("ImportMeta" as __String, /*arity*/ 0, /*reportErrors*/ true) || emptyObjectType;
}
function getGlobalImportMetaExpressionType() {
@@ -13437,72 +13831,76 @@ namespace ts {
return deferredGlobalImportMetaExpressionType;
}
- function getGlobalESSymbolConstructorSymbol(reportErrors: boolean) {
- return deferredGlobalESSymbolConstructorSymbol || (deferredGlobalESSymbolConstructorSymbol = getGlobalValueSymbol("Symbol" as __String, reportErrors));
+ function getGlobalImportCallOptionsType(reportErrors: boolean) {
+ return (deferredGlobalImportCallOptionsType ||= getGlobalType("ImportCallOptions" as __String, /*arity*/ 0, reportErrors)) || emptyObjectType;
}
- function getGlobalESSymbolConstructorTypeSymbol(reportErrors: boolean) {
- return deferredGlobalESSymbolConstructorTypeSymbol || (deferredGlobalESSymbolConstructorTypeSymbol = getGlobalTypeSymbol("SymbolConstructor" as __String, reportErrors));
+ function getGlobalESSymbolConstructorSymbol(reportErrors: boolean): Symbol | undefined {
+ return deferredGlobalESSymbolConstructorSymbol ||= getGlobalValueSymbol("Symbol" as __String, reportErrors);
+ }
+
+ function getGlobalESSymbolConstructorTypeSymbol(reportErrors: boolean): Symbol | undefined {
+ return deferredGlobalESSymbolConstructorTypeSymbol ||= getGlobalTypeSymbol("SymbolConstructor" as __String, reportErrors);
}
function getGlobalESSymbolType(reportErrors: boolean) {
- return deferredGlobalESSymbolType || (deferredGlobalESSymbolType = getGlobalType("Symbol" as __String, /*arity*/ 0, reportErrors)) || emptyObjectType;
+ return (deferredGlobalESSymbolType ||= getGlobalType("Symbol" as __String, /*arity*/ 0, reportErrors)) || emptyObjectType;
}
function getGlobalPromiseType(reportErrors: boolean) {
- return deferredGlobalPromiseType || (deferredGlobalPromiseType = getGlobalType("Promise" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType;
+ return (deferredGlobalPromiseType ||= getGlobalType("Promise" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType;
}
function getGlobalPromiseLikeType(reportErrors: boolean) {
- return deferredGlobalPromiseLikeType || (deferredGlobalPromiseLikeType = getGlobalType("PromiseLike" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType;
+ return (deferredGlobalPromiseLikeType ||= getGlobalType("PromiseLike" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType;
}
function getGlobalPromiseConstructorSymbol(reportErrors: boolean): Symbol | undefined {
- return deferredGlobalPromiseConstructorSymbol || (deferredGlobalPromiseConstructorSymbol = getGlobalValueSymbol("Promise" as __String, reportErrors));
+ return deferredGlobalPromiseConstructorSymbol ||= getGlobalValueSymbol("Promise" as __String, reportErrors);
}
function getGlobalPromiseConstructorLikeType(reportErrors: boolean) {
- return deferredGlobalPromiseConstructorLikeType || (deferredGlobalPromiseConstructorLikeType = getGlobalType("PromiseConstructorLike" as __String, /*arity*/ 0, reportErrors)) || emptyObjectType;
+ return (deferredGlobalPromiseConstructorLikeType ||= getGlobalType("PromiseConstructorLike" as __String, /*arity*/ 0, reportErrors)) || emptyObjectType;
}
function getGlobalAsyncIterableType(reportErrors: boolean) {
- return deferredGlobalAsyncIterableType || (deferredGlobalAsyncIterableType = getGlobalType("AsyncIterable" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType;
+ return (deferredGlobalAsyncIterableType ||= getGlobalType("AsyncIterable" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType;
}
function getGlobalAsyncIteratorType(reportErrors: boolean) {
- return deferredGlobalAsyncIteratorType || (deferredGlobalAsyncIteratorType = getGlobalType("AsyncIterator" as __String, /*arity*/ 3, reportErrors)) || emptyGenericType;
+ return (deferredGlobalAsyncIteratorType ||= getGlobalType("AsyncIterator" as __String, /*arity*/ 3, reportErrors)) || emptyGenericType;
}
function getGlobalAsyncIterableIteratorType(reportErrors: boolean) {
- return deferredGlobalAsyncIterableIteratorType || (deferredGlobalAsyncIterableIteratorType = getGlobalType("AsyncIterableIterator" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType;
+ return (deferredGlobalAsyncIterableIteratorType ||= getGlobalType("AsyncIterableIterator" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType;
}
function getGlobalAsyncGeneratorType(reportErrors: boolean) {
- return deferredGlobalAsyncGeneratorType || (deferredGlobalAsyncGeneratorType = getGlobalType("AsyncGenerator" as __String, /*arity*/ 3, reportErrors)) || emptyGenericType;
+ return (deferredGlobalAsyncGeneratorType ||= getGlobalType("AsyncGenerator" as __String, /*arity*/ 3, reportErrors)) || emptyGenericType;
}
function getGlobalIterableType(reportErrors: boolean) {
- return deferredGlobalIterableType || (deferredGlobalIterableType = getGlobalType("Iterable" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType;
+ return (deferredGlobalIterableType ||= getGlobalType("Iterable" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType;
}
function getGlobalIteratorType(reportErrors: boolean) {
- return deferredGlobalIteratorType || (deferredGlobalIteratorType = getGlobalType("Iterator" as __String, /*arity*/ 3, reportErrors)) || emptyGenericType;
+ return (deferredGlobalIteratorType ||= getGlobalType("Iterator" as __String, /*arity*/ 3, reportErrors)) || emptyGenericType;
}
function getGlobalIterableIteratorType(reportErrors: boolean) {
- return deferredGlobalIterableIteratorType || (deferredGlobalIterableIteratorType = getGlobalType("IterableIterator" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType;
+ return (deferredGlobalIterableIteratorType ||= getGlobalType("IterableIterator" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType;
}
function getGlobalGeneratorType(reportErrors: boolean) {
- return deferredGlobalGeneratorType || (deferredGlobalGeneratorType = getGlobalType("Generator" as __String, /*arity*/ 3, reportErrors)) || emptyGenericType;
+ return (deferredGlobalGeneratorType ||= getGlobalType("Generator" as __String, /*arity*/ 3, reportErrors)) || emptyGenericType;
}
function getGlobalIteratorYieldResultType(reportErrors: boolean) {
- return deferredGlobalIteratorYieldResultType || (deferredGlobalIteratorYieldResultType = getGlobalType("IteratorYieldResult" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType;
+ return (deferredGlobalIteratorYieldResultType ||= getGlobalType("IteratorYieldResult" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType;
}
function getGlobalIteratorReturnResultType(reportErrors: boolean) {
- return deferredGlobalIteratorReturnResultType || (deferredGlobalIteratorReturnResultType = getGlobalType("IteratorReturnResult" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType;
+ return (deferredGlobalIteratorReturnResultType ||= getGlobalType("IteratorReturnResult" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType;
}
function getGlobalTypeOrUndefined(name: __String, arity = 0): ObjectType | undefined {
@@ -13510,16 +13908,26 @@ namespace ts {
return symbol && getTypeOfGlobalSymbol(symbol, arity) as GenericType;
}
- function getGlobalExtractSymbol(): Symbol {
- return deferredGlobalExtractSymbol || (deferredGlobalExtractSymbol = getGlobalSymbol("Extract" as __String, SymbolFlags.TypeAlias, Diagnostics.Cannot_find_global_type_0)!); // TODO: GH#18217
+ function getGlobalExtractSymbol(): Symbol | undefined {
+ // We always report an error, so cache a result in the event we could not resolve the symbol to prevent reporting it multiple times
+ deferredGlobalExtractSymbol ||= getGlobalTypeAliasSymbol("Extract" as __String, /*arity*/ 2, /*reportErrors*/ true) || unknownSymbol;
+ return deferredGlobalExtractSymbol === unknownSymbol ? undefined : deferredGlobalExtractSymbol;
}
- function getGlobalOmitSymbol(): Symbol {
- return deferredGlobalOmitSymbol || (deferredGlobalOmitSymbol = getGlobalSymbol("Omit" as __String, SymbolFlags.TypeAlias, Diagnostics.Cannot_find_global_type_0)!); // TODO: GH#18217
+ function getGlobalOmitSymbol(): Symbol | undefined {
+ // We always report an error, so cache a result in the event we could not resolve the symbol to prevent reporting it multiple times
+ deferredGlobalOmitSymbol ||= getGlobalTypeAliasSymbol("Omit" as __String, /*arity*/ 2, /*reportErrors*/ true) || unknownSymbol;
+ return deferredGlobalOmitSymbol === unknownSymbol ? undefined : deferredGlobalOmitSymbol;
+ }
+
+ function getGlobalAwaitedSymbol(reportErrors: boolean): Symbol | undefined {
+ // Only cache `unknownSymbol` if we are reporting errors so that we don't report the error more than once.
+ deferredGlobalAwaitedSymbol ||= getGlobalTypeAliasSymbol("Awaited" as __String, /*arity*/ 1, reportErrors) || (reportErrors ? unknownSymbol : undefined);
+ return deferredGlobalAwaitedSymbol === unknownSymbol ? undefined : deferredGlobalAwaitedSymbol;
}
function getGlobalBigIntType(reportErrors: boolean) {
- return deferredGlobalBigIntType || (deferredGlobalBigIntType = getGlobalType("BigInt" as __String, /*arity*/ 0, reportErrors)) || emptyObjectType;
+ return (deferredGlobalBigIntType ||= getGlobalType("BigInt" as __String, /*arity*/ 0, reportErrors)) || emptyObjectType;
}
/**
@@ -13608,7 +14016,7 @@ namespace ts {
function mayResolveTypeAlias(node: Node): boolean {
switch (node.kind) {
case SyntaxKind.TypeReference:
- return isJSDocTypeReference(node) || !!(resolveTypeReferenceName((node as TypeReferenceNode).typeName, SymbolFlags.Type).flags & SymbolFlags.TypeAlias);
+ return isJSDocTypeReference(node) || !!(resolveTypeReferenceName(node as TypeReferenceNode, SymbolFlags.Type).flags & SymbolFlags.TypeAlias);
case SyntaxKind.TypeQuery:
return true;
case SyntaxKind.TypeOperator:
@@ -13709,7 +14117,7 @@ namespace ts {
}
}
const fixedLength = properties.length;
- const lengthSymbol = createSymbol(SymbolFlags.Property, "length" as __String);
+ const lengthSymbol = createSymbol(SymbolFlags.Property, "length" as __String, readonly ? CheckFlags.Readonly : 0);
if (combinedFlags & ElementFlags.Variable) {
lengthSymbol.type = numberType;
}
@@ -13893,7 +14301,7 @@ namespace ts {
// We ignore 'never' types in unions
if (!(flags & TypeFlags.Never)) {
includes |= flags & TypeFlags.IncludesMask;
- if (flags & TypeFlags.StructuredOrInstantiable) includes |= TypeFlags.IncludesStructuredOrInstantiable;
+ if (flags & TypeFlags.Instantiable) includes |= TypeFlags.IncludesInstantiable;
if (type === wildcardType) includes |= TypeFlags.IncludesWildcard;
if (!strictNullChecks && flags & TypeFlags.Nullable) {
if (!(getObjectFlags(type) & ObjectFlags.ContainsWideningType)) includes |= TypeFlags.IncludesNonWideningType;
@@ -13998,13 +14406,13 @@ namespace ts {
}
function removeStringLiteralsMatchedByTemplateLiterals(types: Type[]) {
- const templates = filter(types, isPatternLiteralType);
+ const templates = filter(types, isPatternLiteralType) as TemplateLiteralType[];
if (templates.length) {
let i = types.length;
while (i > 0) {
i--;
const t = types[i];
- if (t.flags & TypeFlags.StringLiteral && some(templates, template => isTypeSubtypeOf(t, template))) {
+ if (t.flags & TypeFlags.StringLiteral && some(templates, template => isTypeMatchedByTemplateLiteralType(t, template))) {
orderedRemoveItemAt(types, i);
}
}
@@ -14053,7 +14461,9 @@ namespace ts {
const includes = addTypesToUnion(typeSet, 0, types);
if (unionReduction !== UnionReduction.None) {
if (includes & TypeFlags.AnyOrUnknown) {
- return includes & TypeFlags.Any ? includes & TypeFlags.IncludesWildcard ? wildcardType : anyType : unknownType;
+ return includes & TypeFlags.Any ?
+ includes & TypeFlags.IncludesWildcard ? wildcardType : anyType :
+ includes & TypeFlags.Null || containsType(typeSet, unknownType) ? unknownType : nonNullUnknownType;
}
if (exactOptionalPropertyTypes && includes & TypeFlags.Undefined) {
const missingIndex = binarySearch(typeSet, missingType, getTypeId, compareValues);
@@ -14198,13 +14608,19 @@ namespace ts {
if (flags & TypeFlags.AnyOrUnknown) {
if (type === wildcardType) includes |= TypeFlags.IncludesWildcard;
}
- else if ((strictNullChecks || !(flags & TypeFlags.Nullable)) && !typeSet.has(type.id.toString())) {
- if (type.flags & TypeFlags.Unit && includes & TypeFlags.Unit) {
- // We have seen two distinct unit types which means we should reduce to an
- // empty intersection. Adding TypeFlags.NonPrimitive causes that to happen.
- includes |= TypeFlags.NonPrimitive;
+ else if (strictNullChecks || !(flags & TypeFlags.Nullable)) {
+ if (exactOptionalPropertyTypes && type === missingType) {
+ includes |= TypeFlags.IncludesMissingType;
+ type = undefinedType;
+ }
+ if (!typeSet.has(type.id.toString())) {
+ if (type.flags & TypeFlags.Unit && includes & TypeFlags.Unit) {
+ // We have seen two distinct unit types which means we should reduce to an
+ // empty intersection. Adding TypeFlags.NonPrimitive causes that to happen.
+ includes |= TypeFlags.NonPrimitive;
+ }
+ typeSet.set(type.id.toString(), type);
}
- typeSet.set(type.id.toString(), type);
}
includes |= flags & TypeFlags.IncludesMask;
}
@@ -14279,14 +14695,14 @@ namespace ts {
return false;
}
- function extractIrreducible(types: Type[], flag: TypeFlags) {
- if (every(types, t => !!(t.flags & TypeFlags.Union) && some((t as UnionType).types, tt => !!(tt.flags & flag)))) {
- for (let i = 0; i < types.length; i++) {
- types[i] = filterType(types[i], t => !(t.flags & flag));
- }
- return true;
+ function eachIsUnionContaining(types: Type[], flag: TypeFlags) {
+ return every(types, t => !!(t.flags & TypeFlags.Union) && some((t as UnionType).types, tt => !!(tt.flags & flag)));
+ }
+
+ function removeFromEach(types: Type[], flag: TypeFlags) {
+ for (let i = 0; i < types.length; i++) {
+ types[i] = filterType(types[i], t => !(t.flags & flag));
}
- return false;
}
// If the given list of types contains more than one union of primitive types, replace the
@@ -14396,6 +14812,9 @@ namespace ts {
if (includes & TypeFlags.IncludesEmptyObject && includes & TypeFlags.Object) {
orderedRemoveItemAt(typeSet, findIndex(typeSet, isEmptyAnonymousObjectType));
}
+ if (includes & TypeFlags.IncludesMissingType) {
+ typeSet[typeSet.indexOf(undefinedType)] = missingType;
+ }
if (typeSet.length === 0) {
return unknownType;
}
@@ -14412,10 +14831,13 @@ namespace ts {
// reduced we'll never reduce again, so this occurs at most once.
result = getIntersectionType(typeSet, aliasSymbol, aliasTypeArguments);
}
- else if (extractIrreducible(typeSet, TypeFlags.Undefined)) {
- result = getUnionType([getIntersectionType(typeSet), undefinedType], UnionReduction.Literal, aliasSymbol, aliasTypeArguments);
+ else if (eachIsUnionContaining(typeSet, TypeFlags.Undefined)) {
+ const undefinedOrMissingType = exactOptionalPropertyTypes && some(typeSet, t => containsType((t as UnionType).types, missingType)) ? missingType : undefinedType;
+ removeFromEach(typeSet, TypeFlags.Undefined);
+ result = getUnionType([getIntersectionType(typeSet), undefinedOrMissingType], UnionReduction.Literal, aliasSymbol, aliasTypeArguments);
}
- else if (extractIrreducible(typeSet, TypeFlags.Null)) {
+ else if (eachIsUnionContaining(typeSet, TypeFlags.Null)) {
+ removeFromEach(typeSet, TypeFlags.Null);
result = getUnionType([getIntersectionType(typeSet), nullType], UnionReduction.Literal, aliasSymbol, aliasTypeArguments);
}
else {
@@ -14503,19 +14925,58 @@ namespace ts {
type.resolvedIndexType || (type.resolvedIndexType = createIndexType(type, /*stringsOnly*/ false));
}
- function instantiateTypeAsMappedNameType(nameType: Type, type: MappedType, t: Type) {
- return instantiateType(nameType, appendTypeMapping(type.mapper, getTypeParameterFromMappedType(type), t));
- }
+ /**
+ * This roughly mirrors `resolveMappedTypeMembers` in the nongeneric case, except only reports a union of the keys calculated,
+ * rather than manufacturing the properties. We can't just fetch the `constraintType` since that would ignore mappings
+ * and mapping the `constraintType` directly ignores how mapped types map _properties_ and not keys (thus ignoring subtype
+ * reduction in the constraintType) when possible.
+ * @param noIndexSignatures Indicates if _string_ index signatures should be elided. (other index signatures are always reported)
+ */
+ function getIndexTypeForMappedType(type: MappedType, stringsOnly: boolean, noIndexSignatures: boolean | undefined) {
+ const typeParameter = getTypeParameterFromMappedType(type);
+ const constraintType = getConstraintTypeFromMappedType(type);
+ const nameType = getNameTypeFromMappedType(type.target as MappedType || type);
+ if (!nameType && !noIndexSignatures) {
+ // no mapping and no filtering required, just quickly bail to returning the constraint in the common case
+ return constraintType;
+ }
+ const keyTypes: Type[] = [];
+ if (isMappedTypeWithKeyofConstraintDeclaration(type)) {
+ // We have a { [P in keyof T]: X }
- function getIndexTypeForMappedType(type: MappedType, noIndexSignatures: boolean | undefined) {
- const constraint = filterType(getConstraintTypeFromMappedType(type), t => !(noIndexSignatures && t.flags & (TypeFlags.Any | TypeFlags.String)));
- const nameType = type.declaration.nameType && getTypeFromTypeNode(type.declaration.nameType);
- // If the constraint is exclusively string/number/never type(s), we need to pull the property names from the modified type and run them through the `nameType` mapper as well
- // since they won't appear in the constraint, due to subtype reducing with the string/number index types
- const properties = nameType && everyType(constraint, t => !!(t.flags & (TypeFlags.String | TypeFlags.Number | TypeFlags.Never))) && getPropertiesOfType(getApparentType(getModifiersTypeFromMappedType(type)));
- return nameType ?
- getUnionType([mapType(constraint, t => instantiateTypeAsMappedNameType(nameType, type, t)), mapType(getUnionType(map(properties || emptyArray, p => getLiteralTypeFromProperty(p, TypeFlags.StringOrNumberLiteralOrUnique))), t => instantiateTypeAsMappedNameType(nameType, type, t))]):
- constraint;
+ // `getApparentType` on the T in a generic mapped type can trigger a circularity
+ // (conditionals and `infer` types create a circular dependency in the constraint resolution)
+ // so we only eagerly manifest the keys if the constraint is nongeneric
+ if (!isGenericIndexType(constraintType)) {
+ const modifiersType = getApparentType(getModifiersTypeFromMappedType(type)); // The 'T' in 'keyof T'
+ forEachMappedTypePropertyKeyTypeAndIndexSignatureKeyType(modifiersType, TypeFlags.StringOrNumberLiteralOrUnique, stringsOnly, addMemberForKeyType);
+ }
+ else {
+ // we have a generic index and a homomorphic mapping (but a distributive key remapping) - we need to defer the whole `keyof whatever` for later
+ // since it's not safe to resolve the shape of modifier type
+ return getIndexTypeForGenericType(type, stringsOnly);
+ }
+ }
+ else {
+ forEachType(getLowerBoundOfKeyType(constraintType), addMemberForKeyType);
+ }
+ if (isGenericIndexType(constraintType)) { // include the generic component in the resulting type
+ forEachType(constraintType, addMemberForKeyType);
+ }
+ // we had to pick apart the constraintType to potentially map/filter it - compare the final resulting list with the original constraintType,
+ // so we can return the union that preserves aliases/origin data if possible
+ const result = noIndexSignatures ? filterType(getUnionType(keyTypes), t => !(t.flags & (TypeFlags.Any | TypeFlags.String))) : getUnionType(keyTypes);
+ if (result.flags & TypeFlags.Union && constraintType.flags & TypeFlags.Union && getTypeListId((result as UnionType).types) === getTypeListId((constraintType as UnionType).types)){
+ return constraintType;
+ }
+ return result;
+
+ function addMemberForKeyType(keyType: Type) {
+ const propNameType = nameType ? instantiateType(nameType, appendTypeMapping(type.mapper, typeParameter, keyType)) : keyType;
+ // `keyof` currently always returns `string | number` for concrete `string` index signatures - the below ternary keeps that behavior for mapped types
+ // See `getLiteralTypeFromProperties` where there's a similar ternary to cause the same behavior.
+ keyTypes.push(propNameType === stringType ? stringOrNumberType : propNameType);
+ }
}
// Ordinarily we reduce a keyof M, where M is a mapped type { [P in K as N]: X }, to simply N. This however presumes
@@ -14578,7 +15039,7 @@ namespace ts {
return type.flags & TypeFlags.Union ? getIntersectionType(map((type as UnionType).types, t => getIndexType(t, stringsOnly, noIndexSignatures))) :
type.flags & TypeFlags.Intersection ? getUnionType(map((type as IntersectionType).types, t => getIndexType(t, stringsOnly, noIndexSignatures))) :
type.flags & TypeFlags.InstantiableNonPrimitive || isGenericTupleType(type) || isGenericMappedType(type) && !hasDistributiveNameType(type) ? getIndexTypeForGenericType(type as InstantiableType | UnionOrIntersectionType, stringsOnly) :
- getObjectFlags(type) & ObjectFlags.Mapped ? getIndexTypeForMappedType(type as MappedType, noIndexSignatures) :
+ getObjectFlags(type) & ObjectFlags.Mapped ? getIndexTypeForMappedType(type as MappedType, stringsOnly, noIndexSignatures) :
type === wildcardType ? wildcardType :
type.flags & TypeFlags.Unknown ? neverType :
type.flags & (TypeFlags.Any | TypeFlags.Never) ? keyofConstraintType :
@@ -14803,7 +15264,7 @@ namespace ts {
}
const prop = getPropertyOfType(objectType, propName);
if (prop) {
- if (accessFlags & AccessFlags.ReportDeprecated && accessNode && prop.declarations && getDeclarationNodeFlagsFromSymbol(prop) & NodeFlags.Deprecated && isUncalledFunctionReference(accessNode, prop)) {
+ if (accessFlags & AccessFlags.ReportDeprecated && accessNode && prop.declarations && isDeprecatedSymbol(prop) && isUncalledFunctionReference(accessNode, prop)) {
const deprecatedNode = accessExpression?.argumentExpression ?? (isIndexedAccessTypeNode(accessNode) ? accessNode.indexType : accessNode);
addDeprecatedSuggestion(deprecatedNode, prop.declarations, propName as string);
}
@@ -15012,10 +15473,6 @@ namespace ts {
(type.flags & (TypeFlags.InstantiableNonPrimitive | TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping) && !isPatternLiteralType(type) ? ObjectFlags.IsGenericIndexType : 0);
}
- function isThisTypeParameter(type: Type): boolean {
- return !!(type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType);
- }
-
function getSimplifiedType(type: Type, writing: boolean): Type {
return type.flags & TypeFlags.IndexedAccess ? getSimplifiedIndexedAccessType(type as IndexedAccessType, writing) :
type.flags & TypeFlags.Conditional ? getSimplifiedConditionalType(type as ConditionalType, writing) :
@@ -15091,13 +15548,6 @@ namespace ts {
return type[cache] = type;
}
- function isConditionalTypeAlwaysTrueDisregardingInferTypes(type: ConditionalType) {
- const extendsInferParamMapper = type.root.inferTypeParameters && createTypeMapper(type.root.inferTypeParameters, map(type.root.inferTypeParameters, () => wildcardType));
- const checkType = type.checkType;
- const extendsType = type.extendsType;
- return isTypeAssignableTo(getRestrictiveInstantiation(checkType), getRestrictiveInstantiation(instantiateType(extendsType, extendsInferParamMapper)));
- }
-
function getSimplifiedConditionalType(type: ConditionalType, writing: boolean) {
const checkType = type.checkType;
const extendsType = type.extendsType;
@@ -15421,6 +15871,12 @@ namespace ts {
return result;
}
+ function isDistributionDependent(root: ConditionalRoot) {
+ return root.isDistributive && (
+ isTypeParameterPossiblyReferenced(root.checkType as TypeParameter, root.node.trueType) ||
+ isTypeParameterPossiblyReferenced(root.checkType as TypeParameter, root.node.falseType));
+ }
+
function getTypeFromConditionalTypeNode(node: ConditionalTypeNode): Type {
const links = getNodeLinks(node);
if (!links.resolvedType) {
@@ -15697,7 +16153,7 @@ namespace ts {
const declarations = concatenate(leftProp.declarations, rightProp.declarations);
const flags = SymbolFlags.Property | (leftProp.flags & SymbolFlags.Optional);
const result = createSymbol(flags, leftProp.escapedName);
- result.type = getUnionType([getTypeOfSymbol(leftProp), removeMissingOrUndefinedType(rightType)]);
+ result.type = getUnionType([getTypeOfSymbol(leftProp), removeMissingOrUndefinedType(rightType)], UnionReduction.Subtype);
result.leftSpread = leftProp;
result.rightSpread = rightProp;
result.declarations = declarations;
@@ -16176,7 +16632,9 @@ namespace ts {
}
function getObjectTypeInstantiation(type: AnonymousType | DeferredTypeReference, mapper: TypeMapper, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]) {
- const declaration = type.objectFlags & ObjectFlags.Reference ? (type as TypeReference).node! : type.symbol.declarations![0];
+ const declaration = type.objectFlags & ObjectFlags.Reference ? (type as TypeReference).node! :
+ type.objectFlags & ObjectFlags.InstantiationExpressionType ? (type as InstantiationExpressionType).node :
+ type.symbol.declarations![0];
const links = getNodeLinks(declaration);
const target = type.objectFlags & ObjectFlags.Reference ? links.resolvedType! as DeferredTypeReference :
type.objectFlags & ObjectFlags.Instantiated ? type.target! : type;
@@ -16192,8 +16650,8 @@ namespace ts {
outerTypeParameters = addRange(outerTypeParameters, templateTagParameters);
}
typeParameters = outerTypeParameters || emptyArray;
- const allDeclarations = type.objectFlags & ObjectFlags.Reference ? [declaration] : type.symbol.declarations!;
- typeParameters = (target.objectFlags & ObjectFlags.Reference || target.symbol.flags & SymbolFlags.Method || target.symbol.flags & SymbolFlags.TypeLiteral) && !target.aliasTypeArguments ?
+ const allDeclarations = type.objectFlags & (ObjectFlags.Reference | ObjectFlags.InstantiationExpressionType) ? [declaration] : type.symbol.declarations!;
+ typeParameters = (target.objectFlags & (ObjectFlags.Reference | ObjectFlags.InstantiationExpressionType) || target.symbol.flags & SymbolFlags.Method || target.symbol.flags & SymbolFlags.TypeLiteral) && !target.aliasTypeArguments ?
filter(typeParameters, tp => some(allDeclarations, d => isTypeParameterPossiblyReferenced(tp, d))) :
typeParameters;
links.outerTypeParameters = typeParameters;
@@ -16290,9 +16748,11 @@ namespace ts {
const mappedTypeVariable = instantiateType(typeVariable, mapper);
if (typeVariable !== mappedTypeVariable) {
return mapTypeWithAlias(getReducedType(mappedTypeVariable), t => {
- if (t.flags & (TypeFlags.AnyOrUnknown | TypeFlags.InstantiableNonPrimitive | TypeFlags.Object | TypeFlags.Intersection) && t !== wildcardType && t !== errorType) {
+ if (t.flags & (TypeFlags.AnyOrUnknown | TypeFlags.InstantiableNonPrimitive | TypeFlags.Object | TypeFlags.Intersection) && t !== wildcardType && !isErrorType(t)) {
if (!type.declaration.nameType) {
- if (isArrayType(t)) {
+ let constraint;
+ if (isArrayType(t) || t.flags & TypeFlags.Any && findResolutionCycleStartIndex(typeVariable, TypeSystemPropertyName.ImmediateBaseConstraint) < 0 &&
+ (constraint = getConstraintOfTypeParameter(typeVariable)) && everyType(constraint, or(isArrayType, isTupleType))) {
return instantiateMappedArrayType(t, type, prependTypeMapping(typeVariable, t, mapper));
}
if (isGenericTupleType(t)) {
@@ -16335,7 +16795,7 @@ namespace ts {
function instantiateMappedArrayType(arrayType: Type, mappedType: MappedType, mapper: TypeMapper) {
const elementType = instantiateMappedTypeTemplate(mappedType, numberType, /*isOptional*/ true, mapper);
- return elementType === errorType ? errorType :
+ return isErrorType(elementType) ? errorType :
createArrayType(elementType, getModifiedReadonlyState(isReadonlyArrayType(arrayType), getMappedTypeModifiers(mappedType)));
}
@@ -16372,6 +16832,9 @@ namespace ts {
mapper = combineTypeMappers(makeUnaryTypeMapper(origTypeParameter, freshTypeParameter), mapper);
freshTypeParameter.mapper = mapper;
}
+ if (type.objectFlags & ObjectFlags.InstantiationExpressionType) {
+ (result as InstantiationExpressionType).node = (type as InstantiationExpressionType).node;
+ }
result.target = type;
result.mapper = mapper;
result.aliasSymbol = aliasSymbol || type.aliasSymbol;
@@ -17213,10 +17676,6 @@ namespace ts {
if (sourceRestType || targetRestType) {
void instantiateType(sourceRestType || targetRestType, reportUnreliableMarkers);
}
- if (sourceRestType && targetRestType && sourceCount !== targetCount) {
- // We're not able to relate misaligned complex rest parameters
- return Ternary.False;
- }
const kind = target.declaration ? target.declaration.kind : SyntaxKind.Unknown;
const strictVariance = !(checkMode & SignatureCheckMode.Callback) && strictFunctionTypes && kind !== SyntaxKind.MethodDeclaration &&
@@ -17456,8 +17915,10 @@ namespace ts {
(source as LiteralType).value === (target as LiteralType).value &&
isEnumTypeRelatedTo(getParentOfSymbol(source.symbol)!, getParentOfSymbol(target.symbol)!, errorReporter)) return true;
}
- if (s & TypeFlags.Undefined && (!strictNullChecks || t & (TypeFlags.Undefined | TypeFlags.Void))) return true;
- if (s & TypeFlags.Null && (!strictNullChecks || t & TypeFlags.Null)) return true;
+ // In non-strictNullChecks mode, `undefined` and `null` are assignable to anything except `never`.
+ // Since unions and intersections may reduce to `never`, we exclude them here.
+ if (s & TypeFlags.Undefined && (!strictNullChecks && !(t & TypeFlags.UnionOrIntersection) || t & (TypeFlags.Undefined | TypeFlags.Void))) return true;
+ if (s & TypeFlags.Null && (!strictNullChecks && !(t & TypeFlags.UnionOrIntersection) || t & TypeFlags.Null)) return true;
if (s & TypeFlags.Object && t & TypeFlags.NonPrimitive) return true;
if (relation === assignableRelation || relation === comparableRelation) {
if (s & TypeFlags.Any) return true;
@@ -17492,7 +17953,7 @@ namespace ts {
// We skip the cache lookup shortcut when we're in a variance computation so the outofbandVarianceMarkerHandler
// will get called for cached results that are unreliable or unmeasurable.
if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Object && !outofbandVarianceMarkerHandler) {
- const related = relation.get(getRelationKey(source, target, IntersectionState.None, relation));
+ const related = relation.get(getRelationKey(source, target, IntersectionState.None, relation, /*ignoreConstraints*/ false));
if (related !== undefined) {
return !!(related & RelationComparisonResult.Succeeded);
}
@@ -17549,22 +18010,23 @@ namespace ts {
let sourceStack: Type[];
let targetStack: Type[];
let maybeCount = 0;
- let depth = 0;
+ let sourceDepth = 0;
+ let targetDepth = 0;
let expandingFlags = ExpandingFlags.None;
let overflow = false;
let overrideNextErrorInfo = 0; // How many `reportRelationError` calls should be skipped in the elaboration pyramid
let lastSkippedInfo: [Type, Type] | undefined;
- let incompatibleStack: [DiagnosticMessage, (string | number)?, (string | number)?, (string | number)?, (string | number)?][] = [];
+ let incompatibleStack: [DiagnosticMessage, (string | number)?, (string | number)?, (string | number)?, (string | number)?][] | undefined;
let inPropertyCheck = false;
Debug.assert(relation !== identityRelation || !errorNode, "no error reporting in identity checking");
- const result = isRelatedTo(source, target, /*reportErrors*/ !!errorNode, headMessage);
- if (incompatibleStack.length) {
+ const result = isRelatedTo(source, target, RecursionFlags.Both, /*reportErrors*/ !!errorNode, headMessage);
+ if (incompatibleStack) {
reportIncompatibleStack();
}
if (overflow) {
- tracing?.instant(tracing.Phase.CheckTypes, "checkTypeRelatedTo_DepthLimit", { sourceId: source.id, targetId: target.id, depth });
+ tracing?.instant(tracing.Phase.CheckTypes, "checkTypeRelatedTo_DepthLimit", { sourceId: source.id, targetId: target.id, depth: sourceDepth, targetDepth });
const diag = error(errorNode || currentNode, Diagnostics.Excessive_stack_depth_comparing_types_0_and_1, typeToString(source), typeToString(target));
if (errorOutputContainer) {
(errorOutputContainer.errors || (errorOutputContainer.errors = [])).push(diag);
@@ -17622,21 +18084,21 @@ namespace ts {
return {
errorInfo,
lastSkippedInfo,
- incompatibleStack: incompatibleStack.slice(),
+ incompatibleStack: incompatibleStack?.slice(),
overrideNextErrorInfo,
- relatedInfo: !relatedInfo ? undefined : relatedInfo.slice() as ([DiagnosticRelatedInformation, ...DiagnosticRelatedInformation[]] | undefined)
+ relatedInfo: relatedInfo?.slice() as [DiagnosticRelatedInformation, ...DiagnosticRelatedInformation[]] | undefined,
};
}
function reportIncompatibleError(message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number) {
overrideNextErrorInfo++; // Suppress the next relation error
lastSkippedInfo = undefined; // Reset skipped info cache
- incompatibleStack.push([message, arg0, arg1, arg2, arg3]);
+ (incompatibleStack ||= []).push([message, arg0, arg1, arg2, arg3]);
}
function reportIncompatibleStack() {
- const stack = incompatibleStack;
- incompatibleStack = [];
+ const stack = incompatibleStack || [];
+ incompatibleStack = undefined;
const info = lastSkippedInfo;
lastSkippedInfo = undefined;
if (stack.length === 1) {
@@ -17650,7 +18112,7 @@ namespace ts {
// The first error will be the innermost, while the last will be the outermost - so by popping off the end,
// we can build from left to right
let path = "";
- const secondaryRootErrors: typeof incompatibleStack = [];
+ const secondaryRootErrors: [DiagnosticMessage, (string | number)?, (string | number)?, (string | number)?, (string | number)?][] = [];
while (stack.length) {
const [msg, ...args] = stack.pop()!;
switch (msg.code) {
@@ -17665,7 +18127,7 @@ namespace ts {
path = `${str}`;
}
// Otherwise write a dotted name if possible
- else if (isIdentifierText(str, compilerOptions.target)) {
+ else if (isIdentifierText(str, getEmitScriptTarget(compilerOptions))) {
path = `${path}.${str}`;
}
// Failing that, check if the name is already a computed name
@@ -17744,7 +18206,7 @@ namespace ts {
function reportError(message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): void {
Debug.assert(!!errorNode);
- if (incompatibleStack.length) reportIncompatibleStack();
+ if (incompatibleStack) reportIncompatibleStack();
if (message.elidedInCompatabilityPyramid) return;
errorInfo = chainDiagnosticMessages(errorInfo, message, arg0, arg1, arg2, arg3);
}
@@ -17760,7 +18222,7 @@ namespace ts {
}
function reportRelationError(message: DiagnosticMessage | undefined, source: Type, target: Type) {
- if (incompatibleStack.length) reportIncompatibleStack();
+ if (incompatibleStack) reportIncompatibleStack();
const [sourceType, targetType] = getTypeNamesForErrorDisplay(source, target);
let generalizedSource = source;
let generalizedSourceType = sourceType;
@@ -17803,6 +18265,13 @@ namespace ts {
message = Diagnostics.Type_0_is_not_assignable_to_type_1_with_exactOptionalPropertyTypes_Colon_true_Consider_adding_undefined_to_the_types_of_the_target_s_properties;
}
else {
+ if (source.flags & TypeFlags.StringLiteral && target.flags & TypeFlags.Union) {
+ const suggestedType = getSuggestedTypeForNonexistentStringLiteralType(source as StringLiteralType, target as UnionType);
+ if (suggestedType) {
+ reportError(Diagnostics.Type_0_is_not_assignable_to_type_1_Did_you_mean_2, generalizedSourceType, targetType, typeToString(suggestedType));
+ return;
+ }
+ }
message = Diagnostics.Type_0_is_not_assignable_to_type_1;
}
}
@@ -17862,20 +18331,26 @@ namespace ts {
return true;
}
+ function isRelatedToWorker(source: Type, target: Type, reportErrors: boolean) {
+ return isRelatedTo(source, target, RecursionFlags.Both, reportErrors);
+ }
+
/**
* Compare two types and return
* * Ternary.True if they are related with no assumptions,
* * Ternary.Maybe if they are related with assumptions of other relationships, or
* * Ternary.False if they are not related.
*/
- function isRelatedTo(originalSource: Type, originalTarget: Type, reportErrors = false, headMessage?: DiagnosticMessage, intersectionState = IntersectionState.None): Ternary {
+ function isRelatedTo(originalSource: Type, originalTarget: Type, recursionFlags: RecursionFlags = RecursionFlags.Both, reportErrors = false, headMessage?: DiagnosticMessage, intersectionState = IntersectionState.None): Ternary {
// Before normalization: if `source` is type an object type, and `target` is primitive,
// skip all the checks we don't need and just return `isSimpleTypeRelatedTo` result
if (originalSource.flags & TypeFlags.Object && originalTarget.flags & TypeFlags.Primitive) {
if (isSimpleTypeRelatedTo(originalSource, originalTarget, relation, reportErrors ? reportError : undefined)) {
return Ternary.True;
}
- reportErrorResults(originalSource, originalTarget, Ternary.False, !!(getObjectFlags(originalSource) & ObjectFlags.JsxAttributes));
+ if (reportErrors) {
+ reportErrorResults(originalSource, originalTarget, originalSource, originalTarget, headMessage);
+ }
return Ternary.False;
}
@@ -17889,7 +18364,10 @@ namespace ts {
if (source === target) return Ternary.True;
if (relation === identityRelation) {
- return isIdenticalTo(source, target);
+ if (source.flags !== target.flags) return Ternary.False;
+ if (source.flags & TypeFlags.Singleton) return Ternary.True;
+ traceUnionsOrIntersectionsTooLarge(source, target);
+ return recursiveTypeRelatedTo(source, target, /*reportErrors*/ false, IntersectionState.None, recursionFlags);
}
// We fastpath comparing a type parameter to exactly its constraint, as this is _super_ common,
@@ -17901,172 +18379,136 @@ namespace ts {
return Ternary.True;
}
- // Try to see if we're relating something like `Foo` -> `Bar | null | undefined`.
- // If so, reporting the `null` and `undefined` in the type is hardly useful.
- // First, see if we're even relating an object type to a union.
- // Then see if the target is stripped down to a single non-union type.
- // Note
- // * We actually want to remove null and undefined naively here (rather than using getNonNullableType),
- // since we don't want to end up with a worse error like "`Foo` is not assignable to `NonNullable`"
- // when dealing with generics.
- // * We also don't deal with primitive source types, since we already halt elaboration below.
- if (target.flags & TypeFlags.Union && source.flags & TypeFlags.Object &&
- (target as UnionType).types.length <= 3 && maybeTypeOfKind(target, TypeFlags.Nullable)) {
- const nullStrippedTarget = extractTypesOfKind(target, ~TypeFlags.Nullable);
- if (!(nullStrippedTarget.flags & (TypeFlags.Union | TypeFlags.Never))) {
- target = getNormalizedType(nullStrippedTarget, /*writing*/ true);
+ // See if we're relating a definitely non-nullable type to a union that includes null and/or undefined
+ // plus a single non-nullable type. If so, remove null and/or undefined from the target type.
+ if (source.flags & TypeFlags.DefinitelyNonNullable && target.flags & TypeFlags.Union) {
+ const types = (target as UnionType).types;
+ const candidate = types.length === 2 && types[0].flags & TypeFlags.Nullable ? types[1] :
+ types.length === 3 && types[0].flags & TypeFlags.Nullable && types[1].flags & TypeFlags.Nullable ? types[2] :
+ undefined;
+ if (candidate && !(candidate.flags & TypeFlags.Nullable)) {
+ target = getNormalizedType(candidate, /*writing*/ true);
+ if (source === target) return Ternary.True;
}
- if (source === nullStrippedTarget) return Ternary.True;
}
if (relation === comparableRelation && !(target.flags & TypeFlags.Never) && isSimpleTypeRelatedTo(target, source, relation) ||
isSimpleTypeRelatedTo(source, target, relation, reportErrors ? reportError : undefined)) return Ternary.True;
- const isComparingJsxAttributes = !!(getObjectFlags(source) & ObjectFlags.JsxAttributes);
- const isPerformingExcessPropertyChecks = !(intersectionState & IntersectionState.Target) && (isObjectLiteralType(source) && getObjectFlags(source) & ObjectFlags.FreshLiteral);
- if (isPerformingExcessPropertyChecks) {
- if (hasExcessProperties(source as FreshObjectLiteralType, target, reportErrors)) {
+ if (source.flags & TypeFlags.StructuredOrInstantiable || target.flags & TypeFlags.StructuredOrInstantiable) {
+ const isPerformingExcessPropertyChecks = !(intersectionState & IntersectionState.Target) && (isObjectLiteralType(source) && getObjectFlags(source) & ObjectFlags.FreshLiteral);
+ if (isPerformingExcessPropertyChecks) {
+ if (hasExcessProperties(source as FreshObjectLiteralType, target, reportErrors)) {
+ if (reportErrors) {
+ reportRelationError(headMessage, source, originalTarget.aliasSymbol ? originalTarget : target);
+ }
+ return Ternary.False;
+ }
+ }
+
+ const isPerformingCommonPropertyChecks = relation !== comparableRelation && !(intersectionState & IntersectionState.Target) &&
+ source.flags & (TypeFlags.Primitive | TypeFlags.Object | TypeFlags.Intersection) && source !== globalObjectType &&
+ target.flags & (TypeFlags.Object | TypeFlags.Intersection) && isWeakType(target) &&
+ (getPropertiesOfType(source).length > 0 || typeHasCallOrConstructSignatures(source));
+ const isComparingJsxAttributes = !!(getObjectFlags(source) & ObjectFlags.JsxAttributes);
+ if (isPerformingCommonPropertyChecks && !hasCommonProperties(source, target, isComparingJsxAttributes)) {
if (reportErrors) {
- reportRelationError(headMessage, source, originalTarget.aliasSymbol ? originalTarget : target);
+ const sourceString = typeToString(originalSource.aliasSymbol ? originalSource : source);
+ const targetString = typeToString(originalTarget.aliasSymbol ? originalTarget : target);
+ const calls = getSignaturesOfType(source, SignatureKind.Call);
+ const constructs = getSignaturesOfType(source, SignatureKind.Construct);
+ if (calls.length > 0 && isRelatedTo(getReturnTypeOfSignature(calls[0]), target, RecursionFlags.Source, /*reportErrors*/ false) ||
+ constructs.length > 0 && isRelatedTo(getReturnTypeOfSignature(constructs[0]), target, RecursionFlags.Source, /*reportErrors*/ false)) {
+ reportError(Diagnostics.Value_of_type_0_has_no_properties_in_common_with_type_1_Did_you_mean_to_call_it, sourceString, targetString);
+ }
+ else {
+ reportError(Diagnostics.Type_0_has_no_properties_in_common_with_type_1, sourceString, targetString);
+ }
}
return Ternary.False;
}
- }
- const isPerformingCommonPropertyChecks = relation !== comparableRelation && !(intersectionState & IntersectionState.Target) &&
- source.flags & (TypeFlags.Primitive | TypeFlags.Object | TypeFlags.Intersection) && source !== globalObjectType &&
- target.flags & (TypeFlags.Object | TypeFlags.Intersection) && isWeakType(target) &&
- (getPropertiesOfType(source).length > 0 || typeHasCallOrConstructSignatures(source));
- if (isPerformingCommonPropertyChecks && !hasCommonProperties(source, target, isComparingJsxAttributes)) {
- if (reportErrors) {
- const sourceString = typeToString(originalSource.aliasSymbol ? originalSource : source);
- const targetString = typeToString(originalTarget.aliasSymbol ? originalTarget : target);
- const calls = getSignaturesOfType(source, SignatureKind.Call);
- const constructs = getSignaturesOfType(source, SignatureKind.Construct);
- if (calls.length > 0 && isRelatedTo(getReturnTypeOfSignature(calls[0]), target, /*reportErrors*/ false) ||
- constructs.length > 0 && isRelatedTo(getReturnTypeOfSignature(constructs[0]), target, /*reportErrors*/ false)) {
- reportError(Diagnostics.Value_of_type_0_has_no_properties_in_common_with_type_1_Did_you_mean_to_call_it, sourceString, targetString);
- }
- else {
- reportError(Diagnostics.Type_0_has_no_properties_in_common_with_type_1, sourceString, targetString);
- }
+ traceUnionsOrIntersectionsTooLarge(source, target);
+
+ const skipCaching = source.flags & TypeFlags.Union && (source as UnionType).types.length < 4 && !(target.flags & TypeFlags.Union) ||
+ target.flags & TypeFlags.Union && (target as UnionType).types.length < 4 && !(source.flags & TypeFlags.StructuredOrInstantiable);
+ let result = skipCaching ?
+ unionOrIntersectionRelatedTo(source, target, reportErrors, intersectionState) :
+ recursiveTypeRelatedTo(source, target, reportErrors, intersectionState, recursionFlags);
+ // For certain combinations involving intersections and optional, excess, or mismatched properties we need
+ // an extra property check where the intersection is viewed as a single object. The following are motivating
+ // examples that all should be errors, but aren't without this extra property check:
+ //
+ // let obj: { a: { x: string } } & { c: number } = { a: { x: 'hello', y: 2 }, c: 5 }; // Nested excess property
+ //
+ // declare let wrong: { a: { y: string } };
+ // let weak: { a?: { x?: number } } & { c?: string } = wrong; // Nested weak object type
+ //
+ // function foo(x: { a?: string }, y: T & { a: boolean }) {
+ // x = y; // Mismatched property in source intersection
+ // }
+ //
+ // We suppress recursive intersection property checks because they can generate lots of work when relating
+ // recursive intersections that are structurally similar but not exactly identical. See #37854.
+ if (result && !inPropertyCheck && (
+ target.flags & TypeFlags.Intersection && (isPerformingExcessPropertyChecks || isPerformingCommonPropertyChecks) ||
+ isNonGenericObjectType(target) && !isArrayType(target) && !isTupleType(target) && source.flags & TypeFlags.Intersection && getApparentType(source).flags & TypeFlags.StructuredType && !some((source as IntersectionType).types, t => !!(getObjectFlags(t) & ObjectFlags.NonInferrableType)))) {
+ inPropertyCheck = true;
+ result &= recursiveTypeRelatedTo(source, target, reportErrors, IntersectionState.PropertyCheck, recursionFlags);
+ inPropertyCheck = false;
}
- return Ternary.False;
- }
-
- traceUnionsOrIntersectionsTooLarge(source, target);
-
- let result = Ternary.False;
- const saveErrorInfo = captureErrorCalculationState();
-
- // Note that these checks are specifically ordered to produce correct results. In particular,
- // we need to deconstruct unions before intersections (because unions are always at the top),
- // and we need to handle "each" relations before "some" relations for the same kind of type.
- if (source.flags & TypeFlags.UnionOrIntersection || target.flags & TypeFlags.UnionOrIntersection) {
- result = getConstituentCount(source) * getConstituentCount(target) >= 4 ?
- recursiveTypeRelatedTo(source, target, reportErrors, intersectionState | IntersectionState.UnionIntersectionCheck) :
- structuredTypeRelatedTo(source, target, reportErrors, intersectionState | IntersectionState.UnionIntersectionCheck);
- }
- if (!result && !(source.flags & TypeFlags.Union) && (source.flags & (TypeFlags.StructuredOrInstantiable) || target.flags & TypeFlags.StructuredOrInstantiable)) {
- if (result = recursiveTypeRelatedTo(source, target, reportErrors, intersectionState)) {
- resetErrorInfo(saveErrorInfo);
- }
- }
- if (!result && source.flags & (TypeFlags.Intersection | TypeFlags.TypeParameter)) {
- // The combined constraint of an intersection type is the intersection of the constraints of
- // the constituents. When an intersection type contains instantiable types with union type
- // constraints, there are situations where we need to examine the combined constraint. One is
- // when the target is a union type. Another is when the intersection contains types belonging
- // to one of the disjoint domains. For example, given type variables T and U, each with the
- // constraint 'string | number', the combined constraint of 'T & U' is 'string | number' and
- // we need to check this constraint against a union on the target side. Also, given a type
- // variable V constrained to 'string | number', 'V & number' has a combined constraint of
- // 'string & number | number & number' which reduces to just 'number'.
- // This also handles type parameters, as a type parameter with a union constraint compared against a union
- // needs to have its constraint hoisted into an intersection with said type parameter, this way
- // the type param can be compared with itself in the target (with the influence of its constraint to match other parts)
- // For example, if `T extends 1 | 2` and `U extends 2 | 3` and we compare `T & U` to `T & U & (1 | 2 | 3)`
- const constraint = getEffectiveConstraintOfIntersection(source.flags & TypeFlags.Intersection ? (source as IntersectionType).types: [source], !!(target.flags & TypeFlags.Union));
- if (constraint && (source.flags & TypeFlags.Intersection || target.flags & TypeFlags.Union)) {
- if (everyType(constraint, c => c !== source)) { // Skip comparison if expansion contains the source itself
- // TODO: Stack errors so we get a pyramid for the "normal" comparison above, _and_ a second for this
- if (result = isRelatedTo(constraint, target, /*reportErrors*/ false, /*headMessage*/ undefined, intersectionState)) {
- resetErrorInfo(saveErrorInfo);
- }
- }
+ if (result) {
+ return result;
}
}
- // For certain combinations involving intersections and optional, excess, or mismatched properties we need
- // an extra property check where the intersection is viewed as a single object. The following are motivating
- // examples that all should be errors, but aren't without this extra property check:
- //
- // let obj: { a: { x: string } } & { c: number } = { a: { x: 'hello', y: 2 }, c: 5 }; // Nested excess property
- //
- // declare let wrong: { a: { y: string } };
- // let weak: { a?: { x?: number } } & { c?: string } = wrong; // Nested weak object type
- //
- // function foo(x: { a?: string }, y: T & { a: boolean }) {
- // x = y; // Mismatched property in source intersection
- // }
- //
- // We suppress recursive intersection property checks because they can generate lots of work when relating
- // recursive intersections that are structurally similar but not exactly identical. See #37854.
- if (result && !inPropertyCheck && (
- target.flags & TypeFlags.Intersection && (isPerformingExcessPropertyChecks || isPerformingCommonPropertyChecks) ||
- isNonGenericObjectType(target) && !isArrayType(target) && !isTupleType(target) && source.flags & TypeFlags.Intersection && getApparentType(source).flags & TypeFlags.StructuredType && !some((source as IntersectionType).types, t => !!(getObjectFlags(t) & ObjectFlags.NonInferrableType)))) {
- inPropertyCheck = true;
- result &= recursiveTypeRelatedTo(source, target, reportErrors, IntersectionState.PropertyCheck);
- inPropertyCheck = false;
+ if (reportErrors) {
+ reportErrorResults(originalSource, originalTarget, source, target, headMessage);
}
+ return Ternary.False;
+ }
- reportErrorResults(source, target, result, isComparingJsxAttributes);
- return result;
-
- function reportErrorResults(source: Type, target: Type, result: Ternary, isComparingJsxAttributes: boolean) {
- if (!result && reportErrors) {
- const sourceHasBase = !!getSingleBaseForNonAugmentingSubtype(originalSource);
- const targetHasBase = !!getSingleBaseForNonAugmentingSubtype(originalTarget);
- source = (originalSource.aliasSymbol || sourceHasBase) ? originalSource : source;
- target = (originalTarget.aliasSymbol || targetHasBase) ? originalTarget : target;
- let maybeSuppress = overrideNextErrorInfo > 0;
- if (maybeSuppress) {
- overrideNextErrorInfo--;
- }
- if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Object) {
- const currentError = errorInfo;
- tryElaborateArrayLikeErrors(source, target, reportErrors);
- if (errorInfo !== currentError) {
- maybeSuppress = !!errorInfo;
- }
- }
- if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Primitive) {
- tryElaborateErrorsForPrimitivesAndObjects(source, target);
- }
- else if (source.symbol && source.flags & TypeFlags.Object && globalObjectType === source) {
- reportError(Diagnostics.The_Object_type_is_assignable_to_very_few_other_types_Did_you_mean_to_use_the_any_type_instead);
- }
- else if (isComparingJsxAttributes && target.flags & TypeFlags.Intersection) {
- const targetTypes = (target as IntersectionType).types;
- const intrinsicAttributes = getJsxType(JsxNames.IntrinsicAttributes, errorNode);
- const intrinsicClassAttributes = getJsxType(JsxNames.IntrinsicClassAttributes, errorNode);
- if (intrinsicAttributes !== errorType && intrinsicClassAttributes !== errorType &&
- (contains(targetTypes, intrinsicAttributes) || contains(targetTypes, intrinsicClassAttributes))) {
- // do not report top error
- return result;
- }
- }
- else {
- errorInfo = elaborateNeverIntersection(errorInfo, originalTarget);
- }
- if (!headMessage && maybeSuppress) {
- lastSkippedInfo = [source, target];
- // Used by, eg, missing property checking to replace the top-level message with a more informative one
- return result;
- }
- reportRelationError(headMessage, source, target);
+ function reportErrorResults(originalSource: Type, originalTarget: Type, source: Type, target: Type, headMessage: DiagnosticMessage | undefined) {
+ const sourceHasBase = !!getSingleBaseForNonAugmentingSubtype(originalSource);
+ const targetHasBase = !!getSingleBaseForNonAugmentingSubtype(originalTarget);
+ source = (originalSource.aliasSymbol || sourceHasBase) ? originalSource : source;
+ target = (originalTarget.aliasSymbol || targetHasBase) ? originalTarget : target;
+ let maybeSuppress = overrideNextErrorInfo > 0;
+ if (maybeSuppress) {
+ overrideNextErrorInfo--;
+ }
+ if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Object) {
+ const currentError = errorInfo;
+ tryElaborateArrayLikeErrors(source, target, /*reportErrors*/ true);
+ if (errorInfo !== currentError) {
+ maybeSuppress = !!errorInfo;
}
}
+ if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Primitive) {
+ tryElaborateErrorsForPrimitivesAndObjects(source, target);
+ }
+ else if (source.symbol && source.flags & TypeFlags.Object && globalObjectType === source) {
+ reportError(Diagnostics.The_Object_type_is_assignable_to_very_few_other_types_Did_you_mean_to_use_the_any_type_instead);
+ }
+ else if (getObjectFlags(source) & ObjectFlags.JsxAttributes && target.flags & TypeFlags.Intersection) {
+ const targetTypes = (target as IntersectionType).types;
+ const intrinsicAttributes = getJsxType(JsxNames.IntrinsicAttributes, errorNode);
+ const intrinsicClassAttributes = getJsxType(JsxNames.IntrinsicClassAttributes, errorNode);
+ if (!isErrorType(intrinsicAttributes) && !isErrorType(intrinsicClassAttributes) &&
+ (contains(targetTypes, intrinsicAttributes) || contains(targetTypes, intrinsicClassAttributes))) {
+ // do not report top error
+ return;
+ }
+ }
+ else {
+ errorInfo = elaborateNeverIntersection(errorInfo, originalTarget);
+ }
+ if (!headMessage && maybeSuppress) {
+ lastSkippedInfo = [source, target];
+ // Used by, eg, missing property checking to replace the top-level message with a more informative one
+ return;
+ }
+ reportRelationError(headMessage, source, target);
}
function traceUnionsOrIntersectionsTooLarge(source: Type, target: Type): void {
@@ -18098,20 +18540,6 @@ namespace ts {
}
}
- function isIdenticalTo(source: Type, target: Type): Ternary {
- if (source.flags !== target.flags) return Ternary.False;
- if (source.flags & TypeFlags.Singleton) return Ternary.True;
- traceUnionsOrIntersectionsTooLarge(source, target);
- if (source.flags & TypeFlags.UnionOrIntersection) {
- let result = eachTypeRelatedToSomeType(source as UnionOrIntersectionType, target as UnionOrIntersectionType);
- if (result) {
- result &= eachTypeRelatedToSomeType(target as UnionOrIntersectionType, source as UnionOrIntersectionType);
- }
- return result;
- }
- return recursiveTypeRelatedTo(source, target, /*reportErrors*/ false, IntersectionState.None);
- }
-
function getTypeOfPropertyInTypes(types: Type[], name: __String) {
const appendPropType = (propTypes: Type[] | undefined, type: Type) => {
type = getApparentType(type);
@@ -18193,7 +18621,7 @@ namespace ts {
}
return true;
}
- if (checkTypes && !isRelatedTo(getTypeOfSymbol(prop), getTypeOfPropertyInTypes(checkTypes, prop.escapedName), reportErrors)) {
+ if (checkTypes && !isRelatedTo(getTypeOfSymbol(prop), getTypeOfPropertyInTypes(checkTypes, prop.escapedName), RecursionFlags.Both, reportErrors)) {
if (reportErrors) {
reportIncompatibleError(Diagnostics.Types_of_property_0_are_incompatible, symbolToString(prop));
}
@@ -18208,6 +18636,42 @@ namespace ts {
return prop.valueDeclaration && container.valueDeclaration && prop.valueDeclaration.parent === container.valueDeclaration;
}
+ function unionOrIntersectionRelatedTo(source: Type, target: Type, reportErrors: boolean, intersectionState: IntersectionState): Ternary {
+ // Note that these checks are specifically ordered to produce correct results. In particular,
+ // we need to deconstruct unions before intersections (because unions are always at the top),
+ // and we need to handle "each" relations before "some" relations for the same kind of type.
+ if (source.flags & TypeFlags.Union) {
+ return relation === comparableRelation ?
+ someTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive), intersectionState) :
+ eachTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive), intersectionState);
+ }
+ if (target.flags & TypeFlags.Union) {
+ return typeRelatedToSomeType(getRegularTypeOfObjectLiteral(source), target as UnionType, reportErrors && !(source.flags & TypeFlags.Primitive) && !(target.flags & TypeFlags.Primitive));
+ }
+ if (target.flags & TypeFlags.Intersection) {
+ return typeRelatedToEachType(getRegularTypeOfObjectLiteral(source), target as IntersectionType, reportErrors, IntersectionState.Target);
+ }
+ // Source is an intersection. For the comparable relation, if the target is a primitive type we hoist the
+ // constraints of all non-primitive types in the source into a new intersection. We do this because the
+ // intersection may further constrain the constraints of the non-primitive types. For example, given a type
+ // parameter 'T extends 1 | 2', the intersection 'T & 1' should be reduced to '1' such that it doesn't
+ // appear to be comparable to '2'.
+ if (relation === comparableRelation && target.flags & TypeFlags.Primitive) {
+ const constraints = sameMap((source as IntersectionType).types, getBaseConstraintOrType);
+ if (constraints !== (source as IntersectionType).types) {
+ source = getIntersectionType(constraints);
+ if (!(source.flags & TypeFlags.Intersection)) {
+ return isRelatedTo(source, target, RecursionFlags.Source, /*reportErrors*/ false);
+ }
+ }
+ }
+ // Check to see if any constituents of the intersection are immediately related to the target.
+ // Don't report errors though. Elaborating on whether a source constituent is related to the target is
+ // not actually useful and leads to some confusing error messages. Instead, we rely on the caller
+ // checking whether the full intersection viewed as an object is related to the target.
+ return someTypeRelatedToType(source as IntersectionType, target, /*reportErrors*/ false, IntersectionState.Source);
+ }
+
function eachTypeRelatedToSomeType(source: UnionOrIntersectionType, target: UnionOrIntersectionType): Ternary {
let result = Ternary.True;
const sourceTypes = source.types;
@@ -18229,21 +18693,24 @@ namespace ts {
}
const match = getMatchingUnionConstituentForType(target as UnionType, source);
if (match) {
- const related = isRelatedTo(source, match, /*reportErrors*/ false);
+ const related = isRelatedTo(source, match, RecursionFlags.Target, /*reportErrors*/ false);
if (related) {
return related;
}
}
}
for (const type of targetTypes) {
- const related = isRelatedTo(source, type, /*reportErrors*/ false);
+ const related = isRelatedTo(source, type, RecursionFlags.Target, /*reportErrors*/ false);
if (related) {
return related;
}
}
if (reportErrors) {
+ // Elaborate only if we can find a best matching type in the target union
const bestMatchingType = getBestMatchingType(source, target, isRelatedTo);
- isRelatedTo(source, bestMatchingType || targetTypes[targetTypes.length - 1], /*reportErrors*/ true);
+ if (bestMatchingType) {
+ isRelatedTo(source, bestMatchingType, RecursionFlags.Target, /*reportErrors*/ true);
+ }
}
return Ternary.False;
}
@@ -18252,7 +18719,7 @@ namespace ts {
let result = Ternary.True;
const targetTypes = target.types;
for (const targetType of targetTypes) {
- const related = isRelatedTo(source, targetType, reportErrors, /*headMessage*/ undefined, intersectionState);
+ const related = isRelatedTo(source, targetType, RecursionFlags.Target, reportErrors, /*headMessage*/ undefined, intersectionState);
if (!related) {
return Ternary.False;
}
@@ -18268,7 +18735,7 @@ namespace ts {
}
const len = sourceTypes.length;
for (let i = 0; i < len; i++) {
- const related = isRelatedTo(sourceTypes[i], target, reportErrors && i === len - 1, /*headMessage*/ undefined, intersectionState);
+ const related = isRelatedTo(sourceTypes[i], target, RecursionFlags.Source, reportErrors && i === len - 1, /*headMessage*/ undefined, intersectionState);
if (related) {
return related;
}
@@ -18300,13 +18767,13 @@ namespace ts {
// union has a union of objects intersected with it. In such cases, if the input was, eg `("a" | "b" | "c") & (string | boolean | {} | {whatever})`,
// the result will have the structure `"a" | "b" | "c" | "a" & {} | "b" & {} | "c" & {} | "a" & {whatever} | "b" & {whatever} | "c" & {whatever}`
// - the resulting union has a length which is a multiple of the original union, and the elements correspond modulo the length of the original union
- const related = isRelatedTo(sourceType, (undefinedStrippedTarget as UnionType).types[i % (undefinedStrippedTarget as UnionType).types.length], /*reportErrors*/ false, /*headMessage*/ undefined, intersectionState);
+ const related = isRelatedTo(sourceType, (undefinedStrippedTarget as UnionType).types[i % (undefinedStrippedTarget as UnionType).types.length], RecursionFlags.Both, /*reportErrors*/ false, /*headMessage*/ undefined, intersectionState);
if (related) {
result &= related;
continue;
}
}
- const related = isRelatedTo(sourceType, target, reportErrors, /*headMessage*/ undefined, intersectionState);
+ const related = isRelatedTo(sourceType, target, RecursionFlags.Source, reportErrors, /*headMessage*/ undefined, intersectionState);
if (!related) {
return Ternary.False;
}
@@ -18336,31 +18803,31 @@ namespace ts {
// Even an `Unmeasurable` variance works out without a structural check if the source and target are _identical_.
// We can't simply assume invariance, because `Unmeasurable` marks nonlinear relations, for example, a relation tained by
// the `-?` modifier in a mapped type (where, no matter how the inputs are related, the outputs still might not be)
- related = relation === identityRelation ? isRelatedTo(s, t, /*reportErrors*/ false) : compareTypesIdentical(s, t);
+ related = relation === identityRelation ? isRelatedTo(s, t, RecursionFlags.Both, /*reportErrors*/ false) : compareTypesIdentical(s, t);
}
else if (variance === VarianceFlags.Covariant) {
- related = isRelatedTo(s, t, reportErrors, /*headMessage*/ undefined, intersectionState);
+ related = isRelatedTo(s, t, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState);
}
else if (variance === VarianceFlags.Contravariant) {
- related = isRelatedTo(t, s, reportErrors, /*headMessage*/ undefined, intersectionState);
+ related = isRelatedTo(t, s, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState);
}
else if (variance === VarianceFlags.Bivariant) {
// In the bivariant case we first compare contravariantly without reporting
// errors. Then, if that doesn't succeed, we compare covariantly with error
// reporting. Thus, error elaboration will be based on the the covariant check,
// which is generally easier to reason about.
- related = isRelatedTo(t, s, /*reportErrors*/ false);
+ related = isRelatedTo(t, s, RecursionFlags.Both, /*reportErrors*/ false);
if (!related) {
- related = isRelatedTo(s, t, reportErrors, /*headMessage*/ undefined, intersectionState);
+ related = isRelatedTo(s, t, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState);
}
}
else {
// In the invariant case we first compare covariantly, and only when that
// succeeds do we proceed to compare contravariantly. Thus, error elaboration
// will typically be based on the covariant check.
- related = isRelatedTo(s, t, reportErrors, /*headMessage*/ undefined, intersectionState);
+ related = isRelatedTo(s, t, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState);
if (related) {
- related &= isRelatedTo(t, s, reportErrors, /*headMessage*/ undefined, intersectionState);
+ related &= isRelatedTo(t, s, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState);
}
}
if (!related) {
@@ -18377,11 +18844,12 @@ namespace ts {
// Third, check if both types are part of deeply nested chains of generic type instantiations and if so assume the types are
// equal and infinitely expanding. Fourth, if we have reached a depth of 100 nested comparisons, assume we have runaway recursion
// and issue an error. Otherwise, actually compare the structure of the two types.
- function recursiveTypeRelatedTo(source: Type, target: Type, reportErrors: boolean, intersectionState: IntersectionState): Ternary {
+ function recursiveTypeRelatedTo(source: Type, target: Type, reportErrors: boolean, intersectionState: IntersectionState, recursionFlags: RecursionFlags): Ternary {
if (overflow) {
return Ternary.False;
}
- const id = getRelationKey(source, target, intersectionState | (inPropertyCheck ? IntersectionState.InPropertyCheck : 0), relation);
+ const keyIntersectionState = intersectionState | (inPropertyCheck ? IntersectionState.InPropertyCheck : 0);
+ const id = getRelationKey(source, target, keyIntersectionState, relation, /*ingnoreConstraints*/ false);
const entry = relation.get(id);
if (entry !== undefined) {
if (reportErrors && entry & RelationComparisonResult.Failed && !(entry & RelationComparisonResult.Reported)) {
@@ -18408,20 +18876,17 @@ namespace ts {
targetStack = [];
}
else {
- // generate a key where all type parameter id positions are replaced with unconstrained type parameter ids
- // this isn't perfect - nested type references passed as type arguments will muck up the indexes and thus
- // prevent finding matches- but it should hit up the common cases
- const broadestEquivalentId = id.split(",").map(i => i.replace(/-\d+/g, (_match, offset: number) => {
- const index = length(id.slice(0, offset).match(/[-=]/g) || undefined);
- return `=${index}`;
- })).join(",");
+ // A key that starts with "*" is an indication that we have type references that reference constrained
+ // type parameters. For such keys we also check against the key we would have gotten if all type parameters
+ // were unconstrained.
+ const broadestEquivalentId = id.startsWith("*") ? getRelationKey(source, target, keyIntersectionState, relation, /*ignoreConstraints*/ true) : undefined;
for (let i = 0; i < maybeCount; i++) {
// If source and target are already being compared, consider them related with assumptions
- if (id === maybeKeys[i] || broadestEquivalentId === maybeKeys[i]) {
+ if (id === maybeKeys[i] || broadestEquivalentId && broadestEquivalentId === maybeKeys[i]) {
return Ternary.Maybe;
}
}
- if (depth === 100) {
+ if (sourceDepth === 100 || targetDepth === 100) {
overflow = true;
return Ternary.False;
}
@@ -18429,12 +18894,17 @@ namespace ts {
const maybeStart = maybeCount;
maybeKeys[maybeCount] = id;
maybeCount++;
- sourceStack[depth] = source;
- targetStack[depth] = target;
- depth++;
const saveExpandingFlags = expandingFlags;
- if (!(expandingFlags & ExpandingFlags.Source) && isDeeplyNestedType(source, sourceStack, depth)) expandingFlags |= ExpandingFlags.Source;
- if (!(expandingFlags & ExpandingFlags.Target) && isDeeplyNestedType(target, targetStack, depth)) expandingFlags |= ExpandingFlags.Target;
+ if (recursionFlags & RecursionFlags.Source) {
+ sourceStack[sourceDepth] = source;
+ sourceDepth++;
+ if (!(expandingFlags & ExpandingFlags.Source) && isDeeplyNestedType(source, sourceStack, sourceDepth)) expandingFlags |= ExpandingFlags.Source;
+ }
+ if (recursionFlags & RecursionFlags.Target) {
+ targetStack[targetDepth] = target;
+ targetDepth++;
+ if (!(expandingFlags & ExpandingFlags.Target) && isDeeplyNestedType(target, targetStack, targetDepth)) expandingFlags |= ExpandingFlags.Target;
+ }
let originalHandler: typeof outofbandVarianceMarkerHandler;
let propagatingVarianceFlags: RelationComparisonResult = 0;
if (outofbandVarianceMarkerHandler) {
@@ -18445,24 +18915,36 @@ namespace ts {
};
}
+ let result: Ternary;
if (expandingFlags === ExpandingFlags.Both) {
tracing?.instant(tracing.Phase.CheckTypes, "recursiveTypeRelatedTo_DepthLimit", {
sourceId: source.id,
sourceIdStack: sourceStack.map(t => t.id),
targetId: target.id,
targetIdStack: targetStack.map(t => t.id),
- depth,
+ depth: sourceDepth,
+ targetDepth
});
+ result = Ternary.Maybe;
+ }
+ else {
+ tracing?.push(tracing.Phase.CheckTypes, "structuredTypeRelatedTo", { sourceId: source.id, targetId: target.id });
+ result = structuredTypeRelatedTo(source, target, reportErrors, intersectionState);
+ tracing?.pop();
}
- const result = expandingFlags !== ExpandingFlags.Both ? structuredTypeRelatedTo(source, target, reportErrors, intersectionState) : Ternary.Maybe;
if (outofbandVarianceMarkerHandler) {
outofbandVarianceMarkerHandler = originalHandler;
}
+ if (recursionFlags & RecursionFlags.Source) {
+ sourceDepth--;
+ }
+ if (recursionFlags & RecursionFlags.Target) {
+ targetDepth--;
+ }
expandingFlags = saveExpandingFlags;
- depth--;
if (result) {
- if (result === Ternary.True || depth === 0) {
+ if (result === Ternary.True || (sourceDepth === 0 && targetDepth === 0)) {
if (result === Ternary.True || result === Ternary.Maybe) {
// If result is definitely true, record all maybe keys as having succeeded. Also, record Ternary.Maybe
// results as having succeeded once we reach depth 0, but never record Ternary.Unknown results.
@@ -18483,79 +18965,40 @@ namespace ts {
}
function structuredTypeRelatedTo(source: Type, target: Type, reportErrors: boolean, intersectionState: IntersectionState): Ternary {
- tracing?.push(tracing.Phase.CheckTypes, "structuredTypeRelatedTo", { sourceId: source.id, targetId: target.id });
- const result = structuredTypeRelatedToWorker(source, target, reportErrors, intersectionState);
- tracing?.pop();
- return result;
- }
-
- function structuredTypeRelatedToWorker(source: Type, target: Type, reportErrors: boolean, intersectionState: IntersectionState): Ternary {
if (intersectionState & IntersectionState.PropertyCheck) {
return propertiesRelatedTo(source, target, reportErrors, /*excludedProperties*/ undefined, IntersectionState.None);
}
- if (intersectionState & IntersectionState.UnionIntersectionCheck) {
- // Note that these checks are specifically ordered to produce correct results. In particular,
- // we need to deconstruct unions before intersections (because unions are always at the top),
- // and we need to handle "each" relations before "some" relations for the same kind of type.
- if (source.flags & TypeFlags.Union) {
- return relation === comparableRelation ?
- someTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive), intersectionState & ~IntersectionState.UnionIntersectionCheck) :
- eachTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive), intersectionState & ~IntersectionState.UnionIntersectionCheck);
- }
- if (target.flags & TypeFlags.Union) {
- return typeRelatedToSomeType(getRegularTypeOfObjectLiteral(source), target as UnionType, reportErrors && !(source.flags & TypeFlags.Primitive) && !(target.flags & TypeFlags.Primitive));
- }
- if (target.flags & TypeFlags.Intersection) {
- return typeRelatedToEachType(getRegularTypeOfObjectLiteral(source), target as IntersectionType, reportErrors, IntersectionState.Target);
- }
- // Source is an intersection. For the comparable relation, if the target is a primitive type we hoist the
- // constraints of all non-primitive types in the source into a new intersection. We do this because the
- // intersection may further constrain the constraints of the non-primitive types. For example, given a type
- // parameter 'T extends 1 | 2', the intersection 'T & 1' should be reduced to '1' such that it doesn't
- // appear to be comparable to '2'.
- if (relation === comparableRelation && target.flags & TypeFlags.Primitive) {
- const constraints = sameMap((source as IntersectionType).types, getBaseConstraintOrType);
- if (constraints !== (source as IntersectionType).types) {
- source = getIntersectionType(constraints);
- if (!(source.flags & TypeFlags.Intersection)) {
- return isRelatedTo(source, target, /*reportErrors*/ false);
- }
+ let result: Ternary;
+ let originalErrorInfo: DiagnosticMessageChain | undefined;
+ let varianceCheckFailed = false;
+ const saveErrorInfo = captureErrorCalculationState();
+ let sourceFlags = source.flags;
+ const targetFlags = target.flags;
+ if (relation === identityRelation) {
+ // We've already checked that source.flags and target.flags are identical
+ if (sourceFlags & TypeFlags.UnionOrIntersection) {
+ let result = eachTypeRelatedToSomeType(source as UnionOrIntersectionType, target as UnionOrIntersectionType);
+ if (result) {
+ result &= eachTypeRelatedToSomeType(target as UnionOrIntersectionType, source as UnionOrIntersectionType);
}
+ return result;
}
- // Check to see if any constituents of the intersection are immediately related to the target.
- //
- // Don't report errors though. Checking whether a constituent is related to the source is not actually
- // useful and leads to some confusing error messages. Instead it is better to let the below checks
- // take care of this, or to not elaborate at all. For instance,
- //
- // - For an object type (such as 'C = A & B'), users are usually more interested in structural errors.
- //
- // - For a union type (such as '(A | B) = (C & D)'), it's better to hold onto the whole intersection
- // than to report that 'D' is not assignable to 'A' or 'B'.
- //
- // - For a primitive type or type parameter (such as 'number = A & B') there is no point in
- // breaking the intersection apart.
- return someTypeRelatedToType(source as IntersectionType, target, /*reportErrors*/ false, IntersectionState.Source);
- }
- const flags = source.flags & target.flags;
- if (relation === identityRelation && !(flags & TypeFlags.Object)) {
- if (flags & TypeFlags.Index) {
- return isRelatedTo((source as IndexType).type, (target as IndexType).type, /*reportErrors*/ false);
+ if (sourceFlags & TypeFlags.Index) {
+ return isRelatedTo((source as IndexType).type, (target as IndexType).type, RecursionFlags.Both, /*reportErrors*/ false);
}
- let result = Ternary.False;
- if (flags & TypeFlags.IndexedAccess) {
- if (result = isRelatedTo((source as IndexedAccessType).objectType, (target as IndexedAccessType).objectType, /*reportErrors*/ false)) {
- if (result &= isRelatedTo((source as IndexedAccessType).indexType, (target as IndexedAccessType).indexType, /*reportErrors*/ false)) {
+ if (sourceFlags & TypeFlags.IndexedAccess) {
+ if (result = isRelatedTo((source as IndexedAccessType).objectType, (target as IndexedAccessType).objectType, RecursionFlags.Both, /*reportErrors*/ false)) {
+ if (result &= isRelatedTo((source as IndexedAccessType).indexType, (target as IndexedAccessType).indexType, RecursionFlags.Both, /*reportErrors*/ false)) {
return result;
}
}
}
- if (flags & TypeFlags.Conditional) {
+ if (sourceFlags & TypeFlags.Conditional) {
if ((source as ConditionalType).root.isDistributive === (target as ConditionalType).root.isDistributive) {
- if (result = isRelatedTo((source as ConditionalType).checkType, (target as ConditionalType).checkType, /*reportErrors*/ false)) {
- if (result &= isRelatedTo((source as ConditionalType).extendsType, (target as ConditionalType).extendsType, /*reportErrors*/ false)) {
- if (result &= isRelatedTo(getTrueTypeFromConditionalType(source as ConditionalType), getTrueTypeFromConditionalType(target as ConditionalType), /*reportErrors*/ false)) {
- if (result &= isRelatedTo(getFalseTypeFromConditionalType(source as ConditionalType), getFalseTypeFromConditionalType(target as ConditionalType), /*reportErrors*/ false)) {
+ if (result = isRelatedTo((source as ConditionalType).checkType, (target as ConditionalType).checkType, RecursionFlags.Both, /*reportErrors*/ false)) {
+ if (result &= isRelatedTo((source as ConditionalType).extendsType, (target as ConditionalType).extendsType, RecursionFlags.Both, /*reportErrors*/ false)) {
+ if (result &= isRelatedTo(getTrueTypeFromConditionalType(source as ConditionalType), getTrueTypeFromConditionalType(target as ConditionalType), RecursionFlags.Both, /*reportErrors*/ false)) {
+ if (result &= isRelatedTo(getFalseTypeFromConditionalType(source as ConditionalType), getFalseTypeFromConditionalType(target as ConditionalType), RecursionFlags.Both, /*reportErrors*/ false)) {
return result;
}
}
@@ -18563,21 +19006,57 @@ namespace ts {
}
}
}
- if (flags & TypeFlags.Substitution) {
- return isRelatedTo((source as SubstitutionType).substitute, (target as SubstitutionType).substitute, /*reportErrors*/ false);
+ if (sourceFlags & TypeFlags.Substitution) {
+ return isRelatedTo((source as SubstitutionType).substitute, (target as SubstitutionType).substitute, RecursionFlags.Both, /*reportErrors*/ false);
+ }
+ if (!(sourceFlags & TypeFlags.Object)) {
+ return Ternary.False;
+ }
+ }
+ else if (sourceFlags & TypeFlags.UnionOrIntersection || targetFlags & TypeFlags.UnionOrIntersection) {
+ if (result = unionOrIntersectionRelatedTo(source, target, reportErrors, intersectionState)) {
+ return result;
+ }
+ if (source.flags & TypeFlags.Intersection || source.flags & TypeFlags.TypeParameter && target.flags & TypeFlags.Union) {
+ // The combined constraint of an intersection type is the intersection of the constraints of
+ // the constituents. When an intersection type contains instantiable types with union type
+ // constraints, there are situations where we need to examine the combined constraint. One is
+ // when the target is a union type. Another is when the intersection contains types belonging
+ // to one of the disjoint domains. For example, given type variables T and U, each with the
+ // constraint 'string | number', the combined constraint of 'T & U' is 'string | number' and
+ // we need to check this constraint against a union on the target side. Also, given a type
+ // variable V constrained to 'string | number', 'V & number' has a combined constraint of
+ // 'string & number | number & number' which reduces to just 'number'.
+ // This also handles type parameters, as a type parameter with a union constraint compared against a union
+ // needs to have its constraint hoisted into an intersection with said type parameter, this way
+ // the type param can be compared with itself in the target (with the influence of its constraint to match other parts)
+ // For example, if `T extends 1 | 2` and `U extends 2 | 3` and we compare `T & U` to `T & U & (1 | 2 | 3)`
+ const constraint = getEffectiveConstraintOfIntersection(source.flags & TypeFlags.Intersection ? (source as IntersectionType).types: [source], !!(target.flags & TypeFlags.Union));
+ if (constraint && everyType(constraint, c => c !== source)) { // Skip comparison if expansion contains the source itself
+ // TODO: Stack errors so we get a pyramid for the "normal" comparison above, _and_ a second for this
+ if (result = isRelatedTo(constraint, target, RecursionFlags.Source, /*reportErrors*/ false, /*headMessage*/ undefined, intersectionState)) {
+ resetErrorInfo(saveErrorInfo);
+ return result;
+ }
+ }
+ }
+ // The ordered decomposition above doesn't handle all cases. Specifically, we also need to handle:
+ // Source is instantiable (e.g. source has union or intersection constraint).
+ // Source is an object, target is a union (e.g. { a, b: boolean } <=> { a, b: true } | { a, b: false }).
+ // Source is an intersection, target is an object (e.g. { a } & { b } <=> { a, b }).
+ // Source is an intersection, target is a union (e.g. { a } & { b: boolean } <=> { a, b: true } | { a, b: false }).
+ // Source is an intersection, target instantiable (e.g. string & { tag } <=> T["a"] constrained to string & { tag }).
+ if (!(sourceFlags & TypeFlags.Instantiable ||
+ sourceFlags & TypeFlags.Object && targetFlags & TypeFlags.Union ||
+ sourceFlags & TypeFlags.Intersection && targetFlags & (TypeFlags.Object | TypeFlags.Union | TypeFlags.Instantiable))) {
+ return Ternary.False;
}
- return Ternary.False;
}
-
- let result: Ternary;
- let originalErrorInfo: DiagnosticMessageChain | undefined;
- let varianceCheckFailed = false;
- const saveErrorInfo = captureErrorCalculationState();
// We limit alias variance probing to only object and conditional types since their alias behavior
// is more predictable than other, interned types, which may or may not have an alias depending on
// the order in which things were checked.
- if (source.flags & (TypeFlags.Object | TypeFlags.Conditional) && source.aliasSymbol &&
+ if (sourceFlags & (TypeFlags.Object | TypeFlags.Conditional) && source.aliasSymbol &&
source.aliasTypeArguments && source.aliasSymbol === target.aliasSymbol &&
!(containsMarkerType(source.aliasTypeArguments) || containsMarkerType(target.aliasTypeArguments))) {
const variances = getAliasVariances(source.aliasSymbol);
@@ -18592,36 +19071,36 @@ namespace ts {
// For a generic type T and a type U that is assignable to T, [...U] is assignable to T, U is assignable to readonly [...T],
// and U is assignable to [...T] when U is constrained to a mutable array or tuple type.
- if (isSingleElementGenericTupleType(source) && !source.target.readonly && (result = isRelatedTo(getTypeArguments(source)[0], target)) ||
- isSingleElementGenericTupleType(target) && (target.target.readonly || isMutableArrayOrTuple(getBaseConstraintOfType(source) || source)) && (result = isRelatedTo(source, getTypeArguments(target)[0]))) {
+ if (isSingleElementGenericTupleType(source) && !source.target.readonly && (result = isRelatedTo(getTypeArguments(source)[0], target, RecursionFlags.Source)) ||
+ isSingleElementGenericTupleType(target) && (target.target.readonly || isMutableArrayOrTuple(getBaseConstraintOfType(source) || source)) && (result = isRelatedTo(source, getTypeArguments(target)[0], RecursionFlags.Target))) {
return result;
}
- if (target.flags & TypeFlags.TypeParameter) {
+ if (targetFlags & TypeFlags.TypeParameter) {
// A source type { [P in Q]: X } is related to a target type T if keyof T is related to Q and X is related to T[Q].
- if (getObjectFlags(source) & ObjectFlags.Mapped && !(source as MappedType).declaration.nameType && isRelatedTo(getIndexType(target), getConstraintTypeFromMappedType(source as MappedType))) {
+ if (getObjectFlags(source) & ObjectFlags.Mapped && !(source as MappedType).declaration.nameType && isRelatedTo(getIndexType(target), getConstraintTypeFromMappedType(source as MappedType), RecursionFlags.Both)) {
if (!(getMappedTypeModifiers(source as MappedType) & MappedTypeModifiers.IncludeOptional)) {
const templateType = getTemplateTypeFromMappedType(source as MappedType);
const indexedAccessType = getIndexedAccessType(target, getTypeParameterFromMappedType(source as MappedType));
- if (result = isRelatedTo(templateType, indexedAccessType, reportErrors)) {
+ if (result = isRelatedTo(templateType, indexedAccessType, RecursionFlags.Both, reportErrors)) {
return result;
}
}
}
}
- else if (target.flags & TypeFlags.Index) {
+ else if (targetFlags & TypeFlags.Index) {
const targetType = (target as IndexType).type;
// A keyof S is related to a keyof T if T is related to S.
- if (source.flags & TypeFlags.Index) {
- if (result = isRelatedTo(targetType, (source as IndexType).type, /*reportErrors*/ false)) {
+ if (sourceFlags & TypeFlags.Index) {
+ if (result = isRelatedTo(targetType, (source as IndexType).type, RecursionFlags.Both, /*reportErrors*/ false)) {
return result;
}
}
if (isTupleType(targetType)) {
// An index type can have a tuple type target when the tuple type contains variadic elements.
// Check if the source is related to the known keys of the tuple type.
- if (result = isRelatedTo(source, getKnownKeysOfTupleType(targetType), reportErrors)) {
+ if (result = isRelatedTo(source, getKnownKeysOfTupleType(targetType), RecursionFlags.Target, reportErrors)) {
return result;
}
}
@@ -18634,18 +19113,47 @@ namespace ts {
// false positives. For example, given 'T extends { [K in keyof T]: string }',
// 'keyof T' has itself as its constraint and produces a Ternary.Maybe when
// related to other types.
- if (isRelatedTo(source, getIndexType(constraint, (target as IndexType).stringsOnly), reportErrors) === Ternary.True) {
+ if (isRelatedTo(source, getIndexType(constraint, (target as IndexType).stringsOnly), RecursionFlags.Target, reportErrors) === Ternary.True) {
+ return Ternary.True;
+ }
+ }
+ else if (isGenericMappedType(targetType)) {
+ // generic mapped types that don't simplify or have a constraint still have a very simple set of keys we can compare against
+ // - their nameType or constraintType.
+ // In many ways, this comparison is a deferred version of what `getIndexTypeForMappedType` does to actually resolve the keys for _non_-generic types
+
+ const nameType = getNameTypeFromMappedType(targetType);
+ const constraintType = getConstraintTypeFromMappedType(targetType);
+ let targetKeys;
+ if (nameType && isMappedTypeWithKeyofConstraintDeclaration(targetType)) {
+ // we need to get the apparent mappings and union them with the generic mappings, since some properties may be
+ // missing from the `constraintType` which will otherwise be mapped in the object
+ const modifiersType = getApparentType(getModifiersTypeFromMappedType(targetType));
+ const mappedKeys: Type[] = [];
+ forEachMappedTypePropertyKeyTypeAndIndexSignatureKeyType(
+ modifiersType,
+ TypeFlags.StringOrNumberLiteralOrUnique,
+ /*stringsOnly*/ false,
+ t => void mappedKeys.push(instantiateType(nameType, appendTypeMapping(targetType.mapper, getTypeParameterFromMappedType(targetType), t)))
+ );
+ // We still need to include the non-apparent (and thus still generic) keys in the target side of the comparison (in case they're in the source side)
+ targetKeys = getUnionType([...mappedKeys, nameType]);
+ }
+ else {
+ targetKeys = nameType || constraintType;
+ }
+ if (isRelatedTo(source, targetKeys, RecursionFlags.Target, reportErrors) === Ternary.True) {
return Ternary.True;
}
}
}
}
- else if (target.flags & TypeFlags.IndexedAccess) {
- if (source.flags & TypeFlags.IndexedAccess) {
+ else if (targetFlags & TypeFlags.IndexedAccess) {
+ if (sourceFlags & TypeFlags.IndexedAccess) {
// Relate components directly before falling back to constraint relationships
// A type S[K] is related to a type T[J] if S is related to T and K is related to J.
- if (result = isRelatedTo((source as IndexedAccessType).objectType, (target as IndexedAccessType).objectType, reportErrors)) {
- result &= isRelatedTo((source as IndexedAccessType).indexType, (target as IndexedAccessType).indexType, reportErrors);
+ if (result = isRelatedTo((source as IndexedAccessType).objectType, (target as IndexedAccessType).objectType, RecursionFlags.Both, reportErrors)) {
+ result &= isRelatedTo((source as IndexedAccessType).indexType, (target as IndexedAccessType).indexType, RecursionFlags.Both, reportErrors);
}
if (result) {
resetErrorInfo(saveErrorInfo);
@@ -18670,7 +19178,7 @@ namespace ts {
// create a new chain for the constraint error
resetErrorInfo(saveErrorInfo);
}
- if (result = isRelatedTo(source, constraint, reportErrors)) {
+ if (result = isRelatedTo(source, constraint, RecursionFlags.Target, reportErrors)) {
return result;
}
// prefer the shorter chain of the constraint comparison chain, and the direct comparison chain
@@ -18684,7 +19192,7 @@ namespace ts {
originalErrorInfo = undefined;
}
}
- else if (isGenericMappedType(target)) {
+ else if (isGenericMappedType(target) && relation !== identityRelation) {
// Check if source type `S` is related to target type `{ [P in Q]: T }` or `{ [P in Q as R]: T}`.
const keysRemapped = !!target.declaration.nameType;
const templateType = getTemplateTypeFromMappedType(target);
@@ -18710,14 +19218,15 @@ namespace ts {
// A source type `S` is related to a target type `{ [P in Q as R]?: T }` if some constituent `R'` of `R` is related to `keyof S` and `S[R']` is related to `T`.
if (includeOptional
? !(filteredByApplicability!.flags & TypeFlags.Never)
- : isRelatedTo(targetKeys, sourceKeys)) {
+ : isRelatedTo(targetKeys, sourceKeys, RecursionFlags.Both)) {
+ const templateType = getTemplateTypeFromMappedType(target);
const typeParameter = getTypeParameterFromMappedType(target);
// Fastpath: When the template type has the form `Obj[P]` where `P` is the mapped type parameter, directly compare source `S` with `Obj`
// to avoid creating the (potentially very large) number of new intermediate types made by manufacturing `S[P]`.
const nonNullComponent = extractTypesOfKind(templateType, ~TypeFlags.Nullable);
if (!keysRemapped && nonNullComponent.flags & TypeFlags.IndexedAccess && (nonNullComponent as IndexedAccessType).indexType === typeParameter) {
- if (result = isRelatedTo(source, (nonNullComponent as IndexedAccessType).objectType, reportErrors)) {
+ if (result = isRelatedTo(source, (nonNullComponent as IndexedAccessType).objectType, RecursionFlags.Target, reportErrors)) {
return result;
}
}
@@ -18738,7 +19247,7 @@ namespace ts {
: typeParameter;
const indexedAccessType = getIndexedAccessType(source, indexingType);
// Compare `S[indexingType]` to `T`, where `T` is the type of a property of the target type.
- if (result = isRelatedTo(indexedAccessType, templateType, reportErrors)) {
+ if (result = isRelatedTo(indexedAccessType, templateType, RecursionFlags.Both, reportErrors)) {
return result;
}
}
@@ -18748,104 +19257,117 @@ namespace ts {
}
}
}
- else if (target.flags & TypeFlags.Conditional) {
- const c = target as ConditionalType;
- // Check if the conditional is always true or always false but still deferred for distribution purposes
- const skipTrue = !isTypeAssignableTo(getPermissiveInstantiation(c.checkType), getPermissiveInstantiation(c.extendsType));
- const skipFalse = !skipTrue && isConditionalTypeAlwaysTrueDisregardingInferTypes(c);
-
- // Instantiate with a replacement mapper if the conditional is distributive, replacing the check type with a clone of itself,
- // this way {x: string | number, y: string | number} -> (T extends T ? { x: T, y: T } : never) appropriately _fails_ when
- // T = string | number (since that will end up distributing and producing `{x: string, y: string} | {x: number, y: number}`,
- // to which `{x: string | number, y: string | number}` isn't assignable)
- let distributionMapper: TypeMapper | undefined;
- const checkVar = getActualTypeVariable(c.root.checkType);
- if (c.root.isDistributive && checkVar.flags & TypeFlags.TypeParameter) {
- const newParam = cloneTypeParameter(checkVar);
- distributionMapper = prependTypeMapping(checkVar, newParam, c.mapper);
- newParam.mapper = distributionMapper;
+ else if (targetFlags & TypeFlags.Conditional) {
+ // If we reach 10 levels of nesting for the same conditional type, assume it is an infinitely expanding recursive
+ // conditional type and bail out with a Ternary.Maybe result.
+ if (isDeeplyNestedType(target, targetStack, targetDepth, 10)) {
+ resetErrorInfo(saveErrorInfo);
+ return Ternary.Maybe;
}
-
- // TODO: Find a nice way to include potential conditional type breakdowns in error output, if they seem good (they usually don't)
- let localResult: Ternary | undefined;
- if (skipTrue || (localResult = isRelatedTo(source, distributionMapper ? instantiateType(getTypeFromTypeNode(c.root.node.trueType), distributionMapper) : getTrueTypeFromConditionalType(c), /*reportErrors*/ false))) {
- if (!skipFalse) {
- localResult = (localResult || Ternary.Maybe) & isRelatedTo(source, distributionMapper ? instantiateType(getTypeFromTypeNode(c.root.node.falseType), distributionMapper) : getFalseTypeFromConditionalType(c), /*reportErrors*/ false);
+ const c = target as ConditionalType;
+ // We check for a relationship to a conditional type target only when the conditional type has no
+ // 'infer' positions and is not distributive or is distributive but doesn't reference the check type
+ // parameter in either of the result types.
+ if (!c.root.inferTypeParameters && !isDistributionDependent(c.root)) {
+ // Check if the conditional is always true or always false but still deferred for distribution purposes.
+ const skipTrue = !isTypeAssignableTo(getPermissiveInstantiation(c.checkType), getPermissiveInstantiation(c.extendsType));
+ const skipFalse = !skipTrue && isTypeAssignableTo(getRestrictiveInstantiation(c.checkType), getRestrictiveInstantiation(c.extendsType));
+ // TODO: Find a nice way to include potential conditional type breakdowns in error output, if they seem good (they usually don't)
+ if (result = skipTrue ? Ternary.True : isRelatedTo(source, getTrueTypeFromConditionalType(c), RecursionFlags.Target, /*reportErrors*/ false)) {
+ result &= skipFalse ? Ternary.True : isRelatedTo(source, getFalseTypeFromConditionalType(c), RecursionFlags.Target, /*reportErrors*/ false);
+ if (result) {
+ resetErrorInfo(saveErrorInfo);
+ return result;
+ }
}
}
- if (localResult) {
- resetErrorInfo(saveErrorInfo);
- return localResult;
- }
}
- else if (target.flags & TypeFlags.TemplateLiteral) {
- if (source.flags & TypeFlags.TemplateLiteral) {
+ else if (targetFlags & TypeFlags.TemplateLiteral) {
+ if (sourceFlags & TypeFlags.TemplateLiteral) {
+ if (relation === comparableRelation) {
+ return templateLiteralTypesDefinitelyUnrelated(source as TemplateLiteralType, target as TemplateLiteralType) ? Ternary.False : Ternary.True;
+ }
// Report unreliable variance for type variables referenced in template literal type placeholders.
// For example, `foo-${number}` is related to `foo-${string}` even though number isn't related to string.
instantiateType(source, makeFunctionTypeMapper(reportUnreliableMarkers));
}
- const result = inferTypesFromTemplateLiteralType(source, target as TemplateLiteralType);
- if (result && every(result, (r, i) => isValidTypeForTemplateLiteralPlaceholder(r, (target as TemplateLiteralType).types[i]))) {
+ if (isTypeMatchedByTemplateLiteralType(source, target as TemplateLiteralType)) {
return Ternary.True;
}
}
- if (source.flags & TypeFlags.TypeVariable) {
- // IndexedAccess comparisons are handled above in the `target.flags & TypeFlage.IndexedAccess` branch
- if (!(source.flags & TypeFlags.IndexedAccess && target.flags & TypeFlags.IndexedAccess)) {
+ if (sourceFlags & TypeFlags.TypeVariable) {
+ // IndexedAccess comparisons are handled above in the `targetFlags & TypeFlage.IndexedAccess` branch
+ if (!(sourceFlags & TypeFlags.IndexedAccess && targetFlags & TypeFlags.IndexedAccess)) {
const constraint = getConstraintOfType(source as TypeVariable);
- if (!constraint || (source.flags & TypeFlags.TypeParameter && constraint.flags & TypeFlags.Any)) {
+ if (!constraint || (sourceFlags & TypeFlags.TypeParameter && constraint.flags & TypeFlags.Any)) {
// A type variable with no constraint is not related to the non-primitive object type.
- if (result = isRelatedTo(emptyObjectType, extractTypesOfKind(target, ~TypeFlags.NonPrimitive))) {
+ if (result = isRelatedTo(emptyObjectType, extractTypesOfKind(target, ~TypeFlags.NonPrimitive), RecursionFlags.Both)) {
resetErrorInfo(saveErrorInfo);
return result;
}
}
// hi-speed no-this-instantiation check (less accurate, but avoids costly `this`-instantiation when the constraint will suffice), see #28231 for report on why this is needed
- else if (result = isRelatedTo(constraint, target, /*reportErrors*/ false, /*headMessage*/ undefined, intersectionState)) {
+ else if (result = isRelatedTo(constraint, target, RecursionFlags.Source, /*reportErrors*/ false, /*headMessage*/ undefined, intersectionState)) {
resetErrorInfo(saveErrorInfo);
return result;
}
// slower, fuller, this-instantiated check (necessary when comparing raw `this` types from base classes), see `subclassWithPolymorphicThisIsAssignable.ts` test for example
- else if (result = isRelatedTo(getTypeWithThisArgument(constraint, source), target, reportErrors && !(target.flags & source.flags & TypeFlags.TypeParameter), /*headMessage*/ undefined, intersectionState)) {
+ else if (result = isRelatedTo(getTypeWithThisArgument(constraint, source), target, RecursionFlags.Source, reportErrors && !(targetFlags & sourceFlags & TypeFlags.TypeParameter), /*headMessage*/ undefined, intersectionState)) {
resetErrorInfo(saveErrorInfo);
return result;
}
+ if (isMappedTypeGenericIndexedAccess(source)) {
+ // For an indexed access type { [P in K]: E}[X], above we have already explored an instantiation of E with X
+ // substituted for P. We also want to explore type { [P in K]: E }[C], where C is the constraint of X.
+ const indexConstraint = getConstraintOfType((source as IndexedAccessType).indexType);
+ if (indexConstraint) {
+ if (result = isRelatedTo(getIndexedAccessType((source as IndexedAccessType).objectType, indexConstraint), target, RecursionFlags.Source, reportErrors)) {
+ resetErrorInfo(saveErrorInfo);
+ return result;
+ }
+ }
+ }
}
}
- else if (source.flags & TypeFlags.Index) {
- if (result = isRelatedTo(keyofConstraintType, target, reportErrors)) {
+ else if (sourceFlags & TypeFlags.Index) {
+ if (result = isRelatedTo(keyofConstraintType, target, RecursionFlags.Source, reportErrors)) {
resetErrorInfo(saveErrorInfo);
return result;
}
}
- else if (source.flags & TypeFlags.TemplateLiteral) {
- if (!(target.flags & TypeFlags.TemplateLiteral)) {
- const baseConstraint = getBaseConstraintOfType(source);
- const constraint = baseConstraint && baseConstraint !== source ? baseConstraint : stringType;
- if (result = isRelatedTo(constraint, target, reportErrors)) {
+ else if (sourceFlags & TypeFlags.TemplateLiteral && !(targetFlags & TypeFlags.Object)) {
+ if (!(targetFlags & TypeFlags.TemplateLiteral)) {
+ const constraint = getBaseConstraintOfType(source);
+ if (constraint && constraint !== source && (result = isRelatedTo(constraint, target, RecursionFlags.Source, reportErrors))) {
resetErrorInfo(saveErrorInfo);
return result;
}
}
}
- else if (source.flags & TypeFlags.StringMapping) {
- if (target.flags & TypeFlags.StringMapping && (source as StringMappingType).symbol === (target as StringMappingType).symbol) {
- if (result = isRelatedTo((source as StringMappingType).type, (target as StringMappingType).type, reportErrors)) {
+ else if (sourceFlags & TypeFlags.StringMapping) {
+ if (targetFlags & TypeFlags.StringMapping && (source as StringMappingType).symbol === (target as StringMappingType).symbol) {
+ if (result = isRelatedTo((source as StringMappingType).type, (target as StringMappingType).type, RecursionFlags.Both, reportErrors)) {
resetErrorInfo(saveErrorInfo);
return result;
}
}
else {
const constraint = getBaseConstraintOfType(source);
- if (constraint && (result = isRelatedTo(constraint, target, reportErrors))) {
+ if (constraint && (result = isRelatedTo(constraint, target, RecursionFlags.Source, reportErrors))) {
resetErrorInfo(saveErrorInfo);
return result;
}
}
}
- else if (source.flags & TypeFlags.Conditional) {
- if (target.flags & TypeFlags.Conditional) {
+ else if (sourceFlags & TypeFlags.Conditional) {
+ // If we reach 10 levels of nesting for the same conditional type, assume it is an infinitely expanding recursive
+ // conditional type and bail out with a Ternary.Maybe result.
+ if (isDeeplyNestedType(source, sourceStack, sourceDepth, 10)) {
+ resetErrorInfo(saveErrorInfo);
+ return Ternary.Maybe;
+ }
+ if (targetFlags & TypeFlags.Conditional) {
// Two conditional types 'T1 extends U1 ? X1 : Y1' and 'T2 extends U2 ? X2 : Y2' are related if
// one of T1 and T2 is related to the other, U1 and U2 are identical types, X1 is related to X2,
// and Y1 is related to Y2.
@@ -18854,15 +19376,15 @@ namespace ts {
let mapper: TypeMapper | undefined;
if (sourceParams) {
// If the source has infer type parameters, we instantiate them in the context of the target
- const ctx = createInferenceContext(sourceParams, /*signature*/ undefined, InferenceFlags.None, isRelatedTo);
+ const ctx = createInferenceContext(sourceParams, /*signature*/ undefined, InferenceFlags.None, isRelatedToWorker);
inferTypes(ctx.inferences, (target as ConditionalType).extendsType, sourceExtends, InferencePriority.NoConstraints | InferencePriority.AlwaysStrict);
sourceExtends = instantiateType(sourceExtends, ctx.mapper);
mapper = ctx.mapper;
}
if (isTypeIdenticalTo(sourceExtends, (target as ConditionalType).extendsType) &&
- (isRelatedTo((source as ConditionalType).checkType, (target as ConditionalType).checkType) || isRelatedTo((target as ConditionalType).checkType, (source as ConditionalType).checkType))) {
- if (result = isRelatedTo(instantiateType(getTrueTypeFromConditionalType(source as ConditionalType), mapper), getTrueTypeFromConditionalType(target as ConditionalType), reportErrors)) {
- result &= isRelatedTo(getFalseTypeFromConditionalType(source as ConditionalType), getFalseTypeFromConditionalType(target as ConditionalType), reportErrors);
+ (isRelatedTo((source as ConditionalType).checkType, (target as ConditionalType).checkType, RecursionFlags.Both) || isRelatedTo((target as ConditionalType).checkType, (source as ConditionalType).checkType, RecursionFlags.Both))) {
+ if (result = isRelatedTo(instantiateType(getTrueTypeFromConditionalType(source as ConditionalType), mapper), getTrueTypeFromConditionalType(target as ConditionalType), RecursionFlags.Both, reportErrors)) {
+ result &= isRelatedTo(getFalseTypeFromConditionalType(source as ConditionalType), getFalseTypeFromConditionalType(target as ConditionalType), RecursionFlags.Both, reportErrors);
}
if (result) {
resetErrorInfo(saveErrorInfo);
@@ -18873,19 +19395,20 @@ namespace ts {
else {
// conditionals aren't related to one another via distributive constraint as it is much too inaccurate and allows way
// more assignments than are desirable (since it maps the source check type to its constraint, it loses information)
- const distributiveConstraint = getConstraintOfDistributiveConditionalType(source as ConditionalType);
+ const distributiveConstraint = hasNonCircularBaseConstraint(source) ? getConstraintOfDistributiveConditionalType(source as ConditionalType) : undefined;
if (distributiveConstraint) {
- if (result = isRelatedTo(distributiveConstraint, target, reportErrors)) {
+ if (result = isRelatedTo(distributiveConstraint, target, RecursionFlags.Source, reportErrors)) {
resetErrorInfo(saveErrorInfo);
return result;
}
}
}
+
// conditionals _can_ be related to one another via normal constraint, as, eg, `A extends B ? O : never` should be assignable to `O`
- // when `O` is a conditional (`never` is trivially aissgnable to `O`, as is `O`!).
+ // when `O` is a conditional (`never` is trivially assignable to `O`, as is `O`!).
const defaultConstraint = getDefaultConstraintOfConditionalType(source as ConditionalType);
if (defaultConstraint) {
- if (result = isRelatedTo(defaultConstraint, target, reportErrors)) {
+ if (result = isRelatedTo(defaultConstraint, target, RecursionFlags.Source, reportErrors)) {
resetErrorInfo(saveErrorInfo);
return result;
}
@@ -18905,15 +19428,21 @@ namespace ts {
}
return Ternary.False;
}
- const sourceIsPrimitive = !!(source.flags & TypeFlags.Primitive);
+ const sourceIsPrimitive = !!(sourceFlags & TypeFlags.Primitive);
if (relation !== identityRelation) {
source = getApparentType(source);
+ sourceFlags = source.flags;
}
else if (isGenericMappedType(source)) {
return Ternary.False;
}
if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (source as TypeReference).target === (target as TypeReference).target &&
!isTupleType(source) && !(getObjectFlags(source) & ObjectFlags.MarkerType || getObjectFlags(target) & ObjectFlags.MarkerType)) {
+ // When strictNullChecks is disabled, the element type of the empty array literal is undefinedWideningType,
+ // and an empty array literal wouldn't be assignable to a `never[]` without this check.
+ if (isEmptyArrayLiteralType(source)) {
+ return Ternary.True;
+ }
// We have type references to the same generic type, and the type references are not marker
// type references (which are intended by be compared structurally). Obtain the variance
// information for the type parameters and relate the type arguments accordingly.
@@ -18931,7 +19460,7 @@ namespace ts {
}
else if (isReadonlyArrayType(target) ? isArrayType(source) || isTupleType(source) : isArrayType(target) && isTupleType(source) && !source.target.readonly) {
if (relation !== identityRelation) {
- return isRelatedTo(getIndexTypeOfType(source, numberType) || anyType, getIndexTypeOfType(target, numberType) || anyType, reportErrors);
+ return isRelatedTo(getIndexTypeOfType(source, numberType) || anyType, getIndexTypeOfType(target, numberType) || anyType, RecursionFlags.Both, reportErrors);
}
else {
// By flags alone, we know that the `target` is a readonly array while the source is a normal array or tuple
@@ -18949,7 +19478,7 @@ namespace ts {
// In a check of the form X = A & B, we will have previously checked if A relates to X or B relates
// to X. Failing both of those we want to check if the aggregation of A and B's members structurally
// relates to X. Thus, we include intersection types on the source side here.
- if (source.flags & (TypeFlags.Object | TypeFlags.Intersection) && target.flags & TypeFlags.Object) {
+ if (sourceFlags & (TypeFlags.Object | TypeFlags.Intersection) && targetFlags & TypeFlags.Object) {
// Report structural errors only if we haven't reported any errors yet
const reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo.errorInfo && !sourceIsPrimitive;
result = propertiesRelatedTo(source, target, reportStructuralErrors, /*excludedProperties*/ undefined, intersectionState);
@@ -18973,7 +19502,7 @@ namespace ts {
// there exists a constituent of T for every combination of the discriminants of S
// with respect to T. We do not report errors here, as we will use the existing
// error result from checking each constituent of the union.
- if (source.flags & (TypeFlags.Object | TypeFlags.Intersection) && target.flags & TypeFlags.Union) {
+ if (sourceFlags & (TypeFlags.Object | TypeFlags.Intersection) && targetFlags & TypeFlags.Union) {
const objectOnlyTarget = extractTypesOfKind(target, TypeFlags.Object | TypeFlags.Intersection | TypeFlags.Substitution);
if (objectOnlyTarget.flags & TypeFlags.Union) {
const result = typeRelatedToDiscriminatedType(source, objectOnlyTarget as UnionType);
@@ -19057,10 +19586,10 @@ namespace ts {
let result: Ternary;
const targetConstraint = getConstraintTypeFromMappedType(target);
const sourceConstraint = instantiateType(getConstraintTypeFromMappedType(source), makeFunctionTypeMapper(getCombinedMappedTypeOptionality(source) < 0 ? reportUnmeasurableMarkers : reportUnreliableMarkers));
- if (result = isRelatedTo(targetConstraint, sourceConstraint, reportErrors)) {
+ if (result = isRelatedTo(targetConstraint, sourceConstraint, RecursionFlags.Both, reportErrors)) {
const mapper = createTypeMapper([getTypeParameterFromMappedType(source)], [getTypeParameterFromMappedType(target)]);
if (instantiateType(getNameTypeFromMappedType(source), mapper) === instantiateType(getNameTypeFromMappedType(target), mapper)) {
- return result & isRelatedTo(instantiateType(getTemplateTypeFromMappedType(source), mapper), getTemplateTypeFromMappedType(target), reportErrors);
+ return result & isRelatedTo(instantiateType(getTemplateTypeFromMappedType(source), mapper), getTemplateTypeFromMappedType(target), RecursionFlags.Both, reportErrors);
}
}
}
@@ -19182,7 +19711,7 @@ namespace ts {
const targetIsOptional = strictNullChecks && !!(getCheckFlags(targetProp) & CheckFlags.Partial);
const effectiveTarget = addOptionality(getNonMissingTypeOfSymbol(targetProp), /*isProperty*/ false, targetIsOptional);
const effectiveSource = getTypeOfSourceProperty(sourceProp);
- return isRelatedTo(effectiveSource, effectiveTarget, reportErrors, /*headMessage*/ undefined, intersectionState);
+ return isRelatedTo(effectiveSource, effectiveTarget, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState);
}
function propertyRelatedTo(source: Type, target: Type, sourceProp: Symbol, targetProp: Symbol, getTypeOfSourceProperty: (sym: Symbol) => Type, reportErrors: boolean, intersectionState: IntersectionState, skipOptional: boolean): Ternary {
@@ -19219,6 +19748,18 @@ namespace ts {
}
return Ternary.False;
}
+
+ // Ensure {readonly a: whatever} is not a subtype of {a: whatever},
+ // while {a: whatever} is a subtype of {readonly a: whatever}.
+ // This ensures the subtype relationship is ordered, and preventing declaration order
+ // from deciding which type "wins" in union subtype reduction.
+ // They're still assignable to one another, since `readonly` doesn't affect assignability.
+ if (
+ (relation === subtypeRelation || relation === strictSubtypeRelation) &&
+ !!(sourcePropFlags & ModifierFlags.Readonly) && !(targetPropFlags & ModifierFlags.Readonly)
+ ) {
+ return Ternary.False;
+ }
// If the target comes from a partial union prop, allow `undefined` in the target type
const related = isPropertySymbolTypeRelated(sourceProp, targetProp, getTypeOfSourceProperty, reportErrors, intersectionState);
if (!related) {
@@ -19376,7 +19917,7 @@ namespace ts {
const targetType = targetTypeArguments[i];
const targetCheckType = sourceFlags & ElementFlags.Variadic && targetFlags & ElementFlags.Rest ? createArrayType(targetType) :
removeMissingType(targetType, !!(targetFlags & ElementFlags.Optional));
- const related = isRelatedTo(sourceType, targetCheckType, reportErrors, /*headMessage*/ undefined, intersectionState);
+ const related = isRelatedTo(sourceType, targetCheckType, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState);
if (!related) {
if (reportErrors && (targetArity > 1 || sourceArity > 1)) {
if (i < startCount || i >= targetArity - endCount || sourceArity - startCount - endCount === 1) {
@@ -19496,11 +20037,11 @@ namespace ts {
}
let result = Ternary.True;
- const saveErrorInfo = captureErrorCalculationState();
const incompatibleReporter = kind === SignatureKind.Construct ? reportIncompatibleConstructSignatureReturn : reportIncompatibleCallSignatureReturn;
const sourceObjectFlags = getObjectFlags(source);
const targetObjectFlags = getObjectFlags(target);
- if (sourceObjectFlags & ObjectFlags.Instantiated && targetObjectFlags & ObjectFlags.Instantiated && source.symbol === target.symbol) {
+ if (sourceObjectFlags & ObjectFlags.Instantiated && targetObjectFlags & ObjectFlags.Instantiated && source.symbol === target.symbol ||
+ sourceObjectFlags & ObjectFlags.Reference && targetObjectFlags & ObjectFlags.Reference && (source as TypeReference).target === (target as TypeReference).target) {
// We have instantiations of the same anonymous type (which typically will be the type of a
// method). Simply do a pairwise comparison of the signatures in the two signature lists instead
// of the much more expensive N * M comparison matrix we explore below. We erase type parameters
@@ -19534,6 +20075,7 @@ namespace ts {
}
else {
outer: for (const t of targetSignatures) {
+ const saveErrorInfo = captureErrorCalculationState();
// Only elaborate errors from the first failure
let shouldElaborateErrors = reportErrors;
for (const s of sourceSignatures) {
@@ -19545,7 +20087,6 @@ namespace ts {
}
shouldElaborateErrors = false;
}
-
if (shouldElaborateErrors) {
reportError(Diagnostics.Type_0_provides_no_match_for_the_signature_1,
typeToString(source),
@@ -19576,7 +20117,7 @@ namespace ts {
*/
function signatureRelatedTo(source: Signature, target: Signature, erase: boolean, reportErrors: boolean, incompatibleReporter: (source: Type, target: Type) => void): Ternary {
return compareSignaturesRelated(erase ? getErasedSignature(source) : source, erase ? getErasedSignature(target) : target,
- relation === strictSubtypeRelation ? SignatureCheckMode.StrictArity : 0, reportErrors, reportError, incompatibleReporter, isRelatedTo, makeFunctionTypeMapper(reportUnreliableMarkers));
+ relation === strictSubtypeRelation ? SignatureCheckMode.StrictArity : 0, reportErrors, reportError, incompatibleReporter, isRelatedToWorker, makeFunctionTypeMapper(reportUnreliableMarkers));
}
function signaturesIdenticalTo(source: Type, target: Type, kind: SignatureKind): Ternary {
@@ -19610,7 +20151,7 @@ namespace ts {
const type = exactOptionalPropertyTypes || propType.flags & TypeFlags.Undefined || keyType === numberType || !(prop.flags & SymbolFlags.Optional)
? propType
: getTypeWithFacts(propType, TypeFacts.NEUndefined);
- const related = isRelatedTo(type, targetInfo.type, reportErrors);
+ const related = isRelatedTo(type, targetInfo.type, RecursionFlags.Both, reportErrors);
if (!related) {
if (reportErrors) {
reportError(Diagnostics.Property_0_is_incompatible_with_index_signature, symbolToString(prop));
@@ -19633,7 +20174,7 @@ namespace ts {
}
function indexInfoRelatedTo(sourceInfo: IndexInfo, targetInfo: IndexInfo, reportErrors: boolean) {
- const related = isRelatedTo(sourceInfo.type, targetInfo.type, reportErrors);
+ const related = isRelatedTo(sourceInfo.type, targetInfo.type, RecursionFlags.Both, reportErrors);
if (!related && reportErrors) {
if (sourceInfo.keyType === targetInfo.keyType) {
reportError(Diagnostics._0_index_signatures_are_incompatible, typeToString(sourceInfo.keyType));
@@ -19654,7 +20195,7 @@ namespace ts {
let result = Ternary.True;
for (const targetInfo of indexInfos) {
const related = !sourceIsPrimitive && targetHasStringIndex && targetInfo.type.flags & TypeFlags.Any ? Ternary.True :
- isGenericMappedType(source) && targetHasStringIndex ? isRelatedTo(getTemplateTypeFromMappedType(source), targetInfo.type, reportErrors) :
+ isGenericMappedType(source) && targetHasStringIndex ? isRelatedTo(getTemplateTypeFromMappedType(source), targetInfo.type, RecursionFlags.Both, reportErrors) :
typeRelatedToIndexInfo(source, targetInfo, reportErrors, intersectionState);
if (!related) {
return Ternary.False;
@@ -19687,7 +20228,7 @@ namespace ts {
}
for (const targetInfo of targetInfos) {
const sourceInfo = getIndexInfoOfType(source, targetInfo.keyType);
- if (!(sourceInfo && isRelatedTo(sourceInfo.type, targetInfo.type) && sourceInfo.isReadonly === targetInfo.isReadonly)) {
+ if (!(sourceInfo && isRelatedTo(sourceInfo.type, targetInfo.type, RecursionFlags.Both) && sourceInfo.isReadonly === targetInfo.isReadonly)) {
return Ternary.False;
}
}
@@ -19924,47 +20465,55 @@ namespace ts {
return isNonDeferredTypeReference(type) && some(getTypeArguments(type), t => !!(t.flags & TypeFlags.TypeParameter) || isTypeReferenceWithGenericArguments(t));
}
- /**
- * getTypeReferenceId(A) returns "111=0-12=1"
- * where A.id=111 and number.id=12
- */
- function getTypeReferenceId(type: TypeReference, typeParameters: Type[], depth = 0) {
- let result = "" + type.target.id;
- for (const t of getTypeArguments(type)) {
- if (isUnconstrainedTypeParameter(t)) {
- let index = typeParameters.indexOf(t);
- if (index < 0) {
- index = typeParameters.length;
- typeParameters.push(t);
+ function getGenericTypeReferenceRelationKey(source: TypeReference, target: TypeReference, postFix: string, ignoreConstraints: boolean) {
+ const typeParameters: Type[] = [];
+ let constraintMarker = "";
+ const sourceId = getTypeReferenceId(source, 0);
+ const targetId = getTypeReferenceId(target, 0);
+ return `${constraintMarker}${sourceId},${targetId}${postFix}`;
+ // getTypeReferenceId(A) returns "111=0-12=1"
+ // where A.id=111 and number.id=12
+ function getTypeReferenceId(type: TypeReference, depth = 0) {
+ let result = "" + type.target.id;
+ for (const t of getTypeArguments(type)) {
+ if (t.flags & TypeFlags.TypeParameter) {
+ if (ignoreConstraints || isUnconstrainedTypeParameter(t)) {
+ let index = typeParameters.indexOf(t);
+ if (index < 0) {
+ index = typeParameters.length;
+ typeParameters.push(t);
+ }
+ result += "=" + index;
+ continue;
+ }
+ // We mark type references that reference constrained type parameters such that we know to obtain
+ // and look for a "broadest equivalent key" in the cache.
+ constraintMarker = "*";
+ }
+ else if (depth < 4 && isTypeReferenceWithGenericArguments(t)) {
+ result += "<" + getTypeReferenceId(t as TypeReference, depth + 1) + ">";
+ continue;
}
- result += "=" + index;
- }
- else if (depth < 4 && isTypeReferenceWithGenericArguments(t)) {
- result += "<" + getTypeReferenceId(t as TypeReference, typeParameters, depth + 1) + ">";
- }
- else {
result += "-" + t.id;
}
+ return result;
}
- return result;
}
/**
* To improve caching, the relation key for two generic types uses the target's id plus ids of the type parameters.
* For other cases, the types ids are used.
*/
- function getRelationKey(source: Type, target: Type, intersectionState: IntersectionState, relation: ESMap) {
+ function getRelationKey(source: Type, target: Type, intersectionState: IntersectionState, relation: ESMap, ignoreConstraints: boolean) {
if (relation === identityRelation && source.id > target.id) {
const temp = source;
source = target;
target = temp;
}
const postFix = intersectionState ? ":" + intersectionState : "";
- if (isTypeReferenceWithGenericArguments(source) && isTypeReferenceWithGenericArguments(target)) {
- const typeParameters: Type[] = [];
- return getTypeReferenceId(source as TypeReference, typeParameters) + "," + getTypeReferenceId(target as TypeReference, typeParameters) + postFix;
- }
- return source.id + "," + target.id + postFix;
+ return isTypeReferenceWithGenericArguments(source) && isTypeReferenceWithGenericArguments(target) ?
+ getGenericTypeReferenceRelationKey(source as TypeReference, target as TypeReference, postFix, ignoreConstraints) :
+ `${source.id},${target.id}${postFix}`;
}
// Invoke the callback for each underlying property symbol of the given symbol and return the first
@@ -20018,27 +20567,34 @@ namespace ts {
}
// Return true if the given type is deeply nested. We consider this to be the case when structural type comparisons
- // for 5 or more occurrences or instantiations of the type have been recorded on the given stack. It is possible,
+ // for maxDepth or more occurrences or instantiations of the type have been recorded on the given stack. It is possible,
// though highly unlikely, for this test to be true in a situation where a chain of instantiations is not infinitely
- // expanding. Effectively, we will generate a false positive when two types are structurally equal to at least 5
+ // expanding. Effectively, we will generate a false positive when two types are structurally equal to at least maxDepth
// levels, but unequal at some level beyond that.
- // In addition, this will also detect when an indexed access has been chained off of 5 or more times (which is essentially
- // the dual of the structural comparison), and likewise mark the type as deeply nested, potentially adding false positives
- // for finite but deeply expanding indexed accesses (eg, for `Q[P1][P2][P3][P4][P5]`).
- // It also detects when a recursive type reference has expanded 5 or more times, eg, if the true branch of
+ // In addition, this will also detect when an indexed access has been chained off of maxDepth more times (which is
+ // essentially the dual of the structural comparison), and likewise mark the type as deeply nested, potentially adding
+ // false positives for finite but deeply expanding indexed accesses (eg, for `Q[P1][P2][P3][P4][P5]`).
+ // It also detects when a recursive type reference has expanded maxDepth or more times, e.g. if the true branch of
// `type A = null extends T ? [A>] : [T]`
- // has expanded into `[A>>>>>]`
- // in such cases we need to terminate the expansion, and we do so here.
- function isDeeplyNestedType(type: Type, stack: Type[], depth: number): boolean {
- if (depth >= 5) {
+ // has expanded into `[A>>>>>]`. In such cases we need
+ // to terminate the expansion, and we do so here.
+ function isDeeplyNestedType(type: Type, stack: Type[], depth: number, maxDepth = 3): boolean {
+ if (depth >= maxDepth) {
const identity = getRecursionIdentity(type);
let count = 0;
+ let lastTypeId = 0;
for (let i = 0; i < depth; i++) {
- if (getRecursionIdentity(stack[i]) === identity) {
- count++;
- if (count >= 5) {
- return true;
+ const t = stack[i];
+ if (getRecursionIdentity(t) === identity) {
+ // We only count occurrences with a higher type id than the previous occurrence, since higher
+ // type ids are an indicator of newer instantiations caused by recursion.
+ if (t.id >= lastTypeId) {
+ count++;
+ if (count >= maxDepth) {
+ return true;
+ }
}
+ lastTypeId = t.id;
}
}
}
@@ -20359,7 +20915,7 @@ namespace ts {
function getBaseTypeOfLiteralType(type: Type): Type {
return type.flags & TypeFlags.EnumLiteral ? getBaseTypeOfEnumLiteralType(type as LiteralType) :
- type.flags & TypeFlags.StringLiteral ? stringType :
+ type.flags & (TypeFlags.StringLiteral | TypeFlags.TemplateLiteral | TypeFlags.StringMapping) ? stringType :
type.flags & TypeFlags.NumberLiteral ? numberType :
type.flags & TypeFlags.BigIntLiteral ? bigintType :
type.flags & TypeFlags.BooleanLiteral ? booleanType :
@@ -20596,9 +21152,14 @@ namespace ts {
* with no call or construct signatures.
*/
function isObjectTypeWithInferableIndex(type: Type): boolean {
- return type.flags & TypeFlags.Intersection ? every((type as IntersectionType).types, isObjectTypeWithInferableIndex) :
- !!(type.symbol && (type.symbol.flags & (SymbolFlags.ObjectLiteral | SymbolFlags.TypeLiteral | SymbolFlags.Enum | SymbolFlags.ValueModule)) !== 0 &&
- !typeHasCallOrConstructSignatures(type)) || !!(getObjectFlags(type) & ObjectFlags.ReverseMapped && isObjectTypeWithInferableIndex((type as ReverseMappedType).source));
+ return type.flags & TypeFlags.Intersection
+ ? every((type as IntersectionType).types, isObjectTypeWithInferableIndex)
+ : !!(
+ type.symbol
+ && (type.symbol.flags & (SymbolFlags.ObjectLiteral | SymbolFlags.TypeLiteral | SymbolFlags.Enum | SymbolFlags.ValueModule)) !== 0
+ && !(type.symbol.flags & SymbolFlags.Class)
+ && !typeHasCallOrConstructSignatures(type)
+ ) || !!(getObjectFlags(type) & ObjectFlags.ReverseMapped && isObjectTypeWithInferableIndex((type as ReverseMappedType).source));
}
function createSymbolWithType(source: Symbol, type: Type | undefined) {
@@ -20833,9 +21394,10 @@ namespace ts {
(isCallSignatureDeclaration(param.parent) || isMethodSignature(param.parent) || isFunctionTypeNode(param.parent)) &&
param.parent.parameters.indexOf(param) > -1 &&
(resolveName(param, param.name.escapedText, SymbolFlags.Type, undefined, param.name.escapedText, /*isUse*/ true) ||
- param.name.originalKeywordKind && isTypeNodeKind(param.name.originalKeywordKind))) {
+ param.name.originalKeywordKind && isTypeNodeKind(param.name.originalKeywordKind))) {
const newName = "arg" + param.parent.parameters.indexOf(param);
- errorOrSuggestion(noImplicitAny, declaration, Diagnostics.Parameter_has_a_name_but_no_type_Did_you_mean_0_Colon_1, newName, declarationNameToString(param.name));
+ const typeName = declarationNameToString(param.name) + (param.dotDotDotToken ? "[]" : "");
+ errorOrSuggestion(noImplicitAny, declaration, Diagnostics.Parameter_has_a_name_but_no_type_Did_you_mean_0_Colon_1, newName, typeName);
return;
}
diagnostic = (declaration as ParameterDeclaration).dotDotDotToken ?
@@ -21017,7 +21579,7 @@ namespace ts {
type.flags & TypeFlags.Object && !isNonGenericTopLevelType(type) && (
objectFlags & ObjectFlags.Reference && ((type as TypeReference).node || forEach(getTypeArguments(type as TypeReference), couldContainTypeVariables)) ||
objectFlags & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) && type.symbol.declarations ||
- objectFlags & (ObjectFlags.Mapped | ObjectFlags.ReverseMapped | ObjectFlags.ObjectRestType)) ||
+ objectFlags & (ObjectFlags.Mapped | ObjectFlags.ReverseMapped | ObjectFlags.ObjectRestType | ObjectFlags.InstantiationExpressionType)) ||
type.flags & TypeFlags.UnionOrIntersection && !(type.flags & TypeFlags.EnumLiteral) && !isNonGenericTopLevelType(type) && some((type as UnionOrIntersectionType).types, couldContainTypeVariables));
if (type.flags & TypeFlags.ObjectFlagsType) {
(type as ObjectFlagsType).objectFlags |= ObjectFlags.CouldContainTypeVariablesComputed | (result ? ObjectFlags.CouldContainTypeVariables : 0);
@@ -21190,6 +21752,18 @@ namespace ts {
return !!(type.symbol && some(type.symbol.declarations, hasSkipDirectInferenceFlag));
}
+ function templateLiteralTypesDefinitelyUnrelated(source: TemplateLiteralType, target: TemplateLiteralType) {
+ // Two template literal types with diffences in their starting or ending text spans are definitely unrelated.
+ const sourceStart = source.texts[0];
+ const targetStart = target.texts[0];
+ const sourceEnd = source.texts[source.texts.length - 1];
+ const targetEnd = target.texts[target.texts.length - 1];
+ const startLen = Math.min(sourceStart.length, targetStart.length);
+ const endLen = Math.min(sourceEnd.length, targetEnd.length);
+ return sourceStart.slice(0, startLen) !== targetStart.slice(0, startLen) ||
+ sourceEnd.slice(sourceEnd.length - endLen) !== targetEnd.slice(targetEnd.length - endLen);
+ }
+
function isValidBigIntString(s: string): boolean {
const scanner = createScanner(ScriptTarget.ESNext, /*skipTrivia*/ false);
let success = true;
@@ -21233,6 +21807,11 @@ namespace ts {
undefined;
}
+ function isTypeMatchedByTemplateLiteralType(source: Type, target: TemplateLiteralType): boolean {
+ const inferences = inferTypesFromTemplateLiteralType(source, target);
+ return !!inferences && every(inferences, (r, i) => isValidTypeForTemplateLiteralPlaceholder(r, target.types[i]));
+ }
+
function getStringLikeTypeForType(type: Type) {
return type.flags & (TypeFlags.Any | TypeFlags.StringLike) ? type : getTemplateLiteralType(["", ""], [type]);
}
@@ -21403,12 +21982,14 @@ namespace ts {
// not contain anyFunctionType when we come back to this argument for its second round
// of inference. Also, we exclude inferences for silentNeverType (which is used as a wildcard
// when constructing types from type parameters that had no inference candidates).
- if (getObjectFlags(source) & ObjectFlags.NonInferrableType || source === nonInferrableAnyType || source === silentNeverType ||
- (priority & InferencePriority.ReturnType && (source === autoType || source === autoArrayType)) || isFromInferenceBlockedSource(source)) {
+ if (source === nonInferrableAnyType || source === silentNeverType || (priority & InferencePriority.ReturnType && (source === autoType || source === autoArrayType)) || isFromInferenceBlockedSource(source)) {
return;
}
const inference = getInferenceInfoForType(target);
if (inference) {
+ if (getObjectFlags(source) & ObjectFlags.NonInferrableType) {
+ return;
+ }
if (!inference.isFixed) {
if (inference.priority === undefined || priority < inference.priority) {
inference.candidates = undefined;
@@ -21439,21 +22020,19 @@ namespace ts {
inferencePriority = Math.min(inferencePriority, priority);
return;
}
- else {
- // Infer to the simplified version of an indexed access, if possible, to (hopefully) expose more bare type parameters to the inference engine
- const simplified = getSimplifiedType(target, /*writing*/ false);
- if (simplified !== target) {
- invokeOnce(source, simplified, inferFromTypes);
- }
- else if (target.flags & TypeFlags.IndexedAccess) {
- const indexType = getSimplifiedType((target as IndexedAccessType).indexType, /*writing*/ false);
- // Generally simplifications of instantiable indexes are avoided to keep relationship checking correct, however if our target is an access, we can consider
- // that key of that access to be "instantiated", since we're looking to find the infernce goal in any way we can.
- if (indexType.flags & TypeFlags.Instantiable) {
- const simplified = distributeIndexOverObjectType(getSimplifiedType((target as IndexedAccessType).objectType, /*writing*/ false), indexType, /*writing*/ false);
- if (simplified && simplified !== target) {
- invokeOnce(source, simplified, inferFromTypes);
- }
+ // Infer to the simplified version of an indexed access, if possible, to (hopefully) expose more bare type parameters to the inference engine
+ const simplified = getSimplifiedType(target, /*writing*/ false);
+ if (simplified !== target) {
+ inferFromTypes(source, simplified);
+ }
+ else if (target.flags & TypeFlags.IndexedAccess) {
+ const indexType = getSimplifiedType((target as IndexedAccessType).indexType, /*writing*/ false);
+ // Generally simplifications of instantiable indexes are avoided to keep relationship checking correct, however if our target is an access, we can consider
+ // that key of that access to be "instantiated", since we're looking to find the infernce goal in any way we can.
+ if (indexType.flags & TypeFlags.Instantiable) {
+ const simplified = distributeIndexOverObjectType(getSimplifiedType((target as IndexedAccessType).objectType, /*writing*/ false), indexType, /*writing*/ false);
+ if (simplified && simplified !== target) {
+ inferFromTypes(source, simplified);
}
}
}
@@ -21780,8 +22359,16 @@ namespace ts {
function inferToTemplateLiteralType(source: Type, target: TemplateLiteralType) {
const matches = inferTypesFromTemplateLiteralType(source, target);
const types = target.types;
- for (let i = 0; i < types.length; i++) {
- inferFromTypes(matches ? matches[i] : neverType, types[i]);
+ // When the target template literal contains only placeholders (meaning that inference is intended to extract
+ // single characters and remainder strings) and inference fails to produce matches, we want to infer 'never' for
+ // each placeholder such that instantiation with the inferred value(s) produces 'never', a type for which an
+ // assignment check will fail. If we make no inferences, we'll likely end up with the constraint 'string' which,
+ // upon instantiation, would collapse all the placeholders to just 'string', and an assignment check might
+ // succeed. That would be a pointless and confusing outcome.
+ if (matches || every(target.texts, s => s.length === 0)) {
+ for (let i = 0; i < types.length; i++) {
+ inferFromTypes(matches ? matches[i] : neverType, types[i]);
+ }
}
}
@@ -22014,12 +22601,11 @@ namespace ts {
if (signature) {
const inferredCovariantType = inference.candidates ? getCovariantInference(inference, signature) : undefined;
if (inference.contraCandidates) {
- const inferredContravariantType = getContravariantInference(inference);
// If we have both co- and contra-variant inferences, we prefer the contra-variant inference
- // unless the co-variant inference is a subtype and not 'never'.
+ // unless the co-variant inference is a subtype of some contra-variant inference and not 'never'.
inferredType = inferredCovariantType && !(inferredCovariantType.flags & TypeFlags.Never) &&
- isTypeSubtypeOf(inferredCovariantType, inferredContravariantType) ?
- inferredCovariantType : inferredContravariantType;
+ some(inference.contraCandidates, t => isTypeSubtypeOf(inferredCovariantType, t)) ?
+ inferredCovariantType : getContravariantInference(inference);
}
else if (inferredCovariantType) {
inferredType = inferredCovariantType;
@@ -22116,6 +22702,11 @@ namespace ts {
case "BigInt64Array":
case "BigUint64Array":
return Diagnostics.Cannot_find_name_0_Do_you_need_to_change_your_target_library_Try_changing_the_lib_compiler_option_to_1_or_later;
+ case "await":
+ if (isCallExpression(node.parent)) {
+ return Diagnostics.Cannot_find_name_0_Did_you_mean_to_write_this_in_an_async_function;
+ }
+ // falls through
default:
if (node.parent.kind === SyntaxKind.ShorthandPropertyAssignment) {
return Diagnostics.No_value_exists_in_scope_for_the_shorthand_property_0_Either_declare_one_or_provide_an_initializer;
@@ -22137,8 +22728,7 @@ namespace ts {
getCannotFindNameDiagnosticForName(node),
node,
!isWriteOnlyAccess(node),
- /*excludeGlobals*/ false,
- /*issueSuggestions*/ true) || unknownSymbol;
+ /*excludeGlobals*/ false) || unknownSymbol;
}
return links.resolvedSymbol;
}
@@ -22159,8 +22749,11 @@ namespace ts {
function getFlowCacheKey(node: Node, declaredType: Type, initialType: Type, flowContainer: Node | undefined): string | undefined {
switch (node.kind) {
case SyntaxKind.Identifier:
- const symbol = getResolvedSymbol(node as Identifier);
- return symbol !== unknownSymbol ? `${flowContainer ? getNodeId(flowContainer) : "-1"}|${getTypeId(declaredType)}|${getTypeId(initialType)}|${getSymbolId(symbol)}` : undefined;
+ if (!isThisInTypeQuery(node)) {
+ const symbol = getResolvedSymbol(node as Identifier);
+ return symbol !== unknownSymbol ? `${flowContainer ? getNodeId(flowContainer) : "-1"}|${getTypeId(declaredType)}|${getTypeId(initialType)}|${getSymbolId(symbol)}` : undefined;
+ }
+ // falls through
case SyntaxKind.ThisKeyword:
return `0|${flowContainer ? getNodeId(flowContainer) : "-1"}|${getTypeId(declaredType)}|${getTypeId(initialType)}`;
case SyntaxKind.NonNullExpression:
@@ -22223,42 +22816,12 @@ namespace ts {
return false;
}
- // Given a source x, check if target matches x or is an && operation with an operand that matches x.
- function containsTruthyCheck(source: Node, target: Node): boolean {
- return isMatchingReference(source, target) ||
- (target.kind === SyntaxKind.BinaryExpression && (target as BinaryExpression).operatorToken.kind === SyntaxKind.AmpersandAmpersandToken &&
- (containsTruthyCheck(source, (target as BinaryExpression).left) || containsTruthyCheck(source, (target as BinaryExpression).right)));
- }
-
- function getPropertyAccess(expr: Expression) {
- if (isAccessExpression(expr)) {
- return expr;
- }
- if (isIdentifier(expr)) {
- const symbol = getResolvedSymbol(expr);
- if (isConstVariable(symbol)) {
- const declaration = symbol.valueDeclaration!;
- // Given 'const x = obj.kind', allow 'x' as an alias for 'obj.kind'
- if (isVariableDeclaration(declaration) && !declaration.type && declaration.initializer && isAccessExpression(declaration.initializer)) {
- return declaration.initializer;
- }
- // Given 'const { kind: x } = obj', allow 'x' as an alias for 'obj.kind'
- if (isBindingElement(declaration) && !declaration.initializer) {
- const parent = declaration.parent.parent;
- if (isVariableDeclaration(parent) && !parent.type && parent.initializer && (isIdentifier(parent.initializer) || isAccessExpression(parent.initializer))) {
- return declaration;
- }
- }
- }
- }
- return undefined;
- }
-
- function getAccessedPropertyName(access: AccessExpression | BindingElement): __String | undefined {
+ function getAccessedPropertyName(access: AccessExpression | BindingElement | ParameterDeclaration): __String | undefined {
let propertyName;
return access.kind === SyntaxKind.PropertyAccessExpression ? access.name.escapedText :
access.kind === SyntaxKind.ElementAccessExpression && isStringOrNumericLiteralLike(access.argumentExpression) ? escapeLeadingUnderscores(access.argumentExpression.text) :
access.kind === SyntaxKind.BindingElement && (propertyName = getDestructuringPropertyName(access)) ? escapeLeadingUnderscores(propertyName) :
+ access.kind === SyntaxKind.Parameter ? ("" + access.parent.parameters.indexOf(access)) as __String :
undefined;
}
@@ -22289,7 +22852,7 @@ namespace ts {
if ((prop as TransientSymbol).isDiscriminantProperty === undefined) {
(prop as TransientSymbol).isDiscriminantProperty =
((prop as TransientSymbol).checkFlags & CheckFlags.Discriminant) === CheckFlags.Discriminant &&
- !maybeTypeOfKind(getTypeOfSymbol(prop), TypeFlags.Instantiable & ~TypeFlags.TemplateLiteral);
+ !isGenericType(getTypeOfSymbol(prop));
}
return !!(prop as TransientSymbol).isDiscriminantProperty;
}
@@ -22348,8 +22911,9 @@ namespace ts {
// constituent types keyed by the literal types of the property by that name in each constituent type.
function getKeyPropertyName(unionType: UnionType): __String | undefined {
const types = unionType.types;
- // We only construct maps for large unions with non-primitive constituents.
- if (types.length < 10 || getObjectFlags(unionType) & ObjectFlags.PrimitiveUnion) {
+ // We only construct maps for unions with many non-primitive constituents.
+ if (types.length < 10 || getObjectFlags(unionType) & ObjectFlags.PrimitiveUnion ||
+ countWhere(types, t => !!(t.flags & (TypeFlags.Object | TypeFlags.InstantiableNonPrimitive))) < 10) {
return undefined;
}
if (unionType.keyPropertyName === undefined) {
@@ -22383,7 +22947,7 @@ namespace ts {
const keyPropertyName = getKeyPropertyName(unionType);
const propNode = keyPropertyName && find(node.properties, p => p.symbol && p.kind === SyntaxKind.PropertyAssignment &&
p.symbol.escapedName === keyPropertyName && isPossiblyDiscriminantValue(p.initializer));
- const propType = propNode && getTypeOfExpression((propNode as PropertyAssignment).initializer);
+ const propType = propNode && getContextFreeTypeOfExpression((propNode as PropertyAssignment).initializer);
return propType && getConstituentTypeForKeyType(unionType, propType);
}
@@ -22494,7 +23058,10 @@ namespace ts {
(type === falseType || type === regularFalseType) ? TypeFacts.FalseStrictFacts : TypeFacts.TrueStrictFacts :
(type === falseType || type === regularFalseType) ? TypeFacts.FalseFacts : TypeFacts.TrueFacts;
}
- if (flags & TypeFlags.Object && !ignoreObjects) {
+ if (flags & TypeFlags.Object) {
+ if (ignoreObjects) {
+ return TypeFacts.AndFactsMask; // This is the identity element for computing type facts of intersection.
+ }
return getObjectFlags(type) & ObjectFlags.Anonymous && isEmptyObjectType(type as ObjectType) ?
strictNullChecks ? TypeFacts.EmptyObjectStrictFacts : TypeFacts.EmptyObjectFacts :
isFunctionObjectType(type as ObjectType) ?
@@ -22527,11 +23094,24 @@ namespace ts {
// When an intersection contains a primitive type we ignore object type constituents as they are
// presumably type tags. For example, in string & { __kind__: "name" } we ignore the object type.
ignoreObjects ||= maybeTypeOfKind(type, TypeFlags.Primitive);
- return reduceLeft((type as UnionType).types, (facts, t) => facts & getTypeFacts(t, ignoreObjects), TypeFacts.All);
+ return getIntersectionTypeFacts(type as IntersectionType, ignoreObjects);
}
return TypeFacts.All;
}
+ function getIntersectionTypeFacts(type: IntersectionType, ignoreObjects: boolean): TypeFacts {
+ // When computing the type facts of an intersection type, certain type facts are computed as `and`
+ // and others are computed as `or`.
+ let oredFacts = TypeFacts.None;
+ let andedFacts = TypeFacts.All;
+ for (const t of type.types) {
+ const f = getTypeFacts(t, ignoreObjects);
+ oredFacts |= f;
+ andedFacts &= f;
+ }
+ return oredFacts & TypeFacts.OrFactsMask | andedFacts & TypeFacts.AndFactsMask;
+ }
+
function getTypeWithFacts(type: Type, include: TypeFacts) {
return filterType(type, t => (getTypeFacts(t) & include) !== 0);
}
@@ -22840,23 +23420,21 @@ namespace ts {
mapType(type, mapper);
}
- function getConstituentCount(type: Type) {
- return type.flags & TypeFlags.UnionOrIntersection ? (type as UnionOrIntersectionType).types.length : 1;
- }
-
function extractTypesOfKind(type: Type, kind: TypeFlags) {
return filterType(type, t => (t.flags & kind) !== 0);
}
- // Return a new type in which occurrences of the string and number primitive types in
- // typeWithPrimitives have been replaced with occurrences of string literals and numeric
- // literals in typeWithLiterals, respectively.
+ // Return a new type in which occurrences of the string, number and bigint primitives and placeholder template
+ // literal types in typeWithPrimitives have been replaced with occurrences of compatible and more specific types
+ // from typeWithLiterals. This is essentially a limited form of intersection between the two types. We avoid a
+ // true intersection because it is more costly and, when applied to union types, generates a large number of
+ // types we don't actually care about.
function replacePrimitivesWithLiterals(typeWithPrimitives: Type, typeWithLiterals: Type) {
- if (isTypeSubsetOf(stringType, typeWithPrimitives) && maybeTypeOfKind(typeWithLiterals, TypeFlags.StringLiteral) ||
- isTypeSubsetOf(numberType, typeWithPrimitives) && maybeTypeOfKind(typeWithLiterals, TypeFlags.NumberLiteral) ||
- isTypeSubsetOf(bigintType, typeWithPrimitives) && maybeTypeOfKind(typeWithLiterals, TypeFlags.BigIntLiteral)) {
+ if (maybeTypeOfKind(typeWithPrimitives, TypeFlags.String | TypeFlags.TemplateLiteral | TypeFlags.Number | TypeFlags.BigInt) &&
+ maybeTypeOfKind(typeWithLiterals, TypeFlags.StringLiteral | TypeFlags.TemplateLiteral | TypeFlags.StringMapping | TypeFlags.NumberLiteral | TypeFlags.BigIntLiteral)) {
return mapType(typeWithPrimitives, t =>
- t.flags & TypeFlags.String ? extractTypesOfKind(typeWithLiterals, TypeFlags.String | TypeFlags.StringLiteral) :
+ t.flags & TypeFlags.String ? extractTypesOfKind(typeWithLiterals, TypeFlags.String | TypeFlags.StringLiteral | TypeFlags.TemplateLiteral | TypeFlags.StringMapping) :
+ isPatternLiteralType(t) && !maybeTypeOfKind(typeWithLiterals, TypeFlags.String | TypeFlags.TemplateLiteral | TypeFlags.StringMapping) ? extractTypesOfKind(typeWithLiterals, TypeFlags.StringLiteral) :
t.flags & TypeFlags.Number ? extractTypesOfKind(typeWithLiterals, TypeFlags.Number | TypeFlags.NumberLiteral) :
t.flags & TypeFlags.BigInt ? extractTypesOfKind(typeWithLiterals, TypeFlags.BigInt | TypeFlags.BigIntLiteral) : t);
}
@@ -22951,10 +23529,10 @@ namespace ts {
return isLengthPushOrUnshift || isElementAssignment;
}
- function isDeclarationWithExplicitTypeAnnotation(declaration: Declaration) {
- return (declaration.kind === SyntaxKind.VariableDeclaration || declaration.kind === SyntaxKind.Parameter ||
- declaration.kind === SyntaxKind.PropertyDeclaration || declaration.kind === SyntaxKind.PropertySignature) &&
- !!getEffectiveTypeAnnotationNode(declaration as VariableDeclaration | ParameterDeclaration | PropertyDeclaration | PropertySignature);
+ function isDeclarationWithExplicitTypeAnnotation(node: Declaration) {
+ return (isVariableDeclaration(node) || isPropertyDeclaration(node) || isPropertySignature(node) || isParameter(node)) &&
+ !!(getEffectiveTypeAnnotationNode(node) ||
+ isInJSFile(node) && hasInitializer(node) && node.initializer && isFunctionExpressionOrArrowFunction(node.initializer) && getEffectiveReturnTypeNode(node.initializer));
}
function getExplicitTypeOfSymbol(symbol: Symbol, diagnostic?: Diagnostic) {
@@ -23208,9 +23786,10 @@ namespace ts {
function isConstantReference(node: Node): boolean {
switch (node.kind) {
- case SyntaxKind.Identifier:
+ case SyntaxKind.Identifier: {
const symbol = getResolvedSymbol(node as Identifier);
- return isConstVariable(symbol) || !!symbol.valueDeclaration && getRootDeclaration(symbol.valueDeclaration).kind === SyntaxKind.Parameter && !isParameterAssigned(symbol);
+ return isConstVariable(symbol) || isParameterOrCatchClauseVariable(symbol) && !isSymbolAssigned(symbol);
+ }
case SyntaxKind.PropertyAccessExpression:
case SyntaxKind.ElementAccessExpression:
// The resolvedSymbol property is initialized by checkPropertyAccess or checkElementAccess before we get here.
@@ -23219,20 +23798,19 @@ namespace ts {
return false;
}
- function getFlowTypeOfReference(reference: Node, declaredType: Type, initialType = declaredType, flowContainer?: Node) {
+ function getFlowTypeOfReference(reference: Node, declaredType: Type, initialType = declaredType, flowContainer?: Node, flowNode = reference.flowNode) {
let key: string | undefined;
let isKeySet = false;
let flowDepth = 0;
- let inlineLevel = 0;
if (flowAnalysisDisabled) {
return errorType;
}
- if (!reference.flowNode) {
+ if (!flowNode) {
return declaredType;
}
flowInvocationCount++;
const sharedFlowStart = sharedFlowCount;
- const evolvedType = getTypeFromFlowType(getTypeAtFlowNode(reference.flowNode));
+ const evolvedType = getTypeFromFlowType(getTypeAtFlowNode(flowNode));
sharedFlowCount = sharedFlowStart;
// When the reference is 'x' in an 'x.length', 'x.push(value)', 'x.unshift(value)' or x[n] = value' operation,
// we give type 'any[]' to 'x' instead of using the type determined by control flow analysis such that operations
@@ -23242,7 +23820,8 @@ namespace ts {
if (resultType === unreachableNeverType || reference.parent && reference.parent.kind === SyntaxKind.NonNullExpression && !(resultType.flags & TypeFlags.Never) && getTypeWithFacts(resultType, TypeFacts.NEUndefinedOrNull).flags & TypeFlags.Never) {
return declaredType;
}
- return resultType;
+ // The non-null unknown type should never escape control flow analysis.
+ return resultType === nonNullUnknownType ? unknownType : resultType;
function getOrSetCacheKey() {
if (isKeySet) {
@@ -23675,16 +24254,62 @@ namespace ts {
return result;
}
- function getDiscriminantPropertyAccess(expr: Expression, computedType: Type) {
- let access, name;
- const type = declaredType.flags & TypeFlags.Union ? declaredType : computedType;
- return type.flags & TypeFlags.Union && (access = getPropertyAccess(expr)) && (name = getAccessedPropertyName(access)) &&
- isMatchingReference(reference, isAccessExpression(access) ? access.expression : access.parent.parent.initializer!) &&
- isDiscriminantProperty(type, name) ?
- access : undefined;
+ function getCandidateDiscriminantPropertyAccess(expr: Expression) {
+ if (isBindingPattern(reference) || isFunctionExpressionOrArrowFunction(reference)) {
+ // When the reference is a binding pattern or function or arrow expression, we are narrowing a pesudo-reference in
+ // getNarrowedTypeOfSymbol. An identifier for a destructuring variable declared in the same binding pattern or
+ // parameter declared in the same parameter list is a candidate.
+ if (isIdentifier(expr)) {
+ const symbol = getResolvedSymbol(expr);
+ const declaration = symbol.valueDeclaration;
+ if (declaration && (isBindingElement(declaration) || isParameter(declaration)) && reference === declaration.parent && !declaration.initializer && !declaration.dotDotDotToken) {
+ return declaration;
+ }
+ }
+ }
+ else if (isAccessExpression(expr)) {
+ // An access expression is a candidate if the reference matches the left hand expression.
+ if (isMatchingReference(reference, expr.expression)) {
+ return expr;
+ }
+ }
+ else if (isIdentifier(expr)) {
+ const symbol = getResolvedSymbol(expr);
+ if (isConstVariable(symbol)) {
+ const declaration = symbol.valueDeclaration!;
+ // Given 'const x = obj.kind', allow 'x' as an alias for 'obj.kind'
+ if (isVariableDeclaration(declaration) && !declaration.type && declaration.initializer && isAccessExpression(declaration.initializer) &&
+ isMatchingReference(reference, declaration.initializer.expression)) {
+ return declaration.initializer;
+ }
+ // Given 'const { kind: x } = obj', allow 'x' as an alias for 'obj.kind'
+ if (isBindingElement(declaration) && !declaration.initializer) {
+ const parent = declaration.parent.parent;
+ if (isVariableDeclaration(parent) && !parent.type && parent.initializer && (isIdentifier(parent.initializer) || isAccessExpression(parent.initializer)) &&
+ isMatchingReference(reference, parent.initializer)) {
+ return declaration;
+ }
+ }
+ }
+ }
+ return undefined;
}
- function narrowTypeByDiscriminant(type: Type, access: AccessExpression | BindingElement, narrowType: (t: Type) => Type): Type {
+ function getDiscriminantPropertyAccess(expr: Expression, computedType: Type) {
+ const type = declaredType.flags & TypeFlags.Union ? declaredType : computedType;
+ if (type.flags & TypeFlags.Union) {
+ const access = getCandidateDiscriminantPropertyAccess(expr);
+ if (access) {
+ const name = getAccessedPropertyName(access);
+ if (name && isDiscriminantProperty(type, name)) {
+ return access;
+ }
+ }
+ }
+ return undefined;
+ }
+
+ function narrowTypeByDiscriminant(type: Type, access: AccessExpression | BindingElement | ParameterDeclaration, narrowType: (t: Type) => Type): Type {
const propName = getAccessedPropertyName(access);
if (propName === undefined) {
return type;
@@ -23698,11 +24323,11 @@ namespace ts {
const narrowedPropType = narrowType(propType);
return filterType(type, t => {
const discriminantType = getTypeOfPropertyOrIndexSignature(t, propName);
- return !(discriminantType.flags & TypeFlags.Never) && isTypeComparableTo(discriminantType, narrowedPropType);
+ return !(narrowedPropType.flags & TypeFlags.Never) && isTypeComparableTo(narrowedPropType, discriminantType);
});
}
- function narrowTypeByDiscriminantProperty(type: Type, access: AccessExpression | BindingElement, operator: SyntaxKind, value: Expression, assumeTrue: boolean) {
+ function narrowTypeByDiscriminantProperty(type: Type, access: AccessExpression | BindingElement | ParameterDeclaration, operator: SyntaxKind, value: Expression, assumeTrue: boolean) {
if ((operator === SyntaxKind.EqualsEqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) && type.flags & TypeFlags.Union) {
const keyPropertyName = getKeyPropertyName(type as UnionType);
if (keyPropertyName && keyPropertyName === getAccessedPropertyName(access)) {
@@ -23717,7 +24342,7 @@ namespace ts {
return narrowTypeByDiscriminant(type, access, t => narrowTypeByEquality(t, operator, value, assumeTrue));
}
- function narrowTypeBySwitchOnDiscriminantProperty(type: Type, access: AccessExpression | BindingElement, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number) {
+ function narrowTypeBySwitchOnDiscriminantProperty(type: Type, access: AccessExpression | BindingElement | ParameterDeclaration, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number) {
if (clauseStart < clauseEnd && type.flags & TypeFlags.Union && getKeyPropertyName(type as UnionType) === getAccessedPropertyName(access)) {
const clauseTypes = getSwitchClauseTypes(switchStatement).slice(clauseStart, clauseEnd);
const candidate = getUnionType(map(clauseTypes, t => getConstituentTypeForKeyType(type as UnionType, t) || unknownType));
@@ -23730,7 +24355,8 @@ namespace ts {
function narrowTypeByTruthiness(type: Type, expr: Expression, assumeTrue: boolean): Type {
if (isMatchingReference(reference, expr)) {
- return getTypeWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy);
+ return type.flags & TypeFlags.Unknown && assumeTrue ? nonNullUnknownType :
+ getTypeWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy);
}
if (strictNullChecks && assumeTrue && optionalChainContainsReference(expr, reference)) {
type = getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull);
@@ -23812,6 +24438,9 @@ namespace ts {
case SyntaxKind.InstanceOfKeyword:
return narrowTypeByInstanceof(type, expr, assumeTrue);
case SyntaxKind.InKeyword:
+ if (isPrivateIdentifier(expr.left)) {
+ return narrowTypeByPrivateIdentifierInInExpression(type, expr, assumeTrue);
+ }
const target = getReferenceCandidate(expr.right);
const leftType = getTypeOfNode(expr.left);
if (leftType.flags & TypeFlags.StringLiteral) {
@@ -23842,6 +24471,24 @@ namespace ts {
return type;
}
+ function narrowTypeByPrivateIdentifierInInExpression(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type {
+ const target = getReferenceCandidate(expr.right);
+ if (!isMatchingReference(reference, target)) {
+ return type;
+ }
+
+ Debug.assertNode(expr.left, isPrivateIdentifier);
+ const symbol = getSymbolForPrivateIdentifierExpression(expr.left);
+ if (symbol === undefined) {
+ return type;
+ }
+ const classSymbol = symbol.parent!;
+ const targetType = hasStaticModifier(Debug.checkDefined(symbol.valueDeclaration, "should always have a declaration"))
+ ? getTypeOfSymbol(classSymbol) as InterfaceType
+ : getDeclaredTypeOfSymbol(classSymbol);
+ return getNarrowedType(type, targetType, assumeTrue, isTypeDerivedFrom);
+ }
+
function narrowTypeByOptionalChainContainment(type: Type, operator: SyntaxKind, value: Expression, assumeTrue: boolean): Type {
// We are in a branch of obj?.foo === value (or any one of the other equality operators). We narrow obj as follows:
// When operator is === and type of value excludes undefined, null and undefined is removed from type of obj in true branch.
@@ -23869,6 +24516,9 @@ namespace ts {
assumeTrue = !assumeTrue;
}
const valueType = getTypeOfExpression(value);
+ if (assumeTrue && (type.flags & TypeFlags.Unknown) && (operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsToken) && (valueType.flags & TypeFlags.Null)) {
+ return getUnionType([nullType, undefinedType]);
+ }
if ((type.flags & TypeFlags.Unknown) && assumeTrue && (operator === SyntaxKind.EqualsEqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken)) {
if (valueType.flags & (TypeFlags.Primitive | TypeFlags.NonPrimitive)) {
return valueType;
@@ -23888,7 +24538,7 @@ namespace ts {
valueType.flags & TypeFlags.Null ?
assumeTrue ? TypeFacts.EQNull : TypeFacts.NENull :
assumeTrue ? TypeFacts.EQUndefined : TypeFacts.NEUndefined;
- return getTypeWithFacts(type, facts);
+ return type.flags & TypeFlags.Unknown && facts & (TypeFacts.NENull | TypeFacts.NEUndefinedOrNull) ? nonNullUnknownType : getTypeWithFacts(type, facts);
}
if (assumeTrue) {
const filterFn: (t: Type) => boolean = operator === SyntaxKind.EqualsEqualsToken ?
@@ -23918,15 +24568,10 @@ namespace ts {
return type;
}
if (assumeTrue && type.flags & TypeFlags.Unknown && literal.text === "object") {
- // The pattern x && typeof x === 'object', where x is of type unknown, narrows x to type object. We don't
- // need to check for the reverse typeof x === 'object' && x since that already narrows correctly.
- if (typeOfExpr.parent.parent.kind === SyntaxKind.BinaryExpression) {
- const expr = typeOfExpr.parent.parent as BinaryExpression;
- if (expr.operatorToken.kind === SyntaxKind.AmpersandAmpersandToken && expr.right === typeOfExpr.parent && containsTruthyCheck(reference, expr.left)) {
- return nonPrimitiveType;
- }
- }
- return getUnionType([nonPrimitiveType, nullType]);
+ // The non-null unknown type is used to track whether a previous narrowing operation has removed the null type
+ // from the unknown type. For example, the expression `x && typeof x === 'object'` first narrows x to the non-null
+ // unknown type, and then narrows that to the non-primitive type.
+ return type === nonNullUnknownType ? nonPrimitiveType : getUnionType([nonPrimitiveType, nullType]);
}
const facts = assumeTrue ?
typeofEQFacts.get(literal.text) || TypeFacts.TypeofEQHostObject :
@@ -24180,16 +24825,7 @@ namespace ts {
function getNarrowedType(type: Type, candidate: Type, assumeTrue: boolean, isRelated: (source: Type, target: Type) => boolean) {
if (!assumeTrue) {
- return filterType(type, t => {
- if (!isRelated(t, candidate)) {
- return true;
- }
- const constraint = getBaseConstraintOfType(t);
- if (constraint && constraint !== t) {
- return !isRelated(constraint, candidate);
- }
- return false;
- });
+ return filterType(type, t => !isRelated(t, candidate));
}
// If the current type is a union type, remove all constituents that couldn't be instances of
// the candidate type. If one or more constituents remain, return a union of those.
@@ -24348,37 +24984,38 @@ namespace ts {
node.kind === SyntaxKind.PropertyDeclaration)!;
}
- // Check if a parameter is assigned anywhere within its declaring function.
- function isParameterAssigned(symbol: Symbol) {
+ // Check if a parameter or catch variable is assigned anywhere
+ function isSymbolAssigned(symbol: Symbol) {
if (!symbol.valueDeclaration) {
return false;
}
- const func = getRootDeclaration(symbol.valueDeclaration).parent as FunctionLikeDeclaration;
- const links = getNodeLinks(func);
+ const parent = getRootDeclaration(symbol.valueDeclaration).parent;
+ const links = getNodeLinks(parent);
if (!(links.flags & NodeCheckFlags.AssignmentsMarked)) {
links.flags |= NodeCheckFlags.AssignmentsMarked;
- if (!hasParentWithAssignmentsMarked(func)) {
- markParameterAssignments(func);
+ if (!hasParentWithAssignmentsMarked(parent)) {
+ markNodeAssignments(parent);
}
}
return symbol.isAssigned || false;
}
function hasParentWithAssignmentsMarked(node: Node) {
- return !!findAncestor(node.parent, node => isFunctionLike(node) && !!(getNodeLinks(node).flags & NodeCheckFlags.AssignmentsMarked));
+ return !!findAncestor(node.parent, node =>
+ (isFunctionLike(node) || isCatchClause(node)) && !!(getNodeLinks(node).flags & NodeCheckFlags.AssignmentsMarked));
}
- function markParameterAssignments(node: Node) {
+ function markNodeAssignments(node: Node) {
if (node.kind === SyntaxKind.Identifier) {
if (isAssignmentTarget(node)) {
const symbol = getResolvedSymbol(node as Identifier);
- if (symbol.valueDeclaration && getRootDeclaration(symbol.valueDeclaration).kind === SyntaxKind.Parameter) {
+ if (isParameterOrCatchClauseVariable(symbol)) {
symbol.isAssigned = true;
}
}
}
else {
- forEachChild(node, markParameterAssignments);
+ forEachChild(node, markNodeAssignments);
}
}
@@ -24412,7 +25049,7 @@ namespace ts {
return parent.kind === SyntaxKind.PropertyAccessExpression ||
parent.kind === SyntaxKind.CallExpression && (parent as CallExpression).expression === node ||
parent.kind === SyntaxKind.ElementAccessExpression && (parent as ElementAccessExpression).expression === node &&
- !(isGenericTypeWithoutNullableConstraint(type) && isGenericIndexType(getTypeOfExpression((parent as ElementAccessExpression).argumentExpression)));
+ !(someType(type, isGenericTypeWithoutNullableConstraint) && isGenericIndexType(getTypeOfExpression((parent as ElementAccessExpression).argumentExpression)));
}
function isGenericTypeWithUnionConstraint(type: Type) {
@@ -24423,12 +25060,16 @@ namespace ts {
return !!(type.flags & TypeFlags.Instantiable && !maybeTypeOfKind(getBaseConstraintOrType(type), TypeFlags.Nullable));
}
- function hasNonBindingPatternContextualTypeWithNoGenericTypes(node: Node) {
+ function hasContextualTypeWithNoGenericTypes(node: Node, checkMode: CheckMode | undefined) {
// Computing the contextual type for a child of a JSX element involves resolving the type of the
// element's tag name, so we exclude that here to avoid circularities.
+ // If check mode has `CheckMode.RestBindingElement`, we skip binding pattern contextual types,
+ // as we want the type of a rest element to be generic when possible.
const contextualType = (isIdentifier(node) || isPropertyAccessExpression(node) || isElementAccessExpression(node)) &&
!((isJsxOpeningElement(node.parent) || isJsxSelfClosingElement(node.parent)) && node.parent.tagName === node) &&
- getContextualType(node, ContextFlags.SkipBindingPatterns);
+ (checkMode && checkMode & CheckMode.RestBindingElement ?
+ getContextualType(node, ContextFlags.SkipBindingPatterns)
+ : getContextualType(node));
return contextualType && !isGenericType(contextualType);
}
@@ -24442,12 +25083,24 @@ namespace ts {
// 'string | undefined' to give control flow analysis the opportunity to narrow to type 'string'.
const substituteConstraints = !(checkMode && checkMode & CheckMode.Inferential) &&
someType(type, isGenericTypeWithUnionConstraint) &&
- (isConstraintPosition(type, reference) || hasNonBindingPatternContextualTypeWithNoGenericTypes(reference));
- return substituteConstraints ? mapType(type, t => t.flags & TypeFlags.Instantiable ? getBaseConstraintOrType(t) : t) : type;
+ (isConstraintPosition(type, reference) || hasContextualTypeWithNoGenericTypes(reference, checkMode));
+ return substituteConstraints ? mapType(type, t => t.flags & TypeFlags.Instantiable && !isMappedTypeGenericIndexedAccess(t) ? getBaseConstraintOrType(t) : t) : type;
}
function isExportOrExportExpression(location: Node) {
- return !!findAncestor(location, e => e.parent && isExportAssignment(e.parent) && e.parent.expression === e && isEntityNameExpression(e));
+ return !!findAncestor(location, n => {
+ const parent = n.parent;
+ if (parent === undefined) {
+ return "quit";
+ }
+ if (isExportAssignment(parent)) {
+ return parent.expression === n && isEntityNameExpression(n);
+ }
+ if (isExportSpecifier(parent)) {
+ return parent.name === n || parent.propertyName === n;
+ }
+ return false;
+ });
}
function markAliasReferenced(symbol: Symbol, location: Node) {
@@ -24470,7 +25123,91 @@ namespace ts {
}
}
+ function getNarrowedTypeOfSymbol(symbol: Symbol, location: Identifier) {
+ const declaration = symbol.valueDeclaration;
+ if (declaration) {
+ // If we have a non-rest binding element with no initializer declared as a const variable or a const-like
+ // parameter (a parameter for which there are no assignments in the function body), and if the parent type
+ // for the destructuring is a union type, one or more of the binding elements may represent discriminant
+ // properties, and we want the effects of conditional checks on such discriminants to affect the types of
+ // other binding elements from the same destructuring. Consider:
+ //
+ // type Action =
+ // | { kind: 'A', payload: number }
+ // | { kind: 'B', payload: string };
+ //
+ // function f({ kind, payload }: Action) {
+ // if (kind === 'A') {
+ // payload.toFixed();
+ // }
+ // if (kind === 'B') {
+ // payload.toUpperCase();
+ // }
+ // }
+ //
+ // Above, we want the conditional checks on 'kind' to affect the type of 'payload'. To facilitate this, we use
+ // the binding pattern AST instance for '{ kind, payload }' as a pseudo-reference and narrow this reference
+ // as if it occurred in the specified location. We then recompute the narrowed binding element type by
+ // destructuring from the narrowed parent type.
+ if (isBindingElement(declaration) && !declaration.initializer && !declaration.dotDotDotToken && declaration.parent.elements.length >= 2) {
+ const parent = declaration.parent.parent;
+ if (parent.kind === SyntaxKind.VariableDeclaration && getCombinedNodeFlags(declaration) & NodeFlags.Const || parent.kind === SyntaxKind.Parameter) {
+ const links = getNodeLinks(location);
+ if (!(links.flags & NodeCheckFlags.InCheckIdentifier)) {
+ links.flags |= NodeCheckFlags.InCheckIdentifier;
+ const parentType = getTypeForBindingElementParent(parent, CheckMode.Normal);
+ links.flags &= ~NodeCheckFlags.InCheckIdentifier;
+ if (parentType && parentType.flags & TypeFlags.Union && !(parent.kind === SyntaxKind.Parameter && isSymbolAssigned(symbol))) {
+ const pattern = declaration.parent;
+ const narrowedType = getFlowTypeOfReference(pattern, parentType, parentType, /*flowContainer*/ undefined, location.flowNode);
+ return getBindingElementTypeFromParentType(declaration, narrowedType);
+ }
+ }
+ }
+ }
+ // If we have a const-like parameter with no type annotation or initializer, and if the parameter is contextually
+ // typed by a signature with a single rest parameter of a union of tuple types, one or more of the parameters may
+ // represent discriminant tuple elements, and we want the effects of conditional checks on such discriminants to
+ // affect the types of other parameters in the same parameter list. Consider:
+ //
+ // type Action = [kind: 'A', payload: number] | [kind: 'B', payload: string];
+ //
+ // const f: (...args: Action) => void = (kind, payload) => {
+ // if (kind === 'A') {
+ // payload.toFixed();
+ // }
+ // if (kind === 'B') {
+ // payload.toUpperCase();
+ // }
+ // }
+ //
+ // Above, we want the conditional checks on 'kind' to affect the type of 'payload'. To facilitate this, we use
+ // the arrow function AST node for '(kind, payload) => ...' as a pseudo-reference and narrow this reference as
+ // if it occurred in the specified location. We then recompute the narrowed parameter type by indexing into the
+ // narrowed tuple type.
+ if (isParameter(declaration) && !declaration.type && !declaration.initializer && !declaration.dotDotDotToken) {
+ const func = declaration.parent;
+ if (func.parameters.length >= 2 && isContextSensitiveFunctionOrObjectLiteralMethod(func)) {
+ const contextualSignature = getContextualSignature(func);
+ if (contextualSignature && contextualSignature.parameters.length === 1 && signatureHasRestParameter(contextualSignature)) {
+ const restType = getTypeOfSymbol(contextualSignature.parameters[0]);
+ if (restType.flags & TypeFlags.Union && everyType(restType, isTupleType) && !isSymbolAssigned(symbol)) {
+ const narrowedType = getFlowTypeOfReference(func, restType, restType, /*flowContainer*/ undefined, location.flowNode);
+ const index = func.parameters.indexOf(declaration) - (getThisParameter(func) ? 1 : 0);
+ return getIndexedAccessType(narrowedType, getNumberLiteralType(index));
+ }
+ }
+ }
+ }
+ }
+ return getTypeOfSymbol(symbol);
+ }
+
function checkIdentifier(node: Identifier, checkMode: CheckMode | undefined): Type {
+ if (isThisInTypeQuery(node)) {
+ return checkThisExpression(node);
+ }
+
const symbol = getResolvedSymbol(node);
if (symbol === unknownSymbol) {
return errorType;
@@ -24509,9 +25246,9 @@ namespace ts {
}
const localOrExportSymbol = getExportSymbolOfValueSymbolIfExported(symbol);
- const sourceSymbol = localOrExportSymbol.flags & SymbolFlags.Alias ? resolveAlias(localOrExportSymbol) : localOrExportSymbol;
- if (sourceSymbol.declarations && getDeclarationNodeFlagsFromSymbol(sourceSymbol) & NodeFlags.Deprecated && isUncalledFunctionReference(node, sourceSymbol)) {
- addDeprecatedSuggestion(node, sourceSymbol.declarations, node.escapedText as string);
+ const targetSymbol = checkDeprecatedAliasedSymbol(localOrExportSymbol, node);
+ if (isDeprecatedSymbol(targetSymbol) && isUncalledFunctionReference(node, targetSymbol) && targetSymbol.declarations) {
+ addDeprecatedSuggestion(node, targetSymbol.declarations, node.escapedText as string);
}
let declaration = localOrExportSymbol.valueDeclaration;
@@ -24553,7 +25290,7 @@ namespace ts {
checkNestedBlockScopedBinding(node, symbol);
- let type = getTypeOfSymbol(localOrExportSymbol);
+ let type = getNarrowedTypeOfSymbol(localOrExportSymbol, node);
const assignmentKind = getAssignmentTargetKind(node);
if (assignmentKind) {
@@ -24616,7 +25353,7 @@ namespace ts {
// analysis to include the immediately enclosing function.
while (flowContainer !== declarationContainer && (flowContainer.kind === SyntaxKind.FunctionExpression ||
flowContainer.kind === SyntaxKind.ArrowFunction || isObjectLiteralOrClassExpressionMethodOrAccessor(flowContainer)) &&
- (isConstVariable(localOrExportSymbol) && type !== autoArrayType || isParameter && !isParameterAssigned(localOrExportSymbol))) {
+ (isConstVariable(localOrExportSymbol) && type !== autoArrayType || isParameter && !isSymbolAssigned(localOrExportSymbol))) {
flowContainer = getControlFlowContainer(flowContainer);
}
// We only look for uninitialized variables in strict null checking mode, and only when we can analyze
@@ -25355,7 +26092,7 @@ namespace ts {
const parent = declaration.parent.parent;
const name = declaration.propertyName || declaration.name;
const parentType = getContextualTypeForVariableLikeDeclaration(parent) ||
- parent.kind !== SyntaxKind.BindingElement && parent.initializer && checkDeclarationInitializer(parent);
+ parent.kind !== SyntaxKind.BindingElement && parent.initializer && checkDeclarationInitializer(parent, declaration.dotDotDotToken ? CheckMode.RestBindingElement : CheckMode.Normal);
if (!parentType || isBindingPattern(name) || isComputedNonLiteralName(name)) return undefined;
if (parent.name.kind === SyntaxKind.ArrayBindingPattern) {
const index = indexOfNode(declaration.parent.elements, declaration);
@@ -25390,8 +26127,7 @@ namespace ts {
if (result) {
return result;
}
- if (!(contextFlags! & ContextFlags.SkipBindingPatterns) && isBindingPattern(declaration.name)) {
- // This is less a contextual type and more an implied shape - in some cases, this may be undesirable
+ if (!(contextFlags! & ContextFlags.SkipBindingPatterns) && isBindingPattern(declaration.name)) { // This is less a contextual type and more an implied shape - in some cases, this may be undesirable
return getTypeFromBindingPattern(declaration.name, /*includePatternInType*/ true, /*reportErrors*/ false);
}
}
@@ -25415,7 +26151,8 @@ namespace ts {
}
if (functionFlags & FunctionFlags.Async) { // Async function or AsyncGenerator function
- const contextualAwaitedType = mapType(contextualReturnType, getAwaitedType);
+ // Get the awaited type without the `Awaited` alias
+ const contextualAwaitedType = mapType(contextualReturnType, getAwaitedTypeNoAlias);
return contextualAwaitedType && getUnionType([contextualAwaitedType, createPromiseLikeType(contextualAwaitedType)]);
}
@@ -25428,7 +26165,7 @@ namespace ts {
function getContextualTypeForAwaitOperand(node: AwaitExpression, contextFlags?: ContextFlags): Type | undefined {
const contextualType = getContextualType(node, contextFlags);
if (contextualType) {
- const contextualAwaitedType = getAwaitedType(contextualType);
+ const contextualAwaitedType = getAwaitedTypeNoAlias(contextualType);
return contextualAwaitedType && getUnionType([contextualAwaitedType, createPromiseLikeType(contextualAwaitedType)]);
}
return undefined;
@@ -25504,6 +26241,12 @@ namespace ts {
}
function getContextualTypeForArgumentAtIndex(callTarget: CallLikeExpression, argIndex: number): Type {
+ if (isImportCall(callTarget)) {
+ return argIndex === 0 ? stringType :
+ argIndex === 1 ? getGlobalImportCallOptionsType(/*reportErrors*/ false) :
+ anyType;
+ }
+
// If we're already in the process of resolving the given signature, don't resolve again as
// that could cause infinite recursion. Instead, return anySignature.
const signature = getNodeLinks(callTarget).resolvedSignature === resolvingSignature ? resolvingSignature : getResolvedSignature(callTarget);
@@ -25685,12 +26428,12 @@ namespace ts {
return !!(getCheckFlags(symbol) & CheckFlags.Mapped && !(symbol as MappedSymbol).type && findResolutionCycleStartIndex(symbol, TypeSystemPropertyName.Type) >= 0);
}
- function getTypeOfPropertyOfContextualType(type: Type, name: __String) {
+ function getTypeOfPropertyOfContextualType(type: Type, name: __String, nameType?: Type) {
return mapType(type, t => {
if (isGenericMappedType(t)) {
const constraint = getConstraintTypeFromMappedType(t);
const constraintOfConstraint = getBaseConstraintOfType(constraint) || constraint;
- const propertyNameType = getStringLiteralType(unescapeLeadingUnderscores(name));
+ const propertyNameType = nameType || getStringLiteralType(unescapeLeadingUnderscores(name));
if (isTypeAssignableTo(propertyNameType, constraintOfConstraint)) {
return substituteIndexedMappedType(t, propertyNameType);
}
@@ -25706,7 +26449,7 @@ namespace ts {
return restType;
}
}
- return findApplicableIndexInfo(getIndexInfosOfStructuredType(t), getStringLiteralType(unescapeLeadingUnderscores(name)))?.type;
+ return findApplicableIndexInfo(getIndexInfosOfStructuredType(t), nameType || getStringLiteralType(unescapeLeadingUnderscores(name)))?.type;
}
return undefined;
}, /*noReductions*/ true);
@@ -25736,7 +26479,8 @@ namespace ts {
// For a (non-symbol) computed property, there is no reason to look up the name
// in the type. It will just be "__computed", which does not appear in any
// SymbolTable.
- return getTypeOfPropertyOfContextualType(type, getSymbolOfNode(element).escapedName);
+ const symbol = getSymbolOfNode(element);
+ return getTypeOfPropertyOfContextualType(type, symbol.escapedName, getSymbolLinks(symbol).nameType);
}
if (element.name) {
const nameType = getLiteralTypeFromPropertyName(element.name);
@@ -25857,7 +26601,7 @@ namespace ts {
concatenate(
map(
filter(node.properties, p => !!p.symbol && p.kind === SyntaxKind.JsxAttribute && isDiscriminantProperty(contextualType, p.symbol.escapedName) && (!p.initializer || isPossiblyDiscriminantValue(p.initializer))),
- prop => ([!(prop as JsxAttribute).initializer ? (() => trueType) : (() => checkExpression((prop as JsxAttribute).initializer!)), prop.symbol.escapedName] as [() => Type, __String])
+ prop => ([!(prop as JsxAttribute).initializer ? (() => trueType) : (() => getContextFreeTypeOfExpression((prop as JsxAttribute).initializer!)), prop.symbol.escapedName] as [() => Type, __String])
),
map(
filter(getPropertiesOfType(contextualType), s => !!(s.flags & SymbolFlags.Optional) && !!node?.symbol?.members && !node.symbol.members.has(s.escapedName) && isDiscriminantProperty(contextualType, s.escapedName)),
@@ -25964,10 +26708,6 @@ namespace ts {
case SyntaxKind.AwaitExpression:
return getContextualTypeForAwaitOperand(parent as AwaitExpression, contextFlags);
case SyntaxKind.CallExpression:
- if ((parent as CallExpression).expression.kind === SyntaxKind.ImportKeyword) {
- return stringType;
- }
- /* falls through */
case SyntaxKind.NewExpression:
return getContextualTypeForArgument(parent as CallExpression | NewExpression, node);
case SyntaxKind.TypeAssertionExpression:
@@ -26040,7 +26780,7 @@ namespace ts {
let propsType = getTypeOfFirstParameterOfSignatureWithFallback(sig, unknownType);
propsType = getJsxManagedAttributesFromLocatedAttributes(context, getJsxNamespaceAt(context), propsType);
const intrinsicAttribs = getJsxType(JsxNames.IntrinsicAttributes, context);
- if (intrinsicAttribs !== errorType) {
+ if (!isErrorType(intrinsicAttribs)) {
propsType = intersectTypes(intrinsicAttribs, propsType);
}
return propsType;
@@ -26139,7 +26879,7 @@ namespace ts {
// Normal case -- add in IntrinsicClassElements and IntrinsicElements
let apparentAttributesType = attributesType;
const intrinsicClassAttribs = getJsxType(JsxNames.IntrinsicClassAttributes, context);
- if (intrinsicClassAttribs !== errorType) {
+ if (!isErrorType(intrinsicClassAttribs)) {
const typeParams = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(intrinsicClassAttribs.symbol);
const hostClassType = getReturnTypeOfSignature(sig);
apparentAttributesType = intersectTypes(
@@ -26151,7 +26891,7 @@ namespace ts {
}
const intrinsicAttribs = getJsxType(JsxNames.IntrinsicAttributes, context);
- if (intrinsicAttribs !== errorType) {
+ if (!isErrorType(intrinsicAttribs)) {
apparentAttributesType = intersectTypes(intrinsicAttribs, apparentAttributesType);
}
@@ -26279,10 +27019,6 @@ namespace ts {
return !hasEffectiveRestParameter(signature) && getParameterCount(signature) < targetParameterCount;
}
- function isFunctionExpressionOrArrowFunction(node: Node): node is FunctionExpression | ArrowFunction {
- return node.kind === SyntaxKind.FunctionExpression || node.kind === SyntaxKind.ArrowFunction;
- }
-
function getContextualSignatureForFunctionLikeDeclaration(node: FunctionLikeDeclaration): Signature | undefined {
// Only function expressions, arrow functions, and object literal methods are contextually typed.
return isFunctionExpressionOrArrowFunction(node) || isObjectLiteralMethod(node)
@@ -26450,34 +27186,14 @@ namespace ts {
return isTypeAssignableToKind(checkComputedPropertyName(name), TypeFlags.NumberLike);
}
- function isNumericLiteralName(name: string | __String) {
- // The intent of numeric names is that
- // - they are names with text in a numeric form, and that
- // - setting properties/indexing with them is always equivalent to doing so with the numeric literal 'numLit',
- // acquired by applying the abstract 'ToNumber' operation on the name's text.
- //
- // The subtlety is in the latter portion, as we cannot reliably say that anything that looks like a numeric literal is a numeric name.
- // In fact, it is the case that the text of the name must be equal to 'ToString(numLit)' for this to hold.
- //
- // Consider the property name '"0xF00D"'. When one indexes with '0xF00D', they are actually indexing with the value of 'ToString(0xF00D)'
- // according to the ECMAScript specification, so it is actually as if the user indexed with the string '"61453"'.
- // Thus, the text of all numeric literals equivalent to '61543' such as '0xF00D', '0xf00D', '0170015', etc. are not valid numeric names
- // because their 'ToString' representation is not equal to their original text.
- // This is motivated by ECMA-262 sections 9.3.1, 9.8.1, 11.1.5, and 11.2.1.
- //
- // Here, we test whether 'ToString(ToNumber(name))' is exactly equal to 'name'.
- // The '+' prefix operator is equivalent here to applying the abstract ToNumber operation.
- // Applying the 'toString()' method on a number gives us the abstract ToString operation on a number.
- //
- // Note that this accepts the values 'Infinity', '-Infinity', and 'NaN', and that this is intentional.
- // This is desired behavior, because when indexing with them as numeric entities, you are indexing
- // with the strings '"Infinity"', '"-Infinity"', and '"NaN"' respectively.
- return (+name).toString() === name;
- }
-
function checkComputedPropertyName(node: ComputedPropertyName): Type {
const links = getNodeLinks(node.expression);
if (!links.resolvedType) {
+ if ((isTypeLiteralNode(node.parent.parent) || isClassLike(node.parent.parent) || isInterfaceDeclaration(node.parent.parent))
+ && isBinaryExpression(node.expression) && node.expression.operatorToken.kind === SyntaxKind.InKeyword
+ && node.parent.kind !== SyntaxKind.GetAccessor && node.parent.kind !== SyntaxKind.SetAccessor) {
+ return links.resolvedType = errorType;
+ }
links.resolvedType = checkExpression(node.expression);
// The computed property name of a non-static class field within a loop must be stored in a block-scoped binding.
// (It needs to be bound at class evaluation time.)
@@ -26662,7 +27378,7 @@ namespace ts {
checkSpreadPropOverrides(mergedType, allPropertiesTable, memberDecl);
}
offset = propertiesArray.length;
- if (spread === errorType) {
+ if (isErrorType(spread)) {
continue;
}
spread = getSpreadType(spread, mergedType, node.symbol, objectFlags, inConstContext);
@@ -26722,7 +27438,7 @@ namespace ts {
}
}
- if (spread === errorType) {
+ if (isErrorType(spread)) {
return errorType;
}
@@ -26761,15 +27477,9 @@ namespace ts {
}
function isValidSpreadType(type: Type): boolean {
- if (type.flags & TypeFlags.Instantiable) {
- const constraint = getBaseConstraintOfType(type);
- if (constraint !== undefined) {
- return isValidSpreadType(constraint);
- }
- }
- return !!(type.flags & (TypeFlags.Any | TypeFlags.NonPrimitive | TypeFlags.Object | TypeFlags.InstantiableNonPrimitive) ||
- getFalsyFlags(type) & TypeFlags.DefinitelyFalsy && isValidSpreadType(removeDefinitelyFalsyTypes(type)) ||
- type.flags & TypeFlags.UnionOrIntersection && every((type as UnionOrIntersectionType).types, isValidSpreadType));
+ const t = removeDefinitelyFalsyTypes(mapType(type, getBaseConstraintOrType));
+ return !!(t.flags & (TypeFlags.Any | TypeFlags.NonPrimitive | TypeFlags.Object | TypeFlags.InstantiableNonPrimitive) ||
+ t.flags & TypeFlags.UnionOrIntersection && every((t as UnionOrIntersectionType).types, isValidSpreadType));
}
function checkJsxSelfClosingElementDeferred(node: JsxSelfClosingElement) {
@@ -27016,7 +27726,7 @@ namespace ts {
const links = getNodeLinks(node);
if (!links.resolvedSymbol) {
const intrinsicElementsType = getJsxType(JsxNames.IntrinsicElements, node);
- if (intrinsicElementsType !== errorType) {
+ if (!isErrorType(intrinsicElementsType)) {
// Property case
if (!isIdentifier(node.tagName)) return Debug.fail();
const intrinsicProp = getPropertyOfType(intrinsicElementsType, node.tagName.escapedText);
@@ -27190,7 +27900,7 @@ namespace ts {
// var CustomTag: "h1" = "h1";
// Hello World
const intrinsicElementsType = getJsxType(JsxNames.IntrinsicElements, location);
- if (intrinsicElementsType !== errorType) {
+ if (!isErrorType(intrinsicElementsType)) {
const stringLiteralTypeName = type.value;
const intrinsicProp = getPropertyOfType(intrinsicElementsType, escapeLeadingUnderscores(stringLiteralTypeName));
if (intrinsicProp) {
@@ -27262,7 +27972,7 @@ namespace ts {
function getJsxElementClassTypeAt(location: Node): Type | undefined {
const type = getJsxType(JsxNames.ElementClass, location);
- if (type === errorType) return undefined;
+ if (isErrorType(type)) return undefined;
return type;
}
@@ -27302,7 +28012,7 @@ namespace ts {
const isNodeOpeningLikeElement = isJsxOpeningLikeElement(node);
if (isNodeOpeningLikeElement) {
- checkGrammarJsxElement(node as JsxOpeningLikeElement);
+ checkGrammarJsxElement(node);
}
checkJsxPreconditions(node);
@@ -27312,7 +28022,7 @@ namespace ts {
// And if there is no reactNamespace/jsxFactory's symbol in scope when targeting React emit, we should issue an error.
const jsxFactoryRefErr = diagnostics && compilerOptions.jsx === JsxEmit.React ? Diagnostics.Cannot_find_name_0 : undefined;
const jsxFactoryNamespace = getJsxNamespace(node);
- const jsxFactoryLocation = isNodeOpeningLikeElement ? (node as JsxOpeningLikeElement).tagName : node;
+ const jsxFactoryLocation = isNodeOpeningLikeElement ? node.tagName : node;
// allow null as jsxFragmentFactory
let jsxFactorySym: Symbol | undefined;
@@ -27330,12 +28040,21 @@ namespace ts {
markAliasSymbolAsReferenced(jsxFactorySym);
}
}
+
+ // For JsxFragment, mark jsx pragma as referenced via resolveName
+ if (isJsxOpeningFragment(node)) {
+ const file = getSourceFileOfNode(node);
+ const localJsxNamespace = getLocalJsxNamespace(file);
+ if (localJsxNamespace) {
+ resolveName(jsxFactoryLocation, localJsxNamespace, SymbolFlags.Value, jsxFactoryRefErr, localJsxNamespace, /*isUse*/ true);
+ }
+ }
}
if (isNodeOpeningLikeElement) {
- const jsxOpeningLikeNode = node as JsxOpeningLikeElement;
+ const jsxOpeningLikeNode = node ;
const sig = getResolvedSignature(jsxOpeningLikeNode);
- checkDeprecatedSignature(sig, node as JsxOpeningLikeElement);
+ checkDeprecatedSignature(sig, node);
checkJsxReturnAssignableToAppropriateBound(getJsxReferenceKind(jsxOpeningLikeNode), getReturnTypeOfSignature(sig), jsxOpeningLikeNode);
}
}
@@ -27631,7 +28350,7 @@ namespace ts {
function checkNonNullNonVoidType(type: Type, node: Node): Type {
const nonNullType = checkNonNullType(type, node);
- if (nonNullType !== errorType && nonNullType.flags & TypeFlags.Void) {
+ if (nonNullType.flags & TypeFlags.Void) {
error(node, Diagnostics.Object_is_possibly_undefined);
}
return nonNullType;
@@ -27672,6 +28391,46 @@ namespace ts {
}
}
+ function checkGrammarPrivateIdentifierExpression(privId: PrivateIdentifier): boolean {
+ if (!getContainingClass(privId)) {
+ return grammarErrorOnNode(privId, Diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies);
+ }
+
+ if (!isForInStatement(privId.parent)) {
+ if (!isExpressionNode(privId)) {
+ return grammarErrorOnNode(privId, Diagnostics.Private_identifiers_are_only_allowed_in_class_bodies_and_may_only_be_used_as_part_of_a_class_member_declaration_property_access_or_on_the_left_hand_side_of_an_in_expression);
+ }
+
+ const isInOperation = isBinaryExpression(privId.parent) && privId.parent.operatorToken.kind === SyntaxKind.InKeyword;
+ if (!getSymbolForPrivateIdentifierExpression(privId) && !isInOperation) {
+ return grammarErrorOnNode(privId, Diagnostics.Cannot_find_name_0, idText(privId));
+ }
+ }
+
+ return false;
+ }
+
+ function checkPrivateIdentifierExpression(privId: PrivateIdentifier): Type {
+ checkGrammarPrivateIdentifierExpression(privId);
+ const symbol = getSymbolForPrivateIdentifierExpression(privId);
+ if (symbol) {
+ markPropertyAsReferenced(symbol, /* nodeForCheckWriteOnly: */ undefined, /* isThisAccess: */ false);
+ }
+ return anyType;
+ }
+
+ function getSymbolForPrivateIdentifierExpression(privId: PrivateIdentifier): Symbol | undefined {
+ if (!isExpressionNode(privId)) {
+ return undefined;
+ }
+
+ const links = getNodeLinks(privId);
+ if (links.resolvedSymbol === undefined) {
+ links.resolvedSymbol = lookupSymbolForPrivateIdentifierDeclaration(privId.escapedText, privId);
+ }
+ return links.resolvedSymbol;
+ }
+
function getPrivateIdentifierPropertyOfType(leftType: Type, lexicallyScopedIdentifier: Symbol): Symbol | undefined {
return getPropertyOfType(leftType, lexicallyScopedIdentifier.escapedName);
}
@@ -27763,32 +28522,9 @@ namespace ts {
grammarErrorOnNode(right, Diagnostics.Cannot_assign_to_private_method_0_Private_methods_are_not_writable, idText(right));
}
- if (lexicallyScopedSymbol?.valueDeclaration && (compilerOptions.target === ScriptTarget.ESNext && !useDefineForClassFields)) {
- const lexicalClass = getContainingClass(lexicallyScopedSymbol.valueDeclaration);
- const parentStaticFieldInitializer = findAncestor(node, (n) => {
- if (n === lexicalClass) return "quit";
- if (isPropertyDeclaration(n.parent) && hasStaticModifier(n.parent) && n.parent.initializer === n && n.parent.parent === lexicalClass) {
- return true;
- }
- return false;
- });
- if (parentStaticFieldInitializer) {
- const parentStaticFieldInitializerSymbol = getSymbolOfNode(parentStaticFieldInitializer.parent);
- Debug.assert(parentStaticFieldInitializerSymbol, "Initializer without declaration symbol");
- const diagnostic = error(node,
- Diagnostics.Property_0_may_not_be_used_in_a_static_property_s_initializer_in_the_same_class_when_target_is_esnext_and_useDefineForClassFields_is_false,
- symbolName(lexicallyScopedSymbol));
- addRelatedInfo(diagnostic,
- createDiagnosticForNode(parentStaticFieldInitializer.parent,
- Diagnostics.Initializer_for_property_0,
- symbolName(parentStaticFieldInitializerSymbol))
- );
- }
- }
-
if (isAnyLike) {
if (lexicallyScopedSymbol) {
- return apparentType;
+ return isErrorType(apparentType) ? errorType : apparentType;
}
if (!getContainingClass(right)) {
grammarErrorOnNode(right, Diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies);
@@ -27812,7 +28548,7 @@ namespace ts {
if (isIdentifier(left) && parentSymbol) {
markAliasReferenced(parentSymbol, node);
}
- return apparentType;
+ return isErrorType(apparentType) ? errorType : apparentType;;
}
prop = getPropertyOfType(apparentType, right.escapedText);
}
@@ -27855,9 +28591,12 @@ namespace ts {
if (compilerOptions.noPropertyAccessFromIndexSignature && isPropertyAccessExpression(node)) {
error(right, Diagnostics.Property_0_comes_from_an_index_signature_so_it_must_be_accessed_with_0, unescapeLeadingUnderscores(right.escapedText));
}
+ if (indexInfo.declaration && getCombinedNodeFlags(indexInfo.declaration) & NodeFlags.Deprecated) {
+ addDeprecatedSuggestion(right, [indexInfo.declaration], right.escapedText as string);
+ }
}
else {
- if (prop.declarations && getDeclarationNodeFlagsFromSymbol(prop) & NodeFlags.Deprecated && isUncalledFunctionReference(node, prop)) {
+ if (isDeprecatedSymbol(prop) && isUncalledFunctionReference(node, prop) && prop.declarations) {
addDeprecatedSuggestion(right, prop.declarations, right.escapedText as string);
}
checkPropertyNotUsedBeforeDeclaration(prop, node, right);
@@ -27870,7 +28609,7 @@ namespace ts {
return errorType;
}
- propType = isThisPropertyAccessInConstructor(node, prop) ? autoType : writing ? getSetAccessorTypeOfSymbol(prop) : getTypeOfSymbol(prop);
+ propType = isThisPropertyAccessInConstructor(node, prop) ? autoType : writing ? getWriteTypeOfSymbol(prop) : getTypeOfSymbol(prop);
}
return getFlowTypeOfAccessExpression(node, prop, propType, right, checkMode);
@@ -28082,7 +28821,7 @@ namespace ts {
if (relatedInfo) {
addRelatedInfo(resultDiagnostic, relatedInfo);
}
- addErrorOrSuggestion(!isUncheckedJS, resultDiagnostic);
+ addErrorOrSuggestion(!isUncheckedJS || errorInfo.code !== Diagnostics.Property_0_may_not_exist_on_type_1_Did_you_mean_2.code, resultDiagnostic);
}
function containerSeemsToBeEmptyDomElement(containingType: Type) {
@@ -28156,13 +28895,26 @@ namespace ts {
function getSuggestedSymbolForNonexistentSymbol(location: Node | undefined, outerName: __String, meaning: SymbolFlags): Symbol | undefined {
Debug.assert(outerName !== undefined, "outername should always be defined");
- const result = resolveNameHelper(location, outerName, meaning, /*nameNotFoundMessage*/ undefined, outerName, /*isUse*/ false, /*excludeGlobals*/ false, (symbols, name, meaning) => {
+ const result = resolveNameHelper(location, outerName, meaning, /*nameNotFoundMessage*/ undefined, outerName, /*isUse*/ false, /*excludeGlobals*/ false, /*getSpellingSuggestions*/ true, (symbols, name, meaning) => {
Debug.assertEqual(outerName, name, "name should equal outerName");
const symbol = getSymbol(symbols, name, meaning);
// Sometimes the symbol is found when location is a return type of a function: `typeof x` and `x` is declared in the body of the function
// So the table *contains* `x` but `x` isn't actually in scope.
// However, resolveNameHelper will continue and call this callback again, so we'll eventually get a correct suggestion.
- return symbol || getSpellingSuggestionForName(unescapeLeadingUnderscores(name), arrayFrom(symbols.values()), meaning);
+ if (symbol) return symbol;
+ let candidates: Symbol[];
+ if (symbols === globals) {
+ const primitives = mapDefined(
+ ["string", "number", "boolean", "object", "bigint", "symbol"],
+ s => symbols.has((s.charAt(0).toUpperCase() + s.slice(1)) as __String)
+ ? createSymbol(SymbolFlags.TypeAlias, s as __String) as Symbol
+ : undefined);
+ candidates = primitives.concat(arrayFrom(symbols.values()));
+ }
+ else {
+ candidates = arrayFrom(symbols.values());
+ }
+ return getSpellingSuggestionForName(unescapeLeadingUnderscores(name), candidates, meaning);
});
return result;
}
@@ -28208,6 +28960,11 @@ namespace ts {
return suggestion;
}
+ function getSuggestedTypeForNonexistentStringLiteralType(source: StringLiteralType, target: UnionType): StringLiteralType | undefined {
+ const candidates = target.types.filter((type): type is StringLiteralType => !!(type.flags & TypeFlags.StringLiteral));
+ return getSpellingSuggestion(source.value, candidates, type => type.value);
+ }
+
/**
* Given a name and a list of symbols whose names are *not* equal to the name, return a spelling suggestion if there is one that is close enough.
* Names less than length 3 only check for case-insensitive equality, not levenshtein distance.
@@ -28313,7 +29070,7 @@ namespace ts {
type: Type): boolean {
// Short-circuiting for improved performance.
- if (type === errorType || isTypeAny(type)) {
+ if (isTypeAny(type)) {
return true;
}
@@ -28339,7 +29096,7 @@ namespace ts {
property: Symbol): boolean {
// Short-circuiting for improved performance.
- if (containingType === errorType || isTypeAny(containingType)) {
+ if (isTypeAny(containingType)) {
return true;
}
@@ -28419,7 +29176,7 @@ namespace ts {
const indexExpression = node.argumentExpression;
const indexType = checkExpression(indexExpression);
- if (objectType === errorType || objectType === silentNeverType) {
+ if (isErrorType(objectType) || objectType === silentNeverType) {
return objectType;
}
@@ -28690,7 +29447,7 @@ namespace ts {
// 'let f: (x: string) => number = wrap(s => s.length)', we infer from the declared type of 'f' to the
// return type of 'wrap'.
if (node.kind !== SyntaxKind.Decorator) {
- const contextualType = getContextualType(node, ContextFlags.SkipBindingPatterns);
+ const contextualType = getContextualType(node, every(signature.typeParameters, p => !!getDefaultFromTypeParameter(p)) ? ContextFlags.SkipBindingPatterns : ContextFlags.None);
if (contextualType) {
// We clone the inference context to avoid disturbing a resolution in progress for an
// outer call expression. Effectively we just want a snapshot of whatever has been
@@ -29443,7 +30200,7 @@ namespace ts {
const diags = max > 1 ? allDiagnostics[minIndex] : flatten(allDiagnostics);
Debug.assert(diags.length > 0, "No errors reported for 3 or fewer overload signatures");
const chain = chainDiagnosticMessages(
- map(diags, d => typeof d.messageText === "string" ? (d as DiagnosticMessageChain) : d.messageText),
+ map(diags, createDiagnosticMessageChainFromDiagnostic),
Diagnostics.No_overload_matches_this_call);
// The below is a spread to guarantee we get a new (mutable) array - our `flatMap` helper tries to do "smart" optimizations where it reuses input
// arrays and the emptyArray singleton where possible, which is decidedly not what we want while we're still constructing this diagnostic
@@ -29683,7 +30440,7 @@ namespace ts {
typeArguments.pop();
}
while (typeArguments.length < typeParameters.length) {
- typeArguments.push(getConstraintOfTypeParameter(typeParameters[typeArguments.length]) || getDefaultTypeArgumentType(isJs));
+ typeArguments.push(getDefaultFromTypeParameter(typeParameters[typeArguments.length]) || getConstraintOfTypeParameter(typeParameters[typeArguments.length]) || getDefaultTypeArgumentType(isJs));
}
return typeArguments;
}
@@ -29722,7 +30479,7 @@ namespace ts {
}
return anySignature;
}
- if (superType !== errorType) {
+ if (!isErrorType(superType)) {
// In super call, the candidate signatures are the matching arity signatures of the base constructor function instantiated
// with the type arguments specified in the extends clause.
const baseTypeNode = getEffectiveBaseTypeNode(getContainingClass(node)!);
@@ -29758,7 +30515,7 @@ namespace ts {
}
const apparentType = getApparentType(funcType);
- if (apparentType === errorType) {
+ if (isErrorType(apparentType)) {
// Another error has already been reported
return resolveErrorCall(node);
}
@@ -29776,7 +30533,7 @@ namespace ts {
if (isUntypedFunctionCall(funcType, apparentType, callSignatures.length, numConstructSignatures)) {
// The unknownType indicates that an error already occurred (and was reported). No
// need to report another error in this case.
- if (funcType !== errorType && node.typeArguments) {
+ if (!isErrorType(funcType) && node.typeArguments) {
error(node, Diagnostics.Untyped_function_calls_may_not_accept_type_arguments);
}
return resolveUntypedCall(node);
@@ -29859,7 +30616,7 @@ namespace ts {
// signatures for overload resolution. The result type of the function call becomes
// the result type of the operation.
expressionType = getApparentType(expressionType);
- if (expressionType === errorType) {
+ if (isErrorType(expressionType)) {
// Another error has already been reported
return resolveErrorCall(node);
}
@@ -30112,7 +30869,7 @@ namespace ts {
const tagType = checkExpression(node.tag);
const apparentType = getApparentType(tagType);
- if (apparentType === errorType) {
+ if (isErrorType(apparentType)) {
// Another error has already been reported
return resolveErrorCall(node);
}
@@ -30169,7 +30926,7 @@ namespace ts {
function resolveDecorator(node: Decorator, candidatesOutArray: Signature[] | undefined, checkMode: CheckMode): Signature {
const funcType = checkExpression(node.expression);
const apparentType = getApparentType(funcType);
- if (apparentType === errorType) {
+ if (isErrorType(apparentType)) {
return resolveErrorCall(node);
}
@@ -30239,7 +30996,7 @@ namespace ts {
}
const exprTypes = checkExpression(node.tagName);
const apparentType = getApparentType(exprTypes);
- if (apparentType === errorType) {
+ if (isErrorType(apparentType)) {
return resolveErrorCall(node);
}
@@ -30433,7 +31190,7 @@ namespace ts {
* @returns On success, the expression's signature's return type. On failure, anyType.
*/
function checkCallExpression(node: CallExpression | NewExpression, checkMode?: CheckMode): Type {
- if (!checkGrammarTypeArguments(node, node.typeArguments)) checkGrammarArguments(node.arguments);
+ checkGrammarTypeArguments(node, node.typeArguments);
const signature = getResolvedSignature(node, /*candidatesOutArray*/ undefined, checkMode);
if (signature === resolvingSignature) {
@@ -30553,15 +31310,17 @@ namespace ts {
function checkImportCallExpression(node: ImportCall): Type {
// Check grammar of dynamic import
- if (!checkGrammarArguments(node.arguments)) checkGrammarImportCallExpression(node);
+ checkGrammarImportCallExpression(node);
if (node.arguments.length === 0) {
return createPromiseReturnType(node, anyType);
}
+
const specifier = node.arguments[0];
const specifierType = checkExpressionCached(specifier);
+ const optionsType = node.arguments.length > 1 ? checkExpressionCached(node.arguments[1]) : undefined;
// Even though multiple arguments is grammatically incorrect, type-check extra arguments for completion
- for (let i = 1; i < node.arguments.length; ++i) {
+ for (let i = 2; i < node.arguments.length; ++i) {
checkExpressionCached(node.arguments[i]);
}
@@ -30569,32 +31328,59 @@ namespace ts {
error(specifier, Diagnostics.Dynamic_import_s_specifier_must_be_of_type_string_but_here_has_type_0, typeToString(specifierType));
}
+ if (optionsType) {
+ const importCallOptionsType = getGlobalImportCallOptionsType(/*reportErrors*/ true);
+ if (importCallOptionsType !== emptyObjectType) {
+ checkTypeAssignableTo(optionsType, getNullableType(importCallOptionsType, TypeFlags.Undefined), node.arguments[1]);
+ }
+ }
+
// resolveExternalModuleName will return undefined if the moduleReferenceExpression is not a string literal
const moduleSymbol = resolveExternalModuleName(node, specifier);
if (moduleSymbol) {
const esModuleSymbol = resolveESModuleSymbol(moduleSymbol, specifier, /*dontRecursivelyResolve*/ true, /*suppressUsageError*/ false);
if (esModuleSymbol) {
- return createPromiseReturnType(node, getTypeWithSyntheticDefaultImportType(getTypeOfSymbol(esModuleSymbol), esModuleSymbol, moduleSymbol));
+ return createPromiseReturnType(node,
+ getTypeWithSyntheticDefaultOnly(getTypeOfSymbol(esModuleSymbol), esModuleSymbol, moduleSymbol, specifier) ||
+ getTypeWithSyntheticDefaultImportType(getTypeOfSymbol(esModuleSymbol), esModuleSymbol, moduleSymbol, specifier)
+ );
}
}
return createPromiseReturnType(node, anyType);
}
- function getTypeWithSyntheticDefaultImportType(type: Type, symbol: Symbol, originalSymbol: Symbol): Type {
- if (allowSyntheticDefaultImports && type && type !== errorType) {
+ function createDefaultPropertyWrapperForModule(symbol: Symbol, originalSymbol: Symbol, anonymousSymbol?: Symbol | undefined) {
+ const memberTable = createSymbolTable();
+ const newSymbol = createSymbol(SymbolFlags.Alias, InternalSymbolName.Default);
+ newSymbol.parent = originalSymbol;
+ newSymbol.nameType = getStringLiteralType("default");
+ newSymbol.target = resolveSymbol(symbol);
+ memberTable.set(InternalSymbolName.Default, newSymbol);
+ return createAnonymousType(anonymousSymbol, memberTable, emptyArray, emptyArray, emptyArray);
+ }
+
+ function getTypeWithSyntheticDefaultOnly(type: Type, symbol: Symbol, originalSymbol: Symbol, moduleSpecifier: Expression) {
+ const hasDefaultOnly = isOnlyImportedAsDefault(moduleSpecifier);
+ if (hasDefaultOnly && type && !isErrorType(type)) {
+ const synthType = type as SyntheticDefaultModuleType;
+ if (!synthType.defaultOnlyType) {
+ const type = createDefaultPropertyWrapperForModule(symbol, originalSymbol);
+ synthType.defaultOnlyType = type;
+ }
+ return synthType.defaultOnlyType;
+ }
+ return undefined;
+ }
+
+ function getTypeWithSyntheticDefaultImportType(type: Type, symbol: Symbol, originalSymbol: Symbol, moduleSpecifier: Expression): Type {
+ if (allowSyntheticDefaultImports && type && !isErrorType(type)) {
const synthType = type as SyntheticDefaultModuleType;
if (!synthType.syntheticType) {
const file = originalSymbol.declarations?.find(isSourceFile);
- const hasSyntheticDefault = canHaveSyntheticDefault(file, originalSymbol, /*dontResolveAlias*/ false);
+ const hasSyntheticDefault = canHaveSyntheticDefault(file, originalSymbol, /*dontResolveAlias*/ false, moduleSpecifier);
if (hasSyntheticDefault) {
- const memberTable = createSymbolTable();
- const newSymbol = createSymbol(SymbolFlags.Alias, InternalSymbolName.Default);
- newSymbol.parent = originalSymbol;
- newSymbol.nameType = getStringLiteralType("default");
- newSymbol.target = resolveSymbol(symbol);
- memberTable.set(InternalSymbolName.Default, newSymbol);
const anonymousSymbol = createSymbol(SymbolFlags.TypeLiteral, InternalSymbolName.Type);
- const defaultContainingObject = createAnonymousType(anonymousSymbol, memberTable, emptyArray, emptyArray, emptyArray);
+ const defaultContainingObject = createDefaultPropertyWrapperForModule(symbol, originalSymbol, anonymousSymbol);
anonymousSymbol.type = defaultContainingObject;
synthType.syntheticType = isValidSpreadType(type) ? getSpreadType(type, defaultContainingObject, anonymousSymbol, /*objectFlags*/ 0, /*readonly*/ false) : defaultContainingObject;
}
@@ -30647,6 +31433,12 @@ namespace ts {
}
function checkAssertion(node: AssertionExpression) {
+ if (node.kind === SyntaxKind.TypeAssertionExpression) {
+ const file = getSourceFileOfNode(node);
+ if (file && fileExtensionIsOneOf(file.fileName, [Extension.Cts, Extension.Mts])) {
+ grammarErrorOnNode(node, Diagnostics.This_syntax_is_reserved_in_files_with_the_mts_or_cts_extension_Use_an_as_expression_instead);
+ }
+ }
return checkAssertionWorker(node, node.type, node.expression);
}
@@ -30672,13 +31464,11 @@ namespace ts {
case SyntaxKind.PropertyAccessExpression:
case SyntaxKind.ElementAccessExpression:
const expr = (node as PropertyAccessExpression | ElementAccessExpression).expression;
- if (isIdentifier(expr)) {
- let symbol = getSymbolAtLocation(expr);
- if (symbol && symbol.flags & SymbolFlags.Alias) {
- symbol = resolveAlias(symbol);
- }
- return !!(symbol && (symbol.flags & SymbolFlags.Enum) && getEnumKind(symbol) === EnumKind.Literal);
+ let symbol = getTypeOfNode(expr).symbol;
+ if (symbol && symbol.flags & SymbolFlags.Alias) {
+ symbol = resolveAlias(symbol);
}
+ return !!(symbol && (symbol.flags & SymbolFlags.Enum) && getEnumKind(symbol) === EnumKind.Literal);
}
return false;
}
@@ -30694,7 +31484,7 @@ namespace ts {
checkSourceElement(type);
exprType = getRegularTypeOfObjectLiteral(getBaseTypeOfLiteralType(exprType));
const targetType = getTypeFromTypeNode(type);
- if (produceDiagnostics && targetType !== errorType) {
+ if (produceDiagnostics && !isErrorType(targetType)) {
const widenedType = getWidenedType(exprType);
if (!isTypeComparableTo(targetType, widenedType)) {
checkTypeComparableTo(exprType, targetType, errNode,
@@ -30715,6 +31505,76 @@ namespace ts {
getNonNullableType(checkExpression(node.expression));
}
+ function checkExpressionWithTypeArguments(node: ExpressionWithTypeArguments | TypeQueryNode) {
+ checkGrammarExpressionWithTypeArguments(node);
+ const exprType = node.kind === SyntaxKind.ExpressionWithTypeArguments ? checkExpression(node.expression) :
+ isThisIdentifier(node.exprName) ? checkThisExpression(node.exprName) :
+ checkExpression(node.exprName);
+ const typeArguments = node.typeArguments;
+ if (exprType === silentNeverType || isErrorType(exprType) || !some(typeArguments)) {
+ return exprType;
+ }
+ let hasSomeApplicableSignature = false;
+ let nonApplicableType: Type | undefined;
+ const result = getInstantiatedType(exprType);
+ const errorType = hasSomeApplicableSignature ? nonApplicableType : exprType;
+ if (errorType) {
+ diagnostics.add(createDiagnosticForNodeArray(getSourceFileOfNode(node), typeArguments, Diagnostics.Type_0_has_no_signatures_for_which_the_type_argument_list_is_applicable, typeToString(errorType)));
+ }
+ return result;
+
+ function getInstantiatedType(type: Type): Type {
+ let hasSignatures = false;
+ let hasApplicableSignature = false;
+ const result = getInstantiatedTypePart(type);
+ hasSomeApplicableSignature ||= hasApplicableSignature;
+ if (hasSignatures && !hasApplicableSignature) {
+ nonApplicableType ??= type;
+ }
+ return result;
+
+ function getInstantiatedTypePart(type: Type): Type {
+ if (type.flags & TypeFlags.Object) {
+ const resolved = resolveStructuredTypeMembers(type as ObjectType);
+ const callSignatures = getInstantiatedSignatures(resolved.callSignatures);
+ const constructSignatures = getInstantiatedSignatures(resolved.constructSignatures);
+ hasSignatures ||= resolved.callSignatures.length !== 0 || resolved.constructSignatures.length !== 0;
+ hasApplicableSignature ||= callSignatures.length !== 0 || constructSignatures.length !== 0;
+ if (callSignatures !== resolved.callSignatures || constructSignatures !== resolved.constructSignatures) {
+ const result = createAnonymousType(undefined, resolved.members, callSignatures, constructSignatures, resolved.indexInfos) as ResolvedType & InstantiationExpressionType;
+ result.objectFlags |= ObjectFlags.InstantiationExpressionType;
+ result.node = node;
+ return result;
+ }
+ }
+ else if (type.flags & TypeFlags.InstantiableNonPrimitive) {
+ const constraint = getBaseConstraintOfType(type);
+ if (constraint) {
+ const instantiated = getInstantiatedTypePart(constraint);
+ if (instantiated !== constraint) {
+ return instantiated;
+ }
+ }
+ }
+ else if (type.flags & TypeFlags.Union) {
+ return mapType(type, getInstantiatedType);
+ }
+ else if (type.flags & TypeFlags.Intersection) {
+ return getIntersectionType(sameMap((type as IntersectionType).types, getInstantiatedTypePart));
+ }
+ return type;
+ }
+ }
+
+ function getInstantiatedSignatures(signatures: readonly Signature[]) {
+ const applicableSignatures = filter(signatures, sig => !!sig.typeParameters && hasCorrectTypeArgumentArity(sig, typeArguments));
+ return sameMap(applicableSignatures, sig => {
+ const typeArgumentTypes = checkTypeArguments(sig, typeArguments!, /*reportErrors*/ true);
+ return typeArgumentTypes ? getSignatureInstantiation(sig, typeArgumentTypes, isInJSFile(sig.declaration)) : sig;
+ });
+ }
+ }
+
function checkMetaProperty(node: MetaProperty): Type {
checkGrammarMetaProperty(node);
@@ -30735,7 +31595,7 @@ namespace ts {
return getGlobalImportMetaExpressionType();
case SyntaxKind.NewKeyword:
const type = checkNewTargetMetaProperty(node);
- return type === errorType ? errorType : createNewTargetExpressionType(type);
+ return isErrorType(type) ? errorType : createNewTargetExpressionType(type);
default:
Debug.assertNever(node.keywordToken);
}
@@ -30758,8 +31618,13 @@ namespace ts {
}
function checkImportMetaProperty(node: MetaProperty) {
- if (moduleKind !== ModuleKind.ES2020 && moduleKind !== ModuleKind.ESNext && moduleKind !== ModuleKind.System) {
- error(node, Diagnostics.The_import_meta_meta_property_is_only_allowed_when_the_module_option_is_es2020_esnext_or_system);
+ if (moduleKind === ModuleKind.Node12 || moduleKind === ModuleKind.NodeNext) {
+ if (getSourceFileOfNode(node).impliedNodeFormat !== ModuleKind.ESNext) {
+ error(node, Diagnostics.The_import_meta_meta_property_is_not_allowed_in_files_which_will_build_into_CommonJS_output);
+ }
+ }
+ else if (moduleKind < ModuleKind.ES2020 && moduleKind !== ModuleKind.System) {
+ error(node, Diagnostics.The_import_meta_meta_property_is_only_allowed_when_the_module_option_is_es2020_es2022_esnext_system_node12_or_nodenext);
}
const file = getSourceFileOfNode(node);
Debug.assert(!!(file.flags & NodeFlags.PossiblyContainsImportMeta), "Containing file is missing import meta node flag.");
@@ -30798,6 +31663,9 @@ namespace ts {
}
function getParameterIdentifierNameAtPosition(signature: Signature, pos: number): [parameterName: __String, isRestParameter: boolean] | undefined {
+ if (signature.declaration?.kind === SyntaxKind.JSDocFunctionType) {
+ return undefined;
+ }
const paramCount = signature.parameters.length - (signatureHasRestParameter(signature) ? 1 : 0);
if (pos < paramCount) {
const param = signature.parameters[pos];
@@ -31057,27 +31925,28 @@ namespace ts {
const links = getSymbolLinks(parameter);
if (!links.type) {
const declaration = parameter.valueDeclaration as ParameterDeclaration;
- links.type = type || getWidenedTypeForVariableLikeDeclaration(declaration, /*includeOptionality*/ true);
+ links.type = type || getWidenedTypeForVariableLikeDeclaration(declaration, /*reportErrors*/ true);
if (declaration.name.kind !== SyntaxKind.Identifier) {
// if inference didn't come up with anything but unknown, fall back to the binding pattern if present.
if (links.type === unknownType) {
links.type = getTypeFromBindingPattern(declaration.name);
}
- assignBindingElementTypes(declaration.name);
+ assignBindingElementTypes(declaration.name, links.type);
}
}
}
// When contextual typing assigns a type to a parameter that contains a binding pattern, we also need to push
// the destructured type into the contained binding elements.
- function assignBindingElementTypes(pattern: BindingPattern) {
+ function assignBindingElementTypes(pattern: BindingPattern, parentType: Type) {
for (const element of pattern.elements) {
if (!isOmittedExpression(element)) {
+ const type = getBindingElementTypeFromParentType(element, parentType);
if (element.name.kind === SyntaxKind.Identifier) {
- getSymbolLinks(getSymbolOfNode(element)).type = getTypeForBindingElement(element);
+ getSymbolLinks(getSymbolOfNode(element)).type = type;
}
else {
- assignBindingElementTypes(element.name);
+ assignBindingElementTypes(element.name, type);
}
}
}
@@ -31088,7 +31957,8 @@ namespace ts {
const globalPromiseType = getGlobalPromiseType(/*reportErrors*/ true);
if (globalPromiseType !== emptyGenericType) {
// if the promised type is itself a promise, get the underlying type; otherwise, fallback to the promised type
- promisedType = getAwaitedType(promisedType) || unknownType;
+ // Unwrap an `Awaited` to `T` to improve inference.
+ promisedType = getAwaitedTypeNoAlias(unwrapAwaitedType(promisedType)) || unknownType;
return createTypeReference(globalPromiseType, [promisedType]);
}
@@ -31100,7 +31970,8 @@ namespace ts {
const globalPromiseLikeType = getGlobalPromiseLikeType(/*reportErrors*/ true);
if (globalPromiseLikeType !== emptyGenericType) {
// if the promised type is itself a promise, get the underlying type; otherwise, fallback to the promised type
- promisedType = getAwaitedType(promisedType) || unknownType;
+ // Unwrap an `Awaited` to `T` to improve inference.
+ promisedType = getAwaitedTypeNoAlias(unwrapAwaitedType(promisedType)) || unknownType;
return createTypeReference(globalPromiseLikeType, [promisedType]);
}
@@ -31157,7 +32028,7 @@ namespace ts {
// Promise/A+ compatible implementation will always assimilate any foreign promise, so the
// return type of the body should be unwrapped to its awaited type, which we will wrap in
// the native Promise type later in this function.
- returnType = checkAwaitedType(returnType, /*errorNode*/ func, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
+ returnType = unwrapAwaitedType(checkAwaitedType(returnType, /*withAlias*/ false, /*errorNode*/ func, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member));
}
}
else if (isGenerator) { // Generator or AsyncGenerator function
@@ -31390,7 +32261,7 @@ namespace ts {
// Promise/A+ compatible implementation will always assimilate any foreign promise, so the
// return type of the body should be unwrapped to its awaited type, which should be wrapped in
// the native Promise type by the caller.
- type = checkAwaitedType(type, func, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
+ type = unwrapAwaitedType(checkAwaitedType(type, /*withAlias*/ false, func, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member));
}
if (type.flags & TypeFlags.Never) {
hasReturnOfTypeNever = true;
@@ -31592,7 +32463,7 @@ namespace ts {
const returnOrPromisedType = returnType && unwrapReturnType(returnType, functionFlags);
if (returnOrPromisedType) {
if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Async) { // Async function
- const awaitedType = checkAwaitedType(exprType, node.body, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
+ const awaitedType = checkAwaitedType(exprType, /*withAlias*/ false, node.body, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
checkTypeAssignableToAndOptionallyElaborate(awaitedType, returnOrPromisedType, node.body, node.body);
}
else { // Normal function
@@ -31780,10 +32651,10 @@ namespace ts {
Diagnostics.await_expressions_are_only_allowed_at_the_top_level_of_a_file_when_that_file_is_a_module_but_this_file_has_no_imports_or_exports_Consider_adding_an_empty_export_to_make_this_file_a_module);
diagnostics.add(diagnostic);
}
- if ((moduleKind !== ModuleKind.ESNext && moduleKind !== ModuleKind.System) || languageVersion < ScriptTarget.ES2017) {
+ if ((moduleKind !== ModuleKind.ES2022 && moduleKind !== ModuleKind.ESNext && moduleKind !== ModuleKind.System && !(moduleKind === ModuleKind.NodeNext && getSourceFileOfNode(node).impliedNodeFormat === ModuleKind.ESNext)) || languageVersion < ScriptTarget.ES2017) {
span = getSpanOfTokenAtPosition(sourceFile, node.pos);
const diagnostic = createFileDiagnostic(sourceFile, span.start, span.length,
- Diagnostics.Top_level_await_expressions_are_only_allowed_when_the_module_option_is_set_to_esnext_or_system_and_the_target_option_is_set_to_es2017_or_higher);
+ Diagnostics.Top_level_await_expressions_are_only_allowed_when_the_module_option_is_set_to_es2022_esnext_system_or_nodenext_and_the_target_option_is_set_to_es2017_or_higher);
diagnostics.add(diagnostic);
}
}
@@ -31809,8 +32680,8 @@ namespace ts {
}
const operandType = checkExpression(node.expression);
- const awaitedType = checkAwaitedType(operandType, node, Diagnostics.Type_of_await_operand_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
- if (awaitedType === operandType && awaitedType !== errorType && !(operandType.flags & TypeFlags.AnyOrUnknown)) {
+ const awaitedType = checkAwaitedType(operandType, /*withAlias*/ true, node, Diagnostics.Type_of_await_operand_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
+ if (awaitedType === operandType && !isErrorType(awaitedType) && !(operandType.flags & TypeFlags.AnyOrUnknown)) {
addErrorOrSuggestion(/*isError*/ false, createDiagnosticForNode(node, Diagnostics.await_has_no_effect_on_the_type_of_this_expression));
}
return awaitedType;
@@ -31978,11 +32849,29 @@ namespace ts {
if (leftType === silentNeverType || rightType === silentNeverType) {
return silentNeverType;
}
- leftType = checkNonNullType(leftType, left);
+ if (isPrivateIdentifier(left)) {
+ if (languageVersion < ScriptTarget.ESNext) {
+ checkExternalEmitHelpers(left, ExternalEmitHelpers.ClassPrivateFieldIn);
+ }
+ // Unlike in 'checkPrivateIdentifierExpression' we now have access to the RHS type
+ // which provides us with the opportunity to emit more detailed errors
+ if (!getNodeLinks(left).resolvedSymbol && getContainingClass(left)) {
+ const isUncheckedJS = isUncheckedJSSuggestion(left, rightType.symbol, /*excludeClasses*/ true);
+ reportNonexistentProperty(left, rightType, isUncheckedJS);
+ }
+ }
+ else {
+ leftType = checkNonNullType(leftType, left);
+ // TypeScript 1.0 spec (April 2014): 4.15.5
+ // Require the left operand to be of type Any, the String primitive type, or the Number primitive type.
+ if (!(allTypesAssignableToKind(leftType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbolLike) ||
+ isTypeAssignableToKind(leftType, TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping | TypeFlags.TypeParameter))) {
+ error(left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_a_private_identifier_or_of_type_any_string_number_or_symbol);
+ }
+ }
rightType = checkNonNullType(rightType, right);
// TypeScript 1.0 spec (April 2014): 4.15.5
- // The in operator requires the left operand to be of type Any, the String primitive type, or the Number primitive type,
- // and the right operand to be
+ // The in operator requires the right operand to be
//
// 1. assignable to the non-primitive type,
// 2. an unconstrained type parameter,
@@ -32000,10 +32889,6 @@ namespace ts {
// unless *all* instantiations would result in an error.
//
// The result is always of the Boolean primitive type.
- if (!(allTypesAssignableToKind(leftType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbolLike) ||
- isTypeAssignableToKind(leftType, TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping | TypeFlags.TypeParameter))) {
- error(left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_of_type_any_string_number_or_symbol);
- }
const rightTypeConstraint = getConstraintOfType(rightType);
if (!allTypesAssignableToKind(rightType, TypeFlags.NonPrimitive | TypeFlags.InstantiableNonPrimitive) ||
rightTypeConstraint && (
@@ -32533,7 +33418,7 @@ namespace ts {
else if (isTypeAny(leftType) || isTypeAny(rightType)) {
// Otherwise, the result is of type Any.
// NOTE: unknown type here denotes error type. Old compiler treated this case as any type so do we.
- resultType = leftType === errorType || rightType === errorType ? errorType : anyType;
+ resultType = isErrorType(leftType) || isErrorType(rightType) ? errorType : anyType;
}
// Symbols are not allowed at all in arithmetic expressions
@@ -32761,8 +33646,8 @@ namespace ts {
let wouldWorkWithAwait = false;
const errNode = errorNode || operatorToken;
if (isRelated) {
- const awaitedLeftType = getAwaitedType(leftType);
- const awaitedRightType = getAwaitedType(rightType);
+ const awaitedLeftType = getAwaitedTypeNoAlias(leftType);
+ const awaitedRightType = getAwaitedTypeNoAlias(rightType);
wouldWorkWithAwait = !(awaitedLeftType === leftType && awaitedRightType === rightType)
&& !!(awaitedLeftType && awaitedRightType)
&& isRelated(awaitedLeftType, awaitedRightType);
@@ -32958,11 +33843,11 @@ namespace ts {
}
function checkExpressionCached(node: Expression | QualifiedName, checkMode?: CheckMode): Type {
+ if (checkMode && checkMode !== CheckMode.Normal) {
+ return checkExpression(node, checkMode);
+ }
const links = getNodeLinks(node);
if (!links.resolvedType) {
- if (checkMode && checkMode !== CheckMode.Normal) {
- return checkExpression(node, checkMode);
- }
// When computing a type that we're going to cache, we need to ignore any ongoing control flow
// analysis because variables may have transient types in indeterminable states. Moving flowLoopStart
// to the top of the stack ensures all transient types are computed from a known point.
@@ -32984,10 +33869,16 @@ namespace ts {
isJSDocTypeAssertion(node);
}
- function checkDeclarationInitializer(declaration: HasExpressionInitializer, contextualType?: Type | undefined) {
+ function checkDeclarationInitializer(
+ declaration: HasExpressionInitializer,
+ checkMode: CheckMode,
+ contextualType?: Type | undefined
+ ) {
const initializer = getEffectiveInitializer(declaration)!;
const type = getQuickTypeOfExpression(initializer) ||
- (contextualType ? checkExpressionWithContextualType(initializer, contextualType, /*inferenceContext*/ undefined, CheckMode.Normal) : checkExpressionCached(initializer));
+ (contextualType ?
+ checkExpressionWithContextualType(initializer, contextualType, /*inferenceContext*/ undefined, checkMode || CheckMode.Normal)
+ : checkExpressionCached(initializer, checkMode));
return isParameter(declaration) && declaration.name.kind === SyntaxKind.ArrayBindingPattern &&
isTupleType(type) && !type.target.hasRestElement && getTypeReferenceArity(type) < declaration.name.elements.length ?
padTupleType(type, declaration.name) : type;
@@ -33323,7 +34214,7 @@ namespace ts {
}
function checkExpression(node: Expression | QualifiedName, checkMode?: CheckMode, forceTuple?: boolean): Type {
- tracing?.push(tracing.Phase.Check, "checkExpression", { kind: node.kind, pos: node.pos, end: node.end });
+ tracing?.push(tracing.Phase.Check, "checkExpression", { kind: node.kind, pos: node.pos, end: node.end, path: (node as TracingNode).tracingPath });
const saveCurrentNode = currentNode;
currentNode = node;
instantiationCount = 0;
@@ -33363,7 +34254,7 @@ namespace ts {
}
function checkParenthesizedExpression(node: ParenthesizedExpression, checkMode?: CheckMode): Type {
- if (isJSDocTypeAssertion(node)) {
+ if (hasJSDocNodes(node) && isJSDocTypeAssertion(node)) {
const type = getJSDocTypeAssertionType(node);
return checkAssertionWorker(type, type, node.expression, checkMode);
}
@@ -33385,6 +34276,8 @@ namespace ts {
switch (kind) {
case SyntaxKind.Identifier:
return checkIdentifier(node as Identifier, checkMode);
+ case SyntaxKind.PrivateIdentifier:
+ return checkPrivateIdentifierExpression(node as PrivateIdentifier);
case SyntaxKind.ThisKeyword:
return checkThisExpression(node);
case SyntaxKind.SuperKeyword:
@@ -33444,6 +34337,8 @@ namespace ts {
return checkAssertion(node as AssertionExpression);
case SyntaxKind.NonNullExpression:
return checkNonNullAssertion(node as NonNullExpression);
+ case SyntaxKind.ExpressionWithTypeArguments:
+ return checkExpressionWithTypeArguments(node as ExpressionWithTypeArguments);
case SyntaxKind.MetaProperty:
return checkMetaProperty(node as MetaProperty);
case SyntaxKind.DeleteExpression:
@@ -33679,6 +34574,7 @@ namespace ts {
}
checkTypeParameters(getEffectiveTypeParameterDeclarations(node));
+ checkUnmatchedJSDocParameters(node);
forEach(node.parameters, checkParameter);
@@ -33917,9 +34813,7 @@ namespace ts {
checkVariableLikeDeclaration(node);
setNodeLinksForPrivateIdentifierScope(node);
- if (isPrivateIdentifier(node.name) && hasStaticModifier(node) && node.initializer && languageVersion === ScriptTarget.ESNext && !compilerOptions.useDefineForClassFields) {
- error(node.initializer, Diagnostics.Static_fields_with_private_names_can_t_have_initializers_when_the_useDefineForClassFields_flag_is_not_specified_with_a_target_of_esnext_Consider_adding_the_useDefineForClassFields_flag);
- }
+
// property signatures already report "initializer not allowed in ambient context" elsewhere
if (hasSyntacticModifier(node, ModifierFlags.Abstract) && node.kind === SyntaxKind.PropertyDeclaration && node.initializer) {
error(node, Diagnostics.Property_0_cannot_have_an_initializer_because_it_is_marked_abstract, declarationNameToString(node.name));
@@ -34025,33 +34919,41 @@ namespace ts {
error(superCall, Diagnostics.A_constructor_cannot_contain_a_super_call_when_its_class_extends_null);
}
- // The first statement in the body of a constructor (excluding prologue directives) must be a super call
- // if both of the following are true:
+ // A super call must be root-level in a constructor if both of the following are true:
// - The containing class is a derived class.
// - The constructor declares parameter properties
// or the containing class declares instance member variables with initializers.
- const superCallShouldBeFirst =
- (compilerOptions.target !== ScriptTarget.ESNext || !useDefineForClassFields) &&
+
+ const superCallShouldBeRootLevel =
+ (getEmitScriptTarget(compilerOptions) !== ScriptTarget.ESNext || !useDefineForClassFields) &&
(some((node.parent as ClassDeclaration).members, isInstancePropertyWithInitializerOrPrivateIdentifierProperty) ||
some(node.parameters, p => hasSyntacticModifier(p, ModifierFlags.ParameterPropertyModifier)));
- // Skip past any prologue directives to find the first statement
- // to ensure that it was a super call.
- if (superCallShouldBeFirst) {
- const statements = node.body!.statements;
- let superCallStatement: ExpressionStatement | undefined;
-
- for (const statement of statements) {
- if (statement.kind === SyntaxKind.ExpressionStatement && isSuperCall((statement as ExpressionStatement).expression)) {
- superCallStatement = statement as ExpressionStatement;
- break;
- }
- if (!isPrologueDirective(statement)) {
- break;
- }
+ if (superCallShouldBeRootLevel) {
+ // Until we have better flow analysis, it is an error to place the super call within any kind of block or conditional
+ // See GH #8277
+ if (!superCallIsRootLevelInConstructor(superCall, node.body!)) {
+ error(superCall, Diagnostics.A_super_call_must_be_a_root_level_statement_within_a_constructor_of_a_derived_class_that_contains_initialized_properties_parameter_properties_or_private_identifiers);
}
- if (!superCallStatement) {
- error(node, Diagnostics.A_super_call_must_be_the_first_statement_in_the_constructor_when_a_class_contains_initialized_properties_parameter_properties_or_private_identifiers);
+ // Skip past any prologue directives to check statements for referring to 'super' or 'this' before a super call
+ else {
+ let superCallStatement: ExpressionStatement | undefined;
+
+ for (const statement of node.body!.statements) {
+ if (isExpressionStatement(statement) && isSuperCall(skipOuterExpressions(statement.expression))) {
+ superCallStatement = statement;
+ break;
+ }
+ if (!isPrologueDirective(statement) && nodeImmediatelyReferencesSuperOrThis(statement)) {
+ break;
+ }
+ }
+
+ // Until we have better flow analysis, it is an error to place the super call within any kind of block or conditional
+ // See GH #8277
+ if (superCallStatement === undefined) {
+ error(node, Diagnostics.A_super_call_must_be_the_first_statement_in_the_constructor_to_refer_to_super_or_this_when_a_derived_class_contains_initialized_properties_parameter_properties_or_private_identifiers);
+ }
}
}
}
@@ -34061,6 +34963,23 @@ namespace ts {
}
}
+ function superCallIsRootLevelInConstructor(superCall: Node, body: Block) {
+ const superCallParent = walkUpParenthesizedExpressions(superCall.parent);
+ return isExpressionStatement(superCallParent) && superCallParent.parent === body;
+ }
+
+ function nodeImmediatelyReferencesSuperOrThis(node: Node): boolean {
+ if (node.kind === SyntaxKind.SuperKeyword || node.kind === SyntaxKind.ThisKeyword) {
+ return true;
+ }
+
+ if (isThisContainerOrFunctionBlock(node)) {
+ return false;
+ }
+
+ return !!forEachChild(node, nodeImmediatelyReferencesSuperOrThis);
+ }
+
function checkAccessorDeclaration(node: AccessorDeclaration) {
if (produceDiagnostics) {
// Grammar checking accessors
@@ -34150,7 +35069,7 @@ namespace ts {
function getTypeParametersForTypeReference(node: TypeReferenceNode | ExpressionWithTypeArguments) {
const type = getTypeFromTypeReference(node);
- if (type !== errorType) {
+ if (!isErrorType(type)) {
const symbol = getNodeLinks(node).resolvedSymbol;
if (symbol) {
return symbol.flags & SymbolFlags.TypeAlias && getSymbolLinks(symbol).typeParameters ||
@@ -34167,7 +35086,7 @@ namespace ts {
}
forEach(node.typeArguments, checkSourceElement);
const type = getTypeFromTypeReference(node);
- if (type !== errorType) {
+ if (!isErrorType(type)) {
if (node.typeArguments && produceDiagnostics) {
const typeParameters = getTypeParametersForTypeReference(node);
if (typeParameters) {
@@ -34193,7 +35112,8 @@ namespace ts {
function getTypeArgumentConstraint(node: TypeNode): Type | undefined {
const typeReferenceNode = tryCast(node.parent, isTypeReferenceType);
if (!typeReferenceNode) return undefined;
- const typeParameters = getTypeParametersForTypeReference(typeReferenceNode)!; // TODO: GH#18217
+ const typeParameters = getTypeParametersForTypeReference(typeReferenceNode);
+ if (!typeParameters) return undefined;
const constraint = getConstraintOfTypeParameter(typeParameters[typeReferenceNode.typeArguments!.indexOf(node)]);
return constraint && instantiateType(constraint, createTypeMapper(typeParameters, getEffectiveTypeArguments(typeReferenceNode, typeParameters)));
}
@@ -34206,7 +35126,7 @@ namespace ts {
forEach(node.members, checkSourceElement);
if (produceDiagnostics) {
const type = getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node);
- checkIndexConstraints(type);
+ checkIndexConstraints(type, type.symbol);
checkTypeForDuplicateIndexSignatures(node);
checkObjectTypeForDuplicateDeclarations(node);
}
@@ -34306,6 +35226,7 @@ namespace ts {
}
function checkMappedType(node: MappedTypeNode) {
+ checkGrammarMappedType(node);
checkSourceElement(node.typeParameter);
checkSourceElement(node.nameType);
checkSourceElement(node.type);
@@ -34325,6 +35246,12 @@ namespace ts {
}
}
+ function checkGrammarMappedType(node: MappedTypeNode) {
+ if (node.members?.length) {
+ return grammarErrorOnNode(node.members[0], Diagnostics.A_mapped_type_may_not_declare_properties_or_methods);
+ }
+ }
+
function checkThisType(node: ThisTypeNode) {
getTypeFromThisTypeNode(node);
}
@@ -34807,6 +35734,11 @@ namespace ts {
return typeAsPromise.promisedTypeOfPromise = getTypeArguments(type as GenericType)[0];
}
+ // primitives with a `{ then() }` won't be unwrapped/adopted.
+ if (allTypesAssignableToKind(type, TypeFlags.Primitive | TypeFlags.Never)) {
+ return undefined;
+ }
+
const thenFunction = getTypeOfPropertyOfType(type, "then" as __String)!; // TODO: GH#18217
if (isTypeAny(thenFunction)) {
return undefined;
@@ -34839,23 +35771,92 @@ namespace ts {
/**
* Gets the "awaited type" of a type.
* @param type The type to await.
+ * @param withAlias When `true`, wraps the "awaited type" in `Awaited` if needed.
* @remarks The "awaited type" of an expression is its "promised type" if the expression is a
* Promise-like type; otherwise, it is the type of the expression. This is used to reflect
* The runtime behavior of the `await` keyword.
*/
- function checkAwaitedType(type: Type, errorNode: Node, diagnosticMessage: DiagnosticMessage, arg0?: string | number): Type {
- const awaitedType = getAwaitedType(type, errorNode, diagnosticMessage, arg0);
+ function checkAwaitedType(type: Type, withAlias: boolean, errorNode: Node, diagnosticMessage: DiagnosticMessage, arg0?: string | number): Type {
+ const awaitedType = withAlias ?
+ getAwaitedType(type, errorNode, diagnosticMessage, arg0) :
+ getAwaitedTypeNoAlias(type, errorNode, diagnosticMessage, arg0);
return awaitedType || errorType;
}
/**
- * Determines whether a type has a callable `then` member.
+ * Determines whether a type is an object with a callable `then` member.
*/
function isThenableType(type: Type): boolean {
+ if (allTypesAssignableToKind(type, TypeFlags.Primitive | TypeFlags.Never)) {
+ // primitive types cannot be considered "thenable" since they are not objects.
+ return false;
+ }
+
const thenFunction = getTypeOfPropertyOfType(type, "then" as __String);
return !!thenFunction && getSignaturesOfType(getTypeWithFacts(thenFunction, TypeFacts.NEUndefinedOrNull), SignatureKind.Call).length > 0;
}
+ interface AwaitedTypeInstantiation extends Type {
+ _awaitedTypeBrand: never;
+ aliasSymbol: Symbol;
+ aliasTypeArguments: readonly Type[];
+ }
+
+ function isAwaitedTypeInstantiation(type: Type): type is AwaitedTypeInstantiation {
+ if (type.flags & TypeFlags.Conditional) {
+ const awaitedSymbol = getGlobalAwaitedSymbol(/*reportErrors*/ false);
+ return !!awaitedSymbol && type.aliasSymbol === awaitedSymbol && type.aliasTypeArguments?.length === 1;
+ }
+ return false;
+ }
+
+ /**
+ * For a generic `Awaited`, gets `T`.
+ */
+ function unwrapAwaitedType(type: Type) {
+ return type.flags & TypeFlags.Union ? mapType(type, unwrapAwaitedType) :
+ isAwaitedTypeInstantiation(type) ? type.aliasTypeArguments[0] :
+ type;
+ }
+
+ function createAwaitedTypeIfNeeded(type: Type): Type {
+ // We wrap type `T` in `Awaited` based on the following conditions:
+ // - `T` is not already an `Awaited`, and
+ // - `T` is generic, and
+ // - One of the following applies:
+ // - `T` has no base constraint, or
+ // - The base constraint of `T` is `any`, `unknown`, `object`, or `{}`, or
+ // - The base constraint of `T` is an object type with a callable `then` method.
+
+ if (isTypeAny(type)) {
+ return type;
+ }
+
+ // If this is already an `Awaited`, just return it. This helps to avoid `Awaited>` in higher-order.
+ if (isAwaitedTypeInstantiation(type)) {
+ return type;
+ }
+
+ // Only instantiate `Awaited` if `T` contains possibly non-primitive types.
+ if (isGenericObjectType(type)) {
+ const baseConstraint = getBaseConstraintOfType(type);
+ // Only instantiate `Awaited` if `T` has no base constraint, or the base constraint of `T` is `any`, `unknown`, `{}`, `object`,
+ // or is promise-like.
+ if (!baseConstraint || (baseConstraint.flags & TypeFlags.AnyOrUnknown) || isEmptyObjectType(baseConstraint) || isThenableType(baseConstraint)) {
+ // Nothing to do if `Awaited` doesn't exist
+ const awaitedSymbol = getGlobalAwaitedSymbol(/*reportErrors*/ true);
+ if (awaitedSymbol) {
+ // Unwrap unions that may contain `Awaited`, otherwise its possible to manufacture an `Awaited | U>` where
+ // an `Awaited` would suffice.
+ return getTypeAliasInstantiation(awaitedSymbol, [unwrapAwaitedType(type)]);
+ }
+ }
+ }
+
+ Debug.assert(getPromisedTypeOfPromise(type) === undefined, "type provided should not be a non-generic 'promise'-like.");
+ return type;
+ }
+
/**
* Gets the "awaited type" of a type.
*
@@ -34867,25 +35868,35 @@ namespace ts {
* This is used to reflect the runtime behavior of the `await` keyword.
*/
function getAwaitedType(type: Type, errorNode?: Node, diagnosticMessage?: DiagnosticMessage, arg0?: string | number): Type | undefined {
+ const awaitedType = getAwaitedTypeNoAlias(type, errorNode, diagnosticMessage, arg0);
+ return awaitedType && createAwaitedTypeIfNeeded(awaitedType);
+ }
+
+ /**
+ * Gets the "awaited type" of a type without introducing an `Awaited` wrapper.
+ *
+ * @see {@link getAwaitedType}
+ */
+ function getAwaitedTypeNoAlias(type: Type, errorNode?: Node, diagnosticMessage?: DiagnosticMessage, arg0?: string | number): Type | undefined {
if (isTypeAny(type)) {
return type;
}
+ // If this is already an `Awaited`, just return it. This avoids `Awaited>` in higher-order
+ if (isAwaitedTypeInstantiation(type)) {
+ return type;
+ }
+
+ // If we've already cached an awaited type, return a possible `Awaited` for it.
const typeAsAwaitable = type as PromiseOrAwaitableType;
if (typeAsAwaitable.awaitedTypeOfType) {
return typeAsAwaitable.awaitedTypeOfType;
}
// For a union, get a union of the awaited types of each constituent.
- //
- return typeAsAwaitable.awaitedTypeOfType =
- mapType(type, errorNode ? constituentType => getAwaitedTypeWorker(constituentType, errorNode, diagnosticMessage, arg0) : getAwaitedTypeWorker);
- }
-
- function getAwaitedTypeWorker(type: Type, errorNode?: Node, diagnosticMessage?: DiagnosticMessage, arg0?: string | number): Type | undefined {
- const typeAsAwaitable = type as PromiseOrAwaitableType;
- if (typeAsAwaitable.awaitedTypeOfType) {
- return typeAsAwaitable.awaitedTypeOfType;
+ if (type.flags & TypeFlags.Union) {
+ const mapper = errorNode ? (constituentType: Type) => getAwaitedTypeNoAlias(constituentType, errorNode, diagnosticMessage, arg0) : getAwaitedTypeNoAlias;
+ return typeAsAwaitable.awaitedTypeOfType = mapType(type, mapper);
}
const promisedType = getPromisedTypeOfPromise(type);
@@ -34933,7 +35944,7 @@ namespace ts {
// Keep track of the type we're about to unwrap to avoid bad recursive promise types.
// See the comments above for more information.
awaitedTypeStack.push(type.id);
- const awaitedType = getAwaitedType(promisedType, errorNode, diagnosticMessage, arg0);
+ const awaitedType = getAwaitedTypeNoAlias(promisedType, errorNode, diagnosticMessage, arg0);
awaitedTypeStack.pop();
if (!awaitedType) {
@@ -34960,7 +35971,7 @@ namespace ts {
// be treated as a promise, they can cast to .
if (isThenableType(type)) {
if (errorNode) {
- if (!diagnosticMessage) return Debug.fail();
+ Debug.assertIsDefined(diagnosticMessage);
error(errorNode, diagnosticMessage, arg0);
}
return undefined;
@@ -35009,14 +36020,14 @@ namespace ts {
const returnType = getTypeFromTypeNode(returnTypeNode);
if (languageVersion >= ScriptTarget.ES2015) {
- if (returnType === errorType) {
+ if (isErrorType(returnType)) {
return;
}
const globalPromiseType = getGlobalPromiseType(/*reportErrors*/ true);
if (globalPromiseType !== emptyGenericType && !isReferenceToType(returnType, globalPromiseType)) {
// The promise type was not a valid type reference to the global promise type, so we
// report an error and return the unknown type.
- error(returnTypeNode, Diagnostics.The_return_type_of_an_async_function_or_method_must_be_the_global_Promise_T_type_Did_you_mean_to_write_Promise_0, typeToString(getAwaitedType(returnType) || voidType));
+ error(returnTypeNode, Diagnostics.The_return_type_of_an_async_function_or_method_must_be_the_global_Promise_T_type_Did_you_mean_to_write_Promise_0, typeToString(getAwaitedTypeNoAlias(returnType) || voidType));
return;
}
}
@@ -35024,7 +36035,7 @@ namespace ts {
// Always mark the type node as referenced if it points to a value
markTypeNodeAsReferenced(returnTypeNode);
- if (returnType === errorType) {
+ if (isErrorType(returnType)) {
return;
}
@@ -35036,7 +36047,7 @@ namespace ts {
const promiseConstructorSymbol = resolveEntityName(promiseConstructorName, SymbolFlags.Value, /*ignoreErrors*/ true);
const promiseConstructorType = promiseConstructorSymbol ? getTypeOfSymbol(promiseConstructorSymbol) : errorType;
- if (promiseConstructorType === errorType) {
+ if (isErrorType(promiseConstructorType)) {
if (promiseConstructorName.kind === SyntaxKind.Identifier && promiseConstructorName.escapedText === "Promise" && getTargetType(returnType) === getGlobalPromiseType(/*reportErrors*/ false)) {
error(returnTypeNode, Diagnostics.An_async_function_or_method_in_ES5_SlashES3_requires_the_Promise_constructor_Make_sure_you_have_a_declaration_for_the_Promise_constructor_or_include_ES2015_in_your_lib_option);
}
@@ -35069,7 +36080,7 @@ namespace ts {
return;
}
}
- checkAwaitedType(returnType, node, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
+ checkAwaitedType(returnType, /*withAlias*/ false, node, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
}
/** Check a decorator */
@@ -35081,34 +36092,26 @@ namespace ts {
return;
}
+ let headMessage: DiagnosticMessage;
let expectedReturnType: Type;
- const headMessage = getDiagnosticHeadMessageForDecoratorResolution(node);
- let errorInfo: DiagnosticMessageChain | undefined;
switch (node.parent.kind) {
case SyntaxKind.ClassDeclaration:
+ headMessage = Diagnostics.Decorator_function_return_type_0_is_not_assignable_to_type_1;
const classSymbol = getSymbolOfNode(node.parent);
const classConstructorType = getTypeOfSymbol(classSymbol);
expectedReturnType = getUnionType([classConstructorType, voidType]);
break;
- case SyntaxKind.Parameter:
- expectedReturnType = voidType;
- errorInfo = chainDiagnosticMessages(
- /*details*/ undefined,
- Diagnostics.The_return_type_of_a_parameter_decorator_function_must_be_either_void_or_any);
-
- break;
-
case SyntaxKind.PropertyDeclaration:
+ case SyntaxKind.Parameter:
+ headMessage = Diagnostics.Decorator_function_return_type_is_0_but_is_expected_to_be_void_or_any;
expectedReturnType = voidType;
- errorInfo = chainDiagnosticMessages(
- /*details*/ undefined,
- Diagnostics.The_return_type_of_a_property_decorator_function_must_be_either_void_or_any);
break;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
+ headMessage = Diagnostics.Decorator_function_return_type_0_is_not_assignable_to_type_1;
const methodType = getTypeOfNode(node.parent);
const descriptorType = createTypedPropertyDescriptorType(methodType);
expectedReturnType = getUnionType([descriptorType, voidType]);
@@ -35122,8 +36125,7 @@ namespace ts {
returnType,
expectedReturnType,
node,
- headMessage,
- () => errorInfo);
+ headMessage);
}
/**
@@ -35327,40 +36329,7 @@ namespace ts {
function checkJSDocParameterTag(node: JSDocParameterTag) {
checkSourceElement(node.typeExpression);
- if (!getParameterSymbolFromJSDoc(node)) {
- const decl = getHostSignatureFromJSDoc(node);
- // don't issue an error for invalid hosts -- just functions --
- // and give a better error message when the host function mentions `arguments`
- // but the tag doesn't have an array type
- if (decl) {
- const i = getJSDocTags(decl).filter(isJSDocParameterTag).indexOf(node);
- if (i > -1 && i < decl.parameters.length && isBindingPattern(decl.parameters[i].name)) {
- return;
- }
- if (!containsArgumentsReference(decl)) {
- if (isQualifiedName(node.name)) {
- error(node.name,
- Diagnostics.Qualified_name_0_is_not_allowed_without_a_leading_param_object_1,
- entityNameToString(node.name),
- entityNameToString(node.name.left));
- }
- else {
- error(node.name,
- Diagnostics.JSDoc_param_tag_has_name_0_but_there_is_no_parameter_with_that_name,
- idText(node.name));
- }
- }
- else if (findLast(getJSDocTags(decl), isJSDocParameterTag) === node &&
- node.typeExpression && node.typeExpression.type &&
- !isArrayType(getTypeFromTypeNode(node.typeExpression.type))) {
- error(node.name,
- Diagnostics.JSDoc_param_tag_has_name_0_but_there_is_no_parameter_with_that_name_It_would_match_arguments_if_it_had_an_array_type,
- idText(node.name.kind === SyntaxKind.QualifiedName ? node.name.right : node.name));
- }
- }
- }
}
-
function checkJSDocPropertyTag(node: JSDocPropertyTag) {
checkSourceElement(node.typeExpression);
}
@@ -35402,6 +36371,13 @@ namespace ts {
}
}
+ function checkJSDocAccessibilityModifiers(node: JSDocPublicTag | JSDocProtectedTag | JSDocPrivateTag): void {
+ const host = getJSDocHost(node);
+ if (host && isPrivateIdentifierClassElementDeclaration(host)) {
+ error(node, Diagnostics.An_accessibility_modifier_cannot_be_used_with_a_private_identifier);
+ }
+ }
+
function getIdentifierFromEntityNameExpression(node: Identifier | PropertyAccessExpression): Identifier | PrivateIdentifier;
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | PrivateIdentifier | undefined;
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | PrivateIdentifier | undefined {
@@ -35898,7 +36874,7 @@ namespace ts {
function checkCollisionWithRequireExportsInGeneratedCode(node: Node, name: Identifier | undefined) {
// No need to check for require or exports for ES6 modules and later
- if (moduleKind >= ModuleKind.ES2015) {
+ if (moduleKind >= ModuleKind.ES2015 && !(moduleKind >= ModuleKind.Node12 && getSourceFileOfNode(node).impliedNodeFormat === ModuleKind.CommonJS)) {
return;
}
@@ -36121,7 +37097,8 @@ namespace ts {
// check private/protected variable access
const parent = node.parent.parent;
- const parentType = getTypeForBindingElementParent(parent);
+ const parentCheckMode = node.dotDotDotToken ? CheckMode.RestBindingElement : CheckMode.Normal;
+ const parentType = getTypeForBindingElementParent(parent, parentCheckMode);
const name = node.propertyName || node.name;
if (parentType && !isBindingPattern(name)) {
const exprType = getLiteralTypeFromPropertyName(name);
@@ -36157,7 +37134,7 @@ namespace ts {
// Don't validate for-in initializer as it is already an error
const widenedType = getWidenedTypeForVariableLikeDeclaration(node);
if (needCheckInitializer) {
- const initializerType = checkExpressionCached(node.initializer!);
+ const initializerType = checkExpressionCached(node.initializer);
if (strictNullChecks && needCheckWidenedType) {
checkNonNullNonVoidType(initializerType, node);
}
@@ -36179,7 +37156,7 @@ namespace ts {
}
// For a commonjs `const x = require`, validate the alias and exit
const symbol = getSymbolOfNode(node);
- if (symbol.flags & SymbolFlags.Alias && isRequireVariableDeclaration(node)) {
+ if (symbol.flags & SymbolFlags.Alias && isVariableDeclarationInitializedToBareOrAccessedRequire(node)) {
checkAliasSymbol(node);
return;
}
@@ -36209,7 +37186,7 @@ namespace ts {
// initializer is consistent with type associated with the node
const declarationType = convertAutoToAny(getWidenedTypeForVariableLikeDeclaration(node));
- if (type !== errorType && declarationType !== errorType &&
+ if (!isErrorType(type) && !isErrorType(declarationType) &&
!isTypeIdenticalTo(type, declarationType) &&
!(symbol.flags & SymbolFlags.Assignment)) {
errorNextVariableOrPropertyDeclarationMustHaveSameType(symbol.valueDeclaration, type, node, declarationType);
@@ -36273,7 +37250,7 @@ namespace ts {
}
function checkVariableDeclaration(node: VariableDeclaration) {
- tracing?.push(tracing.Phase.Check, "checkVariableDeclaration", { kind: node.kind, pos: node.pos, end: node.end });
+ tracing?.push(tracing.Phase.Check, "checkVariableDeclaration", { kind: node.kind, pos: node.pos, end: node.end, path: (node as TracingNode).tracingPath });
checkGrammarVariableDeclaration(node);
checkVariableLikeDeclaration(node);
tracing?.pop();
@@ -36888,6 +37865,10 @@ namespace ts {
if (iterationTypes === noIterationTypes) return noIterationTypes;
if (iterationTypes === anyIterationTypes) return anyIterationTypes;
const { yieldType, returnType, nextType } = iterationTypes;
+ // if we're requesting diagnostics, report errors for a missing `Awaited`.
+ if (errorNode) {
+ getGlobalAwaitedSymbol(/*reportErrors*/ true);
+ }
return createIterationTypes(
getAwaitedType(yieldType, errorNode) || anyType,
getAwaitedType(returnType, errorNode) || anyType,
@@ -36914,7 +37895,9 @@ namespace ts {
getIterationTypesOfIterableCached(type, asyncIterationTypesResolver) ||
getIterationTypesOfIterableFast(type, asyncIterationTypesResolver);
if (iterationTypes) {
- return iterationTypes;
+ return use & IterationUse.ForOfFlag ?
+ getAsyncFromSyncIterationTypes(iterationTypes, errorNode) :
+ iterationTypes;
}
}
@@ -37003,7 +37986,7 @@ namespace ts {
// While we define these as `any` and `undefined` in our libs by default, a custom lib *could* use
// different definitions.
const { returnType, nextType } = getIterationTypesOfGlobalIterableType(globalType, resolver);
- return setCachedIterationTypes(type, resolver.iterableCacheKey, createIterationTypes(yieldType, returnType, nextType));
+ return setCachedIterationTypes(type, resolver.iterableCacheKey, createIterationTypes(resolver.resolveIterationType(yieldType, /*errorNode*/ undefined) || yieldType, resolver.resolveIterationType(returnType, /*errorNode*/ undefined) || returnType, nextType));
}
// As an optimization, if the type is an instantiation of the following global type, then
@@ -37011,7 +37994,7 @@ namespace ts {
// - `Generator` or `AsyncGenerator`
if (isReferenceToType(type, resolver.getGlobalGeneratorType(/*reportErrors*/ false))) {
const [yieldType, returnType, nextType] = getTypeArguments(type as GenericType);
- return setCachedIterationTypes(type, resolver.iterableCacheKey, createIterationTypes(yieldType, returnType, nextType));
+ return setCachedIterationTypes(type, resolver.iterableCacheKey, createIterationTypes(resolver.resolveIterationType(yieldType, /*errorNode*/ undefined) || yieldType, resolver.resolveIterationType(returnType, /*errorNode*/ undefined) || returnType, nextType));
}
}
@@ -37343,8 +38326,8 @@ namespace ts {
function unwrapReturnType(returnType: Type, functionFlags: FunctionFlags) {
const isGenerator = !!(functionFlags & FunctionFlags.Generator);
const isAsync = !!(functionFlags & FunctionFlags.Async);
- return isGenerator ? getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Return, returnType, isAsync) ?? errorType :
- isAsync ? getAwaitedType(returnType) ?? errorType :
+ return isGenerator ? getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Return, returnType, isAsync) || errorType :
+ isAsync ? getAwaitedTypeNoAlias(returnType) || errorType :
returnType;
}
@@ -37388,7 +38371,7 @@ namespace ts {
else if (getReturnTypeFromAnnotation(container)) {
const unwrappedReturnType = unwrapReturnType(returnType, functionFlags) ?? returnType;
const unwrappedExprType = functionFlags & FunctionFlags.Async
- ? checkAwaitedType(exprType, node, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member)
+ ? checkAwaitedType(exprType, /*withAlias*/ false, node, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member)
: exprType;
if (unwrappedReturnType) {
// If the function has a return type, but promisedType is
@@ -37513,7 +38496,7 @@ namespace ts {
const declaration = catchClause.variableDeclaration;
const typeNode = getEffectiveTypeAnnotationNode(getRootDeclaration(declaration));
if (typeNode) {
- const type = getTypeForVariableLikeDeclaration(declaration, /*includeOptionality*/ false);
+ const type = getTypeForVariableLikeDeclaration(declaration, /*includeOptionality*/ false, CheckMode.Normal);
if (type && !(type.flags & TypeFlags.AnyOrUnknown)) {
grammarErrorOnFirstToken(typeNode, Diagnostics.Catch_clause_variable_type_annotation_must_be_any_or_unknown_if_specified);
}
@@ -37542,7 +38525,7 @@ namespace ts {
}
}
- function checkIndexConstraints(type: Type, isStaticIndex?: boolean) {
+ function checkIndexConstraints(type: Type, symbol: Symbol, isStaticIndex?: boolean) {
const indexInfos = getIndexInfosOfType(type);
if (indexInfos.length === 0) {
return;
@@ -37552,7 +38535,7 @@ namespace ts {
checkIndexConstraintForProperty(type, prop, getLiteralTypeFromProperty(prop, TypeFlags.StringOrNumberLiteralOrUnique, /*includeNonPublic*/ true), getNonMissingTypeOfSymbol(prop));
}
}
- const typeDeclaration = type.symbol.valueDeclaration;
+ const typeDeclaration = symbol.valueDeclaration;
if (typeDeclaration && isClassLike(typeDeclaration)) {
for (const member of typeDeclaration.members) {
// Only process instance properties with computed names here. Static properties cannot be in conflict with indexers,
@@ -37636,12 +38619,51 @@ namespace ts {
* The name cannot be used as 'Object' of user defined types with special target.
*/
function checkClassNameCollisionWithObject(name: Identifier): void {
- if (languageVersion === ScriptTarget.ES5 && name.escapedText === "Object"
- && moduleKind < ModuleKind.ES2015) {
+ if (languageVersion >= ScriptTarget.ES5 && name.escapedText === "Object"
+ && (moduleKind < ModuleKind.ES2015 || getSourceFileOfNode(name).impliedNodeFormat === ModuleKind.CommonJS)) {
error(name, Diagnostics.Class_name_cannot_be_Object_when_targeting_ES5_with_module_0, ModuleKind[moduleKind]); // https://github.com/Microsoft/TypeScript/issues/17494
}
}
+ function checkUnmatchedJSDocParameters(node: SignatureDeclaration) {
+ const jsdocParameters = filter(getJSDocTags(node), isJSDocParameterTag);
+ if (!length(jsdocParameters)) return;
+
+ const isJs = isInJSFile(node);
+ const parameters = new Set<__String>();
+ const excludedParameters = new Set();
+ forEach(node.parameters, ({ name }, index) => {
+ if (isIdentifier(name)) {
+ parameters.add(name.escapedText);
+ }
+ if (isBindingPattern(name)) {
+ excludedParameters.add(index);
+ }
+ });
+
+ const containsArguments = containsArgumentsReference(node);
+ if (containsArguments) {
+ const lastJSDocParam = lastOrUndefined(jsdocParameters);
+ if (lastJSDocParam && isIdentifier(lastJSDocParam.name) && lastJSDocParam.typeExpression &&
+ lastJSDocParam.typeExpression.type && !parameters.has(lastJSDocParam.name.escapedText) && !isArrayType(getTypeFromTypeNode(lastJSDocParam.typeExpression.type))) {
+ errorOrSuggestion(isJs, lastJSDocParam.name, Diagnostics.JSDoc_param_tag_has_name_0_but_there_is_no_parameter_with_that_name_It_would_match_arguments_if_it_had_an_array_type, idText(lastJSDocParam.name));
+ }
+ }
+ else {
+ forEach(jsdocParameters, ({ name }, index) => {
+ if (excludedParameters.has(index) || isIdentifier(name) && parameters.has(name.escapedText)) {
+ return;
+ }
+ if (isQualifiedName(name)) {
+ errorOrSuggestion(isJs, name, Diagnostics.Qualified_name_0_is_not_allowed_without_a_leading_param_object_1, entityNameToString(name), entityNameToString(name.left));
+ }
+ else {
+ errorOrSuggestion(isJs, name, Diagnostics.JSDoc_param_tag_has_name_0_but_there_is_no_parameter_with_that_name, idText(name));
+ }
+ });
+ }
+ }
+
/**
* Check each type parameter and check that type parameters have no duplicate type parameter declarations
*/
@@ -37863,7 +38885,7 @@ namespace ts {
}
}
- checkMembersForMissingOverrideModifier(node, type, typeWithThis, staticType);
+ checkMembersForOverrideModifier(node, type, typeWithThis, staticType);
const implementedTypeNodes = getEffectiveImplementsTypeNodes(node);
if (implementedTypeNodes) {
@@ -37874,7 +38896,7 @@ namespace ts {
checkTypeReferenceNode(typeRefNode);
if (produceDiagnostics) {
const t = getReducedType(getTypeFromTypeNode(typeRefNode));
- if (t !== errorType) {
+ if (!isErrorType(t)) {
if (isValidBaseType(t)) {
const genericDiag = t.symbol && t.symbol.flags & SymbolFlags.Class ?
Diagnostics.Class_0_incorrectly_implements_class_1_Did_you_mean_to_extend_1_and_inherit_its_members_as_a_subclass :
@@ -37893,15 +38915,14 @@ namespace ts {
}
if (produceDiagnostics) {
- checkIndexConstraints(type);
- checkIndexConstraints(staticType, /*isStaticIndex*/ true);
+ checkIndexConstraints(type, symbol);
+ checkIndexConstraints(staticType, symbol, /*isStaticIndex*/ true);
checkTypeForDuplicateIndexSignatures(node);
checkPropertyInitialization(node);
}
}
- function checkMembersForMissingOverrideModifier(node: ClassLikeDeclaration, type: InterfaceType, typeWithThis: Type, staticType: ObjectType) {
- const nodeInAmbientContext = !!(node.flags & NodeFlags.Ambient);
+ function checkMembersForOverrideModifier(node: ClassLikeDeclaration, type: InterfaceType, typeWithThis: Type, staticType: ObjectType) {
const baseTypeNode = getEffectiveBaseTypeNode(node);
const baseTypes = baseTypeNode && getBaseTypes(type);
const baseWithThis = baseTypes?.length ? getTypeWithThisArgument(first(baseTypes), type.thisType) : undefined;
@@ -37915,56 +38936,163 @@ namespace ts {
if (isConstructorDeclaration(member)) {
forEach(member.parameters, param => {
if (isParameterPropertyDeclaration(param, member)) {
- checkClassMember(param, /*memberIsParameterProperty*/ true);
+ checkExistingMemberForOverrideModifier(
+ node,
+ staticType,
+ baseStaticType,
+ baseWithThis,
+ type,
+ typeWithThis,
+ param,
+ /* memberIsParameterProperty */ true
+ );
}
});
}
- checkClassMember(member);
+ checkExistingMemberForOverrideModifier(
+ node,
+ staticType,
+ baseStaticType,
+ baseWithThis,
+ type,
+ typeWithThis,
+ member,
+ /* memberIsParameterProperty */ false,
+ );
+ }
+ }
+
+ /**
+ * @param member Existing member node to be checked.
+ * Note: `member` cannot be a synthetic node.
+ */
+ function checkExistingMemberForOverrideModifier(
+ node: ClassLikeDeclaration,
+ staticType: ObjectType,
+ baseStaticType: Type,
+ baseWithThis: Type | undefined,
+ type: InterfaceType,
+ typeWithThis: Type,
+ member: ClassElement | ParameterPropertyDeclaration,
+ memberIsParameterProperty: boolean,
+ reportErrors = true,
+ ): MemberOverrideStatus {
+ const declaredProp = member.name
+ && getSymbolAtLocation(member.name)
+ || getSymbolAtLocation(member);
+ if (!declaredProp) {
+ return MemberOverrideStatus.Ok;
}
- function checkClassMember(member: ClassElement | ParameterPropertyDeclaration, memberIsParameterProperty?: boolean) {
- const hasOverride = hasOverrideModifier(member);
- const hasStatic = isStatic(member);
- if (baseWithThis && (hasOverride || compilerOptions.noImplicitOverride)) {
- const declaredProp = member.name && getSymbolAtLocation(member.name) || getSymbolAtLocation(member);
- if (!declaredProp) {
- return;
- }
+ return checkMemberForOverrideModifier(
+ node,
+ staticType,
+ baseStaticType,
+ baseWithThis,
+ type,
+ typeWithThis,
+ hasOverrideModifier(member),
+ hasAbstractModifier(member),
+ isStatic(member),
+ memberIsParameterProperty,
+ symbolName(declaredProp),
+ reportErrors ? member : undefined,
+ );
+ }
- const thisType = hasStatic ? staticType : typeWithThis;
- const baseType = hasStatic ? baseStaticType : baseWithThis;
- const prop = getPropertyOfType(thisType, declaredProp.escapedName);
- const baseProp = getPropertyOfType(baseType, declaredProp.escapedName);
+ /**
+ * Checks a class member declaration for either a missing or an invalid `override` modifier.
+ * Note: this function can be used for speculative checking,
+ * i.e. checking a member that does not yet exist in the program.
+ * An example of that would be to call this function in a completions scenario,
+ * when offering a method declaration as completion.
+ * @param errorNode The node where we should report an error, or undefined if we should not report errors.
+ */
+ function checkMemberForOverrideModifier(
+ node: ClassLikeDeclaration,
+ staticType: ObjectType,
+ baseStaticType: Type,
+ baseWithThis: Type | undefined,
+ type: InterfaceType,
+ typeWithThis: Type,
+ memberHasOverrideModifier: boolean,
+ memberHasAbstractModifier: boolean,
+ memberIsStatic: boolean,
+ memberIsParameterProperty: boolean,
+ memberName: string,
+ errorNode?: Node,
+ ): MemberOverrideStatus {
+ const isJs = isInJSFile(node);
+ const nodeInAmbientContext = !!(node.flags & NodeFlags.Ambient);
+ if (baseWithThis && (memberHasOverrideModifier || compilerOptions.noImplicitOverride)) {
+ const memberEscapedName = escapeLeadingUnderscores(memberName);
+ const thisType = memberIsStatic ? staticType : typeWithThis;
+ const baseType = memberIsStatic ? baseStaticType : baseWithThis;
+ const prop = getPropertyOfType(thisType, memberEscapedName);
+ const baseProp = getPropertyOfType(baseType, memberEscapedName);
- const baseClassName = typeToString(baseWithThis);
- if (prop && !baseProp && hasOverride) {
- const suggestion = getSuggestedSymbolForNonexistentClassMember(symbolName(declaredProp), baseType);
+ const baseClassName = typeToString(baseWithThis);
+ if (prop && !baseProp && memberHasOverrideModifier) {
+ if (errorNode) {
+ const suggestion = getSuggestedSymbolForNonexistentClassMember(memberName, baseType); // Again, using symbol name: note that's different from `symbol.escapedName`
suggestion ?
- error(member, Diagnostics.This_member_cannot_have_an_override_modifier_because_it_is_not_declared_in_the_base_class_0_Did_you_mean_1, baseClassName, symbolToString(suggestion)) :
- error(member, Diagnostics.This_member_cannot_have_an_override_modifier_because_it_is_not_declared_in_the_base_class_0, baseClassName);
- }
- else if (prop && baseProp?.declarations && compilerOptions.noImplicitOverride && !nodeInAmbientContext) {
- const baseHasAbstract = some(baseProp.declarations, hasAbstractModifier);
- if (hasOverride) {
- return;
- }
-
- if (!baseHasAbstract) {
- const diag = memberIsParameterProperty ?
- Diagnostics.This_parameter_property_must_have_an_override_modifier_because_it_overrides_a_member_in_base_class_0 :
- Diagnostics.This_member_must_have_an_override_modifier_because_it_overrides_a_member_in_the_base_class_0;
- error(member, diag, baseClassName);
- }
- else if (hasAbstractModifier(member) && baseHasAbstract) {
- error(member, Diagnostics.This_member_must_have_an_override_modifier_because_it_overrides_an_abstract_method_that_is_declared_in_the_base_class_0, baseClassName);
- }
+ error(
+ errorNode,
+ isJs ?
+ Diagnostics.This_member_cannot_have_a_JSDoc_comment_with_an_override_tag_because_it_is_not_declared_in_the_base_class_0_Did_you_mean_1 :
+ Diagnostics.This_member_cannot_have_an_override_modifier_because_it_is_not_declared_in_the_base_class_0_Did_you_mean_1,
+ baseClassName,
+ symbolToString(suggestion)) :
+ error(
+ errorNode,
+ isJs ?
+ Diagnostics.This_member_cannot_have_a_JSDoc_comment_with_an_override_tag_because_it_is_not_declared_in_the_base_class_0 :
+ Diagnostics.This_member_cannot_have_an_override_modifier_because_it_is_not_declared_in_the_base_class_0,
+ baseClassName);
}
+ return MemberOverrideStatus.HasInvalidOverride;
}
- else if (hasOverride) {
- const className = typeToString(type);
- error(member, Diagnostics.This_member_cannot_have_an_override_modifier_because_its_containing_class_0_does_not_extend_another_class, className);
+ else if (prop && baseProp?.declarations && compilerOptions.noImplicitOverride && !nodeInAmbientContext) {
+ const baseHasAbstract = some(baseProp.declarations, hasAbstractModifier);
+ if (memberHasOverrideModifier) {
+ return MemberOverrideStatus.Ok;
+ }
+
+ if (!baseHasAbstract) {
+ if (errorNode) {
+ const diag = memberIsParameterProperty ?
+ isJs ?
+ Diagnostics.This_parameter_property_must_have_a_JSDoc_comment_with_an_override_tag_because_it_overrides_a_member_in_the_base_class_0 :
+ Diagnostics.This_parameter_property_must_have_an_override_modifier_because_it_overrides_a_member_in_base_class_0 :
+ isJs ?
+ Diagnostics.This_member_must_have_a_JSDoc_comment_with_an_override_tag_because_it_overrides_a_member_in_the_base_class_0 :
+ Diagnostics.This_member_must_have_an_override_modifier_because_it_overrides_a_member_in_the_base_class_0;
+ error(errorNode, diag, baseClassName);
+ }
+ return MemberOverrideStatus.NeedsOverride;
+ }
+ else if (memberHasAbstractModifier && baseHasAbstract) {
+ if (errorNode) {
+ error(errorNode, Diagnostics.This_member_must_have_an_override_modifier_because_it_overrides_an_abstract_method_that_is_declared_in_the_base_class_0, baseClassName);
+ }
+ return MemberOverrideStatus.NeedsOverride;
+ }
}
}
+ else if (memberHasOverrideModifier) {
+ if (errorNode) {
+ const className = typeToString(type);
+ error(
+ errorNode,
+ isJs ?
+ Diagnostics.This_member_cannot_have_a_JSDoc_comment_with_an_override_tag_because_its_containing_class_0_does_not_extend_another_class :
+ Diagnostics.This_member_cannot_have_an_override_modifier_because_its_containing_class_0_does_not_extend_another_class,
+ className);
+ }
+ return MemberOverrideStatus.HasInvalidOverride;
+ }
+
+ return MemberOverrideStatus.Ok;
}
function issueMemberSpecificError(node: ClassLikeDeclaration, typeWithThis: Type, baseWithThis: Type, broadDiag: DiagnosticMessage) {
@@ -38011,6 +39139,48 @@ namespace ts {
}
}
+ /**
+ * Checks a member declaration node to see if has a missing or invalid `override` modifier.
+ * @param node Class-like node where the member is declared.
+ * @param member Member declaration node.
+ * Note: `member` can be a synthetic node without a parent.
+ */
+ function getMemberOverrideModifierStatus(node: ClassLikeDeclaration, member: ClassElement): MemberOverrideStatus {
+ if (!member.name) {
+ return MemberOverrideStatus.Ok;
+ }
+
+ const symbol = getSymbolOfNode(node);
+ const type = getDeclaredTypeOfSymbol(symbol) as InterfaceType;
+ const typeWithThis = getTypeWithThisArgument(type);
+ const staticType = getTypeOfSymbol(symbol) as ObjectType;
+
+ const baseTypeNode = getEffectiveBaseTypeNode(node);
+ const baseTypes = baseTypeNode && getBaseTypes(type);
+ const baseWithThis = baseTypes?.length ? getTypeWithThisArgument(first(baseTypes), type.thisType) : undefined;
+ const baseStaticType = getBaseConstructorTypeOfClass(type);
+
+ const memberHasOverrideModifier = member.parent
+ ? hasOverrideModifier(member)
+ : hasSyntacticModifier(member, ModifierFlags.Override);
+
+ const memberName = unescapeLeadingUnderscores(getTextOfPropertyName(member.name));
+
+ return checkMemberForOverrideModifier(
+ node,
+ staticType,
+ baseStaticType,
+ baseWithThis,
+ type,
+ typeWithThis,
+ memberHasOverrideModifier,
+ hasAbstractModifier(member),
+ isStatic(member),
+ /* memberIsParameterProperty */ false,
+ memberName,
+ );
+ }
+
function getTargetSymbol(s: Symbol) {
// if symbol is instantiated its flags are not copied from the 'target'
// so we'll need to get back original 'target' symbol to work with correct set of flags
@@ -38174,7 +39344,7 @@ namespace ts {
const properties = getPropertiesOfType(getTypeWithThisArgument(base, type.thisType));
for (const prop of properties) {
const existing = seen.get(prop.escapedName);
- if (existing && !isPropertyIdenticalTo(existing, prop)) {
+ if (existing && prop.parent === existing.parent) {
seen.delete(prop.escapedName);
}
}
@@ -38300,7 +39470,7 @@ namespace ts {
for (const baseType of getBaseTypes(type)) {
checkTypeAssignableTo(typeWithThis, getTypeWithThisArgument(baseType, type.thisType), node.name, Diagnostics.Interface_0_incorrectly_extends_interface_1);
}
- checkIndexConstraints(type);
+ checkIndexConstraints(type, symbol);
}
}
checkObjectTypeForDuplicateDeclarations(node);
@@ -38464,16 +39634,15 @@ namespace ts {
return nodeIsMissing(expr) ? 0 : evaluateEnumMember(expr, getSymbolOfNode(member.parent), identifier.escapedText);
case SyntaxKind.ElementAccessExpression:
case SyntaxKind.PropertyAccessExpression:
- const ex = expr as AccessExpression;
- if (isConstantMemberAccess(ex)) {
- const type = getTypeOfExpression(ex.expression);
+ if (isConstantMemberAccess(expr)) {
+ const type = getTypeOfExpression(expr.expression);
if (type.symbol && type.symbol.flags & SymbolFlags.Enum) {
let name: __String;
- if (ex.kind === SyntaxKind.PropertyAccessExpression) {
- name = ex.name.escapedText;
+ if (expr.kind === SyntaxKind.PropertyAccessExpression) {
+ name = expr.name.escapedText;
}
else {
- name = escapeLeadingUnderscores(cast(ex.argumentExpression, isLiteralExpression).text);
+ name = escapeLeadingUnderscores(cast(expr.argumentExpression, isLiteralExpression).text);
}
return evaluateEnumMember(expr, type.symbol, name);
}
@@ -38488,7 +39657,7 @@ namespace ts {
if (memberSymbol) {
const declaration = memberSymbol.valueDeclaration;
if (declaration !== member) {
- if (declaration && isBlockScopedNameDeclaredBeforeUse(declaration, member)) {
+ if (declaration && isBlockScopedNameDeclaredBeforeUse(declaration, member) && isEnumDeclaration(declaration.parent)) {
return getEnumMemberValue(declaration as EnumMember);
}
error(expr, Diagnostics.A_member_initializer_in_a_enum_declaration_cannot_reference_members_declared_after_it_including_members_defined_in_other_enums);
@@ -38502,7 +39671,12 @@ namespace ts {
}
}
- function isConstantMemberAccess(node: Expression): boolean {
+ function isConstantMemberAccess(node: Expression): node is AccessExpression {
+ const type = getTypeOfExpression(node);
+ if(type === errorType) {
+ return false;
+ }
+
return node.kind === SyntaxKind.Identifier ||
node.kind === SyntaxKind.PropertyAccessExpression && isConstantMemberAccess((node as PropertyAccessExpression).expression) ||
node.kind === SyntaxKind.ElementAccessExpression && isConstantMemberAccess((node as ElementAccessExpression).expression) &&
@@ -38613,7 +39787,7 @@ namespace ts {
const isAmbientExternalModule: boolean = isAmbientModule(node);
const contextErrorMessage = isAmbientExternalModule
? Diagnostics.An_ambient_module_declaration_is_only_allowed_at_the_top_level_in_a_file
- : Diagnostics.A_namespace_declaration_is_only_allowed_in_a_namespace_or_module;
+ : Diagnostics.A_namespace_declaration_is_only_allowed_at_the_top_level_of_a_namespace_or_module;
if (checkGrammarModuleElementContext(node, contextErrorMessage)) {
// If we hit a module declaration in an illegal context, just bail out to avoid cascading errors.
return;
@@ -38801,6 +39975,16 @@ namespace ts {
return false;
}
}
+ if (!isImportEqualsDeclaration(node) && node.assertClause) {
+ let hasError = false;
+ for (const clause of node.assertClause.elements) {
+ if (!isStringLiteral(clause.value)) {
+ hasError = true;
+ error(clause.value, Diagnostics.Import_assertion_values_must_be_string_literal_expressions);
+ }
+ }
+ return !hasError;
+ }
return true;
}
@@ -38849,6 +40033,9 @@ namespace ts {
name
);
}
+ if (isType && node.kind === SyntaxKind.ImportEqualsDeclaration && hasEffectiveModifier(node, ModifierFlags.Export)) {
+ error(node, Diagnostics.Cannot_use_export_import_on_a_type_or_type_only_namespace_when_the_isolatedModules_flag_is_provided);
+ }
break;
}
case SyntaxKind.ExportSpecifier: {
@@ -38872,25 +40059,76 @@ namespace ts {
}
}
- if (isImportSpecifier(node) && target.declarations?.every(d => !!(getCombinedNodeFlags(d) & NodeFlags.Deprecated))) {
- addDeprecatedSuggestion(node.name, target.declarations, symbol.escapedName as string);
+ if (isImportSpecifier(node)) {
+ const targetSymbol = checkDeprecatedAliasedSymbol(symbol, node);
+ if (isDeprecatedAliasedSymbol(targetSymbol) && targetSymbol.declarations) {
+ addDeprecatedSuggestion(node, targetSymbol.declarations, targetSymbol.escapedName as string);
+ }
}
}
}
+ function isDeprecatedAliasedSymbol(symbol: Symbol) {
+ return !!symbol.declarations && every(symbol.declarations, d => !!(getCombinedNodeFlags(d) & NodeFlags.Deprecated));
+ }
+
+ function checkDeprecatedAliasedSymbol(symbol: Symbol, location: Node) {
+ if (!(symbol.flags & SymbolFlags.Alias)) return symbol;
+
+ const targetSymbol = resolveAlias(symbol);
+ if (targetSymbol === unknownSymbol) return targetSymbol;
+
+ while (symbol.flags & SymbolFlags.Alias) {
+ const target = getImmediateAliasedSymbol(symbol);
+ if (target) {
+ if (target === targetSymbol) break;
+ if (target.declarations && length(target.declarations)) {
+ if (isDeprecatedAliasedSymbol(target)) {
+ addDeprecatedSuggestion(location, target.declarations, target.escapedName as string);
+ break;
+ }
+ else {
+ if (symbol === targetSymbol) break;
+ symbol = target;
+ }
+ }
+ }
+ else {
+ break;
+ }
+ }
+ return targetSymbol;
+ }
+
function checkImportBinding(node: ImportEqualsDeclaration | ImportClause | NamespaceImport | ImportSpecifier) {
checkCollisionsForDeclarationName(node, node.name);
checkAliasSymbol(node);
if (node.kind === SyntaxKind.ImportSpecifier &&
idText(node.propertyName || node.name) === "default" &&
- compilerOptions.esModuleInterop &&
- moduleKind !== ModuleKind.System && moduleKind < ModuleKind.ES2015) {
+ getESModuleInterop(compilerOptions) &&
+ moduleKind !== ModuleKind.System && (moduleKind < ModuleKind.ES2015 || getSourceFileOfNode(node).impliedNodeFormat === ModuleKind.CommonJS)) {
checkExternalEmitHelpers(node, ExternalEmitHelpers.ImportDefault);
}
}
+ function checkAssertClause(declaration: ImportDeclaration | ExportDeclaration) {
+ if (declaration.assertClause) {
+ const mode = (moduleKind === ModuleKind.NodeNext) && declaration.moduleSpecifier && getUsageModeForExpression(declaration.moduleSpecifier);
+ if (mode !== ModuleKind.ESNext && moduleKind !== ModuleKind.ESNext) {
+ return grammarErrorOnNode(declaration.assertClause,
+ moduleKind === ModuleKind.NodeNext
+ ? Diagnostics.Import_assertions_are_not_allowed_on_statements_that_transpile_to_commonjs_require_calls
+ : Diagnostics.Import_assertions_are_only_supported_when_the_module_option_is_set_to_esnext_or_nodenext);
+ }
+
+ if (isImportDeclaration(declaration) ? declaration.importClause?.isTypeOnly : declaration.isTypeOnly) {
+ return grammarErrorOnNode(declaration.assertClause, Diagnostics.Import_assertions_cannot_be_used_with_type_only_imports_or_exports);
+ }
+ }
+ }
+
function checkImportDeclaration(node: ImportDeclaration) {
- if (checkGrammarModuleElementContext(node, Diagnostics.An_import_declaration_can_only_be_used_in_a_namespace_or_module)) {
+ 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.
return;
}
@@ -38906,7 +40144,7 @@ namespace ts {
if (importClause.namedBindings) {
if (importClause.namedBindings.kind === SyntaxKind.NamespaceImport) {
checkImportBinding(importClause.namedBindings);
- if (moduleKind !== ModuleKind.System && moduleKind < ModuleKind.ES2015 && compilerOptions.esModuleInterop) {
+ if (moduleKind !== ModuleKind.System && (moduleKind < ModuleKind.ES2015 || getSourceFileOfNode(node).impliedNodeFormat === ModuleKind.CommonJS) && getESModuleInterop(compilerOptions)) {
// import * as ns from "foo";
checkExternalEmitHelpers(node, ExternalEmitHelpers.ImportStar);
}
@@ -38920,11 +40158,11 @@ namespace ts {
}
}
}
-
+ checkAssertClause(node);
}
function checkImportEqualsDeclaration(node: ImportEqualsDeclaration) {
- if (checkGrammarModuleElementContext(node, Diagnostics.An_import_declaration_can_only_be_used_in_a_namespace_or_module)) {
+ 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.
return;
}
@@ -38954,7 +40192,7 @@ namespace ts {
}
}
else {
- if (moduleKind >= ModuleKind.ES2015 && !node.isTypeOnly && !(node.flags & NodeFlags.Ambient)) {
+ if (moduleKind >= ModuleKind.ES2015 && getSourceFileOfNode(node).impliedNodeFormat === undefined && !node.isTypeOnly && !(node.flags & NodeFlags.Ambient)) {
// Import equals declaration is deprecated in es6 or above
grammarErrorOnNode(node, Diagnostics.Import_assignment_cannot_be_used_when_targeting_ECMAScript_modules_Consider_using_import_Asterisk_as_ns_from_mod_import_a_from_mod_import_d_from_mod_or_another_module_format_instead);
}
@@ -38963,7 +40201,7 @@ namespace ts {
}
function checkExportDeclaration(node: ExportDeclaration) {
- if (checkGrammarModuleElementContext(node, Diagnostics.An_export_declaration_can_only_be_used_in_a_module)) {
+ if (checkGrammarModuleElementContext(node, isInJSFile(node) ? Diagnostics.An_export_declaration_can_only_be_used_at_the_top_level_of_a_module : Diagnostics.An_export_declaration_can_only_be_used_at_the_top_level_of_a_namespace_or_module)) {
// If we hit an export in an illegal context, just bail out to avoid cascading errors.
return;
}
@@ -38999,12 +40237,12 @@ namespace ts {
else if (node.exportClause) {
checkAliasSymbol(node.exportClause);
}
- if (moduleKind !== ModuleKind.System && moduleKind < ModuleKind.ES2015) {
+ if (moduleKind !== ModuleKind.System && (moduleKind < ModuleKind.ES2015 || getSourceFileOfNode(node).impliedNodeFormat === ModuleKind.CommonJS)) {
if (node.exportClause) {
// export * as ns from "foo";
// For ES2015 modules, we emit it as a pair of `import * as a_1 ...; export { a_1 as ns }` and don't need the helper.
// We only use the helper here when in esModuleInterop
- if (compilerOptions.esModuleInterop) {
+ if (getESModuleInterop(compilerOptions)) {
checkExternalEmitHelpers(node, ExternalEmitHelpers.ImportStar);
}
}
@@ -39015,14 +40253,19 @@ namespace ts {
}
}
}
+ checkAssertClause(node);
}
function checkGrammarExportDeclaration(node: ExportDeclaration): boolean {
- const isTypeOnlyExportStar = node.isTypeOnly && node.exportClause?.kind !== SyntaxKind.NamedExports;
- if (isTypeOnlyExportStar) {
- grammarErrorOnNode(node, Diagnostics.Only_named_exports_may_use_export_type);
+ if (node.isTypeOnly) {
+ if (node.exportClause?.kind === SyntaxKind.NamedExports) {
+ return checkGrammarNamedImportsOrExports(node.exportClause);
+ }
+ else {
+ return grammarErrorOnNode(node, Diagnostics.Only_named_exports_may_use_export_type);
+ }
}
- return !isTypeOnlyExportStar;
+ return false;
}
function checkGrammarModuleElementContext(node: Statement, errorMessage: DiagnosticMessage): boolean {
@@ -39095,9 +40338,9 @@ namespace ts {
}
}
else {
- if (compilerOptions.esModuleInterop &&
+ if (getESModuleInterop(compilerOptions) &&
moduleKind !== ModuleKind.System &&
- moduleKind < ModuleKind.ES2015 &&
+ (moduleKind < ModuleKind.ES2015 || getSourceFileOfNode(node).impliedNodeFormat === ModuleKind.CommonJS) &&
idText(node.propertyName || node.name) === "default") {
checkExternalEmitHelpers(node, ExternalEmitHelpers.ImportDefault);
}
@@ -39165,7 +40408,7 @@ namespace ts {
}
if (node.isExportEquals && !(node.flags & NodeFlags.Ambient)) {
- if (moduleKind >= ModuleKind.ES2015) {
+ if (moduleKind >= ModuleKind.ES2015 && getSourceFileOfNode(node).impliedNodeFormat !== ModuleKind.CommonJS) {
// export assignment is not supported in es6 modules
grammarErrorOnNode(node, Diagnostics.Export_assignment_cannot_be_used_when_targeting_ECMAScript_modules_Consider_using_export_default_or_another_module_format_instead);
}
@@ -39351,6 +40594,10 @@ namespace ts {
return;
case SyntaxKind.JSDocTypeExpression:
return checkSourceElement((node as JSDocTypeExpression).type);
+ case SyntaxKind.JSDocPublicTag:
+ case SyntaxKind.JSDocProtectedTag:
+ case SyntaxKind.JSDocPrivateTag:
+ return checkJSDocAccessibilityModifiers(node as JSDocPublicTag | JSDocProtectedTag | JSDocPrivateTag);
case SyntaxKind.IndexedAccessType:
return checkIndexedAccessType(node as IndexedAccessTypeNode);
case SyntaxKind.MappedType:
@@ -39508,9 +40755,8 @@ namespace ts {
const enclosingFile = getSourceFileOfNode(node);
const links = getNodeLinks(enclosingFile);
if (!(links.flags & NodeCheckFlags.TypeChecked)) {
- links.deferredNodes = links.deferredNodes || new Map();
- const id = getNodeId(node);
- links.deferredNodes.set(id, node);
+ links.deferredNodes ||= new Set();
+ links.deferredNodes.add(node);
}
}
@@ -39522,7 +40768,7 @@ namespace ts {
}
function checkDeferredNode(node: Node) {
- tracing?.push(tracing.Phase.Check, "checkDeferredNode", { kind: node.kind, pos: node.pos, end: node.end });
+ tracing?.push(tracing.Phase.Check, "checkDeferredNode", { kind: node.kind, pos: node.pos, end: node.end, path: (node as TracingNode).tracingPath });
const saveCurrentNode = currentNode;
currentNode = node;
instantiationCount = 0;
@@ -40040,6 +41286,9 @@ namespace ts {
}
return result;
}
+ else if (isPrivateIdentifier(name)) {
+ return getSymbolForPrivateIdentifierExpression(name);
+ }
else if (name.kind === SyntaxKind.PropertyAccessExpression || name.kind === SyntaxKind.QualifiedName) {
const links = getNodeLinks(name);
if (links.resolvedSymbol) {
@@ -40063,7 +41312,8 @@ namespace ts {
}
else if (isTypeReferenceIdentifier(name as EntityName)) {
const meaning = name.parent.kind === SyntaxKind.TypeReference ? SymbolFlags.Type : SymbolFlags.Namespace;
- return resolveEntityName(name as EntityName, meaning, /*ignoreErrors*/ false, /*dontResolveAlias*/ true);
+ const symbol = resolveEntityName(name as EntityName, meaning, /*ignoreErrors*/ false, /*dontResolveAlias*/ true);
+ return symbol && symbol !== unknownSymbol ? symbol : getUnresolvedSymbolForEntityName(name as EntityName);
}
if (name.parent.kind === SyntaxKind.TypePredicate) {
return resolveEntityName(name as Identifier, /*meaning*/ SymbolFlags.FunctionScopedVariable);
@@ -40155,7 +41405,10 @@ namespace ts {
case SyntaxKind.PrivateIdentifier:
case SyntaxKind.PropertyAccessExpression:
case SyntaxKind.QualifiedName:
- return getSymbolOfNameOrPropertyAccessExpression(node as EntityName | PrivateIdentifier | PropertyAccessExpression);
+ if (!isThisInTypeQuery(node)) {
+ return getSymbolOfNameOrPropertyAccessExpression(node as EntityName | PrivateIdentifier | PropertyAccessExpression);
+ }
+ // falls through
case SyntaxKind.ThisKeyword:
const container = getThisContainer(node, /*includeArrowFunctions*/ false);
@@ -40316,14 +41569,14 @@ namespace ts {
}
if (isBindingPattern(node)) {
- return getTypeForVariableLikeDeclaration(node.parent, /*includeOptionality*/ true) || errorType;
+ return getTypeForVariableLikeDeclaration(node.parent, /*includeOptionality*/ true, CheckMode.Normal) || errorType;
}
if (isInRightSideOfImportOrExportAssignment(node as Identifier)) {
const symbol = getSymbolAtLocation(node);
if (symbol) {
const declaredType = getDeclaredTypeOfSymbol(symbol);
- return declaredType !== errorType ? declaredType : getTypeOfSymbol(symbol);
+ return !isErrorType(declaredType) ? declaredType : getTypeOfSymbol(symbol);
}
}
@@ -40686,7 +41939,7 @@ namespace ts {
if (!symbol) {
return false;
}
- const target = resolveAlias(symbol);
+ const target = getExportSymbolOfValueSymbolIfExported(resolveAlias(symbol));
if (target === unknownSymbol) {
return true;
}
@@ -40867,7 +42120,7 @@ namespace ts {
return isTypeOnly ? TypeReferenceSerializationKind.ObjectType : TypeReferenceSerializationKind.Unknown;
}
const type = getDeclaredTypeOfSymbol(typeSymbol);
- if (type === errorType) {
+ if (isErrorType(type)) {
return isTypeOnly ? TypeReferenceSerializationKind.ObjectType : TypeReferenceSerializationKind.Unknown;
}
else if (type.flags & TypeFlags.AnyOrUnknown) {
@@ -41031,11 +42284,11 @@ namespace ts {
// this variable and functions that use it are deliberately moved here from the outer scope
// to avoid scope pollution
const resolvedTypeReferenceDirectives = host.getResolvedTypeReferenceDirectives();
- let fileToDirective: ESMap;
+ let fileToDirective: ESMap;
if (resolvedTypeReferenceDirectives) {
// populate reverse mapping: file path -> type reference directive that was resolved to this file
- fileToDirective = new Map();
- resolvedTypeReferenceDirectives.forEach((resolvedDirective, key) => {
+ fileToDirective = new Map();
+ resolvedTypeReferenceDirectives.forEach((resolvedDirective, key, mode) => {
if (!resolvedDirective || !resolvedDirective.resolvedFileName) {
return;
}
@@ -41043,7 +42296,7 @@ namespace ts {
if (file) {
// Add the transitive closure of path references loaded by this file (as long as they are not)
// part of an existing type reference.
- addReferencedFilesToTypeDirective(file, key);
+ addReferencedFilesToTypeDirective(file, key, mode);
}
});
}
@@ -41166,7 +42419,7 @@ namespace ts {
}
// defined here to avoid outer scope pollution
- function getTypeReferenceDirectivesForEntityName(node: EntityNameOrEntityNameExpression): string[] | undefined {
+ function getTypeReferenceDirectivesForEntityName(node: EntityNameOrEntityNameExpression): [specifier: string, mode: SourceFile["impliedNodeFormat"] | undefined][] | undefined {
// program does not have any files with type reference directives - bail out
if (!fileToDirective) {
return undefined;
@@ -41184,13 +42437,13 @@ namespace ts {
}
// defined here to avoid outer scope pollution
- function getTypeReferenceDirectivesForSymbol(symbol: Symbol, meaning?: SymbolFlags): string[] | undefined {
+ function getTypeReferenceDirectivesForSymbol(symbol: Symbol, meaning?: SymbolFlags): [specifier: string, mode: SourceFile["impliedNodeFormat"] | undefined][] | undefined {
// program does not have any files with type reference directives - bail out
if (!fileToDirective || !isSymbolFromTypeDeclarationFile(symbol)) {
return undefined;
}
// check what declarations in the symbol can contribute to the target meaning
- let typeReferenceDirectives: string[] | undefined;
+ let typeReferenceDirectives: [specifier: string, mode: SourceFile["impliedNodeFormat"] | undefined][] | undefined;
for (const decl of symbol.declarations!) {
// check meaning of the local symbol to see if declaration needs to be analyzed further
if (decl.symbol && decl.symbol.flags & meaning!) {
@@ -41241,14 +42494,14 @@ namespace ts {
return false;
}
- function addReferencedFilesToTypeDirective(file: SourceFile, key: string) {
+ function addReferencedFilesToTypeDirective(file: SourceFile, key: string, mode: SourceFile["impliedNodeFormat"] | undefined) {
if (fileToDirective.has(file.path)) return;
- fileToDirective.set(file.path, key);
- for (const { fileName } of file.referencedFiles) {
+ fileToDirective.set(file.path, [key, mode]);
+ for (const { fileName, resolutionMode } of file.referencedFiles) {
const resolvedFile = resolveTripleslashReference(fileName, file.fileName);
const referencedFile = host.getSourceFile(resolvedFile);
if (referencedFile) {
- addReferencedFilesToTypeDirective(referencedFile, key);
+ addReferencedFilesToTypeDirective(referencedFile, key, resolutionMode || file.impliedNodeFormat);
}
}
}
@@ -41455,6 +42708,7 @@ namespace ts {
case ExternalEmitHelpers.MakeTemplateObject: return "__makeTemplateObject";
case ExternalEmitHelpers.ClassPrivateFieldGet: return "__classPrivateFieldGet";
case ExternalEmitHelpers.ClassPrivateFieldSet: return "__classPrivateFieldSet";
+ case ExternalEmitHelpers.ClassPrivateFieldIn: return "__classPrivateFieldIn";
case ExternalEmitHelpers.CreateBinding: return "__createBinding";
default: return Debug.fail("Unrecognized helper");
}
@@ -41499,7 +42753,7 @@ namespace ts {
return quickResult;
}
- let lastStatic: Node | undefined, lastDeclare: Node | undefined, lastAsync: Node | undefined, lastReadonly: Node | undefined, lastOverride: Node | undefined;
+ let lastStatic: Node | undefined, lastDeclare: Node | undefined, lastAsync: Node | undefined, lastOverride: Node | undefined;
let flags = ModifierFlags.None;
for (const modifier of node.modifiers!) {
if (modifier.kind !== SyntaxKind.ReadonlyKeyword) {
@@ -41606,7 +42860,6 @@ namespace ts {
return grammarErrorOnNode(modifier, Diagnostics.readonly_modifier_can_only_appear_on_a_property_declaration_or_index_signature);
}
flags |= ModifierFlags.Readonly;
- lastReadonly = modifier;
break;
case SyntaxKind.ExportKeyword:
@@ -41725,18 +42978,12 @@ namespace ts {
if (flags & ModifierFlags.Static) {
return grammarErrorOnNode(lastStatic!, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "static");
}
- if (flags & ModifierFlags.Abstract) {
- return grammarErrorOnNode(lastStatic!, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "abstract"); // TODO: GH#18217
- }
if (flags & ModifierFlags.Override) {
return grammarErrorOnNode(lastOverride!, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "override"); // TODO: GH#18217
}
- else if (flags & ModifierFlags.Async) {
+ if (flags & ModifierFlags.Async) {
return grammarErrorOnNode(lastAsync!, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "async");
}
- else if (flags & ModifierFlags.Readonly) {
- return grammarErrorOnNode(lastReadonly!, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "readonly");
- }
return false;
}
else if ((node.kind === SyntaxKind.ImportDeclaration || node.kind === SyntaxKind.ImportEqualsDeclaration) && flags & ModifierFlags.Ambient) {
@@ -41921,6 +43168,12 @@ namespace ts {
return false;
}
+ if (node.typeParameters && !(length(node.typeParameters) > 1 || node.typeParameters.hasTrailingComma || node.typeParameters[0].constraint)) {
+ if (file && fileExtensionIsOneOf(file.fileName, [Extension.Mts, Extension.Cts])) {
+ grammarErrorOnNode(node.typeParameters[0], Diagnostics.This_syntax_is_reserved_in_files_with_the_mts_or_cts_extension_Add_a_trailing_comma_or_explicit_constraint);
+ }
+ }
+
const { equalsGreaterThanToken } = node;
const startLine = getLineAndCharacterOfPosition(file, equalsGreaterThanToken.pos).line;
const endLine = getLineAndCharacterOfPosition(file, equalsGreaterThanToken.end).line;
@@ -41993,21 +43246,6 @@ namespace ts {
return false;
}
- function checkGrammarForOmittedArgument(args: NodeArray | undefined): boolean {
- if (args) {
- for (const arg of args) {
- if (arg.kind === SyntaxKind.OmittedExpression) {
- return grammarErrorAtPos(arg, arg.pos, 0, Diagnostics.Argument_expression_expected);
- }
- }
- }
- return false;
- }
-
- function checkGrammarArguments(args: NodeArray | undefined): boolean {
- return checkGrammarForOmittedArgument(args);
- }
-
function checkGrammarHeritageClause(node: HeritageClause): boolean {
const types = node.types;
if (checkGrammarForDisallowedTrailingComma(types)) {
@@ -42020,7 +43258,7 @@ namespace ts {
return some(types, checkGrammarExpressionWithTypeArguments);
}
- function checkGrammarExpressionWithTypeArguments(node: ExpressionWithTypeArguments) {
+ function checkGrammarExpressionWithTypeArguments(node: ExpressionWithTypeArguments | TypeQueryNode) {
return checkGrammarTypeArguments(node, node.typeArguments);
}
@@ -42143,7 +43381,7 @@ namespace ts {
if (prop.kind === SyntaxKind.ShorthandPropertyAssignment && !inDestructuring && prop.objectAssignmentInitializer) {
// having objectAssignmentInitializer is only valid in ObjectAssignmentPattern
// outside of destructuring it is a syntax error
- return grammarErrorOnNode(prop.equalsToken!, Diagnostics.Did_you_mean_to_use_a_Colon_An_can_only_follow_a_property_name_when_the_containing_object_literal_is_part_of_a_destructuring_pattern);
+ grammarErrorOnNode(prop.equalsToken!, Diagnostics.Did_you_mean_to_use_a_Colon_An_can_only_follow_a_property_name_when_the_containing_object_literal_is_part_of_a_destructuring_pattern);
}
if (name.kind === SyntaxKind.PrivateIdentifier) {
@@ -42152,8 +43390,7 @@ namespace ts {
// Modifiers are never allowed on properties except for 'async' on a method declaration
if (prop.modifiers) {
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
- for (const mod of prop.modifiers!) { // TODO: GH#19955
+ for (const mod of prop.modifiers) {
if (mod.kind !== SyntaxKind.AsyncKeyword || prop.kind !== SyntaxKind.MethodDeclaration) {
grammarErrorOnNode(mod, Diagnostics._0_modifier_cannot_be_used_here, getTextOfNode(mod));
}
@@ -42205,9 +43442,12 @@ namespace ts {
seen.set(effectiveName, currentKind);
}
else {
- if ((currentKind & DeclarationMeaning.PropertyAssignmentOrMethod) && (existingKind & DeclarationMeaning.PropertyAssignmentOrMethod)) {
+ if ((currentKind & DeclarationMeaning.Method) && (existingKind & DeclarationMeaning.Method)) {
grammarErrorOnNode(name, Diagnostics.Duplicate_identifier_0, getTextOfNode(name));
}
+ else if ((currentKind & DeclarationMeaning.PropertyAssignment) && (existingKind & DeclarationMeaning.PropertyAssignment)) {
+ grammarErrorOnNode(name, Diagnostics.An_object_literal_cannot_have_multiple_properties_with_the_same_name, getTextOfNode(name));
+ }
else if ((currentKind & DeclarationMeaning.GetOrSetAccessor) && (existingKind & DeclarationMeaning.GetOrSetAccessor)) {
if (existingKind !== DeclarationMeaning.GetOrSetAccessor && currentKind !== existingKind) {
seen.set(effectiveName, currentKind | existingKind);
@@ -42291,9 +43531,9 @@ namespace ts {
diagnostics.add(createDiagnosticForNode(forInOrOfStatement.awaitModifier,
Diagnostics.for_await_loops_are_only_allowed_at_the_top_level_of_a_file_when_that_file_is_a_module_but_this_file_has_no_imports_or_exports_Consider_adding_an_empty_export_to_make_this_file_a_module));
}
- if ((moduleKind !== ModuleKind.ESNext && moduleKind !== ModuleKind.System) || languageVersion < ScriptTarget.ES2017) {
+ if ((moduleKind !== ModuleKind.ES2022 && moduleKind !== ModuleKind.ESNext && moduleKind !== ModuleKind.System && !(moduleKind === ModuleKind.NodeNext && getSourceFileOfNode(forInOrOfStatement).impliedNodeFormat === ModuleKind.ESNext)) || languageVersion < ScriptTarget.ES2017) {
diagnostics.add(createDiagnosticForNode(forInOrOfStatement.awaitModifier,
- Diagnostics.Top_level_for_await_loops_are_only_allowed_when_the_module_option_is_set_to_esnext_or_system_and_the_target_option_is_set_to_es2017_or_higher));
+ Diagnostics.Top_level_for_await_loops_are_only_allowed_when_the_module_option_is_set_to_es2022_esnext_system_or_nodenext_and_the_target_option_is_set_to_es2017_or_higher));
}
}
}
@@ -42672,8 +43912,7 @@ namespace ts {
return grammarErrorOnNode(node.exclamationToken, message);
}
- const moduleKind = getEmitModuleKind(compilerOptions);
- if (moduleKind < ModuleKind.ES2015 && moduleKind !== ModuleKind.System &&
+ if ((moduleKind < ModuleKind.ES2015 || getSourceFileOfNode(node).impliedNodeFormat === ModuleKind.CommonJS) && moduleKind !== ModuleKind.System &&
!(node.parent.parent.flags & NodeFlags.Ambient) && hasSyntacticModifier(node.parent.parent, ModifierFlags.Export)) {
checkESModuleMarker(node.name);
}
@@ -42838,6 +44077,11 @@ namespace ts {
}
function checkGrammarProperty(node: PropertyDeclaration | PropertySignature) {
+ if (isComputedPropertyName(node.name) && isBinaryExpression(node.name.expression) && node.name.expression.operatorToken.kind === SyntaxKind.InKeyword) {
+ return grammarErrorOnNode(
+ (node.parent as ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode).members[0],
+ Diagnostics.A_mapped_type_may_not_declare_properties_or_methods);
+ }
if (isClassLike(node.parent)) {
if (isStringLiteral(node.name) && node.name.text === "constructor") {
return grammarErrorOnNode(node.name, Diagnostics.Classes_may_not_have_a_field_named_constructor);
@@ -42857,7 +44101,7 @@ namespace ts {
return grammarErrorOnNode(node.initializer, Diagnostics.An_interface_property_cannot_have_an_initializer);
}
}
- else if (node.parent.kind === SyntaxKind.TypeLiteral) {
+ else if (isTypeLiteralNode(node.parent)) {
if (checkGrammarForInvalidDynamicName(node.name, Diagnostics.A_computed_property_name_in_a_type_literal_must_refer_to_an_expression_whose_type_is_a_literal_type_or_a_unique_symbol_type)) {
return true;
}
@@ -42979,19 +44223,24 @@ namespace ts {
}
function checkNumericLiteralValueSize(node: NumericLiteral) {
+ // We should test against `getTextOfNode(node)` rather than `node.text`, because `node.text` for large numeric literals can contain "."
+ // e.g. `node.text` for numeric literal `1100000000000000000000` is `1.1e21`.
+ const isFractional = getTextOfNode(node).indexOf(".") !== -1;
+ const isScientific = node.numericLiteralFlags & TokenFlags.Scientific;
+
// Scientific notation (e.g. 2e54 and 1e00000000010) can't be converted to bigint
- // Literals with 15 or fewer characters aren't long enough to reach past 2^53 - 1
// Fractional numbers (e.g. 9000000000000000.001) are inherently imprecise anyway
- if (node.numericLiteralFlags & TokenFlags.Scientific || node.text.length <= 15 || node.text.indexOf(".") !== -1) {
+ if (isFractional || isScientific) {
return;
}
- // We can't rely on the runtime to accurately store and compare extremely large numeric values
- // Even for internal use, we use getTextOfNode: https://github.com/microsoft/TypeScript/issues/33298
- // Thus, if the runtime claims a too-large number is lower than Number.MAX_SAFE_INTEGER,
- // it's likely addition operations on it will fail too
- const apparentValue = +getTextOfNode(node);
- if (apparentValue <= 2 ** 53 - 1 && apparentValue + 1 > apparentValue) {
+ // Here `node` is guaranteed to be a numeric literal representing an integer.
+ // We need to judge whether the integer `node` represents is <= 2 ** 53 - 1, which can be accomplished by comparing to `value` defined below because:
+ // 1) when `node` represents an integer <= 2 ** 53 - 1, `node.text` is its exact string representation and thus `value` precisely represents the integer.
+ // 2) otherwise, although `node.text` may be imprecise string representation, its mathematical value and consequently `value` cannot be less than 2 ** 53,
+ // thus the result of the predicate won't be affected.
+ const value = +node.text;
+ if (value <= 2 ** 53 - 1) {
return;
}
@@ -43038,12 +44287,27 @@ namespace ts {
if (node.isTypeOnly && node.name && node.namedBindings) {
return grammarErrorOnNode(node, Diagnostics.A_type_only_import_can_specify_a_default_import_or_named_bindings_but_not_both);
}
+ if (node.isTypeOnly && node.namedBindings?.kind === SyntaxKind.NamedImports) {
+ return checkGrammarNamedImportsOrExports(node.namedBindings);
+ }
return false;
}
+ function checkGrammarNamedImportsOrExports(namedBindings: NamedImportsOrExports): boolean {
+ return !!forEach(namedBindings.elements, specifier => {
+ if (specifier.isTypeOnly) {
+ return grammarErrorOnFirstToken(
+ specifier,
+ specifier.kind === SyntaxKind.ImportSpecifier
+ ? Diagnostics.The_type_modifier_cannot_be_used_on_a_named_import_when_import_type_is_used_on_its_import_statement
+ : Diagnostics.The_type_modifier_cannot_be_used_on_a_named_export_when_export_type_is_used_on_its_export_statement);
+ }
+ });
+ }
+
function checkGrammarImportCallExpression(node: ImportCall): boolean {
if (moduleKind === ModuleKind.ES2015) {
- return grammarErrorOnNode(node, Diagnostics.Dynamic_imports_are_only_supported_when_the_module_flag_is_set_to_es2020_esnext_commonjs_amd_system_or_umd);
+ return grammarErrorOnNode(node, Diagnostics.Dynamic_imports_are_only_supported_when_the_module_flag_is_set_to_es2020_es2022_esnext_commonjs_amd_system_umd_node12_or_nodenext);
}
if (node.typeArguments) {
@@ -43051,14 +44315,25 @@ namespace ts {
}
const nodeArguments = node.arguments;
- if (nodeArguments.length !== 1) {
- return grammarErrorOnNode(node, Diagnostics.Dynamic_import_must_have_one_specifier_as_an_argument);
+ if (moduleKind !== ModuleKind.ESNext && moduleKind !== ModuleKind.NodeNext) {
+ // We are allowed trailing comma after proposal-import-assertions.
+ checkGrammarForDisallowedTrailingComma(nodeArguments);
+
+ if (nodeArguments.length > 1) {
+ const assertionArgument = nodeArguments[1];
+ return grammarErrorOnNode(assertionArgument, Diagnostics.Dynamic_imports_only_support_a_second_argument_when_the_module_option_is_set_to_esnext_or_nodenext);
+ }
}
- checkGrammarForDisallowedTrailingComma(nodeArguments);
+
+ if (nodeArguments.length === 0 || nodeArguments.length > 2) {
+ return grammarErrorOnNode(node, Diagnostics.Dynamic_imports_can_only_accept_a_module_specifier_and_an_optional_assertion_as_arguments);
+ }
+
// see: parseArgumentOrArrayLiteralElement...we use this function which parse arguments of callExpression to parse specifier for dynamic import.
// parseArgumentOrArrayLiteralElement allows spread element to be in an argument list which is not allowed as specifier in dynamic import.
- if (isSpreadElement(nodeArguments[0])) {
- return grammarErrorOnNode(nodeArguments[0], Diagnostics.Specifier_of_dynamic_import_cannot_be_spread_element);
+ const spreadElement = find(nodeArguments, isSpreadElement);
+ if (spreadElement) {
+ return grammarErrorOnNode(spreadElement, Diagnostics.Argument_of_dynamic_import_cannot_be_spread_element);
}
return false;
}
@@ -43098,28 +44373,27 @@ namespace ts {
function findMostOverlappyType(source: Type, unionTarget: UnionOrIntersectionType) {
let bestMatch: Type | undefined;
- let matchingCount = 0;
- for (const target of unionTarget.types) {
- const overlap = getIntersectionType([getIndexType(source), getIndexType(target)]);
- if (overlap.flags & TypeFlags.Index) {
- // perfect overlap of keys
- bestMatch = target;
- matchingCount = Infinity;
- }
- else if (overlap.flags & TypeFlags.Union) {
- // We only want to account for literal types otherwise.
- // If we have a union of index types, it seems likely that we
- // needed to elaborate between two generic mapped types anyway.
- const len = length(filter((overlap as UnionType).types, isUnitType));
- if (len >= matchingCount) {
- bestMatch = target;
- matchingCount = len;
+ if (!(source.flags & (TypeFlags.Primitive | TypeFlags.InstantiablePrimitive))) {
+ let matchingCount = 0;
+ for (const target of unionTarget.types) {
+ if (!(target.flags & (TypeFlags.Primitive | TypeFlags.InstantiablePrimitive))) {
+ const overlap = getIntersectionType([getIndexType(source), getIndexType(target)]);
+ if (overlap.flags & TypeFlags.Index) {
+ // perfect overlap of keys
+ return target;
+ }
+ else if (isUnitType(overlap) || overlap.flags & TypeFlags.Union) {
+ // We only want to account for literal types otherwise.
+ // If we have a union of index types, it seems likely that we
+ // needed to elaborate between two generic mapped types anyway.
+ const len = overlap.flags & TypeFlags.Union ? countWhere((overlap as UnionType).types, isUnitType) : 1;
+ if (len >= matchingCount) {
+ bestMatch = target;
+ matchingCount = len;
+ }
+ }
}
}
- else if (isUnitType(overlap) && 1 >= matchingCount) {
- bestMatch = target;
- matchingCount = 1;
- }
}
return bestMatch;
}
diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts
index b76e6f72268..89d131508cb 100644
--- a/src/compiler/commandLineParser.ts
+++ b/src/compiler/commandLineParser.ts
@@ -1,6 +1,10 @@
namespace ts {
/* @internal */
- export const compileOnSaveCommandLineOption: CommandLineOption = { name: "compileOnSave", type: "boolean" };
+ export const compileOnSaveCommandLineOption: CommandLineOption = {
+ name: "compileOnSave",
+ type: "boolean",
+ defaultValueDescription: false,
+ };
const jsxOptionMap = new Map(getEntries({
"preserve": JsxEmit.Preserve,
@@ -29,6 +33,7 @@ namespace ts {
["es2019", "lib.es2019.d.ts"],
["es2020", "lib.es2020.d.ts"],
["es2021", "lib.es2021.d.ts"],
+ ["es2022", "lib.es2022.d.ts"],
["esnext", "lib.esnext.d.ts"],
// Host only
["dom", "lib.dom.d.ts"],
@@ -72,12 +77,16 @@ namespace ts {
["es2021.string", "lib.es2021.string.d.ts"],
["es2021.weakref", "lib.es2021.weakref.d.ts"],
["es2021.intl", "lib.es2021.intl.d.ts"],
- ["esnext.array", "lib.es2019.array.d.ts"],
+ ["es2022.array", "lib.es2022.array.d.ts"],
+ ["es2022.error", "lib.es2022.error.d.ts"],
+ ["es2022.object", "lib.es2022.object.d.ts"],
+ ["es2022.string", "lib.es2022.string.d.ts"],
+ ["esnext.array", "lib.es2022.array.d.ts"],
["esnext.symbol", "lib.es2019.symbol.d.ts"],
["esnext.asynciterable", "lib.es2018.asynciterable.d.ts"],
["esnext.intl", "lib.esnext.intl.d.ts"],
["esnext.bigint", "lib.es2020.bigint.d.ts"],
- ["esnext.string", "lib.es2021.string.d.ts"],
+ ["esnext.string", "lib.es2022.string.d.ts"],
["esnext.promise", "lib.es2021.promise.d.ts"],
["esnext.weakref", "lib.es2021.weakref.d.ts"]
];
@@ -112,6 +121,7 @@ namespace ts {
})),
category: Diagnostics.Watch_and_Build_Modes,
description: Diagnostics.Specify_how_the_TypeScript_watch_mode_works,
+ defaultValueDescription: WatchFileKind.UseFsEvents,
},
{
name: "watchDirectory",
@@ -123,6 +133,7 @@ namespace ts {
})),
category: Diagnostics.Watch_and_Build_Modes,
description: Diagnostics.Specify_how_directories_are_watched_on_systems_that_lack_recursive_file_watching_functionality,
+ defaultValueDescription: WatchDirectoryKind.UseFsEvents,
},
{
name: "fallbackPolling",
@@ -134,12 +145,14 @@ namespace ts {
})),
category: Diagnostics.Watch_and_Build_Modes,
description: Diagnostics.Specify_what_approach_the_watcher_should_use_if_the_system_runs_out_of_native_file_watchers,
+ defaultValueDescription: PollingWatchKind.PriorityInterval,
},
{
name: "synchronousWatchDirectory",
type: "boolean",
category: Diagnostics.Watch_and_Build_Modes,
description: Diagnostics.Synchronously_call_callbacks_and_update_the_state_of_directory_watchers_on_platforms_that_don_t_support_recursive_watching_natively,
+ defaultValueDescription: false,
},
{
name: "excludeDirectories",
@@ -176,11 +189,13 @@ namespace ts {
showInSimplifiedHelpView: true,
category: Diagnostics.Command_line_Options,
description: Diagnostics.Print_this_message,
+ defaultValueDescription: false,
},
{
name: "help",
shortName: "?",
- type: "boolean"
+ type: "boolean",
+ defaultValueDescription: false,
},
{
name: "watch",
@@ -190,6 +205,7 @@ namespace ts {
isCommandLineOnly: true,
category: Diagnostics.Command_line_Options,
description: Diagnostics.Watch_input_files,
+ defaultValueDescription: false,
},
{
name: "preserveWatchOutput",
@@ -197,27 +213,28 @@ namespace ts {
showInSimplifiedHelpView: false,
category: Diagnostics.Output_Formatting,
description: Diagnostics.Disable_wiping_the_console_in_watch_mode,
- defaultValueDescription: "n/a"
+ defaultValueDescription: false,
},
{
name: "listFiles",
type: "boolean",
category: Diagnostics.Compiler_Diagnostics,
description: Diagnostics.Print_all_of_the_files_read_during_the_compilation,
- defaultValueDescription: "false"
+ defaultValueDescription: false,
},
{
name: "explainFiles",
type: "boolean",
category: Diagnostics.Compiler_Diagnostics,
- description: Diagnostics.Print_files_read_during_the_compilation_including_why_it_was_included
+ description: Diagnostics.Print_files_read_during_the_compilation_including_why_it_was_included,
+ defaultValueDescription: false,
},
{
name: "listEmittedFiles",
type: "boolean",
category: Diagnostics.Compiler_Diagnostics,
description: Diagnostics.Print_the_names_of_emitted_files_after_a_compilation,
- defaultValueDescription: "false"
+ defaultValueDescription: false,
},
{
name: "pretty",
@@ -225,28 +242,28 @@ namespace ts {
showInSimplifiedHelpView: true,
category: Diagnostics.Output_Formatting,
description: Diagnostics.Enable_color_and_formatting_in_TypeScript_s_output_to_make_compiler_errors_easier_to_read,
- defaultValueDescription: "true"
+ defaultValueDescription: true,
},
{
name: "traceResolution",
type: "boolean",
category: Diagnostics.Compiler_Diagnostics,
description: Diagnostics.Log_paths_used_during_the_moduleResolution_process,
- defaultValueDescription: "false"
+ defaultValueDescription: false,
},
{
name: "diagnostics",
type: "boolean",
category: Diagnostics.Compiler_Diagnostics,
description: Diagnostics.Output_compiler_performance_information_after_building,
- defaultValueDescription: "false"
+ defaultValueDescription: false,
},
{
name: "extendedDiagnostics",
type: "boolean",
category: Diagnostics.Compiler_Diagnostics,
description: Diagnostics.Output_more_detailed_compiler_performance_information_after_building,
- defaultValueDescription: "false"
+ defaultValueDescription: false,
},
{
name: "generateCpuProfile",
@@ -281,7 +298,8 @@ namespace ts {
affectsSemanticDiagnostics: true,
affectsEmit: true,
category: Diagnostics.Watch_and_Build_Modes,
- description: Diagnostics.Have_recompiles_in_projects_that_use_incremental_and_watch_mode_assume_that_changes_within_a_file_will_only_affect_files_directly_depending_on_it
+ description: Diagnostics.Have_recompiles_in_projects_that_use_incremental_and_watch_mode_assume_that_changes_within_a_file_will_only_affect_files_directly_depending_on_it,
+ defaultValueDescription: false,
},
{
name: "locale",
@@ -308,6 +326,7 @@ namespace ts {
es2019: ScriptTarget.ES2019,
es2020: ScriptTarget.ES2020,
es2021: ScriptTarget.ES2021,
+ es2022: ScriptTarget.ES2022,
esnext: ScriptTarget.ESNext,
})),
affectsSourceFile: true,
@@ -317,7 +336,7 @@ namespace ts {
showInSimplifiedHelpView: true,
category: Diagnostics.Language_and_Environment,
description: Diagnostics.Set_the_JavaScript_language_version_for_emitted_JavaScript_and_include_compatible_library_declarations,
- defaultValueDescription: "ES3"
+ defaultValueDescription: ScriptTarget.ES3,
};
const commandOptionsWithoutBuild: CommandLineOption[] = [
@@ -328,6 +347,7 @@ namespace ts {
showInSimplifiedHelpView: true,
category: Diagnostics.Command_line_Options,
description: Diagnostics.Show_all_compiler_options,
+ defaultValueDescription: false,
},
{
name: "version",
@@ -336,6 +356,7 @@ namespace ts {
showInSimplifiedHelpView: true,
category: Diagnostics.Command_line_Options,
description: Diagnostics.Print_the_compiler_s_version,
+ defaultValueDescription: false,
},
{
name: "init",
@@ -343,6 +364,7 @@ namespace ts {
showInSimplifiedHelpView: true,
category: Diagnostics.Command_line_Options,
description: Diagnostics.Initializes_a_TypeScript_project_and_creates_a_tsconfig_json_file,
+ defaultValueDescription: false,
},
{
name: "project",
@@ -360,7 +382,8 @@ namespace ts {
shortName: "b",
showInSimplifiedHelpView: true,
category: Diagnostics.Command_line_Options,
- description: Diagnostics.Build_one_or_more_projects_and_their_dependencies_if_out_of_date
+ description: Diagnostics.Build_one_or_more_projects_and_their_dependencies_if_out_of_date,
+ defaultValueDescription: false,
},
{
name: "showConfig",
@@ -368,7 +391,8 @@ namespace ts {
showInSimplifiedHelpView: true,
category: Diagnostics.Command_line_Options,
isCommandLineOnly: true,
- description: Diagnostics.Print_the_final_configuration_instead_of_building
+ description: Diagnostics.Print_the_final_configuration_instead_of_building,
+ defaultValueDescription: false,
},
{
name: "listFilesOnly",
@@ -377,7 +401,8 @@ namespace ts {
affectsSemanticDiagnostics: true,
affectsEmit: true,
isCommandLineOnly: true,
- description: Diagnostics.Print_names_of_files_that_are_part_of_the_compilation_and_then_stop_processing
+ description: Diagnostics.Print_names_of_files_that_are_part_of_the_compilation_and_then_stop_processing,
+ defaultValueDescription: false,
},
// Basic
@@ -394,7 +419,10 @@ namespace ts {
es6: ModuleKind.ES2015,
es2015: ModuleKind.ES2015,
es2020: ModuleKind.ES2020,
- esnext: ModuleKind.ESNext
+ es2022: ModuleKind.ES2022,
+ esnext: ModuleKind.ESNext,
+ node12: ModuleKind.Node12,
+ nodenext: ModuleKind.NodeNext,
})),
affectsModuleResolution: true,
affectsEmit: true,
@@ -402,13 +430,15 @@ namespace ts {
showInSimplifiedHelpView: true,
category: Diagnostics.Modules,
description: Diagnostics.Specify_what_module_code_is_generated,
+ defaultValueDescription: undefined,
},
{
name: "lib",
type: "list",
element: {
name: "lib",
- type: libMap
+ type: libMap,
+ defaultValueDescription: undefined,
},
affectsProgramStructure: true,
showInSimplifiedHelpView: true,
@@ -423,7 +453,7 @@ namespace ts {
showInSimplifiedHelpView: true,
category: Diagnostics.JavaScript_Support,
description: Diagnostics.Allow_JavaScript_files_to_be_a_part_of_your_program_Use_the_checkJS_option_to_get_errors_from_these_files,
- defaultValueDescription: "false"
+ defaultValueDescription: false,
},
{
name: "checkJs",
@@ -431,7 +461,7 @@ namespace ts {
showInSimplifiedHelpView: true,
category: Diagnostics.JavaScript_Support,
description: Diagnostics.Enable_error_reporting_in_type_checked_JavaScript_files,
- defaultValueDescription: "false"
+ defaultValueDescription: false,
},
{
name: "jsx",
@@ -443,7 +473,7 @@ namespace ts {
showInSimplifiedHelpView: true,
category: Diagnostics.Language_and_Environment,
description: Diagnostics.Specify_what_JSX_code_is_generated,
- defaultValueDescription: "undefined"
+ defaultValueDescription: undefined,
},
{
name: "declaration",
@@ -463,7 +493,7 @@ namespace ts {
showInSimplifiedHelpView: true,
category: Diagnostics.Emit,
transpileOptionValue: undefined,
- defaultValueDescription: "false",
+ defaultValueDescription: false,
description: Diagnostics.Create_sourcemaps_for_d_ts_files
},
{
@@ -475,7 +505,7 @@ namespace ts {
category: Diagnostics.Emit,
description: Diagnostics.Only_output_d_ts_files_and_not_JavaScript_files,
transpileOptionValue: undefined,
- defaultValueDescription: "false",
+ defaultValueDescription: false,
},
{
name: "sourceMap",
@@ -483,7 +513,7 @@ namespace ts {
affectsEmit: true,
showInSimplifiedHelpView: true,
category: Diagnostics.Emit,
- defaultValueDescription: "false",
+ defaultValueDescription: false,
description: Diagnostics.Create_source_map_files_for_emitted_JavaScript_files,
},
{
@@ -496,7 +526,6 @@ namespace ts {
category: Diagnostics.Emit,
description: Diagnostics.Specify_a_file_that_bundles_all_outputs_into_one_JavaScript_file_If_declaration_is_true_also_designates_a_file_that_bundles_all_d_ts_output,
transpileOptionValue: undefined,
- defaultValueDescription: "n/a"
},
{
name: "outDir",
@@ -507,7 +536,6 @@ namespace ts {
showInSimplifiedHelpView: true,
category: Diagnostics.Emit,
description: Diagnostics.Specify_an_output_folder_for_all_emitted_files,
- defaultValueDescription: "n/a"
},
{
name: "rootDir",
@@ -526,7 +554,7 @@ namespace ts {
isTSConfigOnly: true,
category: Diagnostics.Projects,
transpileOptionValue: undefined,
- defaultValueDescription: "false",
+ defaultValueDescription: false,
description: Diagnostics.Enable_constraints_that_allow_a_TypeScript_project_to_be_used_with_project_references,
},
{
@@ -546,7 +574,7 @@ namespace ts {
affectsEmit: true,
showInSimplifiedHelpView: true,
category: Diagnostics.Emit,
- defaultValueDescription: "false",
+ defaultValueDescription: false,
description: Diagnostics.Disable_emitting_comments,
},
{
@@ -556,7 +584,7 @@ namespace ts {
category: Diagnostics.Emit,
description: Diagnostics.Disable_emitting_files_from_a_compilation,
transpileOptionValue: undefined,
- defaultValueDescription: "false"
+ defaultValueDescription: false,
},
{
name: "importHelpers",
@@ -564,7 +592,7 @@ namespace ts {
affectsEmit: true,
category: Diagnostics.Emit,
description: Diagnostics.Allow_importing_helper_functions_from_tslib_once_per_project_instead_of_including_them_per_file,
- defaultValueDescription: "false"
+ defaultValueDescription: false,
},
{
name: "importsNotUsedAsValues",
@@ -576,7 +604,8 @@ namespace ts {
affectsEmit: true,
affectsSemanticDiagnostics: true,
category: Diagnostics.Emit,
- description: Diagnostics.Specify_emit_Slashchecking_behavior_for_imports_that_are_only_used_for_types
+ description: Diagnostics.Specify_emit_Slashchecking_behavior_for_imports_that_are_only_used_for_types,
+ defaultValueDescription: ImportsNotUsedAsValues.Remove,
},
{
name: "downlevelIteration",
@@ -584,7 +613,7 @@ namespace ts {
affectsEmit: true,
category: Diagnostics.Emit,
description: Diagnostics.Emit_more_compliant_but_verbose_and_less_performant_JavaScript_for_iteration,
- defaultValueDescription: "false"
+ defaultValueDescription: false,
},
{
name: "isolatedModules",
@@ -592,7 +621,7 @@ namespace ts {
category: Diagnostics.Interop_Constraints,
description: Diagnostics.Ensure_that_each_file_can_be_safely_transpiled_without_relying_on_other_imports,
transpileOptionValue: true,
- defaultValueDescription: "false"
+ defaultValueDescription: false,
},
// Strict Type Checks
@@ -604,7 +633,7 @@ namespace ts {
showInSimplifiedHelpView: true,
category: Diagnostics.Type_Checking,
description: Diagnostics.Enable_all_strict_type_checking_options,
- defaultValueDescription: "false"
+ defaultValueDescription: false,
},
{
name: "noImplicitAny",
@@ -665,6 +694,7 @@ namespace ts {
strictFlag: true,
category: Diagnostics.Type_Checking,
description: Diagnostics.Type_catch_clause_variables_as_unknown_instead_of_any,
+ defaultValueDescription: false,
},
{
name: "alwaysStrict",
@@ -683,7 +713,7 @@ namespace ts {
affectsSemanticDiagnostics: true,
category: Diagnostics.Type_Checking,
description: Diagnostics.Enable_error_reporting_when_a_local_variables_aren_t_read,
- defaultValueDescription: "false"
+ defaultValueDescription: false,
},
{
name: "noUnusedParameters",
@@ -691,14 +721,15 @@ namespace ts {
affectsSemanticDiagnostics: true,
category: Diagnostics.Type_Checking,
description: Diagnostics.Raise_an_error_when_a_function_parameter_isn_t_read,
- defaultValueDescription: "false"
+ defaultValueDescription: false,
},
{
name: "exactOptionalPropertyTypes",
type: "boolean",
affectsSemanticDiagnostics: true,
category: Diagnostics.Type_Checking,
- description: Diagnostics.Interpret_optional_property_types_as_written_rather_than_adding_undefined
+ description: Diagnostics.Interpret_optional_property_types_as_written_rather_than_adding_undefined,
+ defaultValueDescription: false,
},
{
name: "noImplicitReturns",
@@ -706,7 +737,7 @@ namespace ts {
affectsSemanticDiagnostics: true,
category: Diagnostics.Type_Checking,
description: Diagnostics.Enable_error_reporting_for_codepaths_that_do_not_explicitly_return_in_a_function,
- defaultValueDescription: "false"
+ defaultValueDescription: false,
},
{
name: "noFallthroughCasesInSwitch",
@@ -714,21 +745,24 @@ namespace ts {
affectsBindDiagnostics: true,
affectsSemanticDiagnostics: true,
category: Diagnostics.Type_Checking,
- description: Diagnostics.Enable_error_reporting_for_fallthrough_cases_in_switch_statements
+ description: Diagnostics.Enable_error_reporting_for_fallthrough_cases_in_switch_statements,
+ defaultValueDescription: false,
},
{
name: "noUncheckedIndexedAccess",
type: "boolean",
affectsSemanticDiagnostics: true,
category: Diagnostics.Type_Checking,
- description: Diagnostics.Include_undefined_in_index_signature_results
+ description: Diagnostics.Include_undefined_in_index_signature_results,
+ defaultValueDescription: false,
},
{
name: "noImplicitOverride",
type: "boolean",
affectsSemanticDiagnostics: true,
category: Diagnostics.Type_Checking,
- description: Diagnostics.Ensure_overriding_members_in_derived_classes_are_marked_with_an_override_modifier
+ description: Diagnostics.Ensure_overriding_members_in_derived_classes_are_marked_with_an_override_modifier,
+ defaultValueDescription: false,
},
{
name: "noPropertyAccessFromIndexSignature",
@@ -736,7 +770,7 @@ namespace ts {
showInSimplifiedHelpView: false,
category: Diagnostics.Type_Checking,
description: Diagnostics.Enforces_using_indexed_accessors_for_keys_declared_using_an_indexed_type,
- defaultValueDescription: "false"
+ defaultValueDescription: false,
},
// Module Resolution
@@ -745,6 +779,8 @@ namespace ts {
type: new Map(getEntries({
node: ModuleResolutionKind.NodeJs,
classic: ModuleResolutionKind.Classic,
+ node12: ModuleResolutionKind.Node12,
+ nodenext: ModuleResolutionKind.NodeNext,
})),
affectsModuleResolution: true,
paramType: Diagnostics.STRATEGY,
@@ -829,14 +865,14 @@ namespace ts {
showInSimplifiedHelpView: true,
category: Diagnostics.Interop_Constraints,
description: Diagnostics.Emit_additional_JavaScript_to_ease_support_for_importing_CommonJS_modules_This_enables_allowSyntheticDefaultImports_for_type_compatibility,
- defaultValueDescription: "false"
+ defaultValueDescription: false,
},
{
name: "preserveSymlinks",
type: "boolean",
category: Diagnostics.Interop_Constraints,
description: Diagnostics.Disable_resolving_symlinks_to_their_realpath_This_correlates_to_the_same_flag_in_node,
- defaultValueDescription: "n/a"
+ defaultValueDescription: false,
},
{
name: "allowUmdGlobalAccess",
@@ -844,7 +880,7 @@ namespace ts {
affectsSemanticDiagnostics: true,
category: Diagnostics.Modules,
description: Diagnostics.Allow_accessing_UMD_globals_from_modules,
- defaultValueDescription: "false"
+ defaultValueDescription: false,
},
// Source Maps
@@ -870,7 +906,7 @@ namespace ts {
affectsEmit: true,
category: Diagnostics.Emit,
description: Diagnostics.Include_sourcemap_files_inside_the_emitted_JavaScript,
- defaultValueDescription: "false"
+ defaultValueDescription: false,
},
{
name: "inlineSources",
@@ -878,7 +914,7 @@ namespace ts {
affectsEmit: true,
category: Diagnostics.Emit,
description: Diagnostics.Include_source_code_in_the_sourcemaps_inside_the_emitted_JavaScript,
- defaultValueDescription: "false"
+ defaultValueDescription: false,
},
// Experimental
@@ -887,7 +923,8 @@ namespace ts {
type: "boolean",
affectsSemanticDiagnostics: true,
category: Diagnostics.Language_and_Environment,
- description: Diagnostics.Enable_experimental_support_for_TC39_stage_2_draft_decorators
+ description: Diagnostics.Enable_experimental_support_for_TC39_stage_2_draft_decorators,
+ defaultValueDescription: false,
},
{
name: "emitDecoratorMetadata",
@@ -895,7 +932,8 @@ namespace ts {
affectsSemanticDiagnostics: true,
affectsEmit: true,
category: Diagnostics.Language_and_Environment,
- description: Diagnostics.Emit_design_type_metadata_for_decorated_declarations_in_source_files
+ description: Diagnostics.Emit_design_type_metadata_for_decorated_declarations_in_source_files,
+ defaultValueDescription: false,
},
// Advanced
@@ -928,7 +966,7 @@ namespace ts {
affectsModuleResolution: true,
category: Diagnostics.Modules,
description: Diagnostics.Enable_importing_json_files,
- defaultValueDescription: "false"
+ defaultValueDescription: false,
},
{
@@ -940,7 +978,6 @@ namespace ts {
category: Diagnostics.Backwards_Compatibility,
paramType: Diagnostics.FILE,
transpileOptionValue: undefined,
- defaultValueDescription: "n/a",
description: Diagnostics.Deprecated_setting_Use_outFile_instead,
},
{
@@ -956,7 +993,7 @@ namespace ts {
type: "boolean",
category: Diagnostics.Completeness,
description: Diagnostics.Skip_type_checking_d_ts_files_that_are_included_with_TypeScript,
- defaultValueDescription: "false",
+ defaultValueDescription: false,
},
{
name: "charset",
@@ -971,7 +1008,7 @@ namespace ts {
affectsEmit: true,
category: Diagnostics.Emit,
description: Diagnostics.Emit_a_UTF_8_Byte_Order_Mark_BOM_in_the_beginning_of_output_files,
- defaultValueDescription: "false"
+ defaultValueDescription: false,
},
{
name: "newLine",
@@ -991,7 +1028,7 @@ namespace ts {
affectsSemanticDiagnostics: true,
category: Diagnostics.Output_Formatting,
description: Diagnostics.Disable_truncating_types_in_error_messages,
- defaultValueDescription: "false"
+ defaultValueDescription: false,
},
{
name: "noLib",
@@ -1002,7 +1039,7 @@ namespace ts {
// We are not returning a sourceFile for lib file when asked by the program,
// so pass --noLib to avoid reporting a file not found error.
transpileOptionValue: true,
- defaultValueDescription: "false"
+ defaultValueDescription: false,
},
{
name: "noResolve",
@@ -1013,7 +1050,7 @@ namespace ts {
// We are not doing a full typecheck, we are not resolving the whole context,
// so pass --noResolve to avoid reporting missing file errors.
transpileOptionValue: true,
- defaultValueDescription: "false"
+ defaultValueDescription: false,
},
{
name: "stripInternal",
@@ -1021,6 +1058,7 @@ namespace ts {
affectsEmit: true,
category: Diagnostics.Emit,
description: Diagnostics.Disable_emitting_declarations_that_have_internal_in_their_JSDoc_comments,
+ defaultValueDescription: false,
},
{
name: "disableSizeLimit",
@@ -1028,28 +1066,31 @@ namespace ts {
affectsProgramStructure: true,
category: Diagnostics.Editor_Support,
description: Diagnostics.Remove_the_20mb_cap_on_total_source_code_size_for_JavaScript_files_in_the_TypeScript_language_server,
- defaultValueDescription: "false"
+ defaultValueDescription: false,
},
{
name: "disableSourceOfProjectReferenceRedirect",
type: "boolean",
isTSConfigOnly: true,
category: Diagnostics.Projects,
- description: Diagnostics.Disable_preferring_source_files_instead_of_declaration_files_when_referencing_composite_projects
+ description: Diagnostics.Disable_preferring_source_files_instead_of_declaration_files_when_referencing_composite_projects,
+ defaultValueDescription: false,
},
{
name: "disableSolutionSearching",
type: "boolean",
isTSConfigOnly: true,
category: Diagnostics.Projects,
- description: Diagnostics.Opt_a_project_out_of_multi_project_reference_checking_when_editing
+ description: Diagnostics.Opt_a_project_out_of_multi_project_reference_checking_when_editing,
+ defaultValueDescription: false,
},
{
name: "disableReferencedProjectLoad",
type: "boolean",
isTSConfigOnly: true,
category: Diagnostics.Projects,
- description: Diagnostics.Reduce_the_number_of_projects_loaded_automatically_by_TypeScript
+ description: Diagnostics.Reduce_the_number_of_projects_loaded_automatically_by_TypeScript,
+ defaultValueDescription: false,
},
{
name: "noImplicitUseStrict",
@@ -1057,7 +1098,7 @@ namespace ts {
affectsSemanticDiagnostics: true,
category: Diagnostics.Backwards_Compatibility,
description: Diagnostics.Disable_adding_use_strict_directives_in_emitted_JavaScript_files,
- defaultValueDescription: "false"
+ defaultValueDescription: false,
},
{
name: "noEmitHelpers",
@@ -1065,7 +1106,7 @@ namespace ts {
affectsEmit: true,
category: Diagnostics.Emit,
description: Diagnostics.Disable_generating_custom_helper_functions_like_extends_in_compiled_output,
- defaultValueDescription: "false"
+ defaultValueDescription: false,
},
{
name: "noEmitOnError",
@@ -1074,7 +1115,7 @@ namespace ts {
category: Diagnostics.Emit,
transpileOptionValue: undefined,
description: Diagnostics.Disable_emitting_files_if_any_type_checking_errors_are_reported,
- defaultValueDescription: "false"
+ defaultValueDescription: false,
},
{
name: "preserveConstEnums",
@@ -1082,7 +1123,7 @@ namespace ts {
affectsEmit: true,
category: Diagnostics.Emit,
description: Diagnostics.Disable_erasing_const_enum_declarations_in_generated_code,
- defaultValueDescription: "n/a"
+ defaultValueDescription: false,
},
{
name: "declarationDir",
@@ -1093,14 +1134,13 @@ namespace ts {
category: Diagnostics.Emit,
transpileOptionValue: undefined,
description: Diagnostics.Specify_the_output_directory_for_generated_declaration_files,
- defaultValueDescription: "n/a"
},
{
name: "skipLibCheck",
type: "boolean",
category: Diagnostics.Completeness,
description: Diagnostics.Skip_type_checking_all_d_ts_files,
- defaultValueDescription: "false"
+ defaultValueDescription: false,
},
{
name: "allowUnusedLabels",
@@ -1109,7 +1149,7 @@ namespace ts {
affectsSemanticDiagnostics: true,
category: Diagnostics.Type_Checking,
description: Diagnostics.Disable_error_reporting_for_unused_labels,
- defaultValueDescription: "undefined"
+ defaultValueDescription: undefined,
},
{
name: "allowUnreachableCode",
@@ -1118,7 +1158,7 @@ namespace ts {
affectsSemanticDiagnostics: true,
category: Diagnostics.Type_Checking,
description: Diagnostics.Disable_error_reporting_for_unreachable_code,
- defaultValueDescription: "undefined"
+ defaultValueDescription: undefined,
},
{
name: "suppressExcessPropertyErrors",
@@ -1126,7 +1166,7 @@ namespace ts {
affectsSemanticDiagnostics: true,
category: Diagnostics.Backwards_Compatibility,
description: Diagnostics.Disable_reporting_of_excess_property_errors_during_the_creation_of_object_literals,
- defaultValueDescription: "false"
+ defaultValueDescription: false,
},
{
name: "suppressImplicitAnyIndexErrors",
@@ -1134,7 +1174,7 @@ namespace ts {
affectsSemanticDiagnostics: true,
category: Diagnostics.Backwards_Compatibility,
description: Diagnostics.Suppress_noImplicitAny_errors_when_indexing_objects_that_lack_index_signatures,
- defaultValueDescription: "false"
+ defaultValueDescription: false,
},
{
name: "forceConsistentCasingInFileNames",
@@ -1142,7 +1182,7 @@ namespace ts {
affectsModuleResolution: true,
category: Diagnostics.Interop_Constraints,
description: Diagnostics.Ensure_that_casing_is_correct_in_imports,
- defaultValueDescription: "false"
+ defaultValueDescription: false,
},
{
name: "maxNodeModuleJsDepth",
@@ -1150,7 +1190,7 @@ namespace ts {
affectsModuleResolution: true,
category: Diagnostics.JavaScript_Support,
description: Diagnostics.Specify_the_maximum_folder_depth_used_for_checking_JavaScript_files_from_node_modules_Only_applicable_with_allowJs,
- defaultValueDescription: "0"
+ defaultValueDescription: 0,
},
{
name: "noStrictGenericChecks",
@@ -1158,7 +1198,7 @@ namespace ts {
affectsSemanticDiagnostics: true,
category: Diagnostics.Backwards_Compatibility,
description: Diagnostics.Disable_strict_checking_of_generic_signatures_in_function_types,
- defaultValueDescription: "false"
+ defaultValueDescription: false,
},
{
name: "useDefineForClassFields",
@@ -1175,6 +1215,7 @@ namespace ts {
affectsEmit: true,
category: Diagnostics.Emit,
description: Diagnostics.Preserve_unused_imported_values_in_the_JavaScript_output_that_would_otherwise_be_removed,
+ defaultValueDescription: false,
},
{
@@ -1182,7 +1223,7 @@ namespace ts {
type: "boolean",
category: Diagnostics.Backwards_Compatibility,
description: Diagnostics.Make_keyof_only_return_strings_instead_of_string_numbers_or_symbols_Legacy_option,
- defaultValueDescription: "false"
+ defaultValueDescription: false,
},
{
// A list of plugins to load in the language service
@@ -1237,27 +1278,31 @@ namespace ts {
shortName: "v",
category: Diagnostics.Command_line_Options,
description: Diagnostics.Enable_verbose_logging,
- type: "boolean"
+ type: "boolean",
+ defaultValueDescription: false,
},
{
name: "dry",
shortName: "d",
category: Diagnostics.Command_line_Options,
description: Diagnostics.Show_what_would_be_built_or_deleted_if_specified_with_clean,
- type: "boolean"
+ type: "boolean",
+ defaultValueDescription: false,
},
{
name: "force",
shortName: "f",
category: Diagnostics.Command_line_Options,
description: Diagnostics.Build_all_projects_including_those_that_appear_to_be_up_to_date,
- type: "boolean"
+ type: "boolean",
+ defaultValueDescription: false,
},
{
name: "clean",
category: Diagnostics.Command_line_Options,
description: Diagnostics.Delete_the_outputs_of_all_projects,
- type: "boolean"
+ type: "boolean",
+ defaultValueDescription: false,
}
];
@@ -1275,10 +1320,12 @@ namespace ts {
*/
name: "enableAutoDiscovery",
type: "boolean",
+ defaultValueDescription: false,
},
{
name: "enable",
type: "boolean",
+ defaultValueDescription: false,
},
{
name: "include",
@@ -1299,6 +1346,7 @@ namespace ts {
{
name: "disableFilenameBasedTypeAcquisition",
type: "boolean",
+ defaultValueDescription: false,
},
];
@@ -1337,7 +1385,7 @@ namespace ts {
/* @internal */
export const defaultInitCompilerOptions: CompilerOptions = {
module: ModuleKind.CommonJS,
- target: ScriptTarget.ES5,
+ target: ScriptTarget.ES2016,
strict: true,
esModuleInterop: true,
forceConsistentCasingInFileNames: true,
@@ -2280,7 +2328,7 @@ namespace ts {
return getCustomTypeMapOfCommandLineOption(optionDefinition.element);
}
else {
- return (optionDefinition as CommandLineOptionOfCustomType).type;
+ return optionDefinition.type;
}
}
@@ -2348,6 +2396,47 @@ namespace ts {
return result;
}
+ /**
+ * Generate a list of the compiler options whose value is not the default.
+ * @param options compilerOptions to be evaluated.
+ /** @internal */
+ export function getCompilerOptionsDiffValue(options: CompilerOptions, newLine: string): string {
+ const compilerOptionsMap = getSerializedCompilerOption(options);
+ return getOverwrittenDefaultOptions();
+
+ function makePadding(paddingLength: number): string {
+ return Array(paddingLength + 1).join(" ");
+ }
+
+ function getOverwrittenDefaultOptions() {
+ const result: string[] = [];
+ const tab = makePadding(2);
+ commandOptionsWithoutBuild.forEach(cmd => {
+ if (!compilerOptionsMap.has(cmd.name)) {
+ return;
+ }
+
+ const newValue = compilerOptionsMap.get(cmd.name);
+ const defaultValue = getDefaultValueForOption(cmd);
+ if (newValue !== defaultValue) {
+ result.push(`${tab}${cmd.name}: ${newValue}`);
+ }
+ else if (hasProperty(defaultInitCompilerOptions, cmd.name)) {
+ result.push(`${tab}${cmd.name}: ${defaultValue}`);
+ }
+ });
+ return result.join(newLine) + newLine;
+ }
+ }
+
+ /**
+ * Get the compiler options to be written into the tsconfig.json.
+ * @param options commandlineOptions to be included in the compileOptions.
+ */
+ function getSerializedCompilerOption(options: CompilerOptions): ESMap {
+ const compilerOptions = extend(options, defaultInitCompilerOptions);
+ return serializeCompilerOptions(compilerOptions);
+ }
/**
* Generate tsconfig configuration when running command line "--init"
* @param options commandlineOptions to be generated into tsconfig.json
@@ -2355,29 +2444,9 @@ namespace ts {
*/
/* @internal */
export function generateTSConfig(options: CompilerOptions, fileNames: readonly string[], newLine: string): string {
- const compilerOptions = extend(options, defaultInitCompilerOptions);
- const compilerOptionsMap = serializeCompilerOptions(compilerOptions);
+ const compilerOptionsMap = getSerializedCompilerOption(options);
return writeConfigurations();
- function getDefaultValueForOption(option: CommandLineOption) {
- switch (option.type) {
- case "number":
- return 1;
- case "boolean":
- return true;
- case "string":
- return option.isFilePath ? "./" : "";
- case "list":
- return [];
- case "object":
- return {};
- default:
- const iterResult = option.type.keys().next();
- if (!iterResult.done) return iterResult.value;
- return Debug.fail("Expected 'option.type' to have entries.");
- }
- }
-
function makePadding(paddingLength: number): string {
return Array(paddingLength + 1).join(" ");
}
@@ -3087,7 +3156,7 @@ namespace ts {
if (isCompilerOptionsValue(opt, value)) {
const optType = opt.type;
if (optType === "list" && isArray(value)) {
- return convertJsonOptionOfListType(opt as CommandLineOptionOfListType, value, basePath, errors);
+ return convertJsonOptionOfListType(opt , value, basePath, errors);
}
else if (!isString(optType)) {
return convertJsonOptionOfCustomType(opt as CommandLineOptionOfCustomType, value as string, errors);
@@ -3216,7 +3285,7 @@ namespace ts {
// Rather than re-query this for each file and filespec, we query the supported extensions
// once and store it on the expansion context.
const supportedExtensions = getSupportedExtensions(options, extraFileExtensions);
- const supportedExtensionsWithJsonIfResolveJsonModule = getSuppoertedExtensionsWithJsonIfResolveJsonModule(options, supportedExtensions);
+ const supportedExtensionsWithJsonIfResolveJsonModule = getSupportedExtensionsWithJsonIfResolveJsonModule(options, supportedExtensions);
// Literal files are always included verbatim. An "include" or "exclude" specification cannot
// remove a literal file.
@@ -3229,7 +3298,7 @@ namespace ts {
let jsonOnlyIncludeRegexes: readonly RegExp[] | undefined;
if (validatedIncludeSpecs && validatedIncludeSpecs.length > 0) {
- for (const file of host.readDirectory(basePath, supportedExtensionsWithJsonIfResolveJsonModule, validatedExcludeSpecs, validatedIncludeSpecs, /*depth*/ undefined)) {
+ for (const file of host.readDirectory(basePath, flatten(supportedExtensionsWithJsonIfResolveJsonModule), validatedExcludeSpecs, validatedIncludeSpecs, /*depth*/ undefined)) {
if (fileExtensionIs(file, Extension.Json)) {
// Valid only if *.json specified
if (!jsonOnlyIncludeRegexes) {
@@ -3440,7 +3509,7 @@ namespace ts {
? WatchDirectoryFlags.Recursive : WatchDirectoryFlags.None
};
}
- if (isImplicitGlob(spec)) {
+ if (isImplicitGlob(spec.substring(spec.lastIndexOf(directorySeparator) + 1))) {
return {
key: useCaseSensitiveFileNames ? spec : toFileNameLowerCase(spec),
flags: WatchDirectoryFlags.Recursive
@@ -3454,16 +3523,24 @@ namespace ts {
* extension priority.
*
* @param file The path to the file.
- * @param extensionPriority The priority of the extension.
- * @param context The expansion context.
*/
- function hasFileWithHigherPriorityExtension(file: string, literalFiles: ESMap, wildcardFiles: ESMap, extensions: readonly string[], keyMapper: (value: string) => string) {
- const extensionPriority = getExtensionPriority(file, extensions);
- const adjustedExtensionPriority = adjustExtensionPriority(extensionPriority, extensions);
- for (let i = ExtensionPriority.Highest; i < adjustedExtensionPriority; i++) {
- const higherPriorityExtension = extensions[i];
- const higherPriorityPath = keyMapper(changeExtension(file, higherPriorityExtension));
+ function hasFileWithHigherPriorityExtension(file: string, literalFiles: ESMap, wildcardFiles: ESMap, extensions: readonly string[][], keyMapper: (value: string) => string) {
+ const extensionGroup = forEach(extensions, group => fileExtensionIsOneOf(file, group) ? group : undefined);
+ if (!extensionGroup) {
+ return false;
+ }
+ for (const ext of extensionGroup) {
+ if (fileExtensionIs(file, ext)) {
+ return false;
+ }
+ const higherPriorityPath = keyMapper(changeExtension(file, ext));
if (literalFiles.has(higherPriorityPath) || wildcardFiles.has(higherPriorityPath)) {
+ if (ext === Extension.Dts && (fileExtensionIs(file, Extension.Js) || fileExtensionIs(file, Extension.Jsx))) {
+ // LEGACY BEHAVIOR: An off-by-one bug somewhere in the extension priority system for wildcard module loading allowed declaration
+ // files to be loaded alongside their js(x) counterparts. We regard this as generally undesirable, but retain the behavior to
+ // prevent breakage.
+ continue;
+ }
return true;
}
}
@@ -3476,15 +3553,18 @@ namespace ts {
* already been included.
*
* @param file The path to the file.
- * @param extensionPriority The priority of the extension.
- * @param context The expansion context.
*/
- function removeWildcardFilesWithLowerPriorityExtension(file: string, wildcardFiles: ESMap, extensions: readonly string[], keyMapper: (value: string) => string) {
- const extensionPriority = getExtensionPriority(file, extensions);
- const nextExtensionPriority = getNextLowestExtensionPriority(extensionPriority, extensions);
- for (let i = nextExtensionPriority; i < extensions.length; i++) {
- const lowerPriorityExtension = extensions[i];
- const lowerPriorityPath = keyMapper(changeExtension(file, lowerPriorityExtension));
+ function removeWildcardFilesWithLowerPriorityExtension(file: string, wildcardFiles: ESMap, extensions: readonly string[][], keyMapper: (value: string) => string) {
+ const extensionGroup = forEach(extensions, group => fileExtensionIsOneOf(file, group) ? group : undefined);
+ if (!extensionGroup) {
+ return;
+ }
+ for (let i = extensionGroup.length - 1; i >= 0; i--) {
+ const ext = extensionGroup[i];
+ if (fileExtensionIs(file, ext)) {
+ return;
+ }
+ const lowerPriorityPath = keyMapper(changeExtension(file, ext));
wildcardFiles.delete(lowerPriorityPath);
}
}
@@ -3528,4 +3608,24 @@ namespace ts {
})!; // TODO: GH#18217
}
}
+
+
+ function getDefaultValueForOption(option: CommandLineOption) {
+ switch (option.type) {
+ case "number":
+ return 1;
+ case "boolean":
+ return true;
+ case "string":
+ return option.isFilePath ? "./" : "";
+ case "list":
+ return [];
+ case "object":
+ return {};
+ default:
+ const iterResult = option.type.keys().next();
+ if (!iterResult.done) return iterResult.value;
+ return Debug.fail("Expected 'option.type' to have entries.");
+ }
+ }
}
diff --git a/src/compiler/core.ts b/src/compiler/core.ts
index eb07a11cbac..cceaa2abd50 100644
--- a/src/compiler/core.ts
+++ b/src/compiler/core.ts
@@ -1,25 +1,5 @@
/* @internal */
namespace ts {
- type GetIteratorCallback = | ReadonlyESMap | undefined>(iterable: I) => Iterator<
- I extends ReadonlyESMap ? [K, V] :
- I extends ReadonlySet ? T :
- I extends readonly (infer T)[] ? T :
- I extends undefined ? undefined :
- never>;
-
- function getCollectionImplementation<
- K1 extends MatchingKeys any>,
- K2 extends MatchingKeys ReturnType<(typeof NativeCollections)[K1]>>
- >(name: string, nativeFactory: K1, shimFactory: K2): NonNullable> {
- // NOTE: ts.ShimCollections will be defined for typescriptServices.js but not for tsc.js, so we must test for it.
- const constructor = NativeCollections[nativeFactory]() ?? ShimCollections?.[shimFactory](getIterator);
- if (constructor) return constructor as NonNullable>;
- throw new Error(`TypeScript requires an environment that provides a compatible native ${name} implementation.`);
- }
-
- export const Map = getCollectionImplementation("Map", "tryGetNativeMap", "createMapShim");
- export const Set = getCollectionImplementation("Set", "tryGetNativeSet", "createSetShim");
-
export function getIterator | ReadonlyESMap | undefined>(iterable: I): Iterator<
I extends ReadonlyESMap ? [K, V] :
I extends ReadonlySet ? T :
@@ -43,34 +23,6 @@ namespace ts {
export const emptyMap: ReadonlyESMap = new Map();
export const emptySet: ReadonlySet = new Set();
- /**
- * Create a new map.
- * @deprecated Use `new Map()` instead.
- */
- export function createMap(): ESMap;
- export function createMap(): ESMap;
- export function createMap(): ESMap {
- return new Map();
- }
-
- /**
- * Create a new map from a template object is provided, the map will copy entries from it.
- * @deprecated Use `new Map(getEntries(template))` instead.
- */
- export function createMapFromTemplate(template: MapLike): ESMap {
- const map: ESMap = new Map();
-
- // Copies keys/values from template. Note that for..in will not throw if
- // template is undefined, and instead will just exit the loop.
- for (const key in template) {
- if (hasOwnProperty.call(template, key)) {
- map.set(key, template[key]);
- }
- }
-
- return map;
- }
-
export function length(array: readonly any[] | undefined): number {
return array ? array.length : 0;
}
@@ -818,7 +770,11 @@ namespace ts {
return deduplicated as any as SortedReadonlyArray;
}
- export function insertSorted(array: SortedArray, insert: T, compare: Comparer): void {
+ export function createSortedArray(): SortedArray {
+ return [] as any as SortedArray; // TODO: GH#19873
+ }
+
+ export function insertSorted(array: SortedArray, insert: T, compare: Comparer, allowDuplicates?: boolean): void {
if (array.length === 0) {
array.push(insert);
return;
@@ -828,6 +784,9 @@ namespace ts {
if (insertIndex < 0) {
array.splice(~insertIndex, 0, insert);
}
+ else if (allowDuplicates) {
+ array.splice(insertIndex, 0, insert);
+ }
}
export function sortAndDeduplicate(array: readonly string[]): SortedReadonlyArray;
@@ -1293,11 +1252,11 @@ namespace ts {
return result;
}
- export function getOwnValues(sparseArray: T[]): T[] {
+ export function getOwnValues(collection: MapLike | T[]): T[] {
const values: T[] = [];
- for (const key in sparseArray) {
- if (hasOwnProperty.call(sparseArray, key)) {
- values.push(sparseArray[key]);
+ for (const key in collection) {
+ if (hasOwnProperty.call(collection, key)) {
+ values.push((collection as MapLike)[key]);
}
}
diff --git a/src/compiler/corePublic.ts b/src/compiler/corePublic.ts
index 58d53aec456..1d84b6726e6 100644
--- a/src/compiler/corePublic.ts
+++ b/src/compiler/corePublic.ts
@@ -1,7 +1,7 @@
namespace ts {
// WARNING: The script `configurePrerelease.ts` uses a regexp to parse out these values.
// If changing the text in this section, be sure to test `configurePrerelease` too.
- export const versionMajorMinor = "4.5";
+ export const versionMajorMinor = "4.7";
// The following is baselined as a literal template type without intervention
/** The version of the TypeScript compiler release */
// eslint-disable-next-line @typescript-eslint/no-inferrable-types
@@ -113,17 +113,22 @@ namespace ts {
}
/* @internal */
- export namespace NativeCollections {
- declare const Map: MapConstructor | undefined;
- declare const Set: SetConstructor | undefined;
+ namespace NativeCollections {
+ declare const self: any;
+
+ const globals = typeof globalThis !== "undefined" ? globalThis :
+ typeof global !== "undefined" ? global :
+ typeof self !== "undefined" ? self :
+ undefined;
/**
* Returns the native Map implementation if it is available and compatible (i.e. supports iteration).
*/
export function tryGetNativeMap(): MapConstructor | undefined {
// Internet Explorer's Map doesn't support iteration, so don't use it.
+ const gMap = globals?.Map;
// eslint-disable-next-line no-in-operator
- return typeof Map !== "undefined" && "entries" in Map.prototype && new Map([[0, 0]]).size === 1 ? Map : undefined;
+ return typeof gMap !== "undefined" && "entries" in gMap.prototype && new gMap([[0, 0]]).size === 1 ? gMap : undefined;
}
/**
@@ -131,8 +136,33 @@ namespace ts {
*/
export function tryGetNativeSet(): SetConstructor | undefined {
// Internet Explorer's Set doesn't support iteration, so don't use it.
+ const gSet = globals?.Set;
// eslint-disable-next-line no-in-operator
- return typeof Set !== "undefined" && "entries" in Set.prototype && new Set([0]).size === 1 ? Set : undefined;
+ return typeof gSet !== "undefined" && "entries" in gSet.prototype && new gSet([0]).size === 1 ? gSet : undefined;
}
}
-}
\ No newline at end of file
+
+ /* @internal */
+ export const Map = getCollectionImplementation("Map", "tryGetNativeMap", "createMapShim");
+ /* @internal */
+ export const Set = getCollectionImplementation("Set", "tryGetNativeSet", "createSetShim");
+
+ /* @internal */
+ type GetIteratorCallback = | ReadonlyESMap | undefined>(iterable: I) => Iterator<
+ I extends ReadonlyESMap ? [K, V] :
+ I extends ReadonlySet ? T :
+ I extends readonly (infer T)[] ? T :
+ I extends undefined ? undefined :
+ never>;
+
+ /* @internal */
+ function getCollectionImplementation<
+ K1 extends MatchingKeys any>,
+ K2 extends MatchingKeys ReturnType<(typeof NativeCollections)[K1]>>
+ >(name: string, nativeFactory: K1, shimFactory: K2): NonNullable> {
+ // NOTE: ts.ShimCollections will be defined for typescriptServices.js but not for tsc.js, so we must test for it.
+ const constructor = NativeCollections[nativeFactory]() ?? ShimCollections?.[shimFactory](getIterator);
+ if (constructor) return constructor as NonNullable>;
+ throw new Error(`TypeScript requires an environment that provides a compatible native ${name} implementation.`);
+ }
+}
diff --git a/src/compiler/debug.ts b/src/compiler/debug.ts
index 2e6b87a30e6..36a402b3340 100644
--- a/src/compiler/debug.ts
+++ b/src/compiler/debug.ts
@@ -171,12 +171,6 @@ namespace ts {
return value;
}
- /**
- * @deprecated Use `checkDefined` to check whether a value is defined inline. Use `assertIsDefined` to check whether
- * a value is defined at the statement level.
- */
- export const assertDefined = checkDefined;
-
export function assertEachIsDefined(value: NodeArray, message?: string, stackCrawlMark?: AnyFunction): asserts value is NodeArray;
export function assertEachIsDefined(value: readonly T[], message?: string, stackCrawlMark?: AnyFunction): asserts value is readonly NonNullable[];
export function assertEachIsDefined(value: readonly T[], message?: string, stackCrawlMark?: AnyFunction) {
@@ -190,12 +184,6 @@ namespace ts {
return value;
}
- /**
- * @deprecated Use `checkEachDefined` to check whether the elements of an array are defined inline. Use `assertEachIsDefined` to check whether
- * the elements of an array are defined at the statement level.
- */
- export const assertEachDefined = checkEachDefined;
-
export function assertNever(member: never, message = "Illegal value:", stackCrawlMark?: AnyFunction): never {
const detail = typeof member === "object" && hasProperty(member, "kind") && hasProperty(member, "pos") && formatSyntaxKind ? "SyntaxKind: " + formatSyntaxKind((member as Node).kind) : JSON.stringify(member);
return fail(`${message} ${detail}`, stackCrawlMark || assertNever);
@@ -351,6 +339,10 @@ namespace ts {
return formatEnum(kind, (ts as any).SyntaxKind, /*isFlags*/ false);
}
+ export function formatSnippetKind(kind: SnippetKind | undefined): string {
+ return formatEnum(kind, (ts as any).SnippetKind, /*isFlags*/ false);
+ }
+
export function formatNodeFlags(flags: NodeFlags | undefined): string {
return formatEnum(flags, (ts as any).NodeFlags, /*isFlags*/ true);
}
@@ -662,7 +654,7 @@ namespace ts {
if (text === undefined) {
const parseNode = getParseTreeNode(this);
const sourceFile = parseNode && getSourceFileOfNode(parseNode);
- text = sourceFile ? getSourceTextOfNodeFromSourceFile(sourceFile, parseNode!, includeTrivia) : "";
+ text = sourceFile ? getSourceTextOfNodeFromSourceFile(sourceFile, parseNode, includeTrivia) : "";
map?.set(this, text);
}
return text;
diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json
index f29fde71ca0..0418b54a2ed 100644
--- a/src/compiler/diagnosticMessages.json
+++ b/src/compiler/diagnosticMessages.json
@@ -335,7 +335,7 @@
"category": "Error",
"code": 1116
},
- "An object literal cannot have multiple properties with the same name in strict mode.": {
+ "An object literal cannot have multiple properties with the same name.": {
"category": "Error",
"code": 1117
},
@@ -727,11 +727,11 @@
"category": "Error",
"code": 1231
},
- "An import declaration can only be used in a namespace or module.": {
+ "An import declaration can only be used at the top level of a namespace or module.": {
"category": "Error",
"code": 1232
},
- "An export declaration can only be used in a module.": {
+ "An export declaration can only be used at the top level of a namespace or module.": {
"category": "Error",
"code": 1233
},
@@ -739,7 +739,7 @@
"category": "Error",
"code": 1234
},
- "A namespace declaration is only allowed in a namespace or module.": {
+ "A namespace declaration is only allowed at the top level of a namespace or module.": {
"category": "Error",
"code": 1235
},
@@ -867,6 +867,18 @@
"category": "Error",
"code": 1268
},
+ "Cannot use 'export import' on a type or type-only namespace when the '--isolatedModules' flag is provided.": {
+ "category": "Error",
+ "code": 1269
+ },
+ "Decorator function return type '{0}' is not assignable to type '{1}'.": {
+ "category": "Error",
+ "code": 1270
+ },
+ "Decorator function return type is '{0}' but is expected to be 'void' or 'any'.": {
+ "category": "Error",
+ "code": 1271
+ },
"'with' statements are not allowed in an async function block.": {
"category": "Error",
@@ -920,15 +932,15 @@
"category": "Error",
"code": 1322
},
- "Dynamic imports are only supported when the '--module' flag is set to 'es2020', 'esnext', 'commonjs', 'amd', 'system', or 'umd'.": {
+ "Dynamic imports are only supported when the '--module' flag is set to 'es2020', 'es2022', 'esnext', 'commonjs', 'amd', 'system', 'umd', 'node12', or 'nodenext'.": {
"category": "Error",
"code": 1323
},
- "Dynamic import must have one specifier as an argument.": {
+ "Dynamic imports only support a second argument when the '--module' option is set to 'esnext' or 'nodenext'.": {
"category": "Error",
"code": 1324
},
- "Specifier of dynamic import cannot be spread element.": {
+ "Argument of dynamic import cannot be spread element.": {
"category": "Error",
"code": 1325
},
@@ -992,7 +1004,7 @@
"category": "Error",
"code": 1342
},
- "The 'import.meta' meta-property is only allowed when the '--module' option is 'es2020', 'esnext', or 'system'.": {
+ "The 'import.meta' meta-property is only allowed when the '--module' option is 'es2020', 'es2022', 'esnext', 'system', 'node12', or 'nodenext'.": {
"category": "Error",
"code": 1343
},
@@ -1116,7 +1128,7 @@
"category": "Message",
"code": 1377
},
- "Top-level 'await' expressions are only allowed when the 'module' option is set to 'esnext' or 'system', and the 'target' option is set to 'es2017' or higher.": {
+ "Top-level 'await' expressions are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', or 'nodenext', and the 'target' option is set to 'es2017' or higher.": {
"category": "Error",
"code": 1378
},
@@ -1140,10 +1152,6 @@
"category": "Error",
"code": 1383
},
- "A 'new' expression with type arguments must always be followed by a parenthesized argument list.": {
- "category": "Error",
- "code": 1384
- },
"Function type notation must be parenthesized when used in a union type.": {
"category": "Error",
"code": 1385
@@ -1164,6 +1172,10 @@
"category": "Error",
"code": 1389
},
+ "'{0}' is not allowed as a parameter name.": {
+ "category": "Error",
+ "code": 1390
+ },
"An import alias cannot use 'import type'": {
"category": "Error",
"code": 1392
@@ -1324,7 +1336,7 @@
"category": "Error",
"code": 1431
},
- "Top-level 'for await' loops are only allowed when the 'module' option is set to 'esnext' or 'system', and the 'target' option is set to 'es2017' or higher.": {
+ "Top-level 'for await' loops are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', or 'nodenext', and the 'target' option is set to 'es2017' or higher.": {
"category": "Error",
"code": 1432
},
@@ -1388,6 +1400,43 @@
"category": "Message",
"code": 1449
},
+ "Dynamic imports can only accept a module specifier and an optional assertion as arguments": {
+ "category": "Message",
+ "code": 1450
+ },
+ "Private identifiers are only allowed in class bodies and may only be used as part of a class member declaration, property access, or on the left-hand-side of an 'in' expression": {
+ "category": "Error",
+ "code": 1451
+ },
+ "Resolution modes are only supported when `moduleResolution` is `node12` or `nodenext`.": {
+ "category": "Error",
+ "code": 1452
+ },
+ "`resolution-mode` should be either `require` or `import`.": {
+ "category": "Error",
+ "code": 1453
+ },
+
+ "The 'import.meta' meta-property is not allowed in files which will build into CommonJS output.": {
+ "category": "Error",
+ "code": 1470
+ },
+ "Module '{0}' cannot be imported using this construct. The specifier only resolves to an ES module, which cannot be imported synchronously. Use dynamic import instead.": {
+ "category": "Error",
+ "code": 1471
+ },
+ "'catch' or 'finally' expected.": {
+ "category": "Error",
+ "code": 1472
+ },
+ "An import declaration can only be used at the top level of a module.": {
+ "category": "Error",
+ "code": 1473
+ },
+ "An export declaration can only be used at the top level of a module.": {
+ "category": "Error",
+ "code": 1474
+ },
"The types of '{0}' are incompatible between these types.": {
"category": "Error",
@@ -1417,6 +1466,14 @@
"code": 2205,
"elidedInCompatabilityPyramid": true
},
+ "The 'type' modifier cannot be used on a named import when 'import type' is used on its import statement.": {
+ "category": "Error",
+ "code": 2206
+ },
+ "The 'type' modifier cannot be used on a named export when 'export type' is used on its export statement.": {
+ "category": "Error",
+ "code": 2207
+ },
"Duplicate identifier '{0}'.": {
"category": "Error",
@@ -1462,6 +1519,10 @@
"category": "Error",
"code": 2310
},
+ "Cannot find name '{0}'. Did you mean to write this in an async function?": {
+ "category": "Error",
+ "code": 2311
+ },
"An interface can only extend an object type or intersection of object types with statically known members.": {
"category": "Error",
"code": 2312
@@ -1650,7 +1711,7 @@
"category": "Error",
"code": 2359
},
- "The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.": {
+ "The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.": {
"category": "Error",
"code": 2360
},
@@ -1714,7 +1775,7 @@
"category": "Error",
"code": 2375
},
- "A 'super' call must be the first statement in the constructor when a class contains initialized properties, parameter properties, or private identifiers.": {
+ "A 'super' call must be the first statement in the constructor to refer to 'super' or 'this' when a derived class contains initialized properties, parameter properties, or private identifiers.": {
"category": "Error",
"code": 2376
},
@@ -1806,6 +1867,10 @@
"category": "Error",
"code": 2400
},
+ "A 'super' call must be a root-level statement within a constructor of a derived class that contains initialized properties, parameter properties, or private identifiers.": {
+ "category": "Error",
+ "code": 2401
+ },
"Expression resolves to '_super' that compiler uses to capture base class reference.": {
"category": "Error",
"code": 2402
@@ -2634,6 +2699,10 @@
"category": "Error",
"code": 2634
},
+ "Type '{0}' has no signatures for which the type argument list is applicable.": {
+ "category": "Error",
+ "code": 2635
+ },
"Cannot augment module '{0}' with value exports because it resolves to a non-module entity.": {
"category": "Error",
@@ -3240,10 +3309,6 @@
"category": "Error",
"code": 2804
},
- "Static fields with private names can't have initializers when the '--useDefineForClassFields' flag is not specified with a '--target' of 'esnext'. Consider adding the '--useDefineForClassFields' flag.": {
- "category": "Error",
- "code": 2805
- },
"Private accessor was defined without a getter.": {
"category": "Error",
"code": 2806
@@ -3260,10 +3325,6 @@
"category": "Error",
"code": 2809
},
- "Property '{0}' may not be used in a static property's initializer in the same class when 'target' is 'esnext' and 'useDefineForClassFields' is 'false'.": {
- "category": "Error",
- "code": 2810
- },
"Initializer for property '{0}'": {
"category": "Error",
"code": 2811
@@ -3300,6 +3361,38 @@
"category": "Error",
"code": 2819
},
+ "Type '{0}' is not assignable to type '{1}'. Did you mean '{2}'?": {
+ "category": "Error",
+ "code": 2820
+ },
+ "Import assertions are only supported when the '--module' option is set to 'esnext' or 'nodenext'.": {
+ "category": "Error",
+ "code": 2821
+ },
+ "Import assertions cannot be used with type-only imports or exports.": {
+ "category": "Error",
+ "code": 2822
+ },
+ "Cannot find namespace '{0}'. Did you mean '{1}'?": {
+ "category": "Error",
+ "code": 2833
+ },
+ "Relative import paths need explicit file extensions in EcmaScript imports when '--moduleResolution' is 'node12' or 'nodenext'. Consider adding an extension to the import path.": {
+ "category": "Error",
+ "code": 2834
+ },
+ "Relative import paths need explicit file extensions in EcmaScript imports when '--moduleResolution' is 'node12' or 'nodenext'. Did you mean '{0}'?": {
+ "category": "Error",
+ "code": 2835
+ },
+ "Import assertions are not allowed on statements that transpile to commonjs 'require' calls.": {
+ "category": "Error",
+ "code": 2836
+ },
+ "Import assertion values must be string literal expressions.": {
+ "category": "Error",
+ "code": 2837
+ },
"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",
@@ -3705,6 +3798,30 @@
"category": "Error",
"code": 4118
},
+ "This member must have a JSDoc comment with an '@override' tag because it overrides a member in the base class '{0}'.": {
+ "category": "Error",
+ "code": 4119
+ },
+ "This parameter property must have a JSDoc comment with an '@override' tag because it overrides a member in the base class '{0}'.": {
+ "category": "Error",
+ "code": 4120
+ },
+ "This member cannot have a JSDoc comment with an '@override' tag because its containing class '{0}' does not extend another class.": {
+ "category": "Error",
+ "code": 4121
+ },
+ "This member cannot have a JSDoc comment with an '@override' tag because it is not declared in the base class '{0}'.": {
+ "category": "Error",
+ "code": 4122
+ },
+ "This member cannot have a JSDoc comment with an 'override' tag because it is not declared in the base class '{0}'. Did you mean '{1}'?": {
+ "category": "Error",
+ "code": 4123
+ },
+ "Compiler option '{0}' of value '{1}' is unstable. Use nightly TypeScript to silence this error. Try updating with 'npm install -D typescript@next'.": {
+ "category": "Error",
+ "code": 4124
+ },
"The current host does not support the '{0}' option.": {
"category": "Error",
@@ -3943,10 +4060,6 @@
"category": "Message",
"code": 6002
},
- "Specify the location where debugger should locate map files instead of generated locations.": {
- "category": "Message",
- "code": 6003
- },
"Specify the location where debugger should locate TypeScript files instead of source locations.": {
"category": "Message",
"code": 6004
@@ -4075,6 +4188,11 @@
"category": "Message",
"code": 6040
},
+ "Errors Files": {
+ "_locale_notes": "There is a double space, and the order cannot be changed (they're table headings) ^",
+ "category": "Message",
+ "code": 6041
+ },
"Generates corresponding '.map' file.": {
"category": "Message",
"code": 6043
@@ -4513,10 +4631,6 @@
"category": "Message",
"code": 6163
},
- "Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files.": {
- "category": "Message",
- "code": 6164
- },
"Do not truncate error messages.": {
"category": "Message",
"code": 6165
@@ -4858,6 +4972,47 @@
"category": "Error",
"code": 6258
},
+ "Found 1 error in {1}": {
+ "category": "Message",
+ "code": 6259
+ },
+ "Found {0} errors in the same file, starting at: {1}": {
+ "category": "Message",
+ "code": 6260
+ },
+ "Found {0} errors in {1} files.": {
+ "category": "Message",
+ "code": 6261
+ },
+
+ "Directory '{0}' has no containing package.json scope. Imports will not resolve.": {
+ "category": "Message",
+ "code": 6270
+ },
+ "Import specifier '{0}' does not exist in package.json scope at path '{1}'.": {
+ "category": "Message",
+ "code": 6271
+ },
+ "Invalid import specifier '{0}' has no possible resolutions.": {
+ "category": "Message",
+ "code": 6272
+ },
+ "package.json scope '{0}' has no imports defined.": {
+ "category": "Message",
+ "code": 6273
+ },
+ "package.json scope '{0}' explicitly maps specifier '{1}' to null.": {
+ "category": "Message",
+ "code": 6274
+ },
+ "package.json scope '{0}' has invalid type for target of specifier '{1}'": {
+ "category": "Message",
+ "code": 6275
+ },
+ "Export specifier '{0}' does not exist in package.json scope at path '{1}'.": {
+ "category": "Message",
+ "code": 6276
+ },
"Enable project compilation": {
"category": "Message",
@@ -5904,7 +6059,22 @@
"category": "Error",
"code": 7058
},
-
+ "This syntax is reserved in files with the .mts or .cts extension. Use an `as` expression instead.": {
+ "category": "Error",
+ "code": 7059
+ },
+ "This syntax is reserved in files with the .mts or .cts extension. Add a trailing comma or explicit constraint.": {
+ "category": "Error",
+ "code": 7060
+ },
+ "A mapped type may not declare properties or methods.": {
+ "category": "Error",
+ "code": 7061
+ },
+ "JSON imports are experimental in ES module mode imports.": {
+ "category": "Error",
+ "code": 7062
+ },
"You cannot rename this element.": {
"category": "Error",
@@ -6127,7 +6297,7 @@
"code": 18003
},
- "File is a CommonJS module; it may be converted to an ES6 module.": {
+ "File is a CommonJS module; it may be converted to an ES module.": {
"category": "Suggestion",
"code": 80001
},
@@ -6204,7 +6374,7 @@
"category": "Message",
"code": 90012
},
- "Import '{0}' from module \"{1}\"": {
+ "Import '{0}' from \"{1}\"": {
"category": "Message",
"code": 90013
},
@@ -6212,10 +6382,6 @@
"category": "Message",
"code": 90014
},
- "Add '{0}' to existing import declaration from \"{1}\"": {
- "category": "Message",
- "code": 90015
- },
"Declare property '{0}'": {
"category": "Message",
"code": 90016
@@ -6280,14 +6446,6 @@
"category": "Message",
"code": 90031
},
- "Import default '{0}' from module \"{1}\"": {
- "category": "Message",
- "code": 90032
- },
- "Add default import '{0}' to existing import declaration from \"{1}\"": {
- "category": "Message",
- "code": 90033
- },
"Add parameter name": {
"category": "Message",
"code": 90034
@@ -6320,6 +6478,27 @@
"category": "Message",
"code": 90053
},
+ "Includes imports of types referenced by '{0}'": {
+ "category": "Message",
+ "code": 90054
+ },
+ "Remove 'type' from import declaration from \"{0}\"": {
+ "category": "Message",
+ "code": 90055
+ },
+ "Remove 'type' from import of '{0}' from \"{1}\"": {
+ "category": "Message",
+ "code": 90056
+ },
+ "Add import from \"{0}\"": {
+ "category": "Message",
+ "code": 90057
+ },
+ "Update import from \"{0}\"": {
+ "category": "Message",
+ "code": 90058
+ },
+
"Convert function to an ES2015 class": {
"category": "Message",
"code": 95001
@@ -6376,7 +6555,7 @@
"category": "Message",
"code": 95016
},
- "Convert to ES6 module": {
+ "Convert to ES module": {
"category": "Message",
"code": 95017
},
@@ -6972,6 +7151,22 @@
"category": "Message",
"code": 95169
},
+ "Convert named imports to default import": {
+ "category": "Message",
+ "code": 95170
+ },
+ "Delete unused '@param' tag '{0}'": {
+ "category": "Message",
+ "code": 95171
+ },
+ "Delete all unused '@param' tags": {
+ "category": "Message",
+ "code": 95172
+ },
+ "Rename '@param' tag name '{0}' to '{1}'": {
+ "category": "Message",
+ "code": 95173
+ },
"No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": {
"category": "Error",
diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts
index 5667e792b46..9e5b39bd7a8 100644
--- a/src/compiler/emitter.ts
+++ b/src/compiler/emitter.ts
@@ -89,7 +89,7 @@ namespace ts {
return getOutputPathsForBundle(options, forceDtsPaths);
}
else {
- const ownOutputFilePath = getOwnEmitOutputFilePath(sourceFile.fileName, host, getOutputExtension(sourceFile, options));
+ const ownOutputFilePath = getOwnEmitOutputFilePath(sourceFile.fileName, host, getOutputExtension(sourceFile.fileName, options));
const isJsonFile = isJsonSourceFile(sourceFile);
// If json file emits to the same location skip writing it, if emitDeclarationOnly skip writing it
const isJsonEmittedToSameLocation = isJsonFile &&
@@ -106,27 +106,13 @@ namespace ts {
return (options.sourceMap && !options.inlineSourceMap) ? jsFilePath + ".map" : undefined;
}
- // JavaScript files are always LanguageVariant.JSX, as JSX syntax is allowed in .js files also.
- // So for JavaScript files, '.jsx' is only emitted if the input was '.jsx', and JsxEmit.Preserve.
- // For TypeScript, the only time to emit with a '.jsx' extension, is on JSX input, and JsxEmit.Preserve
/* @internal */
- export function getOutputExtension(sourceFile: SourceFile, options: CompilerOptions): Extension {
- if (isJsonSourceFile(sourceFile)) {
- return Extension.Json;
- }
-
- if (options.jsx === JsxEmit.Preserve) {
- if (isSourceFileJS(sourceFile)) {
- if (fileExtensionIs(sourceFile.fileName, Extension.Jsx)) {
- return Extension.Jsx;
- }
- }
- else if (sourceFile.languageVariant === LanguageVariant.JSX) {
- // TypeScript source file preserving JSX syntax
- return Extension.Jsx;
- }
- }
- return Extension.Js;
+ export function getOutputExtension(fileName: string, options: CompilerOptions): Extension {
+ return fileExtensionIs(fileName, Extension.Json) ? Extension.Json :
+ options.jsx === JsxEmit.Preserve && fileExtensionIsOneOf(fileName, [Extension.Jsx, Extension.Tsx]) ? Extension.Jsx :
+ fileExtensionIsOneOf(fileName, [Extension.Mts, Extension.Mjs]) ? Extension.Mjs :
+ fileExtensionIsOneOf(fileName, [Extension.Cts, Extension.Cjs]) ? Extension.Cjs :
+ Extension.Js;
}
function getOutputPathWithoutChangingExt(inputFileName: string, configFile: ParsedCommandLine, ignoreCase: boolean, outputDir: string | undefined, getCommonSourceDirectory?: () => string) {
@@ -140,10 +126,9 @@ namespace ts {
/* @internal */
export function getOutputDeclarationFileName(inputFileName: string, configFile: ParsedCommandLine, ignoreCase: boolean, getCommonSourceDirectory?: () => string) {
- Debug.assert(!fileExtensionIs(inputFileName, Extension.Dts) && !fileExtensionIs(inputFileName, Extension.Json));
return changeExtension(
getOutputPathWithoutChangingExt(inputFileName, configFile, ignoreCase, configFile.options.declarationDir || configFile.options.outDir, getCommonSourceDirectory),
- Extension.Dts
+ getDeclarationEmitExtensionForPath(inputFileName)
);
}
@@ -152,11 +137,7 @@ namespace ts {
const isJsonFile = fileExtensionIs(inputFileName, Extension.Json);
const outputFileName = changeExtension(
getOutputPathWithoutChangingExt(inputFileName, configFile, ignoreCase, configFile.options.outDir, getCommonSourceDirectory),
- isJsonFile ?
- Extension.Json :
- configFile.options.jsx === JsxEmit.Preserve && (fileExtensionIs(inputFileName, Extension.Tsx) || fileExtensionIs(inputFileName, Extension.Jsx)) ?
- Extension.Jsx :
- Extension.Js
+ getOutputExtension(inputFileName, configFile.options)
);
return !isJsonFile || comparePaths(inputFileName, outputFileName, Debug.checkDefined(configFile.options.configFilePath), ignoreCase) !== Comparison.EqualTo ?
outputFileName :
@@ -238,7 +219,7 @@ namespace ts {
export function getCommonSourceDirectoryOfConfig({ options, fileNames }: ParsedCommandLine, ignoreCase: boolean): string {
return getCommonSourceDirectory(
options,
- () => filter(fileNames, file => !(options.noEmitForJsFiles && fileExtensionIsOneOf(file, supportedJSExtensions)) && !fileExtensionIs(file, Extension.Dts)),
+ () => filter(fileNames, file => !(options.noEmitForJsFiles && fileExtensionIsOneOf(file, supportedJSExtensionsFlat)) && !fileExtensionIs(file, Extension.Dts)),
getDirectoryPath(normalizeSlashes(Debug.checkDefined(options.configFilePath))),
createGetCanonicalFileName(!ignoreCase)
);
@@ -1070,7 +1051,7 @@ namespace ts {
writeLine();
const pos = writer.getTextPos();
const savedSections = bundleFileInfo && bundleFileInfo.sections;
- if (savedSections) bundleFileInfo!.sections = [];
+ if (savedSections) bundleFileInfo.sections = [];
print(EmitHint.Unspecified, prepend, /*sourceFile*/ undefined);
if (bundleFileInfo) {
const newSections = bundleFileInfo.sections;
@@ -1302,7 +1283,13 @@ namespace ts {
currentParenthesizerRule = undefined;
}
- function pipelineEmitWithHintWorker(hint: EmitHint, node: Node): void {
+ function pipelineEmitWithHintWorker(hint: EmitHint, node: Node, allowSnippets = true): void {
+ if (allowSnippets) {
+ const snippet = getSnippetElement(node);
+ if (snippet) {
+ return emitSnippetNode(hint, node, snippet);
+ }
+ }
if (hint === EmitHint.SourceFile) return emitSourceFile(cast(node, isSourceFile));
if (hint === EmitHint.IdentifierName) return emitIdentifier(cast(node, isIdentifier));
if (hint === EmitHint.JsxAttributeValue) return emitLiteral(cast(node, isStringLiteral), /*jsxAttributeEscape*/ true);
@@ -1515,6 +1502,10 @@ namespace ts {
return emitNamedExports(node as NamedExports);
case SyntaxKind.ExportSpecifier:
return emitExportSpecifier(node as ExportSpecifier);
+ case SyntaxKind.AssertClause:
+ return emitAssertClause(node as AssertClause);
+ case SyntaxKind.AssertEntry:
+ return emitAssertEntry(node as AssertEntry);
case SyntaxKind.MissingDeclaration:
return;
@@ -1613,6 +1604,7 @@ namespace ts {
return emitJSDocSignature(node as JSDocSignature);
case SyntaxKind.JSDocTag:
case SyntaxKind.JSDocClassTag:
+ case SyntaxKind.JSDocOverrideTag:
return emitJSDocSimpleTag(node as JSDocTag);
case SyntaxKind.JSDocAugmentsTag:
case SyntaxKind.JSDocImplementsTag:
@@ -1625,7 +1617,6 @@ namespace ts {
case SyntaxKind.JSDocPrivateTag:
case SyntaxKind.JSDocProtectedTag:
case SyntaxKind.JSDocReadonlyTag:
- case SyntaxKind.JSDocOverrideTag:
return;
case SyntaxKind.JSDocCallbackTag:
return emitJSDocCallbackTag(node as JSDocCallbackTag);
@@ -1680,6 +1671,8 @@ namespace ts {
// Identifiers
case SyntaxKind.Identifier:
return emitIdentifier(node as Identifier);
+ case SyntaxKind.PrivateIdentifier:
+ return emitPrivateIdentifier(node as PrivateIdentifier);
// Expressions
case SyntaxKind.ArrayLiteralExpression:
@@ -1734,6 +1727,8 @@ namespace ts {
return emitAsExpression(node as AsExpression);
case SyntaxKind.NonNullExpression:
return emitNonNullExpression(node as NonNullExpression);
+ case SyntaxKind.ExpressionWithTypeArguments:
+ return emitExpressionWithTypeArguments(node as ExpressionWithTypeArguments);
case SyntaxKind.MetaProperty:
return emitMetaProperty(node as MetaProperty);
case SyntaxKind.SyntheticExpression:
@@ -1937,6 +1932,37 @@ namespace ts {
}
}
+ //
+ // Snippet Elements
+ //
+
+ function emitSnippetNode(hint: EmitHint, node: Node, snippet: SnippetElement) {
+ switch (snippet.kind) {
+ case SnippetKind.Placeholder:
+ emitPlaceholder(hint, node, snippet);
+ break;
+ case SnippetKind.TabStop:
+ emitTabStop(hint, node, snippet);
+ break;
+ }
+ }
+
+ function emitPlaceholder(hint: EmitHint, node: Node, snippet: Placeholder) {
+ nonEscapingWrite(`\$\{${snippet.order}:`); // `${2:`
+ pipelineEmitWithHintWorker(hint, node, /*allowSnippets*/ false); // `...`
+ nonEscapingWrite(`\}`); // `}`
+ // `${2:...}`
+ }
+
+ function emitTabStop(hint: EmitHint, node: Node, snippet: TabStop) {
+ // A tab stop should only be attached to an empty node, i.e. a node that doesn't emit any text.
+ Debug.assert(node.kind === SyntaxKind.EmptyStatement,
+ `A tab stop cannot be attached to a node of kind ${Debug.formatSyntaxKind(node.kind)}.`);
+ Debug.assert(hint !== EmitHint.EmbeddedStatement,
+ `A tab stop cannot be attached to an embedded statement.`);
+ nonEscapingWrite(`\$${snippet.order}`);
+ }
+
//
// Identifiers
//
@@ -2203,6 +2229,7 @@ namespace ts {
writeKeyword("typeof");
writeSpace();
emit(node.exprName);
+ emitTypeArguments(node, node.typeArguments);
}
function emitTypeLiteral(node: TypeLiteralNode) {
@@ -2747,7 +2774,7 @@ namespace ts {
function emitYieldExpression(node: YieldExpression) {
emitTokenWithComment(SyntaxKind.YieldKeyword, node.pos, writeKeyword, node);
emit(node.asteriskToken);
- emitExpressionWithLeadingSpace(node.expression, parenthesizer.parenthesizeExpressionForDisallowedComma);
+ emitExpressionWithLeadingSpace(node.expression && parenthesizeExpressionForNoAsi(node.expression), parenthesizeExpressionForNoAsiAndDisallowedComma);
}
function emitSpreadElement(node: SpreadElement) {
@@ -2971,9 +2998,49 @@ namespace ts {
return pos;
}
+ function commentWillEmitNewLine(node: CommentRange) {
+ return node.kind === SyntaxKind.SingleLineCommentTrivia || !!node.hasTrailingNewLine;
+ }
+
+ function willEmitLeadingNewLine(node: Expression): boolean {
+ if (!currentSourceFile) return false;
+ if (some(getLeadingCommentRanges(currentSourceFile.text, node.pos), commentWillEmitNewLine)) return true;
+ if (some(getSyntheticLeadingComments(node), commentWillEmitNewLine)) return true;
+ if (isPartiallyEmittedExpression(node)) {
+ if (node.pos !== node.expression.pos) {
+ if (some(getTrailingCommentRanges(currentSourceFile.text, node.expression.pos), commentWillEmitNewLine)) return true;
+ }
+ return willEmitLeadingNewLine(node.expression);
+ }
+ return false;
+ }
+
+ /**
+ * Wraps an expression in parens if we would emit a leading comment that would introduce a line separator
+ * between the node and its parent.
+ */
+ function parenthesizeExpressionForNoAsi(node: Expression) {
+ if (!commentsDisabled && isPartiallyEmittedExpression(node) && willEmitLeadingNewLine(node)) {
+ const parseNode = getParseTreeNode(node);
+ if (parseNode && isParenthesizedExpression(parseNode)) {
+ // If the original node was a parenthesized expression, restore it to preserve comment and source map emit
+ const parens = factory.createParenthesizedExpression(node.expression);
+ setOriginalNode(parens, node);
+ setTextRange(parens, parseNode);
+ return parens;
+ }
+ return factory.createParenthesizedExpression(node);
+ }
+ return node;
+ }
+
+ function parenthesizeExpressionForNoAsiAndDisallowedComma(node: Expression) {
+ return parenthesizeExpressionForNoAsi(parenthesizer.parenthesizeExpressionForDisallowedComma(node));
+ }
+
function emitReturnStatement(node: ReturnStatement) {
emitTokenWithComment(SyntaxKind.ReturnKeyword, node.pos, writeKeyword, /*contextNode*/ node);
- emitExpressionWithLeadingSpace(node.expression);
+ emitExpressionWithLeadingSpace(node.expression && parenthesizeExpressionForNoAsi(node.expression), parenthesizeExpressionForNoAsi);
writeTrailingSemicolon();
}
@@ -3005,7 +3072,7 @@ namespace ts {
function emitThrowStatement(node: ThrowStatement) {
emitTokenWithComment(SyntaxKind.ThrowKeyword, node.pos, writeKeyword, node);
- emitExpressionWithLeadingSpace(node.expression);
+ emitExpressionWithLeadingSpace(parenthesizeExpressionForNoAsi(node.expression), parenthesizeExpressionForNoAsi);
writeTrailingSemicolon();
}
@@ -3322,6 +3389,9 @@ namespace ts {
writeSpace();
}
emitExpression(node.moduleSpecifier);
+ if (node.assertClause) {
+ emitWithLeadingSpace(node.assertClause);
+ }
writeTrailingSemicolon();
}
@@ -3390,9 +3460,33 @@ namespace ts {
writeSpace();
emitExpression(node.moduleSpecifier);
}
+ if (node.assertClause) {
+ emitWithLeadingSpace(node.assertClause);
+ }
writeTrailingSemicolon();
}
+ function emitAssertClause(node: AssertClause) {
+ emitTokenWithComment(SyntaxKind.AssertKeyword, node.pos, writeKeyword, node);
+ writeSpace();
+ const elements = node.elements;
+ emitList(node, elements, ListFormat.ImportClauseEntries);
+ }
+
+ function emitAssertEntry(node: AssertEntry) {
+ emit(node.name);
+ writePunctuation(":");
+ writeSpace();
+
+ const value = node.value;
+ /** @see {emitPropertyAssignment} */
+ if ((getEmitFlags(value) & EmitFlags.NoLeadingComments) === 0) {
+ const commentRange = getCommentRange(value);
+ emitTrailingCommentsOfPosition(commentRange.pos);
+ }
+ emit(value);
+ }
+
function emitNamespaceExportDeclaration(node: NamespaceExportDeclaration) {
let nextPos = emitTokenWithComment(SyntaxKind.ExportKeyword, node.pos, writeKeyword, node);
writeSpace();
@@ -3427,6 +3521,10 @@ namespace ts {
}
function emitImportOrExportSpecifier(node: ImportOrExportSpecifier) {
+ if (node.isTypeOnly) {
+ writeKeyword("type");
+ writeSpace();
+ }
if (node.propertyName) {
emit(node.propertyName);
writeSpace();
@@ -3898,8 +3996,11 @@ namespace ts {
}
for (const directive of types) {
const pos = writer.getTextPos();
- writeComment(`/// `);
- if (bundleFileInfo) bundleFileInfo.sections.push({ pos, end: writer.getTextPos(), kind: BundleFileSectionKind.Type, data: directive.fileName });
+ const resolutionMode = directive.resolutionMode && directive.resolutionMode !== currentSourceFile?.impliedNodeFormat
+ ? `resolution-mode="${directive.resolutionMode === ModuleKind.ESNext ? "import" : "require"}"`
+ : "";
+ writeComment(`/// `);
+ if (bundleFileInfo) bundleFileInfo.sections.push({ pos, end: writer.getTextPos(), kind: !directive.resolutionMode ? BundleFileSectionKind.Type : directive.resolutionMode === ModuleKind.ESNext ? BundleFileSectionKind.TypeResolutionModeImport : BundleFileSectionKind.TypeResolutionModeRequire, data: directive.fileName });
writeLine();
}
for (const directive of libs) {
@@ -3924,7 +4025,14 @@ namespace ts {
// Transformation nodes
function emitPartiallyEmittedExpression(node: PartiallyEmittedExpression) {
+ const emitFlags = getEmitFlags(node);
+ if (!(emitFlags & EmitFlags.NoLeadingComments) && node.pos !== node.expression.pos) {
+ emitTrailingCommentsOfPosition(node.expression.pos);
+ }
emitExpression(node.expression);
+ if (!(emitFlags & EmitFlags.NoTrailingComments) && node.end !== node.expression.end) {
+ emitLeadingCommentsOfPosition(node.expression.end);
+ }
}
function emitCommaList(node: CommaListExpression) {
@@ -4312,10 +4420,8 @@ namespace ts {
// Emit this child.
previousSourceFileTextKind = recordBundleFileInternalSectionStart(child);
if (shouldEmitInterveningComments) {
- if (emitTrailingCommentsOfPosition) {
- const commentRange = getCommentRange(child);
- emitTrailingCommentsOfPosition(commentRange.pos);
- }
+ const commentRange = getCommentRange(child);
+ emitTrailingCommentsOfPosition(commentRange.pos);
}
else {
shouldEmitInterveningComments = mayEmitInterveningComments;
@@ -4439,6 +4545,16 @@ namespace ts {
writer.writeProperty(s);
}
+ function nonEscapingWrite(s: string) {
+ // This should be defined in a snippet-escaping text writer.
+ if (writer.nonEscapingWrite) {
+ writer.nonEscapingWrite(s);
+ }
+ else {
+ writer.write(s);
+ }
+ }
+
function writeLine(count = 1) {
for (let i = 0; i < count; i++) {
writer.writeLine(i > 0);
@@ -4683,7 +4799,7 @@ namespace ts {
function writeLineSeparatorsAndIndentBefore(node: Node, parent: Node): boolean {
const leadingNewlines = preserveSourceNewlines && getLeadingLineTerminatorCount(parent, [node], ListFormat.None);
if (leadingNewlines) {
- writeLinesAndIndent(leadingNewlines, /*writeLinesIfNotIndenting*/ false);
+ writeLinesAndIndent(leadingNewlines, /*writeSpaceIfNotIndenting*/ false);
}
return !!leadingNewlines;
}
diff --git a/src/compiler/factory/emitHelpers.ts b/src/compiler/factory/emitHelpers.ts
index 85b00a5ef3e..85a12581965 100644
--- a/src/compiler/factory/emitHelpers.ts
+++ b/src/compiler/factory/emitHelpers.ts
@@ -34,6 +34,7 @@ namespace ts {
// Class Fields Helpers
createClassPrivateFieldGetHelper(receiver: Expression, state: Identifier, kind: PrivateIdentifierKind, f: Identifier | undefined): Expression;
createClassPrivateFieldSetHelper(receiver: Expression, state: Identifier, value: Expression, kind: PrivateIdentifierKind, f: Identifier | undefined): Expression;
+ createClassPrivateFieldInHelper(state: Identifier, receiver: Expression): Expression;
}
export function createEmitHelperFactory(context: TransformationContext): EmitHelperFactory {
@@ -75,6 +76,7 @@ namespace ts {
// Class Fields Helpers
createClassPrivateFieldGetHelper,
createClassPrivateFieldSetHelper,
+ createClassPrivateFieldInHelper
};
/**
@@ -136,7 +138,7 @@ namespace ts {
// ES2018 Helpers
function createAssignHelper(attributesSegments: Expression[]) {
- if (context.getCompilerOptions().target! >= ScriptTarget.ES2015) {
+ if (getEmitScriptTarget(context.getCompilerOptions()) >= ScriptTarget.ES2015) {
return factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier("Object"), "assign"),
/*typeArguments*/ undefined,
attributesSegments);
@@ -395,6 +397,10 @@ namespace ts {
return factory.createCallExpression(getUnscopedHelperName("__classPrivateFieldSet"), /*typeArguments*/ undefined, args);
}
+ function createClassPrivateFieldInHelper(state: Identifier, receiver: Expression) {
+ context.requestEmitHelper(classPrivateFieldInHelper);
+ return factory.createCallExpression(getUnscopedHelperName("__classPrivateFieldIn"), /* typeArguments*/ undefined, [state, receiver]);
+ }
}
/* @internal */
@@ -777,7 +783,11 @@ namespace ts {
text: `
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+ desc = { enumerable: true, get: function() { return m[k]; } };
+ }
+ Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
@@ -961,6 +971,29 @@ namespace ts {
};`
};
+ /**
+ * Parameters:
+ * @param state — One of the following:
+ * - A WeakMap when the member is a private instance field.
+ * - A WeakSet when the member is a private instance method or accessor.
+ * - A function value that should be the undecorated class constructor when the member is a private static field, method, or accessor.
+ * @param receiver — The object being checked if it has the private member.
+ *
+ * Usage:
+ * This helper is used to transform `#field in expression` to
+ * `__classPrivateFieldIn(, expression)`
+ */
+ export const classPrivateFieldInHelper: UnscopedEmitHelper = {
+ name: "typescript:classPrivateFieldIn",
+ importName: "__classPrivateFieldIn",
+ scoped: false,
+ text: `
+ var __classPrivateFieldIn = (this && this.__classPrivateFieldIn) || function(state, receiver) {
+ if (receiver === null || (typeof receiver !== "object" && typeof receiver !== "function")) throw new TypeError("Cannot use 'in' operator on non-object");
+ return typeof state === "function" ? receiver === state : state.has(receiver);
+ };`
+ };
+
let allUnscopedEmitHelpers: ReadonlyESMap | undefined;
export function getAllUnscopedEmitHelpers() {
@@ -986,6 +1019,7 @@ namespace ts {
exportStarHelper,
classPrivateFieldGetHelper,
classPrivateFieldSetHelper,
+ classPrivateFieldInHelper,
createBindingHelper,
setModuleDefaultHelper
], helper => helper.name));
diff --git a/src/compiler/factory/emitNode.ts b/src/compiler/factory/emitNode.ts
index 8f032fd8b76..6c207994331 100644
--- a/src/compiler/factory/emitNode.ts
+++ b/src/compiler/factory/emitNode.ts
@@ -127,7 +127,7 @@ namespace ts {
/**
* Gets a custom text range to use when emitting comments.
*/
- export function getCommentRange(node: Node) {
+ export function getCommentRange(node: Node): TextRange {
return node.emitNode?.commentRange ?? node;
}
@@ -256,6 +256,24 @@ namespace ts {
}
}
+ /**
+ * Gets the SnippetElement of a node.
+ */
+ /* @internal */
+ export function getSnippetElement(node: Node): SnippetElement | undefined {
+ return node.emitNode?.snippetElement;
+ }
+
+ /**
+ * Sets the SnippetElement of a node.
+ */
+ /* @internal */
+ export function setSnippetElement(node: T, snippet: SnippetElement): T {
+ const emitNode = getOrCreateEmitNode(node);
+ emitNode.snippetElement = snippet;
+ return node;
+ }
+
/* @internal */
export function ignoreSourceNewlines(node: T): T {
getOrCreateEmitNode(node).flags |= EmitFlags.IgnoreSourceNewlines;
diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts
index c697a04860b..cbddf4fb324 100644
--- a/src/compiler/factory/nodeFactory.ts
+++ b/src/compiler/factory/nodeFactory.ts
@@ -289,6 +289,10 @@ namespace ts {
updateImportDeclaration,
createImportClause,
updateImportClause,
+ createAssertClause,
+ updateAssertClause,
+ createAssertEntry,
+ updateAssertEntry,
createNamespaceImport,
updateNamespaceImport,
createNamespaceExport,
@@ -1071,7 +1075,7 @@ namespace ts {
if (flags & ModifierFlags.Override) result.push(createModifier(SyntaxKind.OverrideKeyword));
if (flags & ModifierFlags.Readonly) result.push(createModifier(SyntaxKind.ReadonlyKeyword));
if (flags & ModifierFlags.Async) result.push(createModifier(SyntaxKind.AsyncKeyword));
- return result;
+ return result.length ? result : undefined;
}
//
@@ -1835,17 +1839,19 @@ namespace ts {
}
// @api
- function createTypeQueryNode(exprName: EntityName) {
+ function createTypeQueryNode(exprName: EntityName, typeArguments?: readonly TypeNode[]) {
const node = createBaseNode(SyntaxKind.TypeQuery);
node.exprName = exprName;
+ node.typeArguments = typeArguments && parenthesizerRules().parenthesizeTypeArguments(typeArguments);
node.transformFlags = TransformFlags.ContainsTypeScript;
return node;
}
// @api
- function updateTypeQueryNode(node: TypeQueryNode, exprName: EntityName) {
+ function updateTypeQueryNode(node: TypeQueryNode, exprName: EntityName, typeArguments?: readonly TypeNode[]) {
return node.exprName !== exprName
- ? update(createTypeQueryNode(exprName), node)
+ || node.typeArguments !== typeArguments
+ ? update(createTypeQueryNode(exprName, typeArguments), node)
: node;
}
@@ -2108,25 +2114,27 @@ namespace ts {
}
// @api
- function createMappedTypeNode(readonlyToken: ReadonlyKeyword | PlusToken | MinusToken | undefined, typeParameter: TypeParameterDeclaration, nameType: TypeNode | undefined, questionToken: QuestionToken | PlusToken | MinusToken | undefined, type: TypeNode | undefined): MappedTypeNode {
+ function createMappedTypeNode(readonlyToken: ReadonlyKeyword | PlusToken | MinusToken | undefined, typeParameter: TypeParameterDeclaration, nameType: TypeNode | undefined, questionToken: QuestionToken | PlusToken | MinusToken | undefined, type: TypeNode | undefined, members: readonly TypeElement[] | undefined): MappedTypeNode {
const node = createBaseNode(SyntaxKind.MappedType);
node.readonlyToken = readonlyToken;
node.typeParameter = typeParameter;
node.nameType = nameType;
node.questionToken = questionToken;
node.type = type;
+ node.members = members && createNodeArray(members);
node.transformFlags = TransformFlags.ContainsTypeScript;
return node;
}
// @api
- function updateMappedTypeNode(node: MappedTypeNode, readonlyToken: ReadonlyKeyword | PlusToken | MinusToken | undefined, typeParameter: TypeParameterDeclaration, nameType: TypeNode | undefined, questionToken: QuestionToken | PlusToken | MinusToken | undefined, type: TypeNode | undefined): MappedTypeNode {
+ function updateMappedTypeNode(node: MappedTypeNode, readonlyToken: ReadonlyKeyword | PlusToken | MinusToken | undefined, typeParameter: TypeParameterDeclaration, nameType: TypeNode | undefined, questionToken: QuestionToken | PlusToken | MinusToken | undefined, type: TypeNode | undefined, members: readonly TypeElement[] | undefined): MappedTypeNode {
return node.readonlyToken !== readonlyToken
|| node.typeParameter !== typeParameter
|| node.nameType !== nameType
|| node.questionToken !== questionToken
|| node.type !== type
- ? update(createMappedTypeNode(readonlyToken, typeParameter, nameType, questionToken, type), node)
+ || node.members !== members
+ ? update(createMappedTypeNode(readonlyToken, typeParameter, nameType, questionToken, type, members), node)
: node;
}
@@ -3939,7 +3947,8 @@ namespace ts {
decorators: readonly Decorator[] | undefined,
modifiers: readonly Modifier[] | undefined,
importClause: ImportClause | undefined,
- moduleSpecifier: Expression
+ moduleSpecifier: Expression,
+ assertClause: AssertClause | undefined
): ImportDeclaration {
const node = createBaseDeclaration(
SyntaxKind.ImportDeclaration,
@@ -3948,6 +3957,7 @@ namespace ts {
);
node.importClause = importClause;
node.moduleSpecifier = moduleSpecifier;
+ node.assertClause = assertClause;
node.transformFlags |=
propagateChildFlags(node.importClause) |
propagateChildFlags(node.moduleSpecifier);
@@ -3961,13 +3971,15 @@ namespace ts {
decorators: readonly Decorator[] | undefined,
modifiers: readonly Modifier[] | undefined,
importClause: ImportClause | undefined,
- moduleSpecifier: Expression
+ moduleSpecifier: Expression,
+ assertClause: AssertClause | undefined
) {
return node.decorators !== decorators
|| node.modifiers !== modifiers
|| node.importClause !== importClause
|| node.moduleSpecifier !== moduleSpecifier
- ? update(createImportDeclaration(decorators, modifiers, importClause, moduleSpecifier), node)
+ || node.assertClause !== assertClause
+ ? update(createImportDeclaration(decorators, modifiers, importClause, moduleSpecifier, assertClause), node)
: node;
}
@@ -3996,6 +4008,40 @@ namespace ts {
: node;
}
+ // @api
+ function createAssertClause(elements: readonly AssertEntry[], multiLine?: boolean): AssertClause {
+ const node = createBaseNode(SyntaxKind.AssertClause);
+ node.elements = createNodeArray(elements);
+ node.multiLine = multiLine;
+ node.transformFlags |= TransformFlags.ContainsESNext;
+ return node;
+ }
+
+ // @api
+ function updateAssertClause(node: AssertClause, elements: readonly AssertEntry[], multiLine?: boolean): AssertClause {
+ return node.elements !== elements
+ || node.multiLine !== multiLine
+ ? update(createAssertClause(elements, multiLine), node)
+ : node;
+ }
+
+ // @api
+ function createAssertEntry(name: AssertionKey, value: Expression): AssertEntry {
+ const node = createBaseNode(SyntaxKind.AssertEntry);
+ node.name = name;
+ node.value = value;
+ node.transformFlags |= TransformFlags.ContainsESNext;
+ return node;
+ }
+
+ // @api
+ function updateAssertEntry(node: AssertEntry, name: AssertionKey, value: Expression): AssertEntry {
+ return node.name !== name
+ || node.value !== value
+ ? update(createAssertEntry(name, value), node)
+ : node;
+ }
+
// @api
function createNamespaceImport(name: Identifier): NamespaceImport {
const node = createBaseNode(SyntaxKind.NamespaceImport);
@@ -4047,8 +4093,9 @@ namespace ts {
}
// @api
- function createImportSpecifier(propertyName: Identifier | undefined, name: Identifier) {
+ function createImportSpecifier(isTypeOnly: boolean, propertyName: Identifier | undefined, name: Identifier) {
const node = createBaseNode(SyntaxKind.ImportSpecifier);
+ node.isTypeOnly = isTypeOnly;
node.propertyName = propertyName;
node.name = name;
node.transformFlags |=
@@ -4059,10 +4106,11 @@ namespace ts {
}
// @api
- function updateImportSpecifier(node: ImportSpecifier, propertyName: Identifier | undefined, name: Identifier) {
- return node.propertyName !== propertyName
+ function updateImportSpecifier(node: ImportSpecifier, isTypeOnly: boolean, propertyName: Identifier | undefined, name: Identifier) {
+ return node.isTypeOnly !== isTypeOnly
+ || node.propertyName !== propertyName
|| node.name !== name
- ? update(createImportSpecifier(propertyName, name), node)
+ ? update(createImportSpecifier(isTypeOnly, propertyName, name), node)
: node;
}
@@ -4107,7 +4155,8 @@ namespace ts {
modifiers: readonly Modifier[] | undefined,
isTypeOnly: boolean,
exportClause: NamedExportBindings | undefined,
- moduleSpecifier?: Expression
+ moduleSpecifier?: Expression,
+ assertClause?: AssertClause
) {
const node = createBaseDeclaration(
SyntaxKind.ExportDeclaration,
@@ -4117,6 +4166,7 @@ namespace ts {
node.isTypeOnly = isTypeOnly;
node.exportClause = exportClause;
node.moduleSpecifier = moduleSpecifier;
+ node.assertClause = assertClause;
node.transformFlags |=
propagateChildFlags(node.exportClause) |
propagateChildFlags(node.moduleSpecifier);
@@ -4131,14 +4181,16 @@ namespace ts {
modifiers: readonly Modifier[] | undefined,
isTypeOnly: boolean,
exportClause: NamedExportBindings | undefined,
- moduleSpecifier: Expression | undefined
+ moduleSpecifier: Expression | undefined,
+ assertClause: AssertClause | undefined
) {
return node.decorators !== decorators
|| node.modifiers !== modifiers
|| node.isTypeOnly !== isTypeOnly
|| node.exportClause !== exportClause
|| node.moduleSpecifier !== moduleSpecifier
- ? update(createExportDeclaration(decorators, modifiers, isTypeOnly, exportClause, moduleSpecifier), node)
+ || node.assertClause !== assertClause
+ ? update(createExportDeclaration(decorators, modifiers, isTypeOnly, exportClause, moduleSpecifier, assertClause), node)
: node;
}
@@ -4159,8 +4211,9 @@ namespace ts {
}
// @api
- function createExportSpecifier(propertyName: string | Identifier | undefined, name: string | Identifier) {
+ function createExportSpecifier(isTypeOnly: boolean, propertyName: string | Identifier | undefined, name: string | Identifier) {
const node = createBaseNode(SyntaxKind.ExportSpecifier);
+ node.isTypeOnly = isTypeOnly;
node.propertyName = asName(propertyName);
node.name = asName(name);
node.transformFlags |=
@@ -4171,10 +4224,11 @@ namespace ts {
}
// @api
- function updateExportSpecifier(node: ExportSpecifier, propertyName: Identifier | undefined, name: Identifier) {
- return node.propertyName !== propertyName
+ function updateExportSpecifier(node: ExportSpecifier, isTypeOnly: boolean, propertyName: Identifier | undefined, name: Identifier) {
+ return node.isTypeOnly !== isTypeOnly
+ || node.propertyName !== propertyName
|| node.name !== name
- ? update(createExportSpecifier(propertyName, name), node)
+ ? update(createExportSpecifier(isTypeOnly, propertyName, name), node)
: node;
}
@@ -5129,6 +5183,7 @@ namespace ts {
node.transformFlags =
propagateChildrenFlags(node.statements) |
propagateChildFlags(node.endOfFileToken);
+ node.impliedNodeFormat = source.impliedNodeFormat;
return node;
}
@@ -5441,7 +5496,7 @@ namespace ts {
/*modifiers*/ undefined,
/*isTypeOnly*/ false,
createNamedExports([
- createExportSpecifier(/*propertyName*/ undefined, exportName)
+ createExportSpecifier(/*isTypeOnly*/ false, /*propertyName*/ undefined, exportName)
])
);
}
@@ -5832,7 +5887,7 @@ namespace ts {
* @param visitor Optional callback used to visit any custom prologue directives.
*/
function copyPrologue(source: readonly Statement[], target: Push, ensureUseStrict?: boolean, visitor?: (node: Node) => VisitResult): number {
- const offset = copyStandardPrologue(source, target, ensureUseStrict);
+ const offset = copyStandardPrologue(source, target, 0, ensureUseStrict);
return copyCustomPrologue(source, target, offset, visitor);
}
@@ -5848,12 +5903,13 @@ namespace ts {
* Copies only the standard (string-expression) prologue-directives into the target statement-array.
* @param source origin statements array
* @param target result statements array
+ * @param statementOffset The offset at which to begin the copy.
* @param ensureUseStrict boolean determining whether the function need to add prologue-directives
+ * @returns Count of how many directive statements were copied.
*/
- function copyStandardPrologue(source: readonly Statement[], target: Push, ensureUseStrict?: boolean): number {
+ function copyStandardPrologue(source: readonly Statement[], target: Push, statementOffset = 0, ensureUseStrict?: boolean): number {
Debug.assert(target.length === 0, "Prologue directives should be at the first statement in the target statements array");
let foundUseStrict = false;
- let statementOffset = 0;
const numStatements = source.length;
while (statementOffset < numStatements) {
const statement = source[statementOffset];
@@ -6027,32 +6083,36 @@ namespace ts {
function updateModifiers(node: T, modifiers: readonly Modifier[] | ModifierFlags): T;
function updateModifiers(node: HasModifiers, modifiers: readonly Modifier[] | ModifierFlags) {
+ let modifierArray;
if (typeof modifiers === "number") {
- modifiers = createModifiersFromModifierFlags(modifiers);
+ modifierArray = createModifiersFromModifierFlags(modifiers);
}
- return isParameter(node) ? updateParameterDeclaration(node, node.decorators, modifiers, node.dotDotDotToken, node.name, node.questionToken, node.type, node.initializer) :
- isPropertySignature(node) ? updatePropertySignature(node, modifiers, node.name, node.questionToken, node.type) :
- isPropertyDeclaration(node) ? updatePropertyDeclaration(node, node.decorators, modifiers, node.name, node.questionToken ?? node.exclamationToken, node.type, node.initializer) :
- isMethodSignature(node) ? updateMethodSignature(node, modifiers, node.name, node.questionToken, node.typeParameters, node.parameters, node.type) :
- isMethodDeclaration(node) ? updateMethodDeclaration(node, node.decorators, modifiers, node.asteriskToken, node.name, node.questionToken, node.typeParameters, node.parameters, node.type, node.body) :
- isConstructorDeclaration(node) ? updateConstructorDeclaration(node, node.decorators, modifiers, node.parameters, node.body) :
- isGetAccessorDeclaration(node) ? updateGetAccessorDeclaration(node, node.decorators, modifiers, node.name, node.parameters, node.type, node.body) :
- isSetAccessorDeclaration(node) ? updateSetAccessorDeclaration(node, node.decorators, modifiers, node.name, node.parameters, node.body) :
- isIndexSignatureDeclaration(node) ? updateIndexSignature(node, node.decorators, modifiers, node.parameters, node.type) :
- isFunctionExpression(node) ? updateFunctionExpression(node, modifiers, node.asteriskToken, node.name, node.typeParameters, node.parameters, node.type, node.body) :
- isArrowFunction(node) ? updateArrowFunction(node, modifiers, node.typeParameters, node.parameters, node.type, node.equalsGreaterThanToken, node.body) :
- isClassExpression(node) ? updateClassExpression(node, node.decorators, modifiers, node.name, node.typeParameters, node.heritageClauses, node.members) :
- isVariableStatement(node) ? updateVariableStatement(node, modifiers, node.declarationList) :
- isFunctionDeclaration(node) ? updateFunctionDeclaration(node, node.decorators, modifiers, node.asteriskToken, node.name, node.typeParameters, node.parameters, node.type, node.body) :
- isClassDeclaration(node) ? updateClassDeclaration(node, node.decorators, modifiers, node.name, node.typeParameters, node.heritageClauses, node.members) :
- isInterfaceDeclaration(node) ? updateInterfaceDeclaration(node, node.decorators, modifiers, node.name, node.typeParameters, node.heritageClauses, node.members) :
- isTypeAliasDeclaration(node) ? updateTypeAliasDeclaration(node, node.decorators, modifiers, node.name, node.typeParameters, node.type) :
- isEnumDeclaration(node) ? updateEnumDeclaration(node, node.decorators, modifiers, node.name, node.members) :
- isModuleDeclaration(node) ? updateModuleDeclaration(node, node.decorators, modifiers, node.name, node.body) :
- isImportEqualsDeclaration(node) ? updateImportEqualsDeclaration(node, node.decorators, modifiers, node.isTypeOnly, node.name, node.moduleReference) :
- isImportDeclaration(node) ? updateImportDeclaration(node, node.decorators, modifiers, node.importClause, node.moduleSpecifier) :
- isExportAssignment(node) ? updateExportAssignment(node, node.decorators, modifiers, node.expression) :
- isExportDeclaration(node) ? updateExportDeclaration(node, node.decorators, modifiers, node.isTypeOnly, node.exportClause, node.moduleSpecifier) :
+ else {
+ modifierArray = modifiers;
+ }
+ return isParameter(node) ? updateParameterDeclaration(node, node.decorators, modifierArray, node.dotDotDotToken, node.name, node.questionToken, node.type, node.initializer) :
+ isPropertySignature(node) ? updatePropertySignature(node, modifierArray, node.name, node.questionToken, node.type) :
+ isPropertyDeclaration(node) ? updatePropertyDeclaration(node, node.decorators, modifierArray, node.name, node.questionToken ?? node.exclamationToken, node.type, node.initializer) :
+ isMethodSignature(node) ? updateMethodSignature(node, modifierArray, node.name, node.questionToken, node.typeParameters, node.parameters, node.type) :
+ isMethodDeclaration(node) ? updateMethodDeclaration(node, node.decorators, modifierArray, node.asteriskToken, node.name, node.questionToken, node.typeParameters, node.parameters, node.type, node.body) :
+ isConstructorDeclaration(node) ? updateConstructorDeclaration(node, node.decorators, modifierArray, node.parameters, node.body) :
+ isGetAccessorDeclaration(node) ? updateGetAccessorDeclaration(node, node.decorators, modifierArray, node.name, node.parameters, node.type, node.body) :
+ isSetAccessorDeclaration(node) ? updateSetAccessorDeclaration(node, node.decorators, modifierArray, node.name, node.parameters, node.body) :
+ isIndexSignatureDeclaration(node) ? updateIndexSignature(node, node.decorators, modifierArray, node.parameters, node.type) :
+ isFunctionExpression(node) ? updateFunctionExpression(node, modifierArray, node.asteriskToken, node.name, node.typeParameters, node.parameters, node.type, node.body) :
+ isArrowFunction(node) ? updateArrowFunction(node, modifierArray, node.typeParameters, node.parameters, node.type, node.equalsGreaterThanToken, node.body) :
+ isClassExpression(node) ? updateClassExpression(node, node.decorators, modifierArray, node.name, node.typeParameters, node.heritageClauses, node.members) :
+ isVariableStatement(node) ? updateVariableStatement(node, modifierArray, node.declarationList) :
+ isFunctionDeclaration(node) ? updateFunctionDeclaration(node, node.decorators, modifierArray, node.asteriskToken, node.name, node.typeParameters, node.parameters, node.type, node.body) :
+ isClassDeclaration(node) ? updateClassDeclaration(node, node.decorators, modifierArray, node.name, node.typeParameters, node.heritageClauses, node.members) :
+ isInterfaceDeclaration(node) ? updateInterfaceDeclaration(node, node.decorators, modifierArray, node.name, node.typeParameters, node.heritageClauses, node.members) :
+ isTypeAliasDeclaration(node) ? updateTypeAliasDeclaration(node, node.decorators, modifierArray, node.name, node.typeParameters, node.type) :
+ isEnumDeclaration(node) ? updateEnumDeclaration(node, node.decorators, modifierArray, node.name, node.members) :
+ isModuleDeclaration(node) ? updateModuleDeclaration(node, node.decorators, modifierArray, node.name, node.body) :
+ isImportEqualsDeclaration(node) ? updateImportEqualsDeclaration(node, node.decorators, modifierArray, node.isTypeOnly, node.name, node.moduleReference) :
+ isImportDeclaration(node) ? updateImportDeclaration(node, node.decorators, modifierArray, node.importClause, node.moduleSpecifier, node.assertClause) :
+ isExportAssignment(node) ? updateExportAssignment(node, node.decorators, modifierArray, node.expression) :
+ isExportDeclaration(node) ? updateExportDeclaration(node, node.decorators, modifierArray, node.isTypeOnly, node.exportClause, node.moduleSpecifier, node.assertClause) :
Debug.assertNever(node);
}
@@ -6334,7 +6394,7 @@ namespace ts {
sourceMapText = mapTextOrStripInternal as string;
}
const node = oldFileOfCurrentEmit ?
- parseOldFileOfCurrentEmit(Debug.assertDefined(bundleFileInfo)) :
+ parseOldFileOfCurrentEmit(Debug.checkDefined(bundleFileInfo)) :
parseUnparsedSourceFile(bundleFileInfo, stripInternal, length);
node.fileName = fileName;
node.sourceMapPath = sourceMapPath;
@@ -6356,7 +6416,7 @@ namespace ts {
let prologues: UnparsedPrologue[] | undefined;
let helpers: UnscopedEmitHelper[] | undefined;
let referencedFiles: FileReference[] | undefined;
- let typeReferenceDirectives: string[] | undefined;
+ let typeReferenceDirectives: FileReference[] | undefined;
let libReferenceDirectives: FileReference[] | undefined;
let prependChildren: UnparsedTextLike[] | undefined;
let texts: UnparsedSourceText[] | undefined;
@@ -6377,7 +6437,13 @@ namespace ts {
referencedFiles = append(referencedFiles, { pos: -1, end: -1, fileName: section.data });
break;
case BundleFileSectionKind.Type:
- typeReferenceDirectives = append(typeReferenceDirectives, section.data);
+ typeReferenceDirectives = append(typeReferenceDirectives, { pos: -1, end: -1, fileName: section.data });
+ break;
+ case BundleFileSectionKind.TypeResolutionModeImport:
+ typeReferenceDirectives = append(typeReferenceDirectives, { pos: -1, end: -1, fileName: section.data, resolutionMode: ModuleKind.ESNext });
+ break;
+ case BundleFileSectionKind.TypeResolutionModeRequire:
+ typeReferenceDirectives = append(typeReferenceDirectives, { pos: -1, end: -1, fileName: section.data, resolutionMode: ModuleKind.CommonJS });
break;
case BundleFileSectionKind.Lib:
libReferenceDirectives = append(libReferenceDirectives, { pos: -1, end: -1, fileName: section.data });
@@ -6438,6 +6504,8 @@ namespace ts {
case BundleFileSectionKind.NoDefaultLib:
case BundleFileSectionKind.Reference:
case BundleFileSectionKind.Type:
+ case BundleFileSectionKind.TypeResolutionModeImport:
+ case BundleFileSectionKind.TypeResolutionModeRequire:
case BundleFileSectionKind.Lib:
syntheticReferences = append(syntheticReferences, setTextRange(factory.createUnparsedSyntheticReference(section), section));
break;
@@ -6534,13 +6602,13 @@ namespace ts {
};
node.javascriptPath = declarationTextOrJavascriptPath;
node.javascriptMapPath = javascriptMapPath;
- node.declarationPath = Debug.assertDefined(javascriptMapTextOrDeclarationPath);
+ node.declarationPath = Debug.checkDefined(javascriptMapTextOrDeclarationPath);
node.declarationMapPath = declarationMapPath;
node.buildInfoPath = declarationMapTextOrBuildInfoPath;
Object.defineProperties(node, {
javascriptText: { get() { return definedTextGetter(declarationTextOrJavascriptPath); } },
javascriptMapText: { get() { return textGetter(javascriptMapPath); } }, // TODO:: if there is inline sourceMap in jsFile, use that
- declarationText: { get() { return definedTextGetter(Debug.assertDefined(javascriptMapTextOrDeclarationPath)); } },
+ declarationText: { get() { return definedTextGetter(Debug.checkDefined(javascriptMapTextOrDeclarationPath)); } },
declarationMapText: { get() { return textGetter(declarationMapPath); } }, // TODO:: if there is inline sourceMap in dtsFile, use that
buildInfo: { get() { return getAndCacheBuildInfo(() => textGetter(declarationMapTextOrBuildInfoPath)); } }
});
diff --git a/src/compiler/factory/nodeTests.ts b/src/compiler/factory/nodeTests.ts
index 7f5fda1994c..274ade1886d 100644
--- a/src/compiler/factory/nodeTests.ts
+++ b/src/compiler/factory/nodeTests.ts
@@ -597,6 +597,14 @@ namespace ts {
return node.kind === SyntaxKind.ImportClause;
}
+ export function isAssertClause(node: Node): node is AssertClause {
+ return node.kind === SyntaxKind.AssertClause;
+ }
+
+ export function isAssertEntry(node: Node): node is AssertEntry {
+ return node.kind === SyntaxKind.AssertEntry;
+ }
+
export function isNamespaceImport(node: Node): node is NamespaceImport {
return node.kind === SyntaxKind.NamespaceImport;
}
diff --git a/src/compiler/factory/parenthesizerRules.ts b/src/compiler/factory/parenthesizerRules.ts
index 782a0110aba..1e2cb936582 100644
--- a/src/compiler/factory/parenthesizerRules.ts
+++ b/src/compiler/factory/parenthesizerRules.ts
@@ -452,4 +452,4 @@ namespace ts {
parenthesizeConstituentTypesOfUnionOrIntersectionType: nodes => cast(nodes, isNodeArray),
parenthesizeTypeArguments: nodes => nodes && cast(nodes, isNodeArray),
};
-}
\ No newline at end of file
+}
diff --git a/src/compiler/factory/utilities.ts b/src/compiler/factory/utilities.ts
index fdd3200d572..7daa31c5227 100644
--- a/src/compiler/factory/utilities.ts
+++ b/src/compiler/factory/utilities.ts
@@ -481,7 +481,7 @@ namespace ts {
if (compilerOptions.importHelpers && isEffectiveExternalModule(sourceFile, compilerOptions)) {
let namedBindings: NamedImportBindings | undefined;
const moduleKind = getEmitModuleKind(compilerOptions);
- if (moduleKind >= ModuleKind.ES2015 && moduleKind <= ModuleKind.ESNext) {
+ if ((moduleKind >= ModuleKind.ES2015 && moduleKind <= ModuleKind.ESNext) || sourceFile.impliedNodeFormat === ModuleKind.ESNext) {
// use named imports
const helpers = getEmitHelpers(sourceFile);
if (helpers) {
@@ -500,8 +500,8 @@ namespace ts {
// NOTE: We don't need to care about global import collisions as this is a module.
namedBindings = nodeFactory.createNamedImports(
map(helperNames, name => isFileLevelUniqueName(sourceFile, name)
- ? nodeFactory.createImportSpecifier(/*propertyName*/ undefined, nodeFactory.createIdentifier(name))
- : nodeFactory.createImportSpecifier(nodeFactory.createIdentifier(name), helperFactory.getUnscopedHelperName(name))
+ ? nodeFactory.createImportSpecifier(/*isTypeOnly*/ false, /*propertyName*/ undefined, nodeFactory.createIdentifier(name))
+ : nodeFactory.createImportSpecifier(/*isTypeOnly*/ false, nodeFactory.createIdentifier(name), helperFactory.getUnscopedHelperName(name))
)
);
const parseNode = getOriginalNode(sourceFile, isSourceFile);
@@ -522,7 +522,8 @@ namespace ts {
/*decorators*/ undefined,
/*modifiers*/ undefined,
nodeFactory.createImportClause(/*isTypeOnly*/ false, /*name*/ undefined, namedBindings),
- nodeFactory.createStringLiteral(externalHelpersModuleNameText)
+ nodeFactory.createStringLiteral(externalHelpersModuleNameText),
+ /*assertClause*/ undefined
);
addEmitFlags(externalHelpersImportDeclaration, EmitFlags.NeverApplyImportHelper);
return externalHelpersImportDeclaration;
@@ -538,9 +539,9 @@ namespace ts {
}
const moduleKind = getEmitModuleKind(compilerOptions);
- let create = (hasExportStarsToExportValues || (compilerOptions.esModuleInterop && hasImportStarOrImportDefault))
+ let create = (hasExportStarsToExportValues || (getESModuleInterop(compilerOptions) && hasImportStarOrImportDefault))
&& moduleKind !== ModuleKind.System
- && moduleKind < ModuleKind.ES2015;
+ && (moduleKind < ModuleKind.ES2015 || node.impliedNodeFormat === ModuleKind.CommonJS);
if (!create) {
const helpers = getEmitHelpers(node);
if (helpers) {
diff --git a/src/compiler/moduleNameResolver.ts b/src/compiler/moduleNameResolver.ts
index b5f83ae0b71..c63cc791782 100644
--- a/src/compiler/moduleNameResolver.ts
+++ b/src/compiler/moduleNameResolver.ts
@@ -96,6 +96,7 @@ namespace ts {
};
}
+ /*@internal*/
interface ModuleResolutionState {
host: ModuleResolutionHost;
compilerOptions: CompilerOptions;
@@ -103,6 +104,8 @@ namespace ts {
failedLookupLocations: Push;
resultFromCache?: ResolvedModuleWithFailedLookupLocations;
packageJsonInfoCache: PackageJsonInfoCache | undefined;
+ features: NodeResolutionFeatures;
+ conditions: string[];
}
/** Just the fields that we use for module resolution. */
@@ -113,6 +116,10 @@ namespace ts {
typesVersions?: MapLike>;
main?: string;
tsconfig?: string;
+ type?: string;
+ imports?: object;
+ exports?: object;
+ name?: string;
}
interface PackageJson extends PackageJsonPathFields {
@@ -290,7 +297,7 @@ namespace ts {
* This is possible in case if resolution is performed for directives specified via 'types' parameter. In this case initial path for secondary lookups
* is assumed to be the same as root directory of the project.
*/
- export function resolveTypeReferenceDirective(typeReferenceDirectiveName: string, containingFile: string | undefined, options: CompilerOptions, host: ModuleResolutionHost, redirectedReference?: ResolvedProjectReference, cache?: TypeReferenceDirectiveResolutionCache): ResolvedTypeReferenceDirectiveWithFailedLookupLocations {
+ export function resolveTypeReferenceDirective(typeReferenceDirectiveName: string, containingFile: string | undefined, options: CompilerOptions, host: ModuleResolutionHost, redirectedReference?: ResolvedProjectReference, cache?: TypeReferenceDirectiveResolutionCache, resolutionMode?: SourceFile["impliedNodeFormat"]): ResolvedTypeReferenceDirectiveWithFailedLookupLocations {
const traceEnabled = isTraceEnabled(options, host);
if (redirectedReference) {
options = redirectedReference.commandLine.options;
@@ -298,7 +305,7 @@ namespace ts {
const containingDirectory = containingFile ? getDirectoryPath(containingFile) : undefined;
const perFolderCache = containingDirectory ? cache && cache.getOrCreateCacheForDirectory(containingDirectory, redirectedReference) : undefined;
- let result = perFolderCache && perFolderCache.get(typeReferenceDirectiveName);
+ let result = perFolderCache && perFolderCache.get(typeReferenceDirectiveName, /*mode*/ resolutionMode);
if (result) {
if (traceEnabled) {
trace(host, Diagnostics.Resolving_type_reference_directive_0_containing_file_1, typeReferenceDirectiveName, containingFile);
@@ -333,7 +340,19 @@ namespace ts {
}
const failedLookupLocations: string[] = [];
- const moduleResolutionState: ModuleResolutionState = { compilerOptions: options, host, traceEnabled, failedLookupLocations, packageJsonInfoCache: cache };
+ let features = getDefaultNodeResolutionFeatures(options);
+ // Unlike `import` statements, whose mode-calculating APIs are all guaranteed to return `undefined` if we're in an un-mode-ed module resolution
+ // setting, type references will return their target mode regardless of options because of how the parser works, so we guard against the mode being
+ // set in a non-modal module resolution setting here. Do note that our behavior is not particularly well defined when these mode-overriding imports
+ // are present in a non-modal project; while in theory we'd like to either ignore the mode or provide faithful modern resolution, depending on what we feel is best,
+ // in practice, not every cache has the options available to intelligently make the choice to ignore the mode request, and it's unclear how modern "faithful modern
+ // resolution" should be (`node12`? `nodenext`?). As such, witnessing a mode-overriding triple-slash reference in a non-modal module resolution
+ // context should _probably_ be an error - and that should likely be handled by the `Program` (which is what we do).
+ if (resolutionMode === ModuleKind.ESNext && (getEmitModuleResolutionKind(options) === ModuleResolutionKind.Node12 || getEmitModuleResolutionKind(options) === ModuleResolutionKind.NodeNext)) {
+ features |= NodeResolutionFeatures.EsmMode;
+ }
+ const conditions = features & NodeResolutionFeatures.Exports ? features & NodeResolutionFeatures.EsmMode ? ["node", "import", "types"] : ["node", "require", "types"] : [];
+ const moduleResolutionState: ModuleResolutionState = { compilerOptions: options, host, traceEnabled, failedLookupLocations, packageJsonInfoCache: cache, features, conditions };
let resolved = primaryLookup();
let primary = true;
if (!resolved) {
@@ -354,7 +373,7 @@ namespace ts {
};
}
result = { resolvedTypeReferenceDirective, failedLookupLocations };
- perFolderCache?.set(typeReferenceDirectiveName, result);
+ perFolderCache?.set(typeReferenceDirectiveName, /*mode*/ resolutionMode, result);
if (traceEnabled) traceResult(result);
return result;
@@ -409,7 +428,7 @@ namespace ts {
result = searchResult && searchResult.value;
}
else {
- const { path: candidate } = normalizePathAndParts(combinePaths(initialLocationForSecondaryLookup, typeReferenceDirectiveName));
+ const { path: candidate } = normalizePathForCJSResolution(initialLocationForSecondaryLookup, typeReferenceDirectiveName);
result = nodeLoadModuleByRelativeName(Extensions.DtsOnly, candidate, /*onlyRecordFailures*/ false, moduleResolutionState, /*considerPackageJson*/ true);
}
return resolvedTypeScriptOnly(result);
@@ -422,6 +441,42 @@ namespace ts {
}
}
+ function getDefaultNodeResolutionFeatures(options: CompilerOptions) {
+ return getEmitModuleResolutionKind(options) === ModuleResolutionKind.Node12 ? NodeResolutionFeatures.Node12Default :
+ getEmitModuleResolutionKind(options) === ModuleResolutionKind.NodeNext ? NodeResolutionFeatures.NodeNextDefault :
+ NodeResolutionFeatures.None;
+ }
+
+ /**
+ * @internal
+ * Does not try `@types/${packageName}` - use a second pass if needed.
+ */
+ export function resolvePackageNameToPackageJson(
+ packageName: string,
+ containingDirectory: string,
+ options: CompilerOptions,
+ host: ModuleResolutionHost,
+ cache: ModuleResolutionCache | undefined,
+ ): PackageJsonInfo | undefined {
+ const moduleResolutionState: ModuleResolutionState = {
+ compilerOptions: options,
+ host,
+ traceEnabled: isTraceEnabled(options, host),
+ failedLookupLocations: [],
+ packageJsonInfoCache: cache?.getPackageJsonInfoCache(),
+ conditions: emptyArray,
+ features: NodeResolutionFeatures.None,
+ };
+
+ return forEachAncestorDirectory(containingDirectory, ancestorDirectory => {
+ if (getBaseFileName(ancestorDirectory) !== "node_modules") {
+ const nodeModulesFolder = combinePaths(ancestorDirectory, "node_modules");
+ const candidate = combinePaths(nodeModulesFolder, packageName);
+ return getPackageJsonInfo(candidate, /*onlyRecordFailures*/ false, moduleResolutionState);
+ }
+ });
+ }
+
/**
* Given a set of options, returns the set of type directive names
* that should be included for this program automatically.
@@ -470,12 +525,21 @@ namespace ts {
export interface TypeReferenceDirectiveResolutionCache extends PerDirectoryResolutionCache, PackageJsonInfoCache {
}
+ export interface ModeAwareCache {
+ get(key: string, mode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined): T | undefined;
+ set(key: string, mode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined, value: T): this;
+ delete(key: string, mode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined): this;
+ has(key: string, mode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined): boolean;
+ forEach(cb: (elem: T, key: string, mode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined) => void): void;
+ size(): number;
+ }
+
/**
* Cached resolutions per containing directory.
* This assumes that any module id will have the same resolution for sibling files located in the same folder.
*/
export interface PerDirectoryResolutionCache {
- getOrCreateCacheForDirectory(directoryName: string, redirectedReference?: ResolvedProjectReference): Map;
+ getOrCreateCacheForDirectory(directoryName: string, redirectedReference?: ResolvedProjectReference): ModeAwareCache;
clear(): void;
/**
* Updates with the current compilerOptions the cache will operate with.
@@ -493,7 +557,7 @@ namespace ts {
* We support only non-relative module names because resolution of relative module names is usually more deterministic and thus less expensive.
*/
export interface NonRelativeModuleNameResolutionCache extends PackageJsonInfoCache {
- getOrCreateCacheForModuleName(nonRelativeModuleName: string, redirectedReference?: ResolvedProjectReference): PerModuleNameCache;
+ getOrCreateCacheForModuleName(nonRelativeModuleName: string, mode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined, redirectedReference?: ResolvedProjectReference): PerModuleNameCache;
}
export interface PackageJsonInfoCache {
@@ -593,7 +657,7 @@ namespace ts {
function updateRedirectsMap(
options: CompilerOptions,
- directoryToModuleNameMap: CacheWithRedirects>,
+ directoryToModuleNameMap: CacheWithRedirects>,
moduleNameToDirectoryMap?: CacheWithRedirects
) {
if (!options.configFile) return;
@@ -619,7 +683,7 @@ namespace ts {
moduleNameToDirectoryMap?.setOwnOptions(options);
}
- function createPerDirectoryResolutionCache(currentDirectory: string, getCanonicalFileName: GetCanonicalFileName, directoryToModuleNameMap: CacheWithRedirects>): PerDirectoryResolutionCache {
+ function createPerDirectoryResolutionCache(currentDirectory: string, getCanonicalFileName: GetCanonicalFileName, directoryToModuleNameMap: CacheWithRedirects>): PerDirectoryResolutionCache {
return {
getOrCreateCacheForDirectory,
clear,
@@ -636,10 +700,63 @@ namespace ts {
function getOrCreateCacheForDirectory(directoryName: string, redirectedReference?: ResolvedProjectReference) {
const path = toPath(directoryName, currentDirectory, getCanonicalFileName);
- return getOrCreateCache>(directoryToModuleNameMap, redirectedReference, path, () => new Map());
+ return getOrCreateCache>(directoryToModuleNameMap, redirectedReference, path, () => createModeAwareCache());
}
}
+ /* @internal */
+ export function createModeAwareCache(): ModeAwareCache {
+ const underlying = new Map();
+ const memoizedReverseKeys = new Map();
+
+ const cache: ModeAwareCache = {
+ get(specifier, mode) {
+ return underlying.get(getUnderlyingCacheKey(specifier, mode));
+ },
+ set(specifier, mode, value) {
+ underlying.set(getUnderlyingCacheKey(specifier, mode), value);
+ return cache;
+ },
+ delete(specifier, mode) {
+ underlying.delete(getUnderlyingCacheKey(specifier, mode));
+ return cache;
+ },
+ has(specifier, mode) {
+ return underlying.has(getUnderlyingCacheKey(specifier, mode));
+ },
+ forEach(cb) {
+ return underlying.forEach((elem, key) => {
+ const [specifier, mode] = memoizedReverseKeys.get(key)!;
+ return cb(elem, specifier, mode);
+ });
+ },
+ size() {
+ return underlying.size;
+ }
+ };
+ return cache;
+
+ function getUnderlyingCacheKey(specifier: string, mode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined) {
+ const result = mode === undefined ? specifier : `${mode}|${specifier}`;
+ memoizedReverseKeys.set(result, [specifier, mode]);
+ return result;
+ }
+ }
+
+ /* @internal */
+ export function zipToModeAwareCache(file: SourceFile, keys: readonly string[] | readonly FileReference[], values: readonly V[]): ModeAwareCache {
+ Debug.assert(keys.length === values.length);
+ const map = createModeAwareCache();
+ for (let i = 0; i < keys.length; ++i) {
+ const entry = keys[i];
+ // We lower-case all type references because npm automatically lowercases all packages. See GH#9824.
+ const name = !isString(entry) ? entry.fileName.toLowerCase() : entry;
+ const mode = !isString(entry) ? entry.resolutionMode || file.impliedNodeFormat : getModeForResolutionAtIndex(file, i);
+ map.set(name, mode, values[i]);
+ }
+ return map;
+ }
+
export function createModuleResolutionCache(
currentDirectory: string,
getCanonicalFileName: (s: string) => string,
@@ -650,14 +767,14 @@ namespace ts {
currentDirectory: string,
getCanonicalFileName: GetCanonicalFileName,
options: undefined,
- directoryToModuleNameMap: CacheWithRedirects>,
+ directoryToModuleNameMap: CacheWithRedirects>,
moduleNameToDirectoryMap: CacheWithRedirects,
): ModuleResolutionCache;
export function createModuleResolutionCache(
currentDirectory: string,
getCanonicalFileName: GetCanonicalFileName,
options?: CompilerOptions,
- directoryToModuleNameMap?: CacheWithRedirects>,
+ directoryToModuleNameMap?: CacheWithRedirects>,
moduleNameToDirectoryMap?: CacheWithRedirects,
): ModuleResolutionCache {
const preDirectoryResolutionCache = createPerDirectoryResolutionCache(currentDirectory, getCanonicalFileName, directoryToModuleNameMap ||= createCacheWithRedirects(options));
@@ -683,9 +800,9 @@ namespace ts {
updateRedirectsMap(options, directoryToModuleNameMap!, moduleNameToDirectoryMap);
}
- function getOrCreateCacheForModuleName(nonRelativeModuleName: string, redirectedReference?: ResolvedProjectReference): PerModuleNameCache {
+ function getOrCreateCacheForModuleName(nonRelativeModuleName: string, mode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined, redirectedReference?: ResolvedProjectReference): PerModuleNameCache {
Debug.assert(!isExternalModuleNameRelative(nonRelativeModuleName));
- return getOrCreateCache(moduleNameToDirectoryMap!, redirectedReference, nonRelativeModuleName, createPerModuleNameCache);
+ return getOrCreateCache(moduleNameToDirectoryMap!, redirectedReference, mode === undefined ? nonRelativeModuleName : `${mode}|${nonRelativeModuleName}`, createPerModuleNameCache);
}
function createPerModuleNameCache(): PerModuleNameCache {
@@ -773,14 +890,14 @@ namespace ts {
getCanonicalFileName: GetCanonicalFileName,
options: undefined,
packageJsonInfoCache: PackageJsonInfoCache | undefined,
- directoryToModuleNameMap: CacheWithRedirects>,
+ directoryToModuleNameMap: CacheWithRedirects>,
): TypeReferenceDirectiveResolutionCache;
export function createTypeReferenceDirectiveResolutionCache(
currentDirectory: string,
getCanonicalFileName: GetCanonicalFileName,
options?: CompilerOptions,
packageJsonInfoCache?: PackageJsonInfoCache | undefined,
- directoryToModuleNameMap?: CacheWithRedirects>,
+ directoryToModuleNameMap?: CacheWithRedirects>,
): TypeReferenceDirectiveResolutionCache {
const preDirectoryResolutionCache = createPerDirectoryResolutionCache(currentDirectory, getCanonicalFileName, directoryToModuleNameMap ||= createCacheWithRedirects(options));
packageJsonInfoCache ||= createPackageJsonInfoCache(currentDirectory, getCanonicalFileName);
@@ -797,13 +914,14 @@ namespace ts {
}
}
- export function resolveModuleNameFromCache(moduleName: string, containingFile: string, cache: ModuleResolutionCache): ResolvedModuleWithFailedLookupLocations | undefined {
+ export function resolveModuleNameFromCache(moduleName: string, containingFile: string, cache: ModuleResolutionCache, mode?: ModuleKind.CommonJS | ModuleKind.ESNext): ResolvedModuleWithFailedLookupLocations | undefined {
const containingDirectory = getDirectoryPath(containingFile);
const perFolderCache = cache && cache.getOrCreateCacheForDirectory(containingDirectory);
- return perFolderCache && perFolderCache.get(moduleName);
+ if (!perFolderCache) return undefined;
+ return perFolderCache.get(moduleName, mode);
}
- export function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache, redirectedReference?: ResolvedProjectReference): ResolvedModuleWithFailedLookupLocations {
+ export function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache, redirectedReference?: ResolvedProjectReference, resolutionMode?: ModuleKind.CommonJS | ModuleKind.ESNext): ResolvedModuleWithFailedLookupLocations {
const traceEnabled = isTraceEnabled(compilerOptions, host);
if (redirectedReference) {
compilerOptions = redirectedReference.commandLine.options;
@@ -816,7 +934,7 @@ namespace ts {
}
const containingDirectory = getDirectoryPath(containingFile);
const perFolderCache = cache && cache.getOrCreateCacheForDirectory(containingDirectory, redirectedReference);
- let result = perFolderCache && perFolderCache.get(moduleName);
+ let result = perFolderCache && perFolderCache.get(moduleName, resolutionMode);
if (result) {
if (traceEnabled) {
@@ -826,7 +944,20 @@ namespace ts {
else {
let moduleResolution = compilerOptions.moduleResolution;
if (moduleResolution === undefined) {
- moduleResolution = getEmitModuleKind(compilerOptions) === ModuleKind.CommonJS ? ModuleResolutionKind.NodeJs : ModuleResolutionKind.Classic;
+ switch (getEmitModuleKind(compilerOptions)) {
+ case ModuleKind.CommonJS:
+ moduleResolution = ModuleResolutionKind.NodeJs;
+ break;
+ case ModuleKind.Node12:
+ moduleResolution = ModuleResolutionKind.Node12;
+ break;
+ case ModuleKind.NodeNext:
+ moduleResolution = ModuleResolutionKind.NodeNext;
+ break;
+ default:
+ moduleResolution = ModuleResolutionKind.Classic;
+ break;
+ }
if (traceEnabled) {
trace(host, Diagnostics.Module_resolution_kind_is_not_specified_using_0, ModuleResolutionKind[moduleResolution]);
}
@@ -839,6 +970,12 @@ namespace ts {
perfLogger.logStartResolveModule(moduleName /* , containingFile, ModuleResolutionKind[moduleResolution]*/);
switch (moduleResolution) {
+ case ModuleResolutionKind.Node12:
+ result = node12ModuleNameResolver(moduleName, containingFile, compilerOptions, host, cache, redirectedReference, resolutionMode);
+ break;
+ case ModuleResolutionKind.NodeNext:
+ result = nodeNextModuleNameResolver(moduleName, containingFile, compilerOptions, host, cache, redirectedReference, resolutionMode);
+ break;
case ModuleResolutionKind.NodeJs:
result = nodeModuleNameResolver(moduleName, containingFile, compilerOptions, host, cache, redirectedReference);
break;
@@ -852,10 +989,10 @@ namespace ts {
perfLogger.logStopResolveModule((result && result.resolvedModule) ? "" + result.resolvedModule.resolvedFileName : "null");
if (perFolderCache) {
- perFolderCache.set(moduleName, result);
+ perFolderCache.set(moduleName, resolutionMode, result);
if (!isExternalModuleNameRelative(moduleName)) {
// put result in per-module name cache
- cache!.getOrCreateCacheForModuleName(moduleName, redirectedReference).set(containingDirectory, result);
+ cache.getOrCreateCacheForModuleName(moduleName, resolutionMode, redirectedReference).set(containingDirectory, result);
}
}
}
@@ -1083,8 +1220,62 @@ namespace ts {
}
/* @internal */
- export function tryResolveJSModule(moduleName: string, initialDir: string, host: ModuleResolutionHost) {
- return tryResolveJSModuleWorker(moduleName, initialDir, host).resolvedModule;
+ enum NodeResolutionFeatures {
+ None = 0,
+ // resolving `#local` names in your own package.json
+ Imports = 1 << 1,
+ // resolving `your-own-name` from your own package.json
+ SelfName = 1 << 2,
+ // respecting the `.exports` member of packages' package.json files and its (conditional) mappings of export names
+ Exports = 1 << 3,
+ // allowing `*` in the LHS of an export to be followed by more content, eg `"./whatever/*.js"`
+ // not currently backported to node 12 - https://github.com/nodejs/Release/issues/690
+ ExportsPatternTrailers = 1 << 4,
+ AllFeatures = Imports | SelfName | Exports | ExportsPatternTrailers,
+
+ Node12Default = Imports | SelfName | Exports,
+
+ NodeNextDefault = AllFeatures,
+
+ EsmMode = 1 << 5,
+ }
+
+ function node12ModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions,
+ host: ModuleResolutionHost, cache?: ModuleResolutionCache, redirectedReference?: ResolvedProjectReference,
+ resolutionMode?: ModuleKind.CommonJS | ModuleKind.ESNext): ResolvedModuleWithFailedLookupLocations {
+ return nodeNextModuleNameResolverWorker(
+ NodeResolutionFeatures.Node12Default,
+ moduleName,
+ containingFile,
+ compilerOptions,
+ host,
+ cache,
+ redirectedReference,
+ resolutionMode
+ );
+ }
+
+ function nodeNextModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions,
+ host: ModuleResolutionHost, cache?: ModuleResolutionCache, redirectedReference?: ResolvedProjectReference,
+ resolutionMode?: ModuleKind.CommonJS | ModuleKind.ESNext): ResolvedModuleWithFailedLookupLocations {
+ return nodeNextModuleNameResolverWorker(
+ NodeResolutionFeatures.NodeNextDefault,
+ moduleName,
+ containingFile,
+ compilerOptions,
+ host,
+ cache,
+ redirectedReference,
+ resolutionMode
+ );
+ }
+
+ function nodeNextModuleNameResolverWorker(features: NodeResolutionFeatures, moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache, redirectedReference?: ResolvedProjectReference, resolutionMode?: ModuleKind.CommonJS | ModuleKind.ESNext): ResolvedModuleWithFailedLookupLocations {
+ const containingDirectory = getDirectoryPath(containingFile);
+
+ // es module file or cjs-like input file, use a variant of the legacy cjs resolver that supports the selected modern features
+ const esmMode = resolutionMode === ModuleKind.ESNext ? NodeResolutionFeatures.EsmMode : 0;
+ return nodeModuleNameResolverWorker(features | esmMode, moduleName, containingDirectory, compilerOptions, host, cache, compilerOptions.resolveJsonModule ? tsPlusJsonExtensions : tsExtensions, redirectedReference);
}
const jsOnlyExtensions = [Extensions.JavaScript];
@@ -1092,20 +1283,30 @@ namespace ts {
const tsPlusJsonExtensions = [...tsExtensions, Extensions.Json];
const tsconfigExtensions = [Extensions.TSConfig];
function tryResolveJSModuleWorker(moduleName: string, initialDir: string, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
- return nodeModuleNameResolverWorker(moduleName, initialDir, { moduleResolution: ModuleResolutionKind.NodeJs, allowJs: true }, host, /*cache*/ undefined, jsOnlyExtensions, /*redirectedReferences*/ undefined);
+ return nodeModuleNameResolverWorker(NodeResolutionFeatures.None, moduleName, initialDir, { moduleResolution: ModuleResolutionKind.NodeJs, allowJs: true }, host, /*cache*/ undefined, jsOnlyExtensions, /*redirectedReferences*/ undefined);
}
export function nodeModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache, redirectedReference?: ResolvedProjectReference): ResolvedModuleWithFailedLookupLocations;
/* @internal */ export function nodeModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache, redirectedReference?: ResolvedProjectReference, lookupConfig?: boolean): ResolvedModuleWithFailedLookupLocations; // eslint-disable-line @typescript-eslint/unified-signatures
export function nodeModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: ModuleResolutionCache, redirectedReference?: ResolvedProjectReference, lookupConfig?: boolean): ResolvedModuleWithFailedLookupLocations {
- return nodeModuleNameResolverWorker(moduleName, getDirectoryPath(containingFile), compilerOptions, host, cache, lookupConfig ? tsconfigExtensions : (compilerOptions.resolveJsonModule ? tsPlusJsonExtensions : tsExtensions), redirectedReference);
+ return nodeModuleNameResolverWorker(NodeResolutionFeatures.None, moduleName, getDirectoryPath(containingFile), compilerOptions, host, cache, lookupConfig ? tsconfigExtensions : (compilerOptions.resolveJsonModule ? tsPlusJsonExtensions : tsExtensions), redirectedReference);
}
- function nodeModuleNameResolverWorker(moduleName: string, containingDirectory: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache: ModuleResolutionCache | undefined, extensions: Extensions[], redirectedReference: ResolvedProjectReference | undefined): ResolvedModuleWithFailedLookupLocations {
+ function nodeModuleNameResolverWorker(features: NodeResolutionFeatures, moduleName: string, containingDirectory: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache: ModuleResolutionCache | undefined, extensions: Extensions[], redirectedReference: ResolvedProjectReference | undefined): ResolvedModuleWithFailedLookupLocations {
const traceEnabled = isTraceEnabled(compilerOptions, host);
const failedLookupLocations: string[] = [];
- const state: ModuleResolutionState = { compilerOptions, host, traceEnabled, failedLookupLocations, packageJsonInfoCache: cache };
+ // conditions are only used by the node12/nodenext resolver - there's no priority order in the list,
+ //it's essentially a set (priority is determined by object insertion order in the object we look at).
+ const state: ModuleResolutionState = {
+ compilerOptions,
+ host,
+ traceEnabled,
+ failedLookupLocations,
+ packageJsonInfoCache: cache,
+ features,
+ conditions: features & NodeResolutionFeatures.EsmMode ? ["node", "import", "types"] : ["node", "require", "types"]
+ };
const result = forEach(extensions, ext => tryResolve(ext));
return createResolvedModuleWithFailedLookupLocations(result?.value?.resolved, result?.value?.isExternalLibraryImport, failedLookupLocations, state.resultFromCache);
@@ -1118,10 +1319,19 @@ namespace ts {
}
if (!isExternalModuleNameRelative(moduleName)) {
- if (traceEnabled) {
- trace(host, Diagnostics.Loading_module_0_from_node_modules_folder_target_file_type_1, moduleName, Extensions[extensions]);
+ let resolved: SearchResult | undefined;
+ if (features & NodeResolutionFeatures.Imports && startsWith(moduleName, "#")) {
+ resolved = loadModuleFromImports(extensions, moduleName, containingDirectory, state, cache, redirectedReference);
+ }
+ if (!resolved && features & NodeResolutionFeatures.SelfName) {
+ resolved = loadModuleFromSelfNameReference(extensions, moduleName, containingDirectory, state, cache, redirectedReference);
+ }
+ if (!resolved) {
+ if (traceEnabled) {
+ trace(host, Diagnostics.Loading_module_0_from_node_modules_folder_target_file_type_1, moduleName, Extensions[extensions]);
+ }
+ resolved = loadModuleFromNearestNodeModulesDirectory(extensions, moduleName, containingDirectory, state, cache, redirectedReference);
}
- const resolved = loadModuleFromNearestNodeModulesDirectory(extensions, moduleName, containingDirectory, state, cache, redirectedReference);
if (!resolved) return undefined;
let resolvedValue = resolved.value;
@@ -1134,12 +1344,26 @@ namespace ts {
return { value: resolvedValue && { resolved: resolvedValue, isExternalLibraryImport: true } };
}
else {
- const { path: candidate, parts } = normalizePathAndParts(combinePaths(containingDirectory, moduleName));
+ const { path: candidate, parts } = normalizePathForCJSResolution(containingDirectory, moduleName);
const resolved = nodeLoadModuleByRelativeName(extensions, candidate, /*onlyRecordFailures*/ false, state, /*considerPackageJson*/ true);
// Treat explicit "node_modules" import as an external library import.
return resolved && toSearchResult({ resolved, isExternalLibraryImport: contains(parts, "node_modules") });
}
}
+
+ }
+
+ // If you import from "." inside a containing directory "/foo", the result of `normalizePath`
+ // would be "/foo", but this loses the information that `foo` is a directory and we intended
+ // to look inside of it. The Node CommonJS resolution algorithm doesn't call this out
+ // (https://nodejs.org/api/modules.html#all-together), but it seems that module paths ending
+ // in `.` are actually normalized to `./` before proceeding with the resolution algorithm.
+ function normalizePathForCJSResolution(containingDirectory: string, moduleName: string) {
+ const combined = combinePaths(containingDirectory, moduleName);
+ const parts = getPathComponents(combined);
+ const lastPart = lastOrUndefined(parts);
+ const path = lastPart === "." || lastPart === ".." ? ensureTrailingDirectorySeparator(normalizePath(combined)) : normalizePath(combined);
+ return { path, parts };
}
function realPath(path: string, host: ModuleResolutionHost, traceEnabled: boolean): string {
@@ -1185,7 +1409,13 @@ namespace ts {
onlyRecordFailures = true;
}
}
- return loadNodeModuleFromDirectory(extensions, candidate, onlyRecordFailures, state, considerPackageJson);
+ // esm mode relative imports shouldn't do any directory lookups (either inside `package.json`
+ // files or implicit `index.js`es). This is a notable depature from cjs norms, where `./foo/pkg`
+ // could have been redirected by `./foo/pkg/package.json` to an arbitrary location!
+ if (!(state.features & NodeResolutionFeatures.EsmMode)) {
+ return loadNodeModuleFromDirectory(extensions, candidate, onlyRecordFailures, state, considerPackageJson);
+ }
+ return undefined;
}
/*@internal*/
@@ -1237,29 +1467,46 @@ namespace ts {
function loadModuleFromFile(extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState): PathAndExtension | undefined {
if (extensions === Extensions.Json || extensions === Extensions.TSConfig) {
const extensionLess = tryRemoveExtension(candidate, Extension.Json);
- return (extensionLess === undefined && extensions === Extensions.Json) ? undefined : tryAddingExtensions(extensionLess || candidate, extensions, onlyRecordFailures, state);
+ const extension = extensionLess ? candidate.substring(extensionLess.length) : "";
+ return (extensionLess === undefined && extensions === Extensions.Json) ? undefined : tryAddingExtensions(extensionLess || candidate, extensions, extension, onlyRecordFailures, state);
}
- // First, try adding an extension. An import of "foo" could be matched by a file "foo.ts", or "foo.js" by "foo.js.ts"
- const resolvedByAddingExtension = tryAddingExtensions(candidate, extensions, onlyRecordFailures, state);
- if (resolvedByAddingExtension) {
- return resolvedByAddingExtension;
+ // esm mode resolutions don't include automatic extension lookup (without additional flags, at least)
+ if (!(state.features & NodeResolutionFeatures.EsmMode)) {
+ // First, try adding an extension. An import of "foo" could be matched by a file "foo.ts", or "foo.js" by "foo.js.ts"
+ const resolvedByAddingExtension = tryAddingExtensions(candidate, extensions, "", onlyRecordFailures, state);
+ if (resolvedByAddingExtension) {
+ return resolvedByAddingExtension;
+ }
}
+ return loadModuleFromFileNoImplicitExtensions(extensions, candidate, onlyRecordFailures, state);
+ }
+
+ function loadModuleFromFileNoImplicitExtensions(extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState): PathAndExtension | undefined {
// If that didn't work, try stripping a ".js" or ".jsx" extension and replacing it with a TypeScript one;
// e.g. "./foo.js" can be matched by "./foo.ts" or "./foo.d.ts"
- if (hasJSFileExtension(candidate)) {
+ if (hasJSFileExtension(candidate) || (fileExtensionIs(candidate, Extension.Json) && state.compilerOptions.resolveJsonModule)) {
const extensionless = removeFileExtension(candidate);
+ const extension = candidate.substring(extensionless.length);
if (state.traceEnabled) {
- const extension = candidate.substring(extensionless.length);
trace(state.host, Diagnostics.File_name_0_has_a_1_extension_stripping_it, candidate, extension);
}
- return tryAddingExtensions(extensionless, extensions, onlyRecordFailures, state);
+ return tryAddingExtensions(extensionless, extensions, extension, onlyRecordFailures, state);
}
}
+ function loadJSOrExactTSFileName(extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState): PathAndExtension | undefined {
+ if ((extensions === Extensions.TypeScript || extensions === Extensions.DtsOnly) && fileExtensionIsOneOf(candidate, [Extension.Dts, Extension.Dcts, Extension.Dmts])) {
+ const result = tryFile(candidate, onlyRecordFailures, state);
+ return result !== undefined ? { path: candidate, ext: forEach([Extension.Dts, Extension.Dcts, Extension.Dmts], e => fileExtensionIs(candidate, e) ? e : undefined)! } : undefined;
+ }
+
+ return loadModuleFromFileNoImplicitExtensions(extensions, candidate, onlyRecordFailures, state);
+ }
+
/** Try to return an existing file that adds one of the `extensions` to `candidate`. */
- function tryAddingExtensions(candidate: string, extensions: Extensions, onlyRecordFailures: boolean, state: ModuleResolutionState): PathAndExtension | undefined {
+ function tryAddingExtensions(candidate: string, extensions: Extensions, originalExtension: string, onlyRecordFailures: boolean, state: ModuleResolutionState): PathAndExtension | undefined {
if (!onlyRecordFailures) {
// check if containing folder exists - if it doesn't then just record failures for all supported extensions without disk probing
const directory = getDirectoryPath(candidate);
@@ -1270,11 +1517,51 @@ namespace ts {
switch (extensions) {
case Extensions.DtsOnly:
- return tryExtension(Extension.Dts);
+ switch (originalExtension) {
+ case Extension.Mjs:
+ case Extension.Mts:
+ case Extension.Dmts:
+ return tryExtension(Extension.Dmts);
+ case Extension.Cjs:
+ case Extension.Cts:
+ case Extension.Dcts:
+ return tryExtension(Extension.Dcts);
+ case Extension.Json:
+ candidate += Extension.Json;
+ return tryExtension(Extension.Dts);
+ default: return tryExtension(Extension.Dts);
+ }
case Extensions.TypeScript:
- return tryExtension(Extension.Ts) || tryExtension(Extension.Tsx) || tryExtension(Extension.Dts);
+ switch (originalExtension) {
+ case Extension.Mjs:
+ case Extension.Mts:
+ case Extension.Dmts:
+ return tryExtension(Extension.Mts) || tryExtension(Extension.Dmts);
+ case Extension.Cjs:
+ case Extension.Cts:
+ case Extension.Dcts:
+ return tryExtension(Extension.Cts) || tryExtension(Extension.Dcts);
+ case Extension.Json:
+ candidate += Extension.Json;
+ return tryExtension(Extension.Dts);
+ default:
+ return tryExtension(Extension.Ts) || tryExtension(Extension.Tsx) || tryExtension(Extension.Dts);
+ }
case Extensions.JavaScript:
- return tryExtension(Extension.Js) || tryExtension(Extension.Jsx);
+ switch (originalExtension) {
+ case Extension.Mjs:
+ case Extension.Mts:
+ case Extension.Dmts:
+ return tryExtension(Extension.Mjs);
+ case Extension.Cjs:
+ case Extension.Cts:
+ case Extension.Dcts:
+ return tryExtension(Extension.Cjs);
+ case Extension.Json:
+ return tryExtension(Extension.Json);
+ default:
+ return tryExtension(Extension.Js) || tryExtension(Extension.Jsx);
+ }
case Extensions.TSConfig:
case Extensions.Json:
return tryExtension(Extension.Json);
@@ -1312,14 +1599,163 @@ namespace ts {
return withPackageId(packageInfo, loadNodeModuleFromDirectoryWorker(extensions, candidate, onlyRecordFailures, state, packageJsonContent, versionPaths));
}
+ /* @internal */
+ export function getEntrypointsFromPackageJsonInfo(
+ packageJsonInfo: PackageJsonInfo,
+ options: CompilerOptions,
+ host: ModuleResolutionHost,
+ cache: ModuleResolutionCache | undefined,
+ resolveJs?: boolean,
+ ): string[] | false {
+ if (!resolveJs && packageJsonInfo.resolvedEntrypoints !== undefined) {
+ // Cached value excludes resolutions to JS files - those could be
+ // cached separately, but they're used rarely.
+ return packageJsonInfo.resolvedEntrypoints;
+ }
+
+ let entrypoints: string[] | undefined;
+ const extensions = resolveJs ? Extensions.JavaScript : Extensions.TypeScript;
+ const features = getDefaultNodeResolutionFeatures(options);
+ const requireState: ModuleResolutionState = {
+ compilerOptions: options,
+ host,
+ traceEnabled: isTraceEnabled(options, host),
+ failedLookupLocations: [],
+ packageJsonInfoCache: cache?.getPackageJsonInfoCache(),
+ conditions: ["node", "require", "types"],
+ features,
+ };
+ const requireResolution = loadNodeModuleFromDirectoryWorker(
+ extensions,
+ packageJsonInfo.packageDirectory,
+ /*onlyRecordFailures*/ false,
+ requireState,
+ packageJsonInfo.packageJsonContent,
+ packageJsonInfo.versionPaths);
+ entrypoints = append(entrypoints, requireResolution?.path);
+
+ if (features & NodeResolutionFeatures.Exports && packageJsonInfo.packageJsonContent.exports) {
+ for (const conditions of [["node", "import", "types"], ["node", "require", "types"]]) {
+ const exportState = { ...requireState, failedLookupLocations: [], conditions };
+ const exportResolutions = loadEntrypointsFromExportMap(
+ packageJsonInfo,
+ packageJsonInfo.packageJsonContent.exports,
+ exportState,
+ extensions);
+ if (exportResolutions) {
+ for (const resolution of exportResolutions) {
+ entrypoints = appendIfUnique(entrypoints, resolution.path);
+ }
+ }
+ }
+ }
+
+ return packageJsonInfo.resolvedEntrypoints = entrypoints || false;
+ }
+
+ function loadEntrypointsFromExportMap(
+ scope: PackageJsonInfo,
+ exports: object,
+ state: ModuleResolutionState,
+ extensions: Extensions,
+ ): PathAndExtension[] | undefined {
+ let entrypoints: PathAndExtension[] | undefined;
+ if (isArray(exports)) {
+ for (const target of exports) {
+ loadEntrypointsFromTargetExports(target);
+ }
+ }
+ // eslint-disable-next-line no-null/no-null
+ else if (typeof exports === "object" && exports !== null && allKeysStartWithDot(exports as MapLike)) {
+ for (const key in exports) {
+ loadEntrypointsFromTargetExports((exports as MapLike)[key]);
+ }
+ }
+ else {
+ loadEntrypointsFromTargetExports(exports);
+ }
+ return entrypoints;
+
+ function loadEntrypointsFromTargetExports(target: unknown): boolean | undefined {
+ if (typeof target === "string" && startsWith(target, "./") && target.indexOf("*") === -1) {
+ const partsAfterFirst = getPathComponents(target).slice(2);
+ if (partsAfterFirst.indexOf("..") >= 0 || partsAfterFirst.indexOf(".") >= 0 || partsAfterFirst.indexOf("node_modules") >= 0) {
+ return false;
+ }
+ const resolvedTarget = combinePaths(scope.packageDirectory, target);
+ const finalPath = getNormalizedAbsolutePath(resolvedTarget, state.host.getCurrentDirectory?.());
+ const result = loadJSOrExactTSFileName(extensions, finalPath, /*recordOnlyFailures*/ false, state);
+ if (result) {
+ entrypoints = appendIfUnique(entrypoints, result, (a, b) => a.path === b.path);
+ return true;
+ }
+ }
+ else if (Array.isArray(target)) {
+ for (const t of target) {
+ const success = loadEntrypointsFromTargetExports(t);
+ if (success) {
+ return true;
+ }
+ }
+ }
+ // eslint-disable-next-line no-null/no-null
+ else if (typeof target === "object" && target !== null) {
+ return forEach(getOwnKeys(target as MapLike), key => {
+ if (key === "default" || contains(state.conditions, key) || isApplicableVersionedTypesKey(state.conditions, key)) {
+ loadEntrypointsFromTargetExports((target as MapLike)[key]);
+ return true;
+ }
+ });
+ }
+ }
+ }
+
/*@internal*/
interface PackageJsonInfo {
packageDirectory: string;
packageJsonContent: PackageJsonPathFields;
versionPaths: VersionPaths | undefined;
+ /** false: resolved to nothing. undefined: not yet resolved */
+ resolvedEntrypoints: string[] | false | undefined;
}
- function getPackageJsonInfo(packageDirectory: string, onlyRecordFailures: boolean, state: ModuleResolutionState): PackageJsonInfo | undefined {
+ /**
+ * A function for locating the package.json scope for a given path
+ */
+ /*@internal*/
+ export function getPackageScopeForPath(fileName: Path, packageJsonInfoCache: PackageJsonInfoCache | undefined, host: ModuleResolutionHost, options: CompilerOptions): PackageJsonInfo | undefined {
+ const state: {
+ host: ModuleResolutionHost;
+ compilerOptions: CompilerOptions;
+ traceEnabled: boolean;
+ failedLookupLocations: Push;
+ resultFromCache?: ResolvedModuleWithFailedLookupLocations;
+ packageJsonInfoCache: PackageJsonInfoCache | undefined;
+ features: number;
+ conditions: never[];
+ } = {
+ host,
+ compilerOptions: options,
+ traceEnabled: isTraceEnabled(options, host),
+ failedLookupLocations: [],
+ packageJsonInfoCache,
+ features: 0,
+ conditions: [],
+ };
+ const parts = getPathComponents(fileName);
+ parts.pop();
+ while (parts.length > 0) {
+ const pkg = getPackageJsonInfo(getPathFromPathComponents(parts), /*onlyRecordFailures*/ false, state);
+ if (pkg) {
+ return pkg;
+ }
+ parts.pop();
+ }
+ return undefined;
+ }
+
+ /*@internal*/
+ export function getPackageJsonInfo(packageDirectory: string, onlyRecordFailures: boolean, state: ModuleResolutionState): PackageJsonInfo | undefined {
const { host, traceEnabled } = state;
const packageJsonPath = combinePaths(packageDirectory, "package.json");
if (onlyRecordFailures) {
@@ -1346,7 +1782,7 @@ namespace ts {
trace(host, Diagnostics.Found_package_json_at_0, packageJsonPath);
}
const versionPaths = readPackageJsonTypesVersionPaths(packageJsonContent, state);
- const result = { packageDirectory, packageJsonContent, versionPaths };
+ const result = { packageDirectory, packageJsonContent, versionPaths, resolvedEntrypoints: undefined };
state.packageJsonInfoCache?.setPackageJsonInfo(packageJsonPath, result);
return result;
}
@@ -1398,7 +1834,17 @@ namespace ts {
// Even if extensions is DtsOnly, we can still look up a .ts file as a result of package.json "types"
const nextExtensions = extensions === Extensions.DtsOnly ? Extensions.TypeScript : extensions;
// Don't do package.json lookup recursively, because Node.js' package lookup doesn't.
- return nodeLoadModuleByRelativeName(nextExtensions, candidate, onlyRecordFailures, state, /*considerPackageJson*/ false);
+
+ // Disable `EsmMode` for the resolution of the package path for cjs-mode packages (so the `main` field can omit extensions)
+ // (technically it only emits a deprecation warning in esm packages right now, but that's probably
+ // enough to mean we don't need to support it)
+ const features = state.features;
+ if (jsonContent?.type !== "module") {
+ state.features &= ~NodeResolutionFeatures.EsmMode;
+ }
+ const result = nodeLoadModuleByRelativeName(nextExtensions, candidate, onlyRecordFailures, state, /*considerPackageJson*/ false);
+ state.features = features;
+ return result;
};
const onlyRecordFailuresForPackageFile = packageFile ? !directoryProbablyExists(getDirectoryPath(packageFile), state.host) : undefined;
@@ -1420,7 +1866,10 @@ namespace ts {
const packageFileResult = packageFile && removeIgnoredPackageId(loader(extensions, packageFile, onlyRecordFailuresForPackageFile!, state));
if (packageFileResult) return packageFileResult;
- return loadModuleFromFile(extensions, indexPath, onlyRecordFailuresForIndex, state);
+ // esm mode resolutions don't do package `index` lookups
+ if (!(state.features & NodeResolutionFeatures.EsmMode)) {
+ return loadModuleFromFile(extensions, indexPath, onlyRecordFailuresForIndex, state);
+ }
}
/** Resolve from an arbitrarily specified file. Return `undefined` if it has an unsupported extension. */
@@ -1453,7 +1902,237 @@ namespace ts {
return idx === -1 ? { packageName: moduleName, rest: "" } : { packageName: moduleName.slice(0, idx), rest: moduleName.slice(idx + 1) };
}
- function loadModuleFromNearestNodeModulesDirectory(extensions: Extensions, moduleName: string, directory: string, state: ModuleResolutionState, cache: NonRelativeModuleNameResolutionCache | undefined, redirectedReference: ResolvedProjectReference | undefined): SearchResult {
+ /* @internal */
+ export function allKeysStartWithDot(obj: MapLike) {
+ return every(getOwnKeys(obj), k => startsWith(k, "."));
+ }
+
+ function noKeyStartsWithDot(obj: MapLike) {
+ return !some(getOwnKeys(obj), k => startsWith(k, "."));
+ }
+
+ function loadModuleFromSelfNameReference(extensions: Extensions, moduleName: string, directory: string, state: ModuleResolutionState, cache: ModuleResolutionCache | undefined, redirectedReference: ResolvedProjectReference | undefined): SearchResult {
+ const useCaseSensitiveFileNames = typeof state.host.useCaseSensitiveFileNames === "function" ? state.host.useCaseSensitiveFileNames() : state.host.useCaseSensitiveFileNames;
+ const directoryPath = toPath(combinePaths(directory, "dummy"), state.host.getCurrentDirectory?.(), createGetCanonicalFileName(useCaseSensitiveFileNames === undefined ? true : useCaseSensitiveFileNames));
+ const scope = getPackageScopeForPath(directoryPath, state.packageJsonInfoCache, state.host, state.compilerOptions);
+ if (!scope || !scope.packageJsonContent.exports) {
+ return undefined;
+ }
+ if (typeof scope.packageJsonContent.name !== "string") {
+ return undefined;
+ }
+ const parts = getPathComponents(moduleName); // unrooted paths should have `""` as their 0th entry
+ const nameParts = getPathComponents(scope.packageJsonContent.name);
+ if (!every(nameParts, (p, i) => parts[i] === p)) {
+ return undefined;
+ }
+ const trailingParts = parts.slice(nameParts.length);
+ return loadModuleFromExports(scope, extensions, !length(trailingParts) ? "." : `.${directorySeparator}${trailingParts.join(directorySeparator)}`, state, cache, redirectedReference);
+ }
+
+ function loadModuleFromExports(scope: PackageJsonInfo, extensions: Extensions, subpath: string, state: ModuleResolutionState, cache: ModuleResolutionCache | undefined, redirectedReference: ResolvedProjectReference | undefined): SearchResult {
+ if (!scope.packageJsonContent.exports) {
+ return undefined;
+ }
+
+ if (subpath === ".") {
+ let mainExport;
+ if (typeof scope.packageJsonContent.exports === "string" || Array.isArray(scope.packageJsonContent.exports) || (typeof scope.packageJsonContent.exports === "object" && noKeyStartsWithDot(scope.packageJsonContent.exports as MapLike))) {
+ mainExport = scope.packageJsonContent.exports;
+ }
+ else if (hasProperty(scope.packageJsonContent.exports as MapLike, ".")) {
+ mainExport = (scope.packageJsonContent.exports as MapLike)["."];
+ }
+ if (mainExport) {
+ const loadModuleFromTargetImportOrExport = getLoadModuleFromTargetImportOrExport(extensions, state, cache, redirectedReference, subpath, scope, /*isImports*/ false);
+ return loadModuleFromTargetImportOrExport(mainExport, "", /*pattern*/ false);
+ }
+ }
+ else if (allKeysStartWithDot(scope.packageJsonContent.exports as MapLike)) {
+ if (typeof scope.packageJsonContent.exports !== "object") {
+ if (state.traceEnabled) {
+ trace(state.host, Diagnostics.Export_specifier_0_does_not_exist_in_package_json_scope_at_path_1, subpath, scope.packageDirectory);
+ }
+ return toSearchResult(/*value*/ undefined);
+ }
+ const result = loadModuleFromImportsOrExports(extensions, state, cache, redirectedReference, subpath, scope.packageJsonContent.exports, scope, /*isImports*/ false);
+ if (result) {
+ return result;
+ }
+ }
+
+ if (state.traceEnabled) {
+ trace(state.host, Diagnostics.Export_specifier_0_does_not_exist_in_package_json_scope_at_path_1, subpath, scope.packageDirectory);
+ }
+ return toSearchResult(/*value*/ undefined);
+ }
+
+ function loadModuleFromImports(extensions: Extensions, moduleName: string, directory: string, state: ModuleResolutionState, cache: ModuleResolutionCache | undefined, redirectedReference: ResolvedProjectReference | undefined): SearchResult {
+ if (moduleName === "#" || startsWith(moduleName, "#/")) {
+ if (state.traceEnabled) {
+ trace(state.host, Diagnostics.Invalid_import_specifier_0_has_no_possible_resolutions, moduleName);
+ }
+ return toSearchResult(/*value*/ undefined);
+ }
+ const useCaseSensitiveFileNames = typeof state.host.useCaseSensitiveFileNames === "function" ? state.host.useCaseSensitiveFileNames() : state.host.useCaseSensitiveFileNames;
+ const directoryPath = toPath(combinePaths(directory, "dummy"), state.host.getCurrentDirectory?.(), createGetCanonicalFileName(useCaseSensitiveFileNames === undefined ? true : useCaseSensitiveFileNames));
+ const scope = getPackageScopeForPath(directoryPath, state.packageJsonInfoCache, state.host, state.compilerOptions);
+ if (!scope) {
+ if (state.traceEnabled) {
+ trace(state.host, Diagnostics.Directory_0_has_no_containing_package_json_scope_Imports_will_not_resolve, directoryPath);
+ }
+ return toSearchResult(/*value*/ undefined);
+ }
+ if (!scope.packageJsonContent.imports) {
+ if (state.traceEnabled) {
+ trace(state.host, Diagnostics.package_json_scope_0_has_no_imports_defined, scope.packageDirectory);
+ }
+ return toSearchResult(/*value*/ undefined);
+ }
+
+ const result = loadModuleFromImportsOrExports(extensions, state, cache, redirectedReference, moduleName, scope.packageJsonContent.imports, scope, /*isImports*/ true);
+ if (result) {
+ return result;
+ }
+
+ if (state.traceEnabled) {
+ trace(state.host, Diagnostics.Import_specifier_0_does_not_exist_in_package_json_scope_at_path_1, moduleName, scope.packageDirectory);
+ }
+ return toSearchResult(/*value*/ undefined);
+ }
+
+ function loadModuleFromImportsOrExports(extensions: Extensions, state: ModuleResolutionState, cache: ModuleResolutionCache | undefined, redirectedReference: ResolvedProjectReference | undefined, moduleName: string, lookupTable: object, scope: PackageJsonInfo, isImports: boolean): SearchResult | undefined {
+ const loadModuleFromTargetImportOrExport = getLoadModuleFromTargetImportOrExport(extensions, state, cache, redirectedReference, moduleName, scope, isImports);
+
+ if (!endsWith(moduleName, directorySeparator) && moduleName.indexOf("*") === -1 && hasProperty(lookupTable, moduleName)) {
+ const target = (lookupTable as {[idx: string]: unknown})[moduleName];
+ return loadModuleFromTargetImportOrExport(target, /*subpath*/ "", /*pattern*/ false);
+ }
+ const expandingKeys = sort(filter(getOwnKeys(lookupTable as MapLike), k => k.indexOf("*") !== -1 || endsWith(k, "/")), (a, b) => a.length - b.length);
+ for (const potentialTarget of expandingKeys) {
+ if (state.features & NodeResolutionFeatures.ExportsPatternTrailers && matchesPatternWithTrailer(potentialTarget, moduleName)) {
+ const target = (lookupTable as {[idx: string]: unknown})[potentialTarget];
+ const starPos = potentialTarget.indexOf("*");
+ const subpath = moduleName.substring(potentialTarget.substring(0, starPos).length, moduleName.length - (potentialTarget.length - 1 - starPos));
+ return loadModuleFromTargetImportOrExport(target, subpath, /*pattern*/ true);
+ }
+ else if (endsWith(potentialTarget, "*") && startsWith(moduleName, potentialTarget.substring(0, potentialTarget.length - 1))) {
+ const target = (lookupTable as {[idx: string]: unknown})[potentialTarget];
+ const subpath = moduleName.substring(potentialTarget.length - 1);
+ return loadModuleFromTargetImportOrExport(target, subpath, /*pattern*/ true);
+ }
+ else if (startsWith(moduleName, potentialTarget)) {
+ const target = (lookupTable as {[idx: string]: unknown})[potentialTarget];
+ const subpath = moduleName.substring(potentialTarget.length);
+ return loadModuleFromTargetImportOrExport(target, subpath, /*pattern*/ false);
+ }
+ }
+
+ function matchesPatternWithTrailer(target: string, name: string) {
+ if (endsWith(target, "*")) return false; // handled by next case in loop
+ const starPos = target.indexOf("*");
+ if (starPos === -1) return false; // handled by last case in loop
+ return startsWith(name, target.substring(0, starPos)) && endsWith(name, target.substring(starPos + 1));
+ }
+ }
+
+ /**
+ * Gets the self-recursive function specialized to retrieving the targeted import/export element for the given resolution configuration
+ */
+ function getLoadModuleFromTargetImportOrExport(extensions: Extensions, state: ModuleResolutionState, cache: ModuleResolutionCache | undefined, redirectedReference: ResolvedProjectReference | undefined, moduleName: string, scope: PackageJsonInfo, isImports: boolean) {
+ return loadModuleFromTargetImportOrExport;
+ function loadModuleFromTargetImportOrExport(target: unknown, subpath: string, pattern: boolean): SearchResult | undefined {
+ if (typeof target === "string") {
+ if (!pattern && subpath.length > 0 && !endsWith(target, "/")) {
+ if (state.traceEnabled) {
+ trace(state.host, Diagnostics.package_json_scope_0_has_invalid_type_for_target_of_specifier_1, scope.packageDirectory, moduleName);
+ }
+ return toSearchResult(/*value*/ undefined);
+ }
+ if (!startsWith(target, "./")) {
+ if (isImports && !startsWith(target, "../") && !startsWith(target, "/") && !isRootedDiskPath(target)) {
+ const combinedLookup = pattern ? target.replace(/\*/g, subpath) : target + subpath;
+ const result = nodeModuleNameResolverWorker(state.features, combinedLookup, scope.packageDirectory + "/", state.compilerOptions, state.host, cache, [extensions], redirectedReference);
+ return toSearchResult(result.resolvedModule ? { path: result.resolvedModule.resolvedFileName, extension: result.resolvedModule.extension, packageId: result.resolvedModule.packageId, originalPath: result.resolvedModule.originalPath } : undefined);
+ }
+ if (state.traceEnabled) {
+ trace(state.host, Diagnostics.package_json_scope_0_has_invalid_type_for_target_of_specifier_1, scope.packageDirectory, moduleName);
+ }
+ return toSearchResult(/*value*/ undefined);
+ }
+ const parts = pathIsRelative(target) ? getPathComponents(target).slice(1) : getPathComponents(target);
+ const partsAfterFirst = parts.slice(1);
+ if (partsAfterFirst.indexOf("..") >= 0 || partsAfterFirst.indexOf(".") >= 0 || partsAfterFirst.indexOf("node_modules") >= 0) {
+ if (state.traceEnabled) {
+ trace(state.host, Diagnostics.package_json_scope_0_has_invalid_type_for_target_of_specifier_1, scope.packageDirectory, moduleName);
+ }
+ return toSearchResult(/*value*/ undefined);
+ }
+ const resolvedTarget = combinePaths(scope.packageDirectory, target);
+ // TODO: Assert that `resolvedTarget` is actually within the package directory? That's what the spec says.... but I'm not sure we need
+ // to be in the business of validating everyone's import and export map correctness.
+ const subpathParts = getPathComponents(subpath);
+ if (subpathParts.indexOf("..") >= 0 || subpathParts.indexOf(".") >= 0 || subpathParts.indexOf("node_modules") >= 0) {
+ if (state.traceEnabled) {
+ trace(state.host, Diagnostics.package_json_scope_0_has_invalid_type_for_target_of_specifier_1, scope.packageDirectory, moduleName);
+ }
+ return toSearchResult(/*value*/ undefined);
+ }
+ const finalPath = getNormalizedAbsolutePath(pattern ? resolvedTarget.replace(/\*/g, subpath) : resolvedTarget + subpath, state.host.getCurrentDirectory?.());
+
+ return toSearchResult(withPackageId(scope, loadJSOrExactTSFileName(extensions, finalPath, /*onlyRecordFailures*/ false, state)));
+ }
+ else if (typeof target === "object" && target !== null) { // eslint-disable-line no-null/no-null
+ if (!Array.isArray(target)) {
+ for (const key of getOwnKeys(target as MapLike)) {
+ if (key === "default" || state.conditions.indexOf(key) >= 0 || isApplicableVersionedTypesKey(state.conditions, key)) {
+ const subTarget = (target as MapLike)[key];
+ const result = loadModuleFromTargetImportOrExport(subTarget, subpath, pattern);
+ if (result) {
+ return result;
+ }
+ }
+ }
+ return undefined;
+ }
+ else {
+ if (!length(target)) {
+ if (state.traceEnabled) {
+ trace(state.host, Diagnostics.package_json_scope_0_has_invalid_type_for_target_of_specifier_1, scope.packageDirectory, moduleName);
+ }
+ return toSearchResult(/*value*/ undefined);
+ }
+ for (const elem of target) {
+ const result = loadModuleFromTargetImportOrExport(elem, subpath, pattern);
+ if (result) {
+ return result;
+ }
+ }
+ }
+ }
+ else if (target === null) { // eslint-disable-line no-null/no-null
+ if (state.traceEnabled) {
+ trace(state.host, Diagnostics.package_json_scope_0_explicitly_maps_specifier_1_to_null, scope.packageDirectory, moduleName);
+ }
+ return toSearchResult(/*value*/ undefined);
+ }
+ if (state.traceEnabled) {
+ trace(state.host, Diagnostics.package_json_scope_0_has_invalid_type_for_target_of_specifier_1, scope.packageDirectory, moduleName);
+ }
+ return toSearchResult(/*value*/ undefined);
+ }
+ }
+
+ /* @internal */
+ export function isApplicableVersionedTypesKey(conditions: string[], key: string) {
+ if (conditions.indexOf("types") === -1) return false; // only apply versioned types conditions if the types condition is applied
+ if (!startsWith(key, "types@")) return false;
+ const range = VersionRange.tryParse(key.substring("types@".length));
+ if (!range) return false;
+ return range.test(version);
+ }
+
+ function loadModuleFromNearestNodeModulesDirectory(extensions: Extensions, moduleName: string, directory: string, state: ModuleResolutionState, cache: ModuleResolutionCache | undefined, redirectedReference: ResolvedProjectReference | undefined): SearchResult {
return loadModuleFromNearestNodeModulesDirectoryWorker(extensions, moduleName, directory, state, /*typesScopeOnly*/ false, cache, redirectedReference);
}
@@ -1462,27 +2141,27 @@ namespace ts {
return loadModuleFromNearestNodeModulesDirectoryWorker(Extensions.DtsOnly, moduleName, directory, state, /*typesScopeOnly*/ true, /*cache*/ undefined, /*redirectedReference*/ undefined);
}
- function loadModuleFromNearestNodeModulesDirectoryWorker(extensions: Extensions, moduleName: string, directory: string, state: ModuleResolutionState, typesScopeOnly: boolean, cache: NonRelativeModuleNameResolutionCache | undefined, redirectedReference: ResolvedProjectReference | undefined): SearchResult {
- const perModuleNameCache = cache && cache.getOrCreateCacheForModuleName(moduleName, redirectedReference);
+ function loadModuleFromNearestNodeModulesDirectoryWorker(extensions: Extensions, moduleName: string, directory: string, state: ModuleResolutionState, typesScopeOnly: boolean, cache: ModuleResolutionCache | undefined, redirectedReference: ResolvedProjectReference | undefined): SearchResult {
+ const perModuleNameCache = cache && cache.getOrCreateCacheForModuleName(moduleName, state.features === 0 ? undefined : state.features & NodeResolutionFeatures.EsmMode ? ModuleKind.ESNext : ModuleKind.CommonJS, redirectedReference);
return forEachAncestorDirectory(normalizeSlashes(directory), ancestorDirectory => {
if (getBaseFileName(ancestorDirectory) !== "node_modules") {
const resolutionFromCache = tryFindNonRelativeModuleNameInCache(perModuleNameCache, moduleName, ancestorDirectory, state);
if (resolutionFromCache) {
return resolutionFromCache;
}
- return toSearchResult(loadModuleFromImmediateNodeModulesDirectory(extensions, moduleName, ancestorDirectory, state, typesScopeOnly));
+ return toSearchResult(loadModuleFromImmediateNodeModulesDirectory(extensions, moduleName, ancestorDirectory, state, typesScopeOnly, cache, redirectedReference));
}
});
}
- function loadModuleFromImmediateNodeModulesDirectory(extensions: Extensions, moduleName: string, directory: string, state: ModuleResolutionState, typesScopeOnly: boolean): Resolved | undefined {
+ function loadModuleFromImmediateNodeModulesDirectory(extensions: Extensions, moduleName: string, directory: string, state: ModuleResolutionState, typesScopeOnly: boolean, cache: ModuleResolutionCache | undefined, redirectedReference: ResolvedProjectReference | undefined): Resolved | undefined {
const nodeModulesFolder = combinePaths(directory, "node_modules");
const nodeModulesFolderExists = directoryProbablyExists(nodeModulesFolder, state.host);
if (!nodeModulesFolderExists && state.traceEnabled) {
trace(state.host, Diagnostics.Directory_0_does_not_exist_skipping_all_lookups_in_it, nodeModulesFolder);
}
- const packageResult = typesScopeOnly ? undefined : loadModuleFromSpecificNodeModulesDirectory(extensions, moduleName, nodeModulesFolder, nodeModulesFolderExists, state);
+ const packageResult = typesScopeOnly ? undefined : loadModuleFromSpecificNodeModulesDirectory(extensions, moduleName, nodeModulesFolder, nodeModulesFolderExists, state, cache, redirectedReference);
if (packageResult) {
return packageResult;
}
@@ -1495,34 +2174,42 @@ namespace ts {
}
nodeModulesAtTypesExists = false;
}
- return loadModuleFromSpecificNodeModulesDirectory(Extensions.DtsOnly, mangleScopedPackageNameWithTrace(moduleName, state), nodeModulesAtTypes, nodeModulesAtTypesExists, state);
+ return loadModuleFromSpecificNodeModulesDirectory(Extensions.DtsOnly, mangleScopedPackageNameWithTrace(moduleName, state), nodeModulesAtTypes, nodeModulesAtTypesExists, state, cache, redirectedReference);
}
}
- function loadModuleFromSpecificNodeModulesDirectory(extensions: Extensions, moduleName: string, nodeModulesDirectory: string, nodeModulesDirectoryExists: boolean, state: ModuleResolutionState): Resolved | undefined {
+ function loadModuleFromSpecificNodeModulesDirectory(extensions: Extensions, moduleName: string, nodeModulesDirectory: string, nodeModulesDirectoryExists: boolean, state: ModuleResolutionState, cache: ModuleResolutionCache | undefined, redirectedReference: ResolvedProjectReference | undefined): Resolved | undefined {
const candidate = normalizePath(combinePaths(nodeModulesDirectory, moduleName));
// First look for a nested package.json, as in `node_modules/foo/bar/package.json`.
let packageInfo = getPackageJsonInfo(candidate, !nodeModulesDirectoryExists, state);
- if (packageInfo) {
- const fromFile = loadModuleFromFile(extensions, candidate, !nodeModulesDirectoryExists, state);
- if (fromFile) {
- return noPackageId(fromFile);
- }
+ // But only if we're not respecting export maps (if we are, we might redirect around this location)
+ if (!(state.features & NodeResolutionFeatures.Exports)) {
+ if (packageInfo) {
+ const fromFile = loadModuleFromFile(extensions, candidate, !nodeModulesDirectoryExists, state);
+ if (fromFile) {
+ return noPackageId(fromFile);
+ }
- const fromDirectory = loadNodeModuleFromDirectoryWorker(
- extensions,
- candidate,
- !nodeModulesDirectoryExists,
- state,
- packageInfo.packageJsonContent,
- packageInfo.versionPaths
- );
- return withPackageId(packageInfo, fromDirectory);
+ const fromDirectory = loadNodeModuleFromDirectoryWorker(
+ extensions,
+ candidate,
+ !nodeModulesDirectoryExists,
+ state,
+ packageInfo.packageJsonContent,
+ packageInfo.versionPaths
+ );
+ return withPackageId(packageInfo, fromDirectory);
+ }
}
+ const { packageName, rest } = parsePackageName(moduleName);
const loader: ResolutionKindSpecificLoader = (extensions, candidate, onlyRecordFailures, state) => {
- const pathAndExtension =
+ // package exports are higher priority than file/directory lookups (and, if there's exports present, blocks them)
+ if (packageInfo && packageInfo.packageJsonContent.exports && state.features & NodeResolutionFeatures.Exports) {
+ return loadModuleFromExports(packageInfo, extensions, combinePaths(".", rest), state, cache, redirectedReference)?.value;
+ }
+ let pathAndExtension =
loadModuleFromFile(extensions, candidate, onlyRecordFailures, state) ||
loadNodeModuleFromDirectoryWorker(
extensions,
@@ -1532,10 +2219,19 @@ namespace ts {
packageInfo && packageInfo.packageJsonContent,
packageInfo && packageInfo.versionPaths
);
+ if (
+ !pathAndExtension && packageInfo
+ && packageInfo.packageJsonContent.exports === undefined
+ && packageInfo.packageJsonContent.main === undefined
+ && state.features & NodeResolutionFeatures.EsmMode
+ ) {
+ // EsmMode disables index lookup in `loadNodeModuleFromDirectoryWorker` generally, however non-relative package resolutions still assume
+ // a default `index.js` entrypoint if no `main` or `exports` are present
+ pathAndExtension = loadModuleFromFile(extensions, combinePaths(candidate, "index.js"), onlyRecordFailures, state);
+ }
return withPackageId(packageInfo, pathAndExtension);
};
- const { packageName, rest } = parsePackageName(moduleName);
if (rest !== "") { // If "rest" is empty, we just did this search above.
const packageDirectory = combinePaths(nodeModulesDirectory, packageName);
@@ -1644,7 +2340,7 @@ namespace ts {
export function classicNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, cache?: NonRelativeModuleNameResolutionCache, redirectedReference?: ResolvedProjectReference): ResolvedModuleWithFailedLookupLocations {
const traceEnabled = isTraceEnabled(compilerOptions, host);
const failedLookupLocations: string[] = [];
- const state: ModuleResolutionState = { compilerOptions, host, traceEnabled, failedLookupLocations, packageJsonInfoCache: cache };
+ const state: ModuleResolutionState = { compilerOptions, host, traceEnabled, failedLookupLocations, packageJsonInfoCache: cache, features: NodeResolutionFeatures.None, conditions: [] };
const containingDirectory = getDirectoryPath(containingFile);
const resolved = tryResolve(Extensions.TypeScript) || tryResolve(Extensions.JavaScript);
@@ -1658,7 +2354,7 @@ namespace ts {
}
if (!isExternalModuleNameRelative(moduleName)) {
- const perModuleNameCache = cache && cache.getOrCreateCacheForModuleName(moduleName, redirectedReference);
+ const perModuleNameCache = cache && cache.getOrCreateCacheForModuleName(moduleName, /*mode*/ undefined, redirectedReference);
// Climb up parent directories looking for a module.
const resolved = forEachAncestorDirectory(containingDirectory, directory => {
const resolutionFromCache = tryFindNonRelativeModuleNameInCache(perModuleNameCache, moduleName, directory, state);
@@ -1694,8 +2390,8 @@ namespace ts {
trace(host, Diagnostics.Auto_discovery_for_typings_is_enabled_in_project_0_Running_extra_resolution_pass_for_module_1_using_cache_location_2, projectName, moduleName, globalCache);
}
const failedLookupLocations: string[] = [];
- const state: ModuleResolutionState = { compilerOptions, host, traceEnabled, failedLookupLocations, packageJsonInfoCache };
- const resolved = loadModuleFromImmediateNodeModulesDirectory(Extensions.DtsOnly, moduleName, globalCache, state, /*typesScopeOnly*/ false);
+ const state: ModuleResolutionState = { compilerOptions, host, traceEnabled, failedLookupLocations, packageJsonInfoCache, features: NodeResolutionFeatures.None, conditions: [] };
+ const resolved = loadModuleFromImmediateNodeModulesDirectory(Extensions.DtsOnly, moduleName, globalCache, state, /*typesScopeOnly*/ false, /*cache*/ undefined, /*redirectedReference*/ undefined);
return createResolvedModuleWithFailedLookupLocations(resolved, /*isExternalLibraryImport*/ true, failedLookupLocations, state.resultFromCache);
}
diff --git a/src/compiler/moduleSpecifiers.ts b/src/compiler/moduleSpecifiers.ts
index f246f44c013..c740f8996ee 100644
--- a/src/compiler/moduleSpecifiers.ts
+++ b/src/compiler/moduleSpecifiers.ts
@@ -11,7 +11,7 @@ namespace ts.moduleSpecifiers {
readonly ending: Ending;
}
- function getPreferences({ importModuleSpecifierPreference, importModuleSpecifierEnding }: UserPreferences, compilerOptions: CompilerOptions, importingSourceFile: SourceFile): Preferences {
+ function getPreferences(host: ModuleSpecifierResolutionHost, { importModuleSpecifierPreference, importModuleSpecifierEnding }: UserPreferences, compilerOptions: CompilerOptions, importingSourceFile: SourceFile): Preferences {
return {
relativePreference:
importModuleSpecifierPreference === "relative" ? RelativePreference.Relative :
@@ -25,34 +25,63 @@ namespace ts.moduleSpecifiers {
case "minimal": return Ending.Minimal;
case "index": return Ending.Index;
case "js": return Ending.JsExtension;
- default: return usesJsExtensionOnImports(importingSourceFile) ? Ending.JsExtension
+ default: return usesJsExtensionOnImports(importingSourceFile) || isFormatRequiringExtensions(compilerOptions, importingSourceFile.path, host) ? Ending.JsExtension
: getEmitModuleResolutionKind(compilerOptions) !== ModuleResolutionKind.NodeJs ? Ending.Index : Ending.Minimal;
}
}
}
- function getPreferencesForUpdate(compilerOptions: CompilerOptions, oldImportSpecifier: string): Preferences {
+ function getPreferencesForUpdate(compilerOptions: CompilerOptions, oldImportSpecifier: string, importingSourceFileName: Path, host: ModuleSpecifierResolutionHost): Preferences {
return {
relativePreference: isExternalModuleNameRelative(oldImportSpecifier) ? RelativePreference.Relative : RelativePreference.NonRelative,
- ending: hasJSFileExtension(oldImportSpecifier) ?
+ ending: hasJSFileExtension(oldImportSpecifier) || isFormatRequiringExtensions(compilerOptions, importingSourceFileName, host) ?
Ending.JsExtension :
getEmitModuleResolutionKind(compilerOptions) !== ModuleResolutionKind.NodeJs || endsWith(oldImportSpecifier, "index") ? Ending.Index : Ending.Minimal,
};
}
+ function isFormatRequiringExtensions(compilerOptions: CompilerOptions, importingSourceFileName: Path, host: ModuleSpecifierResolutionHost) {
+ if (getEmitModuleResolutionKind(compilerOptions) !== ModuleResolutionKind.Node12
+ && getEmitModuleResolutionKind(compilerOptions) !== ModuleResolutionKind.NodeNext) {
+ return false;
+ }
+ return getImpliedNodeFormatForFile(importingSourceFileName, host.getPackageJsonInfoCache?.(), getModuleResolutionHost(host), compilerOptions) !== ModuleKind.CommonJS;
+ }
+
+ function getModuleResolutionHost(host: ModuleSpecifierResolutionHost): ModuleResolutionHost {
+ return {
+ fileExists: host.fileExists,
+ readFile: Debug.checkDefined(host.readFile),
+ directoryExists: host.directoryExists,
+ getCurrentDirectory: host.getCurrentDirectory,
+ realpath: host.realpath,
+ useCaseSensitiveFileNames: host.useCaseSensitiveFileNames?.(),
+ };
+ }
+
+ // `importingSourceFile` and `importingSourceFileName`? Why not just use `importingSourceFile.path`?
+ // Because when this is called by the file renamer, `importingSourceFile` is the file being renamed,
+ // while `importingSourceFileName` its *new* name. We need a source file just to get its
+ // `impliedNodeFormat` and to detect certain preferences from existing import module specifiers.
export function updateModuleSpecifier(
compilerOptions: CompilerOptions,
+ importingSourceFile: SourceFile,
importingSourceFileName: Path,
toFileName: string,
host: ModuleSpecifierResolutionHost,
oldImportSpecifier: string,
): string | undefined {
- const res = getModuleSpecifierWorker(compilerOptions, importingSourceFileName, toFileName, host, getPreferencesForUpdate(compilerOptions, oldImportSpecifier), {});
+ const res = getModuleSpecifierWorker(compilerOptions, importingSourceFile, importingSourceFileName, toFileName, host, getPreferencesForUpdate(compilerOptions, oldImportSpecifier, importingSourceFileName, host), {});
if (res === oldImportSpecifier) return undefined;
return res;
}
- // Note: importingSourceFile is just for usesJsExtensionOnImports
+ // `importingSourceFile` and `importingSourceFileName`? Why not just use `importingSourceFile.path`?
+ // Because when this is called by the declaration emitter, `importingSourceFile` is the implementation
+ // file, but `importingSourceFileName` and `toFileName` refer to declaration files (the former to the
+ // one currently being produced; the latter to the one being imported). We need an implementation file
+ // just to get its `impliedNodeFormat` and to detect certain preferences from existing import module
+ // specifiers.
export function getModuleSpecifier(
compilerOptions: CompilerOptions,
importingSourceFile: SourceFile,
@@ -60,24 +89,25 @@ namespace ts.moduleSpecifiers {
toFileName: string,
host: ModuleSpecifierResolutionHost,
): string {
- return getModuleSpecifierWorker(compilerOptions, importingSourceFileName, toFileName, host, getPreferences({}, compilerOptions, importingSourceFile), {});
+ return getModuleSpecifierWorker(compilerOptions, importingSourceFile, importingSourceFileName, toFileName, host, getPreferences(host, {}, compilerOptions, importingSourceFile), {});
}
export function getNodeModulesPackageName(
compilerOptions: CompilerOptions,
- importingSourceFileName: Path,
+ importingSourceFile: SourceFile,
nodeModulesFileName: string,
host: ModuleSpecifierResolutionHost,
preferences: UserPreferences,
): string | undefined {
- const info = getInfo(importingSourceFileName, host);
- const modulePaths = getAllModulePaths(importingSourceFileName, nodeModulesFileName, host, preferences);
+ const info = getInfo(importingSourceFile.path, host);
+ const modulePaths = getAllModulePaths(importingSourceFile.path, nodeModulesFileName, host, preferences);
return firstDefined(modulePaths,
- modulePath => tryGetModuleNameAsNodeModule(modulePath, info, host, compilerOptions, /*packageNameOnly*/ true));
+ modulePath => tryGetModuleNameAsNodeModule(modulePath, info, importingSourceFile, host, compilerOptions, /*packageNameOnly*/ true));
}
function getModuleSpecifierWorker(
compilerOptions: CompilerOptions,
+ importingSourceFile: SourceFile,
importingSourceFileName: Path,
toFileName: string,
host: ModuleSpecifierResolutionHost,
@@ -86,7 +116,7 @@ namespace ts.moduleSpecifiers {
): string {
const info = getInfo(importingSourceFileName, host);
const modulePaths = getAllModulePaths(importingSourceFileName, toFileName, host, userPreferences);
- return firstDefined(modulePaths, modulePath => tryGetModuleNameAsNodeModule(modulePath, info, host, compilerOptions)) ||
+ return firstDefined(modulePaths, modulePath => tryGetModuleNameAsNodeModule(modulePath, info, importingSourceFile, host, compilerOptions)) ||
getLocalModuleSpecifier(toFileName, info, compilerOptions, host, preferences);
}
@@ -175,7 +205,7 @@ namespace ts.moduleSpecifiers {
userPreferences: UserPreferences,
): readonly string[] {
const info = getInfo(importingSourceFile.path, host);
- const preferences = getPreferences(userPreferences, compilerOptions, importingSourceFile);
+ const preferences = getPreferences(host, userPreferences, compilerOptions, importingSourceFile);
const existingSpecifier = forEach(modulePaths, modulePath => forEach(
host.getFileIncludeReasons().get(toPath(modulePath.path, host.getCurrentDirectory(), info.getCanonicalFileName)),
reason => {
@@ -203,7 +233,7 @@ namespace ts.moduleSpecifiers {
let pathsSpecifiers: string[] | undefined;
let relativeSpecifiers: string[] | undefined;
for (const modulePath of modulePaths) {
- const specifier = tryGetModuleNameAsNodeModule(modulePath, info, host, compilerOptions);
+ const specifier = tryGetModuleNameAsNodeModule(modulePath, info, importingSourceFile, host, compilerOptions);
nodeModulesSpecifiers = append(nodeModulesSpecifiers, specifier);
if (specifier && modulePath.isRedirect) {
// If we got a specifier for a redirect, it was a bare package specifier (e.g. "@foo/bar",
@@ -536,6 +566,77 @@ namespace ts.moduleSpecifiers {
}
}
+ const enum MatchingMode {
+ Exact,
+ Directory,
+ Pattern
+ }
+
+ function tryGetModuleNameFromExports(options: CompilerOptions, targetFilePath: string, packageDirectory: string, packageName: string, exports: unknown, conditions: string[], mode = MatchingMode.Exact): { moduleFileToTry: string } | undefined {
+ if (typeof exports === "string") {
+ const pathOrPattern = getNormalizedAbsolutePath(combinePaths(packageDirectory, exports), /*currentDirectory*/ undefined);
+ const extensionSwappedTarget = hasTSFileExtension(targetFilePath) ? removeFileExtension(targetFilePath) + tryGetJSExtensionForFile(targetFilePath, options) : undefined;
+ switch (mode) {
+ case MatchingMode.Exact:
+ if (comparePaths(targetFilePath, pathOrPattern) === Comparison.EqualTo || (extensionSwappedTarget && comparePaths(extensionSwappedTarget, pathOrPattern) === Comparison.EqualTo)) {
+ return { moduleFileToTry: packageName };
+ }
+ break;
+ case MatchingMode.Directory:
+ if (containsPath(pathOrPattern, targetFilePath)) {
+ const fragment = getRelativePathFromDirectory(pathOrPattern, targetFilePath, /*ignoreCase*/ false);
+ return { moduleFileToTry: getNormalizedAbsolutePath(combinePaths(combinePaths(packageName, exports), fragment), /*currentDirectory*/ undefined) };
+ }
+ break;
+ case MatchingMode.Pattern:
+ const starPos = pathOrPattern.indexOf("*");
+ const leadingSlice = pathOrPattern.slice(0, starPos);
+ const trailingSlice = pathOrPattern.slice(starPos + 1);
+ if (startsWith(targetFilePath, leadingSlice) && endsWith(targetFilePath, trailingSlice)) {
+ const starReplacement = targetFilePath.slice(leadingSlice.length, targetFilePath.length - trailingSlice.length);
+ return { moduleFileToTry: packageName.replace("*", starReplacement) };
+ }
+ if (extensionSwappedTarget && startsWith(extensionSwappedTarget, leadingSlice) && endsWith(extensionSwappedTarget, trailingSlice)) {
+ const starReplacement = extensionSwappedTarget.slice(leadingSlice.length, extensionSwappedTarget.length - trailingSlice.length);
+ return { moduleFileToTry: packageName.replace("*", starReplacement) };
+ }
+ break;
+ }
+ }
+ else if (Array.isArray(exports)) {
+ return forEach(exports, e => tryGetModuleNameFromExports(options, targetFilePath, packageDirectory, packageName, e, conditions));
+ }
+ else if (typeof exports === "object" && exports !== null) { // eslint-disable-line no-null/no-null
+ if (allKeysStartWithDot(exports as MapLike)) {
+ // sub-mappings
+ // 3 cases:
+ // * directory mappings (legacyish, key ends with / (technically allows index/extension resolution under cjs mode))
+ // * pattern mappings (contains a *)
+ // * exact mappings (no *, does not end with /)
+ return forEach(getOwnKeys(exports as MapLike), k => {
+ const subPackageName = getNormalizedAbsolutePath(combinePaths(packageName, k), /*currentDirectory*/ undefined);
+ const mode = endsWith(k, "/") ? MatchingMode.Directory
+ : stringContains(k, "*") ? MatchingMode.Pattern
+ : MatchingMode.Exact;
+ return tryGetModuleNameFromExports(options, targetFilePath, packageDirectory, subPackageName, (exports as MapLike)[k], conditions, mode);
+ });
+ }
+ else {
+ // conditional mapping
+ for (const key of getOwnKeys(exports as MapLike)) {
+ if (key === "default" || conditions.indexOf(key) >= 0 || isApplicableVersionedTypesKey(conditions, key)) {
+ const subTarget = (exports as MapLike)[key];
+ const result = tryGetModuleNameFromExports(options, targetFilePath, packageDirectory, packageName, subTarget, conditions);
+ if (result) {
+ return result;
+ }
+ }
+ }
+ }
+ }
+ return undefined;
+ }
+
function tryGetModuleNameFromRootDirs(rootDirs: readonly string[], moduleFileName: string, sourceDirectory: string, getCanonicalFileName: (file: string) => string, ending: Ending, compilerOptions: CompilerOptions): string | undefined {
const normalizedTargetPath = getPathRelativeToRootDirs(moduleFileName, rootDirs, getCanonicalFileName);
if (normalizedTargetPath === undefined) {
@@ -549,7 +650,7 @@ namespace ts.moduleSpecifiers {
: removeFileExtension(relativePath);
}
- function tryGetModuleNameAsNodeModule({ path, isRedirect }: ModulePath, { getCanonicalFileName, sourceDirectory }: Info, host: ModuleSpecifierResolutionHost, options: CompilerOptions, packageNameOnly?: boolean): string | undefined {
+ function tryGetModuleNameAsNodeModule({ path, isRedirect }: ModulePath, { getCanonicalFileName, sourceDirectory }: Info, importingSourceFile: SourceFile , host: ModuleSpecifierResolutionHost, options: CompilerOptions, packageNameOnly?: boolean): string | undefined {
if (!host.fileExists || !host.readFile) {
return undefined;
}
@@ -567,7 +668,15 @@ namespace ts.moduleSpecifiers {
let moduleFileNameForExtensionless: string | undefined;
while (true) {
// If the module could be imported by a directory name, use that directory's name
- const { moduleFileToTry, packageRootPath } = tryDirectoryWithPackageJson(packageRootIndex);
+ const { moduleFileToTry, packageRootPath, blockedByExports, verbatimFromExports } = tryDirectoryWithPackageJson(packageRootIndex);
+ if (getEmitModuleResolutionKind(options) !== ModuleResolutionKind.Classic) {
+ if (blockedByExports) {
+ return undefined; // File is under this package.json, but is not publicly exported - there's no way to name it via `node_modules` resolution
+ }
+ if (verbatimFromExports) {
+ return moduleFileToTry;
+ }
+ }
if (packageRootPath) {
moduleSpecifier = packageRootPath;
isPackageRootPath = true;
@@ -600,14 +709,34 @@ namespace ts.moduleSpecifiers {
const nodeModulesDirectoryName = moduleSpecifier.substring(parts.topLevelPackageNameIndex + 1);
const packageName = getPackageNameFromTypesPackageName(nodeModulesDirectoryName);
// For classic resolution, only allow importing from node_modules/@types, not other node_modules
- return getEmitModuleResolutionKind(options) !== ModuleResolutionKind.NodeJs && packageName === nodeModulesDirectoryName ? undefined : packageName;
+ return getEmitModuleResolutionKind(options) === ModuleResolutionKind.Classic && packageName === nodeModulesDirectoryName ? undefined : packageName;
- function tryDirectoryWithPackageJson(packageRootIndex: number) {
+ function tryDirectoryWithPackageJson(packageRootIndex: number): { moduleFileToTry: string, packageRootPath?: string, blockedByExports?: true, verbatimFromExports?: true } {
const packageRootPath = path.substring(0, packageRootIndex);
const packageJsonPath = combinePaths(packageRootPath, "package.json");
let moduleFileToTry = path;
- if (host.fileExists(packageJsonPath)) {
- const packageJsonContent = JSON.parse(host.readFile!(packageJsonPath)!);
+ const cachedPackageJson = host.getPackageJsonInfoCache?.()?.getPackageJsonInfo(packageJsonPath);
+ if (typeof cachedPackageJson === "object" || cachedPackageJson === undefined && host.fileExists(packageJsonPath)) {
+ const packageJsonContent = cachedPackageJson?.packageJsonContent || JSON.parse(host.readFile!(packageJsonPath)!);
+ if (getEmitModuleResolutionKind(options) === ModuleResolutionKind.Node12 || getEmitModuleResolutionKind(options) === ModuleResolutionKind.NodeNext) {
+ // `conditions` *could* be made to go against `importingSourceFile.impliedNodeFormat` if something wanted to generate
+ // an ImportEqualsDeclaration in an ESM-implied file or an ImportCall in a CJS-implied file. But since this function is
+ // usually called to conjure an import out of thin air, we don't have an existing usage to call `getModeForUsageAtIndex`
+ // with, so for now we just stick with the mode of the file.
+ const conditions = ["node", importingSourceFile.impliedNodeFormat === ModuleKind.ESNext ? "import" : "require", "types"];
+ const fromExports = packageJsonContent.exports && typeof packageJsonContent.name === "string"
+ ? tryGetModuleNameFromExports(options, path, packageRootPath, getPackageNameFromTypesPackageName(packageJsonContent.name), packageJsonContent.exports, conditions)
+ : undefined;
+ if (fromExports) {
+ const withJsExtension = !hasTSFileExtension(fromExports.moduleFileToTry)
+ ? fromExports
+ : { moduleFileToTry: removeFileExtension(fromExports.moduleFileToTry) + tryGetJSExtensionForFile(fromExports.moduleFileToTry, options) };
+ return { ...withJsExtension, verbatimFromExports: true };
+ }
+ if (packageJsonContent.exports) {
+ return { moduleFileToTry: path, blockedByExports: true };
+ }
+ }
const versionPaths = packageJsonContent.typesVersions
? getPackageJsonTypesVersionsPaths(packageJsonContent.typesVersions)
: undefined;
@@ -622,7 +751,6 @@ namespace ts.moduleSpecifiers {
moduleFileToTry = combinePaths(packageRootPath, fromPaths);
}
}
-
// If the file is the main module, it can be imported by the package name
const mainFileRelative = packageJsonContent.typings || packageJsonContent.types || packageJsonContent.main;
if (isString(mainFileRelative)) {
@@ -652,7 +780,7 @@ namespace ts.moduleSpecifiers {
function tryGetAnyFileFromPath(host: ModuleSpecifierResolutionHost, path: string) {
if (!host.fileExists) return;
// We check all js, `node` and `json` extensions in addition to TS, since node module resolution would also choose those over the directory
- const extensions = getSupportedExtensions({ allowJs: true }, [{ extension: "node", isMixedContent: false }, { extension: "json", isMixedContent: false, scriptKind: ScriptKind.JSON }]);
+ const extensions = flatten(getSupportedExtensions({ allowJs: true }, [{ extension: "node", isMixedContent: false }, { extension: "json", isMixedContent: false, scriptKind: ScriptKind.JSON }]));
for (const e of extensions) {
const fullPath = path + e;
if (host.fileExists(fullPath)) {
@@ -661,80 +789,17 @@ namespace ts.moduleSpecifiers {
}
}
- interface NodeModulePathParts {
- readonly topLevelNodeModulesIndex: number;
- readonly topLevelPackageNameIndex: number;
- readonly packageRootIndex: number;
- readonly fileNameIndex: number;
- }
- function getNodeModulePathParts(fullPath: string): NodeModulePathParts | undefined {
- // If fullPath can't be valid module file within node_modules, returns undefined.
- // Example of expected pattern: /base/path/node_modules/[@scope/otherpackage/@otherscope/node_modules/]package/[subdirectory/]file.js
- // Returns indices: ^ ^ ^ ^
-
- let topLevelNodeModulesIndex = 0;
- let topLevelPackageNameIndex = 0;
- let packageRootIndex = 0;
- let fileNameIndex = 0;
-
- const enum States {
- BeforeNodeModules,
- NodeModules,
- Scope,
- PackageContent
- }
-
- let partStart = 0;
- let partEnd = 0;
- let state = States.BeforeNodeModules;
-
- while (partEnd >= 0) {
- partStart = partEnd;
- partEnd = fullPath.indexOf("/", partStart + 1);
- switch (state) {
- case States.BeforeNodeModules:
- if (fullPath.indexOf(nodeModulesPathPart, partStart) === partStart) {
- topLevelNodeModulesIndex = partStart;
- topLevelPackageNameIndex = partEnd;
- state = States.NodeModules;
- }
- break;
- case States.NodeModules:
- case States.Scope:
- if (state === States.NodeModules && fullPath.charAt(partStart + 1) === "@") {
- state = States.Scope;
- }
- else {
- packageRootIndex = partEnd;
- state = States.PackageContent;
- }
- break;
- case States.PackageContent:
- if (fullPath.indexOf(nodeModulesPathPart, partStart) === partStart) {
- state = States.NodeModules;
- }
- else {
- state = States.PackageContent;
- }
- break;
- }
- }
-
- fileNameIndex = partStart;
-
- return state > States.NodeModules ? { topLevelNodeModulesIndex, topLevelPackageNameIndex, packageRootIndex, fileNameIndex } : undefined;
- }
-
function getPathRelativeToRootDirs(path: string, rootDirs: readonly string[], getCanonicalFileName: GetCanonicalFileName): string | undefined {
return firstDefined(rootDirs, rootDir => {
- const relativePath = getRelativePathIfInDirectory(path, rootDir, getCanonicalFileName)!; // TODO: GH#18217
- return isPathRelativeToParent(relativePath) ? undefined : relativePath;
+ const relativePath = getRelativePathIfInDirectory(path, rootDir, getCanonicalFileName);
+ return relativePath !== undefined && isPathRelativeToParent(relativePath) ? undefined : relativePath;
});
}
function removeExtensionAndIndexPostFix(fileName: string, ending: Ending, options: CompilerOptions): string {
- if (fileExtensionIs(fileName, Extension.Json)) return fileName;
+ if (fileExtensionIsOneOf(fileName, [Extension.Json, Extension.Mjs, Extension.Cjs])) return fileName;
const noExtension = removeFileExtension(fileName);
+ if (fileExtensionIsOneOf(fileName, [Extension.Dmts, Extension.Mts, Extension.Dcts, Extension.Cts])) return noExtension + getJSExtensionForFile(fileName, options);
switch (ending) {
case Ending.Minimal:
return removeSuffix(noExtension, "/index");
@@ -763,6 +828,14 @@ namespace ts.moduleSpecifiers {
case Extension.Jsx:
case Extension.Json:
return ext;
+ case Extension.Dmts:
+ case Extension.Mts:
+ case Extension.Mjs:
+ return Extension.Mjs;
+ case Extension.Dcts:
+ case Extension.Cts:
+ case Extension.Cjs:
+ return Extension.Cjs;
default:
return undefined;
}
diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index dbe5394908d..cd7ed6285cb 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -180,7 +180,8 @@ namespace ts {
visitNode(cbNode, (node as TypePredicateNode).parameterName) ||
visitNode(cbNode, (node as TypePredicateNode).type);
case SyntaxKind.TypeQuery:
- return visitNode(cbNode, (node as TypeQueryNode).exprName);
+ return visitNode(cbNode, (node as TypeQueryNode).exprName) ||
+ visitNodes(cbNode, cbNodes, (node as TypeQueryNode).typeArguments);
case SyntaxKind.TypeLiteral:
return visitNodes(cbNode, cbNodes, (node as TypeLiteralNode).members);
case SyntaxKind.ArrayType:
@@ -212,7 +213,8 @@ namespace ts {
visitNode(cbNode, (node as MappedTypeNode).typeParameter) ||
visitNode(cbNode, (node as MappedTypeNode).nameType) ||
visitNode(cbNode, (node as MappedTypeNode).questionToken) ||
- visitNode(cbNode, (node as MappedTypeNode).type);
+ visitNode(cbNode, (node as MappedTypeNode).type) ||
+ visitNodes(cbNode, cbNodes, (node as MappedTypeNode).members);
case SyntaxKind.LiteralType:
return visitNode(cbNode, (node as LiteralTypeNode).literal);
case SyntaxKind.NamedTupleMember:
@@ -398,13 +400,18 @@ namespace ts {
return visitNodes(cbNode, cbNodes, node.decorators) ||
visitNodes(cbNode, cbNodes, node.modifiers) ||
visitNode(cbNode, (node as ImportDeclaration).importClause) ||
- visitNode(cbNode, (node as ImportDeclaration).moduleSpecifier);
+ visitNode(cbNode, (node as ImportDeclaration).moduleSpecifier) ||
+ visitNode(cbNode, (node as ImportDeclaration).assertClause);
case SyntaxKind.ImportClause:
return visitNode(cbNode, (node as ImportClause).name) ||
visitNode(cbNode, (node as ImportClause).namedBindings);
+ case SyntaxKind.AssertClause:
+ return visitNodes(cbNode, cbNodes, (node as AssertClause).elements);
+ case SyntaxKind.AssertEntry:
+ return visitNode(cbNode, (node as AssertEntry).name) ||
+ visitNode(cbNode, (node as AssertEntry).value);
case SyntaxKind.NamespaceExportDeclaration:
return visitNode(cbNode, (node as NamespaceExportDeclaration).name);
-
case SyntaxKind.NamespaceImport:
return visitNode(cbNode, (node as NamespaceImport).name);
case SyntaxKind.NamespaceExport:
@@ -416,7 +423,8 @@ namespace ts {
return visitNodes(cbNode, cbNodes, node.decorators) ||
visitNodes(cbNode, cbNodes, node.modifiers) ||
visitNode(cbNode, (node as ExportDeclaration).exportClause) ||
- visitNode(cbNode, (node as ExportDeclaration).moduleSpecifier);
+ visitNode(cbNode, (node as ExportDeclaration).moduleSpecifier) ||
+ visitNode(cbNode, (node as ExportDeclaration).assertClause);
case SyntaxKind.ImportSpecifier:
case SyntaxKind.ExportSpecifier:
return visitNode(cbNode, (node as ImportOrExportSpecifier).propertyName) ||
@@ -868,7 +876,7 @@ namespace ts {
initializeState("", content, languageVersion, /*syntaxCursor*/ undefined, ScriptKind.JS);
// Prime the scanner.
nextToken();
- const entityName = parseEntityName(/*allowReservedWords*/ true);
+ const entityName = parseEntityName(/*allowReservedWords*/ true, /*allowPrivateIdentifiers*/ false);
const isInvalid = token() === SyntaxKind.EndOfFileToken && !parseDiagnostics.length;
clearState();
return isInvalid ? entityName : undefined;
@@ -1633,7 +1641,7 @@ namespace ts {
parseErrorAtCurrentToken(blankDiagnostic);
}
else {
- parseErrorAtCurrentToken(nameDiagnostic, tokenToString(token()));
+ parseErrorAtCurrentToken(nameDiagnostic, scanner.getTokenValue());
}
}
@@ -1886,6 +1894,11 @@ namespace ts {
token() === SyntaxKind.NumericLiteral;
}
+ function isAssertionKey(): boolean {
+ return tokenIsIdentifierOrKeyword(token()) ||
+ token() === SyntaxKind.StringLiteral;
+ }
+
function parsePropertyNameWorker(allowComputedPropertyNames: boolean): PropertyName {
if (token() === SyntaxKind.StringLiteral || token() === SyntaxKind.NumericLiteral) {
const node = parseLiteralNode() as StringLiteral | NumericLiteral;
@@ -1963,7 +1976,6 @@ namespace ts {
case SyntaxKind.DefaultKeyword:
return nextTokenCanFollowDefaultKeyword();
case SyntaxKind.StaticKeyword:
- return nextTokenIsOnSameLineAndCanFollowModifier();
case SyntaxKind.GetKeyword:
case SyntaxKind.SetKeyword:
nextToken();
@@ -2051,6 +2063,8 @@ namespace ts {
return isLiteralPropertyName();
case ParsingContext.ObjectBindingElements:
return token() === SyntaxKind.OpenBracketToken || token() === SyntaxKind.DotDotDotToken || isLiteralPropertyName();
+ case ParsingContext.AssertEntries:
+ return isAssertionKey();
case ParsingContext.HeritageClauseElement:
// If we see `{ ... }` then only consume it as an expression if it is followed by `,` or `{`
// That way we won't consume the body of a class in its heritage clause.
@@ -2171,6 +2185,7 @@ namespace ts {
case ParsingContext.ObjectLiteralMembers:
case ParsingContext.ObjectBindingElements:
case ParsingContext.ImportOrExportSpecifiers:
+ case ParsingContext.AssertEntries:
return token() === SyntaxKind.CloseBraceToken;
case ParsingContext.SwitchClauseStatements:
return token() === SyntaxKind.CloseBraceToken || token() === SyntaxKind.CaseKeyword || token() === SyntaxKind.DefaultKeyword;
@@ -2597,7 +2612,10 @@ namespace ts {
case ParsingContext.ObjectLiteralMembers: return parseErrorAtCurrentToken(Diagnostics.Property_assignment_expected);
case ParsingContext.ArrayLiteralMembers: return parseErrorAtCurrentToken(Diagnostics.Expression_or_comma_expected);
case ParsingContext.JSDocParameters: return parseErrorAtCurrentToken(Diagnostics.Parameter_declaration_expected);
- case ParsingContext.Parameters: return parseErrorAtCurrentToken(Diagnostics.Parameter_declaration_expected);
+ case ParsingContext.Parameters:
+ return isKeyword(token())
+ ? parseErrorAtCurrentToken(Diagnostics._0_is_not_allowed_as_a_parameter_name, tokenToString(token()))
+ : parseErrorAtCurrentToken(Diagnostics.Parameter_declaration_expected);
case ParsingContext.TypeParameters: return parseErrorAtCurrentToken(Diagnostics.Type_parameter_declaration_expected);
case ParsingContext.TypeArguments: return parseErrorAtCurrentToken(Diagnostics.Type_argument_expected);
case ParsingContext.TupleElementTypes: return parseErrorAtCurrentToken(Diagnostics.Type_expected);
@@ -2702,7 +2720,7 @@ namespace ts {
return createMissingList();
}
- function parseEntityName(allowReservedWords: boolean, diagnosticMessage?: DiagnosticMessage): EntityName {
+ function parseEntityName(allowReservedWords: boolean, allowPrivateIdentifiers: boolean, diagnosticMessage?: DiagnosticMessage): EntityName {
const pos = getNodePos();
let entity: EntityName = allowReservedWords ? parseIdentifierName(diagnosticMessage) : parseIdentifier(diagnosticMessage);
let dotPos = getNodePos();
@@ -2716,7 +2734,7 @@ namespace ts {
entity = finishNode(
factory.createQualifiedName(
entity,
- parseRightSideOfDot(allowReservedWords, /* allowPrivateIdentifiers */ false) as Identifier
+ parseRightSideOfDot(allowReservedWords, allowPrivateIdentifiers) as Identifier
),
pos
);
@@ -2901,7 +2919,7 @@ namespace ts {
// TYPES
function parseEntityNameOfTypeReference() {
- return parseEntityName(/*allowReservedWords*/ true, Diagnostics.Type_expected);
+ return parseEntityName(/*allowReservedWords*/ true, /*allowPrivateIdentifiers*/ false, Diagnostics.Type_expected);
}
function parseTypeArgumentsOfTypeReference() {
@@ -3061,7 +3079,9 @@ namespace ts {
function parseTypeQuery(): TypeQueryNode {
const pos = getNodePos();
parseExpected(SyntaxKind.TypeOfKeyword);
- return finishNode(factory.createTypeQueryNode(parseEntityName(/*allowReservedWords*/ true)), pos);
+ const entityName = parseEntityName(/*allowReservedWords*/ true, /*allowPrivateIdentifiers*/ true);
+ const typeArguments = tryParseTypeArguments();
+ return finishNode(factory.createTypeQueryNode(entityName, typeArguments), pos);
}
function parseTypeParameter(): TypeParameterDeclaration {
@@ -3521,8 +3541,9 @@ namespace ts {
}
const type = parseTypeAnnotation();
parseSemicolon();
+ const members = parseList(ParsingContext.TypeMembers, parseTypeMember);
parseExpected(SyntaxKind.CloseBraceToken);
- return finishNode(factory.createMappedTypeNode(readonlyToken, typeParameter, nameType, questionToken, type), pos);
+ return finishNode(factory.createMappedTypeNode(readonlyToken, typeParameter, nameType, questionToken, type, members), pos);
}
function parseTupleElementType() {
@@ -4415,7 +4436,7 @@ namespace ts {
return true;
}
}
- else if (third === SyntaxKind.CommaToken) {
+ else if (third === SyntaxKind.CommaToken || third === SyntaxKind.EqualsToken) {
return true;
}
return false;
@@ -4521,9 +4542,16 @@ namespace ts {
// - "(x,y)" is a comma expression parsed as a signature with two parameters.
// - "a ? (b): c" will have "(b):" parsed as a signature with a return type annotation.
// - "a ? (b): function() {}" will too, since function() is a valid JSDoc function type.
+ // - "a ? (b): (function() {})" as well, but inside of a parenthesized type with an arbitrary amount of nesting.
//
// So we need just a bit of lookahead to ensure that it can only be a signature.
- const hasJSDocFunctionType = type && isJSDocFunctionType(type);
+
+ let unwrappedType = type;
+ while (unwrappedType?.kind === SyntaxKind.ParenthesizedType) {
+ unwrappedType = (unwrappedType as ParenthesizedTypeNode).type; // Skip parens if need be
+ }
+
+ const hasJSDocFunctionType = unwrappedType && isJSDocFunctionType(unwrappedType);
if (!allowAmbiguity && token() !== SyntaxKind.EqualsGreaterThanToken && (hasJSDocFunctionType || token() !== SyntaxKind.OpenBraceToken)) {
// Returning undefined here will cause our caller to rewind to where we started from.
return undefined;
@@ -5042,12 +5070,12 @@ namespace ts {
&& !tagNamesAreEquivalent(lastChild.openingElement.tagName, lastChild.closingElement.tagName)
&& tagNamesAreEquivalent(opening.tagName, lastChild.closingElement.tagName)) {
// when an unclosed JsxOpeningElement incorrectly parses its parent's JsxClosingElement,
- // restructure ((...
)) --> ((...)
)
+ // restructure ((......
)) --> ((......>)
)
// (no need to error; the parent will error)
- const end = lastChild.openingElement.end; // newly-created children and closing are both zero-width end/end
+ const end = lastChild.children.end;
const newLast = finishNode(factory.createJsxElement(
lastChild.openingElement,
- createNodeArray([], end, end),
+ lastChild.children,
finishNode(factory.createJsxClosingElement(finishNode(factory.createIdentifier(""), end, end)), end, end)),
lastChild.openingElement.pos,
end);
@@ -5403,12 +5431,6 @@ namespace ts {
continue;
}
- if (!questionDotToken && token() === SyntaxKind.ExclamationToken && !scanner.hasPrecedingLineBreak()) {
- nextToken();
- expression = finishNode(factory.createNonNullExpression(expression), pos);
- continue;
- }
-
// when in the [Decorator] context, we do not parse ElementAccess as it could be part of a ComputedPropertyName
if ((questionDotToken || !inDecoratorContext()) && parseOptional(SyntaxKind.OpenBracketToken)) {
expression = parseElementAccessExpressionRest(pos, expression, questionDotToken);
@@ -5416,10 +5438,26 @@ namespace ts {
}
if (isTemplateStartOfTaggedTemplate()) {
- expression = parseTaggedTemplateRest(pos, expression, questionDotToken, /*typeArguments*/ undefined);
+ // Absorb type arguments into TemplateExpression when preceding expression is ExpressionWithTypeArguments
+ expression = !questionDotToken && expression.kind === SyntaxKind.ExpressionWithTypeArguments ?
+ parseTaggedTemplateRest(pos, (expression as ExpressionWithTypeArguments).expression, questionDotToken, (expression as ExpressionWithTypeArguments).typeArguments) :
+ parseTaggedTemplateRest(pos, expression, questionDotToken, /*typeArguments*/ undefined);
continue;
}
+ if (!questionDotToken) {
+ if (token() === SyntaxKind.ExclamationToken && !scanner.hasPrecedingLineBreak()) {
+ nextToken();
+ expression = finishNode(factory.createNonNullExpression(expression), pos);
+ continue;
+ }
+ const typeArguments = tryParse(parseTypeArgumentsInExpression);
+ if (typeArguments) {
+ expression = finishNode(factory.createExpressionWithTypeArguments(expression, typeArguments), pos);
+ continue;
+ }
+ }
+
return expression as MemberExpression;
}
}
@@ -5446,39 +5484,30 @@ namespace ts {
function parseCallExpressionRest(pos: number, expression: LeftHandSideExpression): LeftHandSideExpression {
while (true) {
expression = parseMemberExpressionRest(pos, expression, /*allowOptionalChain*/ true);
+ let typeArguments: NodeArray | undefined;
const questionDotToken = parseOptionalToken(SyntaxKind.QuestionDotToken);
- // handle 'foo<()'
- // parse template arguments only in TypeScript files (not in JavaScript files).
- if ((contextFlags & NodeFlags.JavaScriptFile) === 0 && (token() === SyntaxKind.LessThanToken || token() === SyntaxKind.LessThanLessThanToken)) {
- // See if this is the start of a generic invocation. If so, consume it and
- // keep checking for postfix expressions. Otherwise, it's just a '<' that's
- // part of an arithmetic expression. Break out so we consume it higher in the
- // stack.
- const typeArguments = tryParse(parseTypeArgumentsInExpression);
- if (typeArguments) {
- if (isTemplateStartOfTaggedTemplate()) {
- expression = parseTaggedTemplateRest(pos, expression, questionDotToken, typeArguments);
- continue;
- }
-
- const argumentList = parseArgumentList();
- const callExpr = questionDotToken || tryReparseOptionalChain(expression) ?
- factory.createCallChain(expression, questionDotToken, typeArguments, argumentList) :
- factory.createCallExpression(expression, typeArguments, argumentList);
- expression = finishNode(callExpr, pos);
+ if (questionDotToken) {
+ typeArguments = tryParse(parseTypeArgumentsInExpression);
+ if (isTemplateStartOfTaggedTemplate()) {
+ expression = parseTaggedTemplateRest(pos, expression, questionDotToken, typeArguments);
continue;
}
}
- else if (token() === SyntaxKind.OpenParenToken) {
+ if (typeArguments || token() === SyntaxKind.OpenParenToken) {
+ // Absorb type arguments into CallExpression when preceding expression is ExpressionWithTypeArguments
+ if (!questionDotToken && expression.kind === SyntaxKind.ExpressionWithTypeArguments) {
+ typeArguments = (expression as ExpressionWithTypeArguments).typeArguments;
+ expression = (expression as ExpressionWithTypeArguments).expression;
+ }
const argumentList = parseArgumentList();
const callExpr = questionDotToken || tryReparseOptionalChain(expression) ?
- factory.createCallChain(expression, questionDotToken, /*typeArguments*/ undefined, argumentList) :
- factory.createCallExpression(expression, /*typeArguments*/ undefined, argumentList);
+ factory.createCallChain(expression, questionDotToken, typeArguments, argumentList) :
+ factory.createCallExpression(expression, typeArguments, argumentList);
expression = finishNode(callExpr, pos);
continue;
}
if (questionDotToken) {
- // We failed to parse anything, so report a missing identifier here.
+ // We parsed `?.` but then failed to parse anything, so report a missing identifier here.
const name = createMissingNode(SyntaxKind.Identifier, /*reportAtCurrentPosition*/ false, Diagnostics.Identifier_expected);
expression = finishNode(factory.createPropertyAccessChain(expression, questionDotToken, name), pos);
}
@@ -5511,22 +5540,26 @@ namespace ts {
return undefined;
}
- // If we have a '<', then only parse this as a argument list if the type arguments
- // are complete and we have an open paren. if we don't, rewind and return nothing.
- return typeArguments && canFollowTypeArgumentsInExpression()
- ? typeArguments
- : undefined;
+ // We successfully parsed a type argument list. The next token determines whether we want to
+ // treat it as such. If the type argument list is followed by `(` or a template literal, as in
+ // `f(42)`, we favor the type argument interpretation even though JavaScript would view
+ // it as a relational expression.
+ return typeArguments && canFollowTypeArgumentsInExpression() ? typeArguments : undefined;
}
function canFollowTypeArgumentsInExpression(): boolean {
switch (token()) {
+ // These tokens can follow a type argument list in a call expression.
case SyntaxKind.OpenParenToken: // foo(
case SyntaxKind.NoSubstitutionTemplateLiteral: // foo `...`
case SyntaxKind.TemplateHead: // foo `...${100}...`
- // these are the only tokens can legally follow a type argument
- // list. So we definitely want to treat them as type arg lists.
+ // These tokens can't follow in a call expression, nor can they start an
+ // expression. So, consider the type argument list part of an instantiation
+ // expression.
// falls through
+ case SyntaxKind.CommaToken: // foo,
case SyntaxKind.DotToken: // foo.
+ case SyntaxKind.QuestionDotToken: // foo?.
case SyntaxKind.CloseParenToken: // foo)
case SyntaxKind.CloseBracketToken: // foo]
case SyntaxKind.ColonToken: // foo:
@@ -5544,21 +5577,10 @@ namespace ts {
case SyntaxKind.BarToken: // foo |
case SyntaxKind.CloseBraceToken: // foo }
case SyntaxKind.EndOfFileToken: // foo
- // these cases can't legally follow a type arg list. However, they're not legal
- // expressions either. The user is probably in the middle of a generic type. So
- // treat it as such.
return true;
-
- case SyntaxKind.CommaToken: // foo,
- case SyntaxKind.OpenBraceToken: // foo {
- // We don't want to treat these as type arguments. Otherwise we'll parse this
- // as an invocation expression. Instead, we want to parse out the expression
- // in isolation from the type arguments.
- // falls through
- default:
- // Anything else treat as an expression.
- return false;
}
+ // Treat anything else as an expression.
+ return false;
}
function parsePrimaryExpression(): PrimaryExpression {
@@ -5603,6 +5625,8 @@ namespace ts {
break;
case SyntaxKind.TemplateHead:
return parseTemplateExpression(/* isTaggedTemplate */ false);
+ case SyntaxKind.PrivateIdentifier:
+ return parsePrivateIdentifier();
}
return parseIdentifier(Diagnostics.Expression_expected);
@@ -5763,30 +5787,16 @@ namespace ts {
const name = parseIdentifierName();
return finishNode(factory.createMetaProperty(SyntaxKind.NewKeyword, name), pos);
}
-
const expressionPos = getNodePos();
- let expression: MemberExpression = parsePrimaryExpression();
- let typeArguments;
- while (true) {
- expression = parseMemberExpressionRest(expressionPos, expression, /*allowOptionalChain*/ false);
- typeArguments = tryParse(parseTypeArgumentsInExpression);
- if (isTemplateStartOfTaggedTemplate()) {
- Debug.assert(!!typeArguments,
- "Expected a type argument list; all plain tagged template starts should be consumed in 'parseMemberExpressionRest'");
- expression = parseTaggedTemplateRest(expressionPos, expression, /*optionalChain*/ undefined, typeArguments);
- typeArguments = undefined;
- }
- break;
+ let expression: LeftHandSideExpression = parseMemberExpressionRest(expressionPos, parsePrimaryExpression(), /*allowOptionalChain*/ false);
+ let typeArguments: NodeArray | undefined;
+ // Absorb type arguments into NewExpression when preceding expression is ExpressionWithTypeArguments
+ if (expression.kind === SyntaxKind.ExpressionWithTypeArguments) {
+ typeArguments = (expression as ExpressionWithTypeArguments).typeArguments;
+ expression = (expression as ExpressionWithTypeArguments).expression;
}
-
- let argumentsArray: NodeArray | undefined;
- if (token() === SyntaxKind.OpenParenToken) {
- argumentsArray = parseArgumentList();
- }
- else if (typeArguments) {
- parseErrorAt(pos, scanner.getStartPos(), Diagnostics.A_new_expression_with_type_arguments_must_always_be_followed_by_a_parenthesized_argument_list);
- }
- return finishNode(factory.createNewExpression(expression, typeArguments, argumentsArray), pos);
+ const argumentList = token() === SyntaxKind.OpenParenToken ? parseArgumentList() : undefined;
+ return finishNode(factory.createNewExpression(expression, typeArguments, argumentList), pos);
}
// STATEMENTS
@@ -5978,11 +5988,12 @@ namespace ts {
function parseCaseClause(): CaseClause {
const pos = getNodePos();
+ const hasJSDoc = hasPrecedingJSDocComment();
parseExpected(SyntaxKind.CaseKeyword);
const expression = allowInAnd(parseExpression);
parseExpected(SyntaxKind.ColonToken);
const statements = parseList(ParsingContext.SwitchClauseStatements, parseStatement);
- return finishNode(factory.createCaseClause(expression, statements), pos);
+ return withJSDoc(finishNode(factory.createCaseClause(expression, statements), pos), hasJSDoc);
}
function parseDefaultClause(): DefaultClause {
@@ -6053,7 +6064,7 @@ namespace ts {
// one out no matter what.
let finallyBlock: Block | undefined;
if (!catchClause || token() === SyntaxKind.FinallyKeyword) {
- parseExpected(SyntaxKind.FinallyKeyword);
+ parseExpected(SyntaxKind.FinallyKeyword, Diagnostics.catch_or_finally_expected);
finallyBlock = parseBlock(/*ignoreMissingOpenBrace*/ false);
}
@@ -6854,7 +6865,7 @@ namespace ts {
return list && createNodeArray(list, pos);
}
- function tryParseModifier(permitInvalidConstAsModifier?: boolean, stopOnStartOfClassStaticBlock?: boolean): Modifier | undefined {
+ function tryParseModifier(permitInvalidConstAsModifier?: boolean, stopOnStartOfClassStaticBlock?: boolean, hasSeenStaticModifier?: boolean): Modifier | undefined {
const pos = getNodePos();
const kind = token();
@@ -6868,6 +6879,9 @@ namespace ts {
else if (stopOnStartOfClassStaticBlock && token() === SyntaxKind.StaticKeyword && lookAhead(nextTokenIsOpenBrace)) {
return undefined;
}
+ else if (hasSeenStaticModifier && token() === SyntaxKind.StaticKeyword) {
+ return undefined;
+ }
else {
if (!parseAnyContextualModifier()) {
return undefined;
@@ -6886,8 +6900,9 @@ namespace ts {
*/
function parseModifiers(permitInvalidConstAsModifier?: boolean, stopOnStartOfClassStaticBlock?: boolean): NodeArray | undefined {
const pos = getNodePos();
- let list, modifier;
- while (modifier = tryParseModifier(permitInvalidConstAsModifier, stopOnStartOfClassStaticBlock)) {
+ let list, modifier, hasSeenStatic = false;
+ while (modifier = tryParseModifier(permitInvalidConstAsModifier, stopOnStartOfClassStaticBlock, hasSeenStatic)) {
+ if (modifier.kind === SyntaxKind.StaticKeyword) hasSeenStatic = true;
list = append(list, modifier);
}
return list && createNodeArray(list, pos);
@@ -7039,6 +7054,9 @@ namespace ts {
function parseExpressionWithTypeArguments(): ExpressionWithTypeArguments {
const pos = getNodePos();
const expression = parseLeftHandSideExpressionOrHigher();
+ if (expression.kind === SyntaxKind.ExpressionWithTypeArguments) {
+ return expression as ExpressionWithTypeArguments;
+ }
const typeArguments = tryParseTypeArguments();
return finishNode(factory.createExpressionWithTypeArguments(expression, typeArguments), pos);
}
@@ -7234,13 +7252,50 @@ namespace ts {
importClause = parseImportClause(identifier, afterImportPos, isTypeOnly);
parseExpected(SyntaxKind.FromKeyword);
}
-
const moduleSpecifier = parseModuleSpecifier();
+
+ let assertClause: AssertClause | undefined;
+ if (token() === SyntaxKind.AssertKeyword && !scanner.hasPrecedingLineBreak()) {
+ assertClause = parseAssertClause();
+ }
+
parseSemicolon();
- const node = factory.createImportDeclaration(decorators, modifiers, importClause, moduleSpecifier);
+ const node = factory.createImportDeclaration(decorators, modifiers, importClause, moduleSpecifier, assertClause);
return withJSDoc(finishNode(node, pos), hasJSDoc);
}
+ function parseAssertEntry() {
+ const pos = getNodePos();
+ const name = tokenIsIdentifierOrKeyword(token()) ? parseIdentifierName() : parseLiteralLikeNode(SyntaxKind.StringLiteral) as StringLiteral;
+ parseExpected(SyntaxKind.ColonToken);
+ const value = parseAssignmentExpressionOrHigher();
+ return finishNode(factory.createAssertEntry(name, value), pos);
+ }
+
+ function parseAssertClause() {
+ const pos = getNodePos();
+ parseExpected(SyntaxKind.AssertKeyword);
+ const openBracePosition = scanner.getTokenPos();
+ if (parseExpected(SyntaxKind.OpenBraceToken)) {
+ const multiLine = scanner.hasPrecedingLineBreak();
+ const elements = parseDelimitedList(ParsingContext.AssertEntries, parseAssertEntry, /*considerSemicolonAsDelimiter*/ true);
+ if (!parseExpected(SyntaxKind.CloseBraceToken)) {
+ const lastError = lastOrUndefined(parseDiagnostics);
+ if (lastError && lastError.code === Diagnostics._0_expected.code) {
+ addRelatedInfo(
+ lastError,
+ createDetachedDiagnostic(fileName, openBracePosition, 1, Diagnostics.The_parser_expected_to_find_a_to_match_the_token_here)
+ );
+ }
+ }
+ return finishNode(factory.createAssertClause(elements, multiLine), pos);
+ }
+ else {
+ const elements = createNodeArray([], getNodePos(), /*end*/ undefined, /*hasTrailingComma*/ false);
+ return finishNode(factory.createAssertClause(elements, /*multiLine*/ false), pos);
+ }
+ }
+
function tokenAfterImportDefinitelyProducesImportDeclaration() {
return token() === SyntaxKind.AsteriskToken || token() === SyntaxKind.OpenBraceToken;
}
@@ -7282,7 +7337,7 @@ namespace ts {
function parseModuleReference() {
return isExternalModuleReference()
? parseExternalModuleReference()
- : parseEntityName(/*allowReservedWords*/ false);
+ : parseEntityName(/*allowReservedWords*/ false, /*allowPrivateIdentifiers*/ false);
}
function parseExternalModuleReference() {
@@ -7338,7 +7393,8 @@ namespace ts {
}
function parseExportSpecifier() {
- return parseImportOrExportSpecifier(SyntaxKind.ExportSpecifier) as ExportSpecifier;
+ const hasJSDoc = hasPrecedingJSDocComment();
+ return withJSDoc(parseImportOrExportSpecifier(SyntaxKind.ExportSpecifier) as ExportSpecifier, hasJSDoc);
}
function parseImportSpecifier() {
@@ -7356,27 +7412,76 @@ namespace ts {
let checkIdentifierIsKeyword = isKeyword(token()) && !isIdentifier();
let checkIdentifierStart = scanner.getTokenPos();
let checkIdentifierEnd = scanner.getTextPos();
- const identifierName = parseIdentifierName();
+ let isTypeOnly = false;
let propertyName: Identifier | undefined;
- let name: Identifier;
- if (token() === SyntaxKind.AsKeyword) {
- propertyName = identifierName;
- parseExpected(SyntaxKind.AsKeyword);
- checkIdentifierIsKeyword = isKeyword(token()) && !isIdentifier();
- checkIdentifierStart = scanner.getTokenPos();
- checkIdentifierEnd = scanner.getTextPos();
- name = parseIdentifierName();
+ let canParseAsKeyword = true;
+ let name = parseIdentifierName();
+ if (name.escapedText === "type") {
+ // If the first token of an import specifier is 'type', there are a lot of possibilities,
+ // especially if we see 'as' afterwards:
+ //
+ // import { type } from "mod"; - isTypeOnly: false, name: type
+ // import { type as } from "mod"; - isTypeOnly: true, name: as
+ // import { type as as } from "mod"; - isTypeOnly: false, name: as, propertyName: type
+ // import { type as as as } from "mod"; - isTypeOnly: true, name: as, propertyName: as
+ if (token() === SyntaxKind.AsKeyword) {
+ // { type as ...? }
+ const firstAs = parseIdentifierName();
+ if (token() === SyntaxKind.AsKeyword) {
+ // { type as as ...? }
+ const secondAs = parseIdentifierName();
+ if (tokenIsIdentifierOrKeyword(token())) {
+ // { type as as something }
+ isTypeOnly = true;
+ propertyName = firstAs;
+ name = parseNameWithKeywordCheck();
+ canParseAsKeyword = false;
+ }
+ else {
+ // { type as as }
+ propertyName = name;
+ name = secondAs;
+ canParseAsKeyword = false;
+ }
+ }
+ else if (tokenIsIdentifierOrKeyword(token())) {
+ // { type as something }
+ propertyName = name;
+ canParseAsKeyword = false;
+ name = parseNameWithKeywordCheck();
+ }
+ else {
+ // { type as }
+ isTypeOnly = true;
+ name = firstAs;
+ }
+ }
+ else if (tokenIsIdentifierOrKeyword(token())) {
+ // { type something ...? }
+ isTypeOnly = true;
+ name = parseNameWithKeywordCheck();
+ }
}
- else {
- name = identifierName;
+
+ if (canParseAsKeyword && token() === SyntaxKind.AsKeyword) {
+ propertyName = name;
+ parseExpected(SyntaxKind.AsKeyword);
+ name = parseNameWithKeywordCheck();
}
if (kind === SyntaxKind.ImportSpecifier && checkIdentifierIsKeyword) {
parseErrorAt(checkIdentifierStart, checkIdentifierEnd, Diagnostics.Identifier_expected);
}
const node = kind === SyntaxKind.ImportSpecifier
- ? factory.createImportSpecifier(propertyName, name)
- : factory.createExportSpecifier(propertyName, name);
+ ? factory.createImportSpecifier(isTypeOnly, propertyName, name)
+ : factory.createExportSpecifier(isTypeOnly, propertyName, name);
return finishNode(node, pos);
+
+ function parseNameWithKeywordCheck() {
+ checkIdentifierIsKeyword = isKeyword(token()) && !isIdentifier();
+ checkIdentifierStart = scanner.getTokenPos();
+ checkIdentifierEnd = scanner.getTextPos();
+ return parseIdentifierName();
+ }
}
function parseNamespaceExport(pos: number): NamespaceExport {
@@ -7388,6 +7493,7 @@ namespace ts {
setAwaitContext(/*value*/ true);
let exportClause: NamedExportBindings | undefined;
let moduleSpecifier: Expression | undefined;
+ let assertClause: AssertClause | undefined;
const isTypeOnly = parseOptional(SyntaxKind.TypeKeyword);
const namespaceExportPos = getNodePos();
if (parseOptional(SyntaxKind.AsteriskToken)) {
@@ -7407,9 +7513,12 @@ namespace ts {
moduleSpecifier = parseModuleSpecifier();
}
}
+ if (moduleSpecifier && token() === SyntaxKind.AssertKeyword && !scanner.hasPrecedingLineBreak()) {
+ assertClause = parseAssertClause();
+ }
parseSemicolon();
setAwaitContext(savedAwaitContext);
- const node = factory.createExportDeclaration(decorators, modifiers, isTypeOnly, exportClause, moduleSpecifier);
+ const node = factory.createExportDeclaration(decorators, modifiers, isTypeOnly, exportClause, moduleSpecifier, assertClause);
return withJSDoc(finishNode(node, pos), hasJSDoc);
}
@@ -7489,7 +7598,8 @@ namespace ts {
TypeArguments, // Type arguments in type argument list
TupleElementTypes, // Element types in tuple element type list
HeritageClauses, // Heritage clauses for a class or interface declaration.
- ImportOrExportSpecifiers, // Named import clause's import specifier list
+ ImportOrExportSpecifiers, // Named import clause's import specifier list,
+ AssertEntries, // Import entries list.
Count // Number of parsing contexts
}
@@ -7535,7 +7645,7 @@ namespace ts {
const pos = getNodePos();
const hasBrace = parseOptional(SyntaxKind.OpenBraceToken);
const p2 = getNodePos();
- let entityName: EntityName | JSDocMemberName = parseEntityName(/* allowReservedWords*/ false);
+ let entityName: EntityName | JSDocMemberName = parseEntityName(/* allowReservedWords*/ false, /*allowPrivateIdentifiers*/ false);
while (token() === SyntaxKind.PrivateIdentifier) {
reScanHashToken(); // rescan #id as # id
nextTokenJSDoc(); // then skip the #
@@ -7998,7 +8108,7 @@ namespace ts {
// parseEntityName logs an error for non-identifier, so create a MissingNode ourselves to avoid the error
const p2 = getNodePos();
let name: EntityName | JSDocMemberName | undefined = tokenIsIdentifierOrKeyword(token())
- ? parseEntityName(/*allowReservedWords*/ true)
+ ? parseEntityName(/*allowReservedWords*/ true, /*allowPrivateIdentifiers*/ false)
: undefined;
if (name) {
while (token() === SyntaxKind.PrivateIdentifier) {
@@ -8024,12 +8134,14 @@ namespace ts {
&& nextTokenJSDoc() === SyntaxKind.AtToken
&& tokenIsIdentifierOrKeyword(nextTokenJSDoc())) {
const kind = scanner.getTokenValue();
- if(kind === "link" || kind === "linkcode" || kind === "linkplain") {
- return kind;
- }
+ if (isJSDocLinkTag(kind)) return kind;
}
}
+ function isJSDocLinkTag(kind: string) {
+ return kind === "link" || kind === "linkcode" || kind === "linkplain";
+ }
+
function parseUnknownTag(start: number, tagName: Identifier, indent: number, indentText: string) {
return finishNode(factory.createJSDocUnknownTag(tagName, parseTrailingTagComments(start, getNodePos(), indent, indentText)), start);
}
@@ -8151,8 +8263,9 @@ namespace ts {
}
function parseSeeTag(start: number, tagName: Identifier, indent?: number, indentText?: string): JSDocSeeTag {
- const isLink = lookAhead(() => nextTokenJSDoc() === SyntaxKind.AtToken && tokenIsIdentifierOrKeyword(nextTokenJSDoc()) && scanner.getTokenValue() === "link");
- const nameExpression = isLink ? undefined : parseJSDocNameReference();
+ const isMarkdownOrJSDocLink = token() === SyntaxKind.OpenBracketToken
+ || lookAhead(() => nextTokenJSDoc() === SyntaxKind.AtToken && tokenIsIdentifierOrKeyword(nextTokenJSDoc()) && isJSDocLinkTag(scanner.getTokenValue()));
+ const nameExpression = isMarkdownOrJSDocLink ? undefined : parseJSDocNameReference();
const comments = indent !== undefined && indentText !== undefined ? parseTrailingTagComments(start, getNodePos(), indent, indentText) : undefined;
return finishNode(factory.createJSDocSeeTag(tagName, nameExpression, comments), start);
}
@@ -8624,6 +8737,7 @@ namespace ts {
newText,
aggressiveChecks
);
+ result.impliedNodeFormat = sourceFile.impliedNodeFormat;
return result;
}
@@ -9164,7 +9278,7 @@ namespace ts {
/** @internal */
export function isDeclarationFileName(fileName: string): boolean {
- return fileExtensionIs(fileName, Extension.Dts);
+ return fileExtensionIsOneOf(fileName, [Extension.Dts, Extension.Dmts, Extension.Dcts]);
}
/*@internal*/
@@ -9180,6 +9294,20 @@ namespace ts {
moduleName?: string;
}
+ function parseResolutionMode(mode: string | undefined, pos: number, end: number, reportDiagnostic: PragmaDiagnosticReporter): ModuleKind.ESNext | ModuleKind.CommonJS | undefined {
+ if (!mode) {
+ return undefined;
+ }
+ if (mode === "import") {
+ return ModuleKind.ESNext;
+ }
+ if (mode === "require") {
+ return ModuleKind.CommonJS;
+ }
+ reportDiagnostic(pos, end - pos, Diagnostics.resolution_mode_should_be_either_require_or_import);
+ return undefined;
+ }
+
/*@internal*/
export function processCommentPragmas(context: PragmaContext, sourceText: string): void {
const pragmas: PragmaPseudoMapEntry[] = [];
@@ -9225,12 +9353,13 @@ namespace ts {
const typeReferenceDirectives = context.typeReferenceDirectives;
const libReferenceDirectives = context.libReferenceDirectives;
forEach(toArray(entryOrList) as PragmaPseudoMap["reference"][], arg => {
- const { types, lib, path } = arg.arguments;
+ const { types, lib, path, ["resolution-mode"]: res } = arg.arguments;
if (arg.arguments["no-default-lib"]) {
context.hasNoDefaultLib = true;
}
else if (types) {
- typeReferenceDirectives.push({ pos: types.pos, end: types.end, fileName: types.value });
+ const parsed = parseResolutionMode(res, types.pos, types.end, reportDiagnostic);
+ typeReferenceDirectives.push({ pos: types.pos, end: types.end, fileName: types.value, ...(parsed ? { resolutionMode: parsed } : {}) });
}
else if (lib) {
libReferenceDirectives.push({ pos: lib.pos, end: lib.end, fileName: lib.value });
diff --git a/src/compiler/path.ts b/src/compiler/path.ts
index d09108d34c7..ebc837feb33 100644
--- a/src/compiler/path.ts
+++ b/src/compiler/path.ts
@@ -585,18 +585,6 @@ namespace ts {
return getCanonicalFileName(nonCanonicalizedPath) as Path;
}
- export function normalizePathAndParts(path: string): { path: string, parts: string[] } {
- path = normalizeSlashes(path);
- const [root, ...parts] = reducePathComponents(getPathComponents(path));
- if (parts.length) {
- const joinedParts = root + parts.join(directorySeparator);
- return { path: hasTrailingDirectorySeparator(path) ? ensureTrailingDirectorySeparator(joinedParts) : joinedParts, parts };
- }
- else {
- return { path: root, parts };
- }
- }
-
//// Path Mutation
/**
diff --git a/src/compiler/program.ts b/src/compiler/program.ts
index 4ee8195cbd5..004986b55db 100644
--- a/src/compiler/program.ts
+++ b/src/compiler/program.ts
@@ -510,7 +510,7 @@ namespace ts {
}
/* @internal */
- export function loadWithLocalCache(names: string[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, loader: (name: string, containingFile: string, redirectedReference: ResolvedProjectReference | undefined) => T): T[] {
+ export function loadWithTypeDirectiveCache(names: string[] | readonly FileReference[], containingFile: string, redirectedReference: ResolvedProjectReference | undefined, containingFileMode: SourceFile["impliedNodeFormat"], loader: (name: string, containingFile: string, redirectedReference: ResolvedProjectReference | undefined, resolutionMode: SourceFile["impliedNodeFormat"]) => T): T[] {
if (names.length === 0) {
return [];
}
@@ -518,11 +518,72 @@ namespace ts {
const cache = new Map();
for (const name of names) {
let result: T;
- if (cache.has(name)) {
- result = cache.get(name)!;
+ const mode = getModeForFileReference(name, containingFileMode);
+ // We lower-case all type references because npm automatically lowercases all packages. See GH#9824.
+ const strName = isString(name) ? name : name.fileName.toLowerCase();
+ const cacheKey = mode !== undefined ? `${mode}|${strName}` : strName;
+ if (cache.has(cacheKey)) {
+ result = cache.get(cacheKey)!;
}
else {
- cache.set(name, result = loader(name, containingFile, redirectedReference));
+ cache.set(cacheKey, result = loader(strName, containingFile, redirectedReference, mode));
+ }
+ resolutions.push(result);
+ }
+ return resolutions;
+ }
+
+ /* @internal */
+ interface SourceFileImportsList {
+ imports: SourceFile["imports"];
+ moduleAugmentations: SourceFile["moduleAugmentations"];
+ impliedNodeFormat?: SourceFile["impliedNodeFormat"];
+ };
+
+ /* @internal */
+ export function getModeForFileReference(ref: FileReference | string, containingFileMode: SourceFile["impliedNodeFormat"]) {
+ return (isString(ref) ? containingFileMode : ref.resolutionMode) || containingFileMode;
+ }
+
+ /* @internal */
+ export function getModeForResolutionAtIndex(file: SourceFileImportsList, index: number) {
+ if (file.impliedNodeFormat === undefined) return undefined;
+ // we ensure all elements of file.imports and file.moduleAugmentations have the relevant parent pointers set during program setup,
+ // so it's safe to use them even pre-bind
+ return getModeForUsageLocation(file, getModuleNameStringLiteralAt(file, index));
+ }
+
+ /* @internal */
+ export function getModeForUsageLocation(file: {impliedNodeFormat?: SourceFile["impliedNodeFormat"]}, usage: StringLiteralLike) {
+ if (file.impliedNodeFormat === undefined) return undefined;
+ if (file.impliedNodeFormat !== ModuleKind.ESNext) {
+ // in cjs files, import call expressions are esm format, otherwise everything is cjs
+ return isImportCall(walkUpParenthesizedExpressions(usage.parent)) ? ModuleKind.ESNext : ModuleKind.CommonJS;
+ }
+ // in esm files, import=require statements are cjs format, otherwise everything is esm
+ // imports are only parent'd up to their containing declaration/expression, so access farther parents with care
+ const exprParentParent = walkUpParenthesizedExpressions(usage.parent)?.parent;
+ return exprParentParent && isImportEqualsDeclaration(exprParentParent) ? ModuleKind.CommonJS : ModuleKind.ESNext;
+ }
+
+ /* @internal */
+ export function loadWithModeAwareCache(names: string[], containingFile: SourceFile, containingFileName: string, redirectedReference: ResolvedProjectReference | undefined, loader: (name: string, resolverMode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined, containingFileName: string, redirectedReference: ResolvedProjectReference | undefined) => T): T[] {
+ if (names.length === 0) {
+ return [];
+ }
+ const resolutions: T[] = [];
+ const cache = new Map();
+ let i = 0;
+ for (const name of names) {
+ let result: T;
+ const mode = getModeForResolutionAtIndex(containingFile, i);
+ i++;
+ const cacheKey = mode !== undefined ? `${mode}|${name}` : name;
+ if (cache.has(cacheKey)) {
+ result = cache.get(cacheKey)!;
+ }
+ else {
+ cache.set(cacheKey, result = loader(name, mode, containingFileName, redirectedReference));
}
resolutions.push(result);
}
@@ -619,11 +680,11 @@ namespace ts {
export function getReferencedFileLocation(getSourceFileByPath: (path: Path) => SourceFile | undefined, ref: ReferencedFile): ReferenceFileLocation | SyntheticReferenceFileLocation {
const file = Debug.checkDefined(getSourceFileByPath(ref.file));
const { kind, index } = ref;
- let pos: number | undefined, end: number | undefined, packageId: PackageId | undefined;
+ let pos: number | undefined, end: number | undefined, packageId: PackageId | undefined, resolutionMode: FileReference["resolutionMode"] | undefined;
switch (kind) {
case FileIncludeKind.Import:
const importLiteral = getModuleNameStringLiteralAt(file, index);
- packageId = file.resolvedModules?.get(importLiteral.text)?.packageId;
+ packageId = file.resolvedModules?.get(importLiteral.text, getModeForResolutionAtIndex(file, index))?.packageId;
if (importLiteral.pos === -1) return { file, packageId, text: importLiteral.text };
pos = skipTrivia(file.text, importLiteral.pos);
end = importLiteral.end;
@@ -632,8 +693,8 @@ namespace ts {
({ pos, end } = file.referencedFiles[index]);
break;
case FileIncludeKind.TypeReferenceDirective:
- ({ pos, end } = file.typeReferenceDirectives[index]);
- packageId = file.resolvedTypeReferenceDirectiveNames?.get(toFileNameLowerCase(file.typeReferenceDirectives[index].fileName))?.packageId;
+ ({ pos, end, resolutionMode } = file.typeReferenceDirectives[index]);
+ packageId = file.resolvedTypeReferenceDirectiveNames?.get(toFileNameLowerCase(file.typeReferenceDirectives[index].fileName), resolutionMode || file.impliedNodeFormat)?.packageId;
break;
case FileIncludeKind.LibReferenceDirective:
({ pos, end } = file.libReferenceDirectives[index]);
@@ -738,6 +799,129 @@ namespace ts {
configFileParseResult.errors;
}
+ /**
+ * A function for determining if a given file is esm or cjs format, assuming modern node module resolution rules, as configured by the
+ * `options` parameter.
+ *
+ * @param fileName The normalized absolute path to check the format of (it need not exist on disk)
+ * @param [packageJsonInfoCache] A cache for package file lookups - it's best to have a cache when this function is called often
+ * @param host The ModuleResolutionHost which can perform the filesystem lookups for package json data
+ * @param options The compiler options to perform the analysis under - relevant options are `moduleResolution` and `traceResolution`
+ * @returns `undefined` if the path has no relevant implied format, `ModuleKind.ESNext` for esm format, and `ModuleKind.CommonJS` for cjs format
+ */
+ export function getImpliedNodeFormatForFile(fileName: Path, packageJsonInfoCache: PackageJsonInfoCache | undefined, host: ModuleResolutionHost, options: CompilerOptions): ModuleKind.ESNext | ModuleKind.CommonJS | undefined {
+ switch (getEmitModuleResolutionKind(options)) {
+ case ModuleResolutionKind.Node12:
+ case ModuleResolutionKind.NodeNext:
+ return fileExtensionIsOneOf(fileName, [Extension.Dmts, Extension.Mts, Extension.Mjs]) ? ModuleKind.ESNext :
+ fileExtensionIsOneOf(fileName, [Extension.Dcts, Extension.Cts, Extension.Cjs]) ? ModuleKind.CommonJS :
+ fileExtensionIsOneOf(fileName, [Extension.Dts, Extension.Ts, Extension.Tsx, Extension.Js, Extension.Jsx]) ? lookupFromPackageJson() :
+ undefined; // other extensions, like `json` or `tsbuildinfo`, are set as `undefined` here but they should never be fed through the transformer pipeline
+ default:
+ return undefined;
+ }
+ function lookupFromPackageJson(): ModuleKind.ESNext | ModuleKind.CommonJS {
+ const scope = getPackageScopeForPath(fileName, packageJsonInfoCache, host, options);
+ return scope?.packageJsonContent.type === "module" ? ModuleKind.ESNext : ModuleKind.CommonJS;
+
+ }
+ }
+
+ /** @internal */
+ export const plainJSErrors: Set = new Set([
+ // binder errors
+ Diagnostics.Cannot_redeclare_block_scoped_variable_0.code,
+ Diagnostics.A_module_cannot_have_multiple_default_exports.code,
+ Diagnostics.Another_export_default_is_here.code,
+ Diagnostics.The_first_export_default_is_here.code,
+ Diagnostics.Identifier_expected_0_is_a_reserved_word_at_the_top_level_of_a_module.code,
+ Diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode_Modules_are_automatically_in_strict_mode.code,
+ Diagnostics.Identifier_expected_0_is_a_reserved_word_that_cannot_be_used_here.code,
+ Diagnostics.constructor_is_a_reserved_word.code,
+ Diagnostics.delete_cannot_be_called_on_an_identifier_in_strict_mode.code,
+ Diagnostics.Code_contained_in_a_class_is_evaluated_in_JavaScript_s_strict_mode_which_does_not_allow_this_use_of_0_For_more_information_see_https_Colon_Slash_Slashdeveloper_mozilla_org_Slashen_US_Slashdocs_SlashWeb_SlashJavaScript_SlashReference_SlashStrict_mode.code,
+ Diagnostics.Invalid_use_of_0_Modules_are_automatically_in_strict_mode.code,
+ Diagnostics.Invalid_use_of_0_in_strict_mode.code,
+ Diagnostics.A_label_is_not_allowed_here.code,
+ Diagnostics.Octal_literals_are_not_allowed_in_strict_mode.code,
+ Diagnostics.with_statements_are_not_allowed_in_strict_mode.code,
+ // grammar errors
+ Diagnostics.A_break_statement_can_only_be_used_within_an_enclosing_iteration_or_switch_statement.code,
+ Diagnostics.A_break_statement_can_only_jump_to_a_label_of_an_enclosing_statement.code,
+ Diagnostics.A_class_declaration_without_the_default_modifier_must_have_a_name.code,
+ Diagnostics.A_class_member_cannot_have_the_0_keyword.code,
+ Diagnostics.A_comma_expression_is_not_allowed_in_a_computed_property_name.code,
+ Diagnostics.A_continue_statement_can_only_be_used_within_an_enclosing_iteration_statement.code,
+ Diagnostics.A_continue_statement_can_only_jump_to_a_label_of_an_enclosing_iteration_statement.code,
+ Diagnostics.A_continue_statement_can_only_jump_to_a_label_of_an_enclosing_iteration_statement.code,
+ Diagnostics.A_default_clause_cannot_appear_more_than_once_in_a_switch_statement.code,
+ Diagnostics.A_default_export_must_be_at_the_top_level_of_a_file_or_module_declaration.code,
+ Diagnostics.A_definite_assignment_assertion_is_not_permitted_in_this_context.code,
+ Diagnostics.A_destructuring_declaration_must_have_an_initializer.code,
+ Diagnostics.A_get_accessor_cannot_have_parameters.code,
+ Diagnostics.A_rest_element_cannot_contain_a_binding_pattern.code,
+ Diagnostics.A_rest_element_cannot_have_a_property_name.code,
+ Diagnostics.A_rest_element_cannot_have_an_initializer.code,
+ Diagnostics.A_rest_element_must_be_last_in_a_destructuring_pattern.code,
+ Diagnostics.A_rest_parameter_cannot_have_an_initializer.code,
+ Diagnostics.A_rest_parameter_must_be_last_in_a_parameter_list.code,
+ Diagnostics.A_rest_parameter_or_binding_pattern_may_not_have_a_trailing_comma.code,
+ Diagnostics.A_return_statement_can_only_be_used_within_a_function_body.code,
+ Diagnostics.A_return_statement_cannot_be_used_inside_a_class_static_block.code,
+ Diagnostics.A_set_accessor_cannot_have_rest_parameter.code,
+ Diagnostics.A_set_accessor_must_have_exactly_one_parameter.code,
+ Diagnostics.An_export_declaration_can_only_be_used_at_the_top_level_of_a_module.code,
+ Diagnostics.An_export_declaration_cannot_have_modifiers.code,
+ Diagnostics.An_import_declaration_can_only_be_used_at_the_top_level_of_a_module.code,
+ Diagnostics.An_import_declaration_cannot_have_modifiers.code,
+ Diagnostics.An_object_member_cannot_be_declared_optional.code,
+ Diagnostics.Argument_of_dynamic_import_cannot_be_spread_element.code,
+ Diagnostics.Cannot_assign_to_private_method_0_Private_methods_are_not_writable.code,
+ Diagnostics.Cannot_redeclare_identifier_0_in_catch_clause.code,
+ Diagnostics.Catch_clause_variable_cannot_have_an_initializer.code,
+ Diagnostics.Class_decorators_can_t_be_used_with_static_private_identifier_Consider_removing_the_experimental_decorator.code,
+ Diagnostics.Classes_can_only_extend_a_single_class.code,
+ Diagnostics.Classes_may_not_have_a_field_named_constructor.code,
+ Diagnostics.Did_you_mean_to_use_a_Colon_An_can_only_follow_a_property_name_when_the_containing_object_literal_is_part_of_a_destructuring_pattern.code,
+ Diagnostics.Duplicate_label_0.code,
+ Diagnostics.Dynamic_imports_can_only_accept_a_module_specifier_and_an_optional_assertion_as_arguments.code,
+ Diagnostics.For_await_loops_cannot_be_used_inside_a_class_static_block.code,
+ Diagnostics.JSX_attributes_must_only_be_assigned_a_non_empty_expression.code,
+ Diagnostics.JSX_elements_cannot_have_multiple_attributes_with_the_same_name.code,
+ Diagnostics.JSX_expressions_may_not_use_the_comma_operator_Did_you_mean_to_write_an_array.code,
+ Diagnostics.JSX_property_access_expressions_cannot_include_JSX_namespace_names.code,
+ Diagnostics.Jump_target_cannot_cross_function_boundary.code,
+ Diagnostics.Line_terminator_not_permitted_before_arrow.code,
+ Diagnostics.Modifiers_cannot_appear_here.code,
+ Diagnostics.Only_a_single_variable_declaration_is_allowed_in_a_for_in_statement.code,
+ Diagnostics.Only_a_single_variable_declaration_is_allowed_in_a_for_of_statement.code,
+ Diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies.code,
+ Diagnostics.Private_identifiers_are_only_allowed_in_class_bodies_and_may_only_be_used_as_part_of_a_class_member_declaration_property_access_or_on_the_left_hand_side_of_an_in_expression.code,
+ Diagnostics.Property_0_is_not_accessible_outside_class_1_because_it_has_a_private_identifier.code,
+ Diagnostics.Tagged_template_expressions_are_not_permitted_in_an_optional_chain.code,
+ Diagnostics.The_left_hand_side_of_a_for_of_statement_may_not_be_async.code,
+ Diagnostics.The_variable_declaration_of_a_for_in_statement_cannot_have_an_initializer.code,
+ Diagnostics.The_variable_declaration_of_a_for_of_statement_cannot_have_an_initializer.code,
+ Diagnostics.Trailing_comma_not_allowed.code,
+ Diagnostics.Variable_declaration_list_cannot_be_empty.code,
+ Diagnostics._0_and_1_operations_cannot_be_mixed_without_parentheses.code,
+ Diagnostics._0_expected.code,
+ Diagnostics._0_is_not_a_valid_meta_property_for_keyword_1_Did_you_mean_2.code,
+ Diagnostics._0_list_cannot_be_empty.code,
+ Diagnostics._0_modifier_already_seen.code,
+ Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration.code,
+ Diagnostics._0_modifier_cannot_appear_on_a_module_or_namespace_element.code,
+ Diagnostics._0_modifier_cannot_appear_on_a_parameter.code,
+ Diagnostics._0_modifier_cannot_appear_on_class_elements_of_this_kind.code,
+ Diagnostics._0_modifier_cannot_be_used_here.code,
+ Diagnostics._0_modifier_must_precede_1_modifier.code,
+ Diagnostics.const_declarations_can_only_be_declared_inside_a_block.code,
+ Diagnostics.const_declarations_must_be_initialized.code,
+ Diagnostics.extends_clause_already_seen.code,
+ Diagnostics.let_declarations_can_only_be_declared_inside_a_block.code,
+ Diagnostics.let_is_not_allowed_to_be_used_as_a_name_in_let_or_const_declarations.code,
+ ]);
+
/**
* Determine if source file needs to be re-created even if its text hasn't changed
*/
@@ -802,7 +986,7 @@ namespace ts {
const cachedBindAndCheckDiagnosticsForFile: DiagnosticCache = {};
const cachedDeclarationDiagnosticsForFile: DiagnosticCache = {};
- let resolvedTypeReferenceDirectives = new Map();
+ let resolvedTypeReferenceDirectives = createModeAwareCache();
let fileProcessingDiagnostics: FilePreprocessingDiagnostics[] | undefined;
// The below settings are to track if a .js file should be add to the program if loaded via searching under node_modules.
@@ -834,7 +1018,7 @@ namespace ts {
const programDiagnostics = createDiagnosticCollection();
const currentDirectory = host.getCurrentDirectory();
const supportedExtensions = getSupportedExtensions(options);
- const supportedExtensionsWithJsonIfResolveJsonModule = getSuppoertedExtensionsWithJsonIfResolveJsonModule(options, supportedExtensions);
+ const supportedExtensionsWithJsonIfResolveJsonModule = getSupportedExtensionsWithJsonIfResolveJsonModule(options, supportedExtensions);
// Map storing if there is emit blocking diagnostics for given input
const hasEmitBlockingDiagnostics = new Map();
@@ -842,10 +1026,10 @@ namespace ts {
let moduleResolutionCache: ModuleResolutionCache | undefined;
let typeReferenceDirectiveResolutionCache: TypeReferenceDirectiveResolutionCache | undefined;
- let actualResolveModuleNamesWorker: (moduleNames: string[], containingFile: string, reusedNames?: string[], redirectedReference?: ResolvedProjectReference) => ResolvedModuleFull[];
+ let actualResolveModuleNamesWorker: (moduleNames: string[], containingFile: SourceFile, containingFileName: string, reusedNames?: string[], redirectedReference?: ResolvedProjectReference) => ResolvedModuleFull[];
const hasInvalidatedResolution = host.hasInvalidatedResolution || returnFalse;
if (host.resolveModuleNames) {
- actualResolveModuleNamesWorker = (moduleNames, containingFile, reusedNames, redirectedReference) => host.resolveModuleNames!(Debug.checkEachDefined(moduleNames), containingFile, reusedNames, redirectedReference, options).map(resolved => {
+ actualResolveModuleNamesWorker = (moduleNames, containingFile, containingFileName, reusedNames, redirectedReference) => host.resolveModuleNames!(Debug.checkEachDefined(moduleNames), containingFileName, reusedNames, redirectedReference, options, containingFile).map(resolved => {
// An older host may have omitted extension, in which case we should infer it from the file extension of resolvedFileName.
if (!resolved || (resolved as ResolvedModuleFull).extension !== undefined) {
return resolved as ResolvedModuleFull;
@@ -854,28 +1038,30 @@ namespace ts {
withExtension.extension = extensionFromPath(resolved.resolvedFileName);
return withExtension;
});
+ moduleResolutionCache = host.getModuleResolutionCache?.();
}
else {
moduleResolutionCache = createModuleResolutionCache(currentDirectory, getCanonicalFileName, options);
- const loader = (moduleName: string, containingFile: string, redirectedReference: ResolvedProjectReference | undefined) => resolveModuleName(moduleName, containingFile, options, host, moduleResolutionCache, redirectedReference).resolvedModule!; // TODO: GH#18217
- actualResolveModuleNamesWorker = (moduleNames, containingFile, _reusedNames, redirectedReference) => loadWithLocalCache(Debug.checkEachDefined(moduleNames), containingFile, redirectedReference, loader);
+ const loader = (moduleName: string, resolverMode: ModuleKind.CommonJS | ModuleKind.ESNext | undefined, containingFileName: string, redirectedReference: ResolvedProjectReference | undefined) => resolveModuleName(moduleName, containingFileName, options, host, moduleResolutionCache, redirectedReference, resolverMode).resolvedModule!; // TODO: GH#18217
+ actualResolveModuleNamesWorker = (moduleNames, containingFile, containingFileName, _reusedNames, redirectedReference) => loadWithModeAwareCache(Debug.checkEachDefined(moduleNames), containingFile, containingFileName, redirectedReference, loader);
}
- let actualResolveTypeReferenceDirectiveNamesWorker: (typeDirectiveNames: string[], containingFile: string, redirectedReference?: ResolvedProjectReference) => (ResolvedTypeReferenceDirective | undefined)[];
+ let actualResolveTypeReferenceDirectiveNamesWorker: (typeDirectiveNames: string[] | readonly FileReference[], containingFile: string, redirectedReference?: ResolvedProjectReference, containingFileMode?: SourceFile["impliedNodeFormat"] | undefined) => (ResolvedTypeReferenceDirective | undefined)[];
if (host.resolveTypeReferenceDirectives) {
- actualResolveTypeReferenceDirectiveNamesWorker = (typeDirectiveNames, containingFile, redirectedReference) => host.resolveTypeReferenceDirectives!(Debug.checkEachDefined(typeDirectiveNames), containingFile, redirectedReference, options);
+ actualResolveTypeReferenceDirectiveNamesWorker = (typeDirectiveNames, containingFile, redirectedReference, containingFileMode) => host.resolveTypeReferenceDirectives!(Debug.checkEachDefined(typeDirectiveNames), containingFile, redirectedReference, options, containingFileMode);
}
else {
typeReferenceDirectiveResolutionCache = createTypeReferenceDirectiveResolutionCache(currentDirectory, getCanonicalFileName, /*options*/ undefined, moduleResolutionCache?.getPackageJsonInfoCache());
- const loader = (typesRef: string, containingFile: string, redirectedReference: ResolvedProjectReference | undefined) => resolveTypeReferenceDirective(
+ const loader = (typesRef: string, containingFile: string, redirectedReference: ResolvedProjectReference | undefined, resolutionMode: SourceFile["impliedNodeFormat"] | undefined) => resolveTypeReferenceDirective(
typesRef,
containingFile,
options,
host,
redirectedReference,
typeReferenceDirectiveResolutionCache,
+ resolutionMode,
).resolvedTypeReferenceDirective!; // TODO: GH#18217
- actualResolveTypeReferenceDirectiveNamesWorker = (typeReferenceDirectiveNames, containingFile, redirectedReference) => loadWithLocalCache(Debug.checkEachDefined(typeReferenceDirectiveNames), containingFile, redirectedReference, loader);
+ actualResolveTypeReferenceDirectiveNamesWorker = (typeReferenceDirectiveNames, containingFile, redirectedReference, containingFileMode) => loadWithTypeDirectiveCache(Debug.checkEachDefined(typeReferenceDirectiveNames), containingFile, redirectedReference, containingFileMode, loader);
}
// Map from a stringified PackageId to the source file with that id.
@@ -917,6 +1103,7 @@ namespace ts {
getSourceOfProjectReferenceRedirect,
forEachResolvedProjectReference
});
+ const readFile = host.readFile.bind(host) as typeof host.readFile;
tracing?.push(tracing.Phase.Program, "shouldProgramCreateNewSourceFiles", { hasOldProgram: !!oldProgram });
const shouldCreateNewSourceFile = shouldProgramCreateNewSourceFiles(oldProgram, options);
@@ -977,7 +1164,8 @@ namespace ts {
const containingFilename = combinePaths(containingDirectory, inferredTypesContainingFile);
const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeReferences, containingFilename);
for (let i = 0; i < typeReferences.length; i++) {
- processTypeReferenceDirective(typeReferences[i], resolutions[i], { kind: FileIncludeKind.AutomaticTypeDirectiveFile, typeReference: typeReferences[i], packageId: resolutions[i]?.packageId });
+ // under node12/nodenext module resolution, load `types`/ata include names as cjs resolution results by passing an `undefined` mode
+ processTypeReferenceDirective(typeReferences[i], /*mode*/ undefined, resolutions[i], { kind: FileIncludeKind.AutomaticTypeDirectiveFile, typeReference: typeReferences[i], packageId: resolutions[i]?.packageId });
}
tracing?.pop();
}
@@ -995,7 +1183,7 @@ namespace ts {
}
else {
forEach(options.lib, (libFileName, index) => {
- processRootFile(combinePaths(defaultLibraryPath, libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ false, { kind: FileIncludeKind.LibFile, index });
+ processRootFile(pathForLibFile(libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ false, { kind: FileIncludeKind.LibFile, index });
});
}
}
@@ -1101,6 +1289,7 @@ namespace ts {
isSourceOfProjectReferenceRedirect,
emitBuildInfo,
fileExists,
+ readFile,
directoryExists,
getSymlinkCache,
realpath: host.realpath?.bind(host),
@@ -1137,20 +1326,21 @@ namespace ts {
const redirectedReference = getRedirectReferenceForResolution(containingFile);
tracing?.push(tracing.Phase.Program, "resolveModuleNamesWorker", { containingFileName });
performance.mark("beforeResolveModule");
- const result = actualResolveModuleNamesWorker(moduleNames, containingFileName, reusedNames, redirectedReference);
+ const result = actualResolveModuleNamesWorker(moduleNames, containingFile, containingFileName, reusedNames, redirectedReference);
performance.mark("afterResolveModule");
performance.measure("ResolveModule", "beforeResolveModule", "afterResolveModule");
tracing?.pop();
return result;
}
- function resolveTypeReferenceDirectiveNamesWorker(typeDirectiveNames: string[], containingFile: string | SourceFile): readonly (ResolvedTypeReferenceDirective | undefined)[] {
+ function resolveTypeReferenceDirectiveNamesWorker(typeDirectiveNames: string[] | readonly FileReference[], containingFile: string | SourceFile): readonly (ResolvedTypeReferenceDirective | undefined)[] {
if (!typeDirectiveNames.length) return [];
const containingFileName = !isString(containingFile) ? getNormalizedAbsolutePath(containingFile.originalFileName, currentDirectory) : containingFile;
const redirectedReference = !isString(containingFile) ? getRedirectReferenceForResolution(containingFile) : undefined;
+ const containingFileMode = !isString(containingFile) ? containingFile.impliedNodeFormat : undefined;
tracing?.push(tracing.Phase.Program, "resolveTypeReferenceDirectiveNamesWorker", { containingFileName });
performance.mark("beforeResolveTypeReference");
- const result = actualResolveTypeReferenceDirectiveNamesWorker(typeDirectiveNames, containingFileName, redirectedReference);
+ const result = actualResolveTypeReferenceDirectiveNamesWorker(typeDirectiveNames, containingFileName, redirectedReference, containingFileMode);
performance.mark("afterResolveTypeReference");
performance.measure("ResolveTypeReference", "beforeResolveTypeReference", "afterResolveTypeReference");
tracing?.pop();
@@ -1159,7 +1349,7 @@ namespace ts {
function getRedirectReferenceForResolution(file: SourceFile) {
const redirect = getResolvedProjectReferenceToRedirect(file.originalFileName);
- if (redirect || !fileExtensionIs(file.originalFileName, Extension.Dts)) return redirect;
+ if (redirect || !fileExtensionIsOneOf(file.originalFileName, [Extension.Dts, Extension.Dcts, Extension.Dmts])) return redirect;
// The originalFileName could not be actual source file name if file found was d.ts from referecned project
// So in this case try to look up if this is output from referenced project, if it is use the redirected project in that case
@@ -1202,8 +1392,8 @@ namespace ts {
return libs.length + 2;
}
- function getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string): ResolvedModuleWithFailedLookupLocations | undefined {
- return moduleResolutionCache && resolveModuleNameFromCache(moduleName, containingFile, moduleResolutionCache);
+ function getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string, mode?: ModuleKind.CommonJS | ModuleKind.ESNext): ResolvedModuleWithFailedLookupLocations | undefined {
+ return moduleResolutionCache && resolveModuleNameFromCache(moduleName, containingFile, moduleResolutionCache, mode);
}
function toPath(fileName: string): Path {
@@ -1256,8 +1446,10 @@ namespace ts {
// Since we assume the filesystem does not change during program creation,
// it is safe to reuse resolutions from the earlier call.
const result: ResolvedModuleFull[] = [];
+ let i = 0;
for (const moduleName of moduleNames) {
- const resolvedModule = file.resolvedModules.get(moduleName)!;
+ const resolvedModule = file.resolvedModules.get(moduleName, getModeForResolutionAtIndex(file, i))!;
+ i++;
result.push(resolvedModule);
}
return result;
@@ -1287,7 +1479,7 @@ namespace ts {
const moduleName = moduleNames[i];
// If the source file is unchanged and doesnt have invalidated resolution, reuse the module resolutions
if (file === oldSourceFile && !hasInvalidatedResolution(oldSourceFile.path)) {
- const oldResolvedModule = getResolvedModule(oldSourceFile, moduleName);
+ const oldResolvedModule = getResolvedModule(oldSourceFile, moduleName, getModeForResolutionAtIndex(oldSourceFile, i));
if (oldResolvedModule) {
if (isTraceEnabled(options, host)) {
trace(host,
@@ -1317,7 +1509,7 @@ namespace ts {
}
}
else {
- resolvesToAmbientModuleInNonModifiedFile = moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName);
+ resolvesToAmbientModuleInNonModifiedFile = moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName, i);
}
if (resolvesToAmbientModuleInNonModifiedFile) {
@@ -1360,8 +1552,9 @@ namespace ts {
// If we change our policy of rechecking failed lookups on each program create,
// we should adjust the value returned here.
- function moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName: string): boolean {
- const resolutionToFile = getResolvedModule(oldSourceFile, moduleName);
+ function moduleNameResolvesToAmbientModuleInNonModifiedFile(moduleName: string, index: number): boolean {
+ if (index >= length(oldSourceFile?.imports) + length(oldSourceFile?.moduleAugmentations)) return false; // mode index out of bounds, don't reuse resolution
+ const resolutionToFile = getResolvedModule(oldSourceFile, moduleName, oldSourceFile && getModeForResolutionAtIndex(oldSourceFile, index));
const resolvedFile = resolutionToFile && oldProgram!.getSourceFile(resolutionToFile.resolvedFileName);
if (resolutionToFile && resolvedFile) {
// In the old program, we resolved to an ambient module that was in the same
@@ -1455,8 +1648,8 @@ namespace ts {
for (const oldSourceFile of oldSourceFiles) {
let newSourceFile = host.getSourceFileByPath
- ? host.getSourceFileByPath(oldSourceFile.fileName, oldSourceFile.resolvedPath, options.target!, /*onError*/ undefined, shouldCreateNewSourceFile)
- : host.getSourceFile(oldSourceFile.fileName, options.target!, /*onError*/ undefined, shouldCreateNewSourceFile); // TODO: GH#18217
+ ? host.getSourceFileByPath(oldSourceFile.fileName, oldSourceFile.resolvedPath, getEmitScriptTarget(options), /*onError*/ undefined, shouldCreateNewSourceFile)
+ : host.getSourceFile(oldSourceFile.fileName, getEmitScriptTarget(options), /*onError*/ undefined, shouldCreateNewSourceFile); // TODO: GH#18217
if (!newSourceFile) {
return StructureIsReused.Not;
@@ -1491,6 +1684,7 @@ namespace ts {
newSourceFile.originalFileName = oldSourceFile.originalFileName;
newSourceFile.resolvedPath = oldSourceFile.resolvedPath;
newSourceFile.fileName = oldSourceFile.fileName;
+ newSourceFile.impliedNodeFormat = oldSourceFile.impliedNodeFormat;
const packageName = oldProgram.sourceFileToPackageName.get(oldSourceFile.path);
if (packageName !== undefined) {
@@ -1576,22 +1770,21 @@ namespace ts {
const moduleNames = getModuleNames(newSourceFile);
const resolutions = resolveModuleNamesReusingOldState(moduleNames, newSourceFile);
// ensure that module resolution results are still correct
- const resolutionsChanged = hasChangesInResolutions(moduleNames, resolutions, oldSourceFile.resolvedModules, moduleResolutionIsEqualTo);
+ const resolutionsChanged = hasChangesInResolutions(moduleNames, resolutions, oldSourceFile.resolvedModules, oldSourceFile, moduleResolutionIsEqualTo);
if (resolutionsChanged) {
structureIsReused = StructureIsReused.SafeModules;
- newSourceFile.resolvedModules = zipToMap(moduleNames, resolutions);
+ newSourceFile.resolvedModules = zipToModeAwareCache(newSourceFile, moduleNames, resolutions);
}
else {
newSourceFile.resolvedModules = oldSourceFile.resolvedModules;
}
- // We lower-case all type references because npm automatically lowercases all packages. See GH#9824.
- const typesReferenceDirectives = map(newSourceFile.typeReferenceDirectives, ref => toFileNameLowerCase(ref.fileName));
+ const typesReferenceDirectives = newSourceFile.typeReferenceDirectives;
const typeReferenceResolutions = resolveTypeReferenceDirectiveNamesWorker(typesReferenceDirectives, newSourceFile);
// ensure that types resolutions are still correct
- const typeReferenceEesolutionsChanged = hasChangesInResolutions(typesReferenceDirectives, typeReferenceResolutions, oldSourceFile.resolvedTypeReferenceDirectiveNames, typeDirectiveIsEqualTo);
- if (typeReferenceEesolutionsChanged) {
+ const typeReferenceResolutionsChanged = hasChangesInResolutions(typesReferenceDirectives, typeReferenceResolutions, oldSourceFile.resolvedTypeReferenceDirectiveNames, oldSourceFile, typeDirectiveIsEqualTo);
+ if (typeReferenceResolutionsChanged) {
structureIsReused = StructureIsReused.SafeModules;
- newSourceFile.resolvedTypeReferenceDirectiveNames = zipToMap(typesReferenceDirectives, typeReferenceResolutions);
+ newSourceFile.resolvedTypeReferenceDirectiveNames = zipToModeAwareCache(newSourceFile, typesReferenceDirectives, typeReferenceResolutions);
}
else {
newSourceFile.resolvedTypeReferenceDirectiveNames = oldSourceFile.resolvedTypeReferenceDirectiveNames;
@@ -1737,7 +1930,7 @@ namespace ts {
return equalityComparer(file.fileName, getDefaultLibraryFileName());
}
else {
- return some(options.lib, libFileName => equalityComparer(file.fileName, combinePaths(defaultLibraryPath, libFileName)));
+ return some(options.lib, libFileName => equalityComparer(file.fileName, pathForLibFile(libFileName)));
}
}
@@ -1918,15 +2111,25 @@ namespace ts {
Debug.assert(!!sourceFile.bindDiagnostics);
- const isCheckJs = isCheckJsEnabledForFile(sourceFile, options);
+ const isJs = sourceFile.scriptKind === ScriptKind.JS || sourceFile.scriptKind === ScriptKind.JSX;
+ const isCheckJs = isJs && isCheckJsEnabledForFile(sourceFile, options);
+ const isPlainJs = isPlainJsFile(sourceFile, options.checkJs);
const isTsNoCheck = !!sourceFile.checkJsDirective && sourceFile.checkJsDirective.enabled === false;
- // By default, only type-check .ts, .tsx, 'Deferred' and 'External' files (external files are added by plugins)
- const includeBindAndCheckDiagnostics = !isTsNoCheck && (sourceFile.scriptKind === ScriptKind.TS || sourceFile.scriptKind === ScriptKind.TSX
- || sourceFile.scriptKind === ScriptKind.External || isCheckJs || sourceFile.scriptKind === ScriptKind.Deferred);
- const bindDiagnostics: readonly Diagnostic[] = includeBindAndCheckDiagnostics ? sourceFile.bindDiagnostics : emptyArray;
- const checkDiagnostics = includeBindAndCheckDiagnostics ? typeChecker.getDiagnostics(sourceFile, cancellationToken) : emptyArray;
- return getMergedBindAndCheckDiagnostics(sourceFile, includeBindAndCheckDiagnostics, bindDiagnostics, checkDiagnostics, isCheckJs ? sourceFile.jsDocDiagnostics : undefined);
+ // By default, only type-check .ts, .tsx, Deferred, plain JS, checked JS and External
+ // - plain JS: .js files with no // ts-check and checkJs: undefined
+ // - check JS: .js files with either // ts-check or checkJs: true
+ // - external: files that are added by plugins
+ const includeBindAndCheckDiagnostics = !isTsNoCheck && (sourceFile.scriptKind === ScriptKind.TS || sourceFile.scriptKind === ScriptKind.TSX
+ || sourceFile.scriptKind === ScriptKind.External || isPlainJs || isCheckJs || sourceFile.scriptKind === ScriptKind.Deferred);
+ let bindDiagnostics: readonly Diagnostic[] = includeBindAndCheckDiagnostics ? sourceFile.bindDiagnostics : emptyArray;
+ let checkDiagnostics = includeBindAndCheckDiagnostics ? typeChecker.getDiagnostics(sourceFile, cancellationToken) : emptyArray;
+ if (isPlainJs) {
+ bindDiagnostics = filter(bindDiagnostics, d => plainJSErrors.has(d.code));
+ checkDiagnostics = filter(checkDiagnostics, d => plainJSErrors.has(d.code));
+ }
+ // skip ts-expect-error errors in plain JS files, and skip JSDoc errors except in checked JS
+ return getMergedBindAndCheckDiagnostics(sourceFile, includeBindAndCheckDiagnostics && !isPlainJs, bindDiagnostics, checkDiagnostics, isCheckJs ? sourceFile.jsDocDiagnostics : undefined);
});
}
@@ -2269,7 +2472,7 @@ namespace ts {
function createSyntheticImport(text: string, file: SourceFile) {
const externalHelpersModuleReference = factory.createStringLiteral(text);
- const importDecl = factory.createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, /*importClause*/ undefined, externalHelpersModuleReference);
+ const importDecl = factory.createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, /*importClause*/ undefined, externalHelpersModuleReference, /*assertClause*/ undefined);
addEmitFlags(importDecl, EmitFlags.NeverApplyImportHelper);
setParent(externalHelpersModuleReference, importDecl);
setParent(importDecl, file);
@@ -2328,6 +2531,7 @@ namespace ts {
// An ExternalImportDeclaration in an AmbientExternalModuleDeclaration may reference other external modules
// only through top - level external module names. Relative external module names are not permitted.
if (moduleNameExpr && isStringLiteral(moduleNameExpr) && moduleNameExpr.text && (!inAmbientModule || !isExternalModuleNameRelative(moduleNameExpr.text))) {
+ setParentRecursive(node, /*incremental*/ false); // we need parent data on imports before the program is fully bound, so we ensure it's set here
imports = append(imports, moduleNameExpr);
if (!usesUriStyleNodeCoreModules && currentNodeModulesDepth === 0 && !file.isDeclarationFile) {
usesUriStyleNodeCoreModules = startsWith(moduleNameExpr.text, "node:");
@@ -2336,6 +2540,7 @@ namespace ts {
}
else if (isModuleDeclaration(node)) {
if (isAmbientModule(node) && (inAmbientModule || hasSyntacticModifier(node, ModifierFlags.Ambient) || file.isDeclarationFile)) {
+ (node.name as Mutable).parent = node;
const nameText = getTextOfIdentifierOrLiteral(node.name);
// Ambient module declarations can be interpreted as augmentations for some existing external modules.
// This will happen in two cases:
@@ -2372,13 +2577,16 @@ namespace ts {
while (r.exec(file.text) !== null) { // eslint-disable-line no-null/no-null
const node = getNodeAtPosition(file, r.lastIndex);
if (isJavaScriptFile && isRequireCall(node, /*checkArgumentIsStringLiteralLike*/ true)) {
+ setParentRecursive(node, /*incremental*/ false); // we need parent data on imports before the program is fully bound, so we ensure it's set here
imports = append(imports, node.arguments[0]);
}
- // we have to check the argument list has length of 1. We will still have to process these even though we have parsing error.
- else if (isImportCall(node) && node.arguments.length === 1 && isStringLiteralLike(node.arguments[0])) {
+ // we have to check the argument list has length of at least 1. We will still have to process these even though we have parsing error.
+ else if (isImportCall(node) && node.arguments.length >= 1 && isStringLiteralLike(node.arguments[0])) {
+ setParentRecursive(node, /*incremental*/ false); // we need parent data on imports before the program is fully bound, so we ensure it's set here
imports = append(imports, node.arguments[0]);
}
else if (isLiteralImportTypeNode(node)) {
+ setParentRecursive(node, /*incremental*/ false); // we need parent data on imports before the program is fully bound, so we ensure it's set here
imports = append(imports, node.argument.literal);
}
}
@@ -2406,7 +2614,7 @@ namespace ts {
const libName = toFileNameLowerCase(ref.fileName);
const libFileName = libMap.get(libName);
if (libFileName) {
- return getSourceFile(combinePaths(defaultLibraryPath, libFileName));
+ return getSourceFile(pathForLibFile(libFileName));
}
}
@@ -2423,13 +2631,13 @@ namespace ts {
if (hasExtension(fileName)) {
const canonicalFileName = host.getCanonicalFileName(fileName);
- if (!options.allowNonTsExtensions && !forEach(supportedExtensionsWithJsonIfResolveJsonModule, extension => fileExtensionIs(canonicalFileName, extension))) {
+ if (!options.allowNonTsExtensions && !forEach(flatten(supportedExtensionsWithJsonIfResolveJsonModule), extension => fileExtensionIs(canonicalFileName, extension))) {
if (fail) {
if (hasJSFileExtension(canonicalFileName)) {
fail(Diagnostics.File_0_is_a_JavaScript_file_Did_you_mean_to_enable_the_allowJs_option, fileName);
}
else {
- fail(Diagnostics.File_0_has_an_unsupported_extension_The_only_supported_extensions_are_1, fileName, "'" + supportedExtensions.join("', '") + "'");
+ fail(Diagnostics.File_0_has_an_unsupported_extension_The_only_supported_extensions_are_1, fileName, "'" + flatten(supportedExtensions).join("', '") + "'");
}
}
return undefined;
@@ -2461,8 +2669,9 @@ namespace ts {
return undefined;
}
- const sourceFileWithAddedExtension = forEach(supportedExtensions, extension => getSourceFile(fileName + extension));
- if (fail && !sourceFileWithAddedExtension) fail(Diagnostics.Could_not_resolve_the_path_0_with_the_extensions_Colon_1, fileName, "'" + supportedExtensions.join("', '") + "'");
+ // Only try adding extensions from the first supported group (which should be .ts/.tsx/.d.ts)
+ const sourceFileWithAddedExtension = forEach(supportedExtensions[0], extension => getSourceFile(fileName + extension));
+ if (fail && !sourceFileWithAddedExtension) fail(Diagnostics.Could_not_resolve_the_path_0_with_the_extensions_Colon_1, fileName, "'" + flatten(supportedExtensions).join("', '") + "'");
return sourceFileWithAddedExtension;
}
}
@@ -2616,7 +2825,7 @@ namespace ts {
// We haven't looked for this file, do so now and cache result
const file = host.getSourceFile(
fileName,
- options.target!,
+ getEmitScriptTarget(options),
hostErrorMessage => addFilePreprocessingFileExplainingDiagnostic(/*file*/ undefined, reason, Diagnostics.Cannot_read_file_0_Colon_1, [fileName, hostErrorMessage]),
shouldCreateNewSourceFile
);
@@ -2631,14 +2840,14 @@ namespace ts {
redirectTargetsMap.add(fileFromPackageId.path, fileName);
addFileToFilesByName(dupFile, path, redirectedPath);
addFileIncludeReason(dupFile, reason);
- sourceFileToPackageName.set(path, packageId.name);
+ sourceFileToPackageName.set(path, packageIdToPackageName(packageId));
processingOtherFiles!.push(dupFile);
return dupFile;
}
else if (file) {
// This is the first source file to have this packageId.
packageIdToSourceFile.set(packageIdKey, file);
- sourceFileToPackageName.set(path, packageId.name);
+ sourceFileToPackageName.set(path, packageIdToPackageName(packageId));
}
}
addFileToFilesByName(file, path, redirectedPath);
@@ -2649,6 +2858,10 @@ namespace ts {
file.path = path;
file.resolvedPath = toPath(fileName);
file.originalFileName = originalFileName;
+ // It's a _little odd_ that we can't set `impliedNodeFormat` until the program step - but it's the first and only time we have a resolution cache
+ // and a freshly made source file node on hand at the same time, and we need both to set the field. Persisting the resolution cache all the way
+ // to the check and emit steps would be bad - so we much prefer detecting and storing the format information on the source file node upfront.
+ file.impliedNodeFormat = getImpliedNodeFormatForFile(file.resolvedPath, moduleResolutionCache?.getPackageJsonInfoCache(), host, options);
addFileIncludeReason(file, reason);
if (host.useCaseSensitiveFileNames()) {
@@ -2800,8 +3013,7 @@ namespace ts {
}
function processTypeReferenceDirectives(file: SourceFile) {
- // We lower-case all type references because npm automatically lowercases all packages. See GH#9824.
- const typeDirectives = map(file.typeReferenceDirectives, ref => toFileNameLowerCase(ref.fileName));
+ const typeDirectives = file.typeReferenceDirectives;
if (!typeDirectives) {
return;
}
@@ -2813,28 +3025,34 @@ namespace ts {
// store resolved type directive on the file
const fileName = toFileNameLowerCase(ref.fileName);
setResolvedTypeReferenceDirective(file, fileName, resolvedTypeReferenceDirective);
- processTypeReferenceDirective(fileName, resolvedTypeReferenceDirective, { kind: FileIncludeKind.TypeReferenceDirective, file: file.path, index, });
+ const mode = ref.resolutionMode || file.impliedNodeFormat;
+ if (mode && getEmitModuleResolutionKind(options) !== ModuleResolutionKind.Node12 && getEmitModuleResolutionKind(options) !== ModuleResolutionKind.NodeNext) {
+ programDiagnostics.add(createDiagnosticForRange(file, ref, Diagnostics.Resolution_modes_are_only_supported_when_moduleResolution_is_node12_or_nodenext));
+ }
+ processTypeReferenceDirective(fileName, mode, resolvedTypeReferenceDirective, { kind: FileIncludeKind.TypeReferenceDirective, file: file.path, index, });
}
}
function processTypeReferenceDirective(
typeReferenceDirective: string,
+ mode: SourceFile["impliedNodeFormat"] | undefined,
resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined,
reason: FileIncludeReason
): void {
tracing?.push(tracing.Phase.Program, "processTypeReferenceDirective", { directive: typeReferenceDirective, hasResolved: !!resolveModuleNamesReusingOldState, refKind: reason.kind, refPath: isReferencedFile(reason) ? reason.file : undefined });
- processTypeReferenceDirectiveWorker(typeReferenceDirective, resolvedTypeReferenceDirective, reason);
+ processTypeReferenceDirectiveWorker(typeReferenceDirective, mode, resolvedTypeReferenceDirective, reason);
tracing?.pop();
}
function processTypeReferenceDirectiveWorker(
typeReferenceDirective: string,
+ mode: SourceFile["impliedNodeFormat"] | undefined,
resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined,
reason: FileIncludeReason
): void {
// If we already found this library as a primary reference - nothing to do
- const previousResolution = resolvedTypeReferenceDirectives.get(typeReferenceDirective);
+ const previousResolution = resolvedTypeReferenceDirectives.get(typeReferenceDirective, mode);
if (previousResolution && previousResolution.primary) {
return;
}
@@ -2879,17 +3097,36 @@ namespace ts {
}
if (saveResolution) {
- resolvedTypeReferenceDirectives.set(typeReferenceDirective, resolvedTypeReferenceDirective);
+ resolvedTypeReferenceDirectives.set(typeReferenceDirective, mode, resolvedTypeReferenceDirective);
}
}
+ function pathForLibFile(libFileName: string): string {
+ // Support resolving to lib.dom.d.ts -> @typescript/lib-dom, and
+ // lib.dom.iterable.d.ts -> @typescript/lib-dom/iterable
+ // lib.es2015.symbol.wellknown.d.ts -> @typescript/lib-es2015/symbol-wellknown
+ const components = libFileName.split(".");
+ let path = components[1];
+ let i = 2;
+ while (components[i] && components[i] !== "d") {
+ path += (i === 2 ? "/" : "-") + components[i];
+ i++;
+ }
+ const resolveFrom = combinePaths(currentDirectory, `__lib_node_modules_lookup_${libFileName}__.ts`);
+ const localOverrideModuleResult = resolveModuleName("@typescript/lib-" + path, resolveFrom, { moduleResolution: ModuleResolutionKind.NodeJs }, host, moduleResolutionCache);
+ if (localOverrideModuleResult?.resolvedModule) {
+ return localOverrideModuleResult.resolvedModule.resolvedFileName;
+ }
+ return combinePaths(defaultLibraryPath, libFileName);
+ }
+
function processLibReferenceDirectives(file: SourceFile) {
forEach(file.libReferenceDirectives, (libReference, index) => {
const libName = toFileNameLowerCase(libReference.fileName);
const libFileName = libMap.get(libName);
if (libFileName) {
// we ignore any 'no-default-lib' reference set on this file.
- processRootFile(combinePaths(defaultLibraryPath, libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ true, { kind: FileIncludeKind.LibReferenceDirective, file: file.path, index, });
+ processRootFile(pathForLibFile(libFileName), /*isDefaultLib*/ true, /*ignoreNoDefaultLib*/ true, { kind: FileIncludeKind.LibReferenceDirective, file: file.path, index, });
}
else {
const unqualifiedLibName = removeSuffix(removePrefix(libName, "lib."), ".d.ts");
@@ -2919,7 +3156,7 @@ namespace ts {
const optionsForFile = (useSourceOfProjectReferenceRedirect ? getRedirectReferenceForResolution(file)?.commandLine.options : undefined) || options;
for (let index = 0; index < moduleNames.length; index++) {
const resolution = resolutions[index];
- setResolvedModule(file, moduleNames[index], resolution);
+ setResolvedModule(file, moduleNames[index], resolution, getModeForResolutionAtIndex(file, index));
if (!resolution) {
continue;
@@ -3045,6 +3282,21 @@ namespace ts {
}
function verifyCompilerOptions() {
+ const isNightly = stringContains(version, "-dev") || stringContains(version, "-insiders");
+ if (!isNightly) {
+ if (getEmitModuleKind(options) === ModuleKind.Node12) {
+ createOptionValueDiagnostic("module", Diagnostics.Compiler_option_0_of_value_1_is_unstable_Use_nightly_TypeScript_to_silence_this_error_Try_updating_with_npm_install_D_typescript_next, "module", "node12");
+ }
+ else if (getEmitModuleKind(options) === ModuleKind.NodeNext) {
+ createOptionValueDiagnostic("module", Diagnostics.Compiler_option_0_of_value_1_is_unstable_Use_nightly_TypeScript_to_silence_this_error_Try_updating_with_npm_install_D_typescript_next, "module", "nodenext");
+ }
+ else if (getEmitModuleResolutionKind(options) === ModuleResolutionKind.Node12) {
+ createOptionValueDiagnostic("moduleResolution", Diagnostics.Compiler_option_0_of_value_1_is_unstable_Use_nightly_TypeScript_to_silence_this_error_Try_updating_with_npm_install_D_typescript_next, "moduleResolution", "node12");
+ }
+ else if (getEmitModuleResolutionKind(options) === ModuleResolutionKind.NodeNext) {
+ createOptionValueDiagnostic("moduleResolution", Diagnostics.Compiler_option_0_of_value_1_is_unstable_Use_nightly_TypeScript_to_silence_this_error_Try_updating_with_npm_install_D_typescript_next, "moduleResolution", "nodenext");
+ }
+ }
if (options.strictPropertyInitialization && !getStrictOptionValue(options, "strictNullChecks")) {
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "strictPropertyInitialization", "strictNullChecks");
}
@@ -3181,7 +3433,7 @@ namespace ts {
createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "noImplicitUseStrict", "alwaysStrict");
}
- const languageVersion = options.target || ScriptTarget.ES3;
+ const languageVersion = getEmitScriptTarget(options);
const firstNonAmbientExternalModuleSourceFile = find(files, f => isExternalModule(f) && !f.isDeclarationFile);
if (options.isolatedModules) {
@@ -3218,7 +3470,9 @@ namespace ts {
}
if (options.resolveJsonModule) {
- if (getEmitModuleResolutionKind(options) !== ModuleResolutionKind.NodeJs) {
+ if (getEmitModuleResolutionKind(options) !== ModuleResolutionKind.NodeJs &&
+ getEmitModuleResolutionKind(options) !== ModuleResolutionKind.Node12 &&
+ getEmitModuleResolutionKind(options) !== ModuleResolutionKind.NodeNext) {
createDiagnosticForOptionName(Diagnostics.Option_resolveJsonModule_cannot_be_specified_without_node_module_resolution_strategy, "resolveJsonModule");
}
// Any emit other than common js, amd, es2015 or esnext is error
@@ -3470,7 +3724,7 @@ namespace ts {
message = Diagnostics.File_is_library_specified_here;
break;
}
- const target = forEachEntry(targetOptionDeclaration.type, (value, key) => value === options.target ? key : undefined);
+ const target = forEachEntry(targetOptionDeclaration.type, (value, key) => value === getEmitScriptTarget(options) ? key : undefined);
configFileNode = target ? getOptionsSyntaxByValue("target", target) : undefined;
message = Diagnostics.File_is_default_library_for_target_specified_here;
break;
@@ -3579,8 +3833,8 @@ namespace ts {
createDiagnosticForOption(/*onKey*/ true, option1, option2, message, option1, option2, option3);
}
- function createOptionValueDiagnostic(option1: string, message: DiagnosticMessage, arg0?: string) {
- createDiagnosticForOption(/*onKey*/ false, option1, /*option2*/ undefined, message, arg0);
+ function createOptionValueDiagnostic(option1: string, message: DiagnosticMessage, arg0?: string, arg1?: string) {
+ createDiagnosticForOption(/*onKey*/ false, option1, /*option2*/ undefined, message, arg0, arg1);
}
function createDiagnosticForReference(sourceFile: JsonSourceFile | undefined, index: number, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number) {
@@ -3660,7 +3914,7 @@ namespace ts {
return containsPath(options.outDir, filePath, currentDirectory, !host.useCaseSensitiveFileNames());
}
- if (fileExtensionIsOneOf(filePath, supportedJSExtensions) || fileExtensionIs(filePath, Extension.Dts)) {
+ if (fileExtensionIsOneOf(filePath, supportedJSExtensionsFlat) || fileExtensionIs(filePath, Extension.Dts)) {
// Otherwise just check if sourceFile with the name exists
const filePathWithoutExtension = removeFileExtension(filePath);
return !!getSourceFileByPath((filePathWithoutExtension + Extension.Ts) as Path) ||
@@ -4014,7 +4268,7 @@ namespace ts {
}
/* @internal */
- export function getModuleNameStringLiteralAt({ imports, moduleAugmentations }: SourceFile, index: number): StringLiteralLike {
+ export function getModuleNameStringLiteralAt({ imports, moduleAugmentations }: SourceFileImportsList, index: number): StringLiteralLike {
if (index < imports.length) return imports[index];
let augIndex = imports.length;
for (const aug of moduleAugmentations) {
diff --git a/src/compiler/resolutionCache.ts b/src/compiler/resolutionCache.ts
index f15cc9766d5..c42d99c33c4 100644
--- a/src/compiler/resolutionCache.ts
+++ b/src/compiler/resolutionCache.ts
@@ -5,9 +5,9 @@ namespace ts {
startRecordingFilesWithChangedResolutions(): void;
finishRecordingFilesWithChangedResolutions(): Path[] | undefined;
- resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference?: ResolvedProjectReference): (ResolvedModuleFull | undefined)[];
- getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string): CachedResolvedModuleWithFailedLookupLocations | undefined;
- resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string, redirectedReference?: ResolvedProjectReference): (ResolvedTypeReferenceDirective | undefined)[];
+ resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference?: ResolvedProjectReference, containingSourceFile?: SourceFile): (ResolvedModuleFull | undefined)[];
+ getResolvedModuleWithFailedLookupLocationsFromCache(moduleName: string, containingFile: string, resolutionMode?: ModuleKind.CommonJS | ModuleKind.ESNext): CachedResolvedModuleWithFailedLookupLocations | undefined;
+ resolveTypeReferenceDirectives(typeDirectiveNames: string[] | readonly FileReference[], containingFile: string, redirectedReference?: ResolvedProjectReference, containingFileMode?: SourceFile["impliedNodeFormat"]): (ResolvedTypeReferenceDirective | undefined)[];
invalidateResolutionsOfFailedLookupLocations(): boolean;
invalidateResolutionOfFile(filePath: Path): void;
@@ -66,6 +66,7 @@ namespace ts {
getCurrentProgram(): Program | undefined;
fileIsOpen(filePath: Path): boolean;
getCompilerHost?(): CompilerHost | undefined;
+ onDiscoveredSymlink?(): void;
}
interface DirectoryWatchesOfFailedLookup {
@@ -166,8 +167,8 @@ namespace ts {
// The resolvedModuleNames and resolvedTypeReferenceDirectives are the cache of resolutions per file.
// The key in the map is source file's path.
// The values are Map of resolutions with key being name lookedup.
- const resolvedModuleNames = new Map>();
- const perDirectoryResolvedModuleNames: CacheWithRedirects> = createCacheWithRedirects();
+ const resolvedModuleNames = new Map>();
+ const perDirectoryResolvedModuleNames: CacheWithRedirects> = createCacheWithRedirects();
const nonRelativeModuleNameCache: CacheWithRedirects = createCacheWithRedirects();
const moduleResolutionCache = createModuleResolutionCache(
getCurrentDirectory(),
@@ -177,8 +178,8 @@ namespace ts {
nonRelativeModuleNameCache,
);
- const resolvedTypeReferenceDirectives = new Map>();
- const perDirectoryResolvedTypeReferenceDirectives: CacheWithRedirects> = createCacheWithRedirects();
+ const resolvedTypeReferenceDirectives = new Map>();
+ const perDirectoryResolvedTypeReferenceDirectives: CacheWithRedirects> = createCacheWithRedirects();
const typeReferenceDirectiveResolutionCache = createTypeReferenceDirectiveResolutionCache(
getCurrentDirectory(),
resolutionHost.getCanonicalFileName,
@@ -314,8 +315,8 @@ namespace ts {
hasChangedAutomaticTypeDirectiveNames = false;
}
- function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, redirectedReference?: ResolvedProjectReference): CachedResolvedModuleWithFailedLookupLocations {
- const primaryResult = ts.resolveModuleName(moduleName, containingFile, compilerOptions, host, moduleResolutionCache, redirectedReference);
+ function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost, redirectedReference?: ResolvedProjectReference, _containingSourceFile?: never, mode?: ModuleKind.CommonJS | ModuleKind.ESNext | undefined): CachedResolvedModuleWithFailedLookupLocations {
+ const primaryResult = ts.resolveModuleName(moduleName, containingFile, compilerOptions, host, moduleResolutionCache, redirectedReference, mode);
// return result immediately only if global cache support is not enabled or if it is .ts, .tsx or .d.ts
if (!resolutionHost.getGlobalCache) {
return primaryResult;
@@ -346,35 +347,37 @@ namespace ts {
return primaryResult;
}
- function resolveTypeReferenceDirective(typeReferenceDirectiveName: string, containingFile: string | undefined, options: CompilerOptions, host: ModuleResolutionHost, redirectedReference?: ResolvedProjectReference): CachedResolvedTypeReferenceDirectiveWithFailedLookupLocations {
- return ts.resolveTypeReferenceDirective(typeReferenceDirectiveName, containingFile, options, host, redirectedReference, typeReferenceDirectiveResolutionCache);
+ function resolveTypeReferenceDirective(typeReferenceDirectiveName: string, containingFile: string | undefined, options: CompilerOptions, host: ModuleResolutionHost, redirectedReference?: ResolvedProjectReference, _containingSourceFile?: SourceFile, resolutionMode?: SourceFile["impliedNodeFormat"] | undefined): CachedResolvedTypeReferenceDirectiveWithFailedLookupLocations {
+ return ts.resolveTypeReferenceDirective(typeReferenceDirectiveName, containingFile, options, host, redirectedReference, typeReferenceDirectiveResolutionCache, resolutionMode);
}
interface ResolveNamesWithLocalCacheInput