Merge branch 'master' into typesVersions

This commit is contained in:
Ron Buckton
2018-08-28 17:37:23 -07:00
225 changed files with 43601 additions and 32361 deletions
+303 -217
View File
@@ -31,6 +31,18 @@ namespace ts {
}
export function createTypeChecker(host: TypeCheckerHost, produceDiagnostics: boolean): TypeChecker {
const getPackagesSet: () => Map<true> = memoize(() => {
const set = createMap<true>();
host.getSourceFiles().forEach(sf => {
if (!sf.resolvedModules) return;
forEachEntry(sf.resolvedModules, r => {
if (r && r.packageId) set.set(r.packageId.name, true);
});
});
return set;
});
// Cancellation that controls whether or not we can cancel in the middle of type checking.
// In general cancelling is *not* safe for the type checker. We might be in the middle of
// computing something, and we will leave our internals in an inconsistent state. Callers
@@ -53,7 +65,8 @@ namespace ts {
let typeCount = 0;
let symbolCount = 0;
let enumCount = 0;
let symbolInstantiationDepth = 0;
let instantiationDepth = 0;
let constraintDepth = 0;
const emptySymbols = createSymbolTable();
const identityMapper: (type: Type) => Type = identity;
@@ -357,7 +370,9 @@ namespace ts {
finally {
cancellationToken = undefined;
}
}
},
getLocalTypeParametersOfClassOrInterfaceOrTypeAlias,
};
function getResolvedSignatureWorker(nodeIn: CallLikeExpression, candidatesOutArray: Signature[] | undefined, argumentCount: number | undefined, isForSignatureHelp: boolean): Signature | undefined {
@@ -1195,17 +1210,23 @@ namespace ts {
// local types not visible outside the function body
: false;
}
if (meaning & SymbolFlags.Value && result.flags & SymbolFlags.FunctionScopedVariable) {
// parameters are visible only inside function body, parameter list and return type
// technically for parameter list case here we might mix parameters and variables declared in function,
// however it is detected separately when checking initializers of parameters
// to make sure that they reference no variables declared after them.
useResult =
if (meaning & SymbolFlags.Value && result.flags & SymbolFlags.Variable) {
// parameter initializer will lookup as normal variable scope when targeting es2015+
if (compilerOptions.target && compilerOptions.target >= ScriptTarget.ES2015 && isParameter(lastLocation) && result.valueDeclaration !== lastLocation) {
useResult = false;
}
else if (result.flags & SymbolFlags.FunctionScopedVariable) {
// parameters are visible only inside function body, parameter list and return type
// technically for parameter list case here we might mix parameters and variables declared in function,
// however it is detected separately when checking initializers of parameters
// to make sure that they reference no variables declared after them.
useResult =
lastLocation.kind === SyntaxKind.Parameter ||
(
lastLocation === (<FunctionLikeDeclaration>location).type &&
!!findAncestor(result.valueDeclaration, isParameter)
);
}
}
}
else if (location.kind === SyntaxKind.ConditionalType) {
@@ -2264,12 +2285,9 @@ namespace ts {
resolvedFileName));
}
function typesPackageExists(packageName: string): boolean {
return host.getSourceFiles().some(sf => !!sf.resolvedModules && !!forEachEntry(sf.resolvedModules, r =>
r && r.packageId && r.packageId.name === getTypesPackageName(packageName)));
return getPackagesSet().has(getTypesPackageName(packageName));
}
// An external module with an 'export =' declaration resolves to the target of the 'export =' declaration,
// and an external module with no 'export =' declaration resolves to the module itself.
function resolveExternalModuleSymbol(moduleSymbol: Symbol, dontResolveAlias?: boolean): Symbol;
function resolveExternalModuleSymbol(moduleSymbol: Symbol | undefined, dontResolveAlias?: boolean): Symbol | undefined;
function resolveExternalModuleSymbol(moduleSymbol: Symbol, dontResolveAlias?: boolean): Symbol {
@@ -3908,13 +3926,22 @@ namespace ts {
const links = getSymbolLinks(symbol);
let specifier = links.specifierCache && links.specifierCache.get(contextFile.path);
if (!specifier) {
specifier = moduleSpecifiers.getModuleSpecifierForDeclarationFile(
const isBundle = (compilerOptions.out || compilerOptions.outFile);
// For declaration bundles, we need to generate absolute paths relative to the common source dir for imports,
// just like how the declaration emitter does for the ambient module declarations - we can easily accomplish this
// using the `baseUrl` compiler option (which we would otherwise never use in declaration emit) and a non-relative
// specifier preference
const { moduleResolverHost } = context.tracker;
const specifierCompilerOptions = isBundle ? { ...compilerOptions, baseUrl: moduleResolverHost.getCommonSourceDirectory() } : compilerOptions;
specifier = first(first(moduleSpecifiers.getModuleSpecifiers(
symbol,
compilerOptions,
specifierCompilerOptions,
contextFile,
context.tracker.moduleResolverHost,
moduleResolverHost,
host.getSourceFiles(),
{ importModuleSpecifierPreference: isBundle ? "non-relative" : "relative" },
host.redirectTargetsMap,
);
)));
links.specifierCache = links.specifierCache || createMap();
links.specifierCache.set(contextFile.path, specifier);
}
@@ -4698,6 +4725,12 @@ namespace ts {
return getReturnTypeOfSignature(getterSignature);
}
}
if (isInJavaScriptFile(declaration)) {
const typeTag = getJSDocType(func);
if (typeTag && isFunctionTypeNode(typeTag)) {
return getTypeAtPosition(getSignatureFromDeclaration(typeTag), func.parameters.indexOf(declaration));
}
}
// Use contextual parameter type if one is available
const type = declaration.symbol.escapedName === InternalSymbolName.This ? getContextualThisParameterType(func) : getContextuallyTypedParameterType(declaration);
if (type) {
@@ -4794,7 +4827,7 @@ namespace ts {
}
function getJSExpandoObjectType(decl: Node, symbol: Symbol, init: Expression | undefined): Type | undefined {
if (!init || !isObjectLiteralExpression(init) || init.properties.length) {
if (!isInJavaScriptFile(decl) || !init || !isObjectLiteralExpression(init) || init.properties.length) {
return undefined;
}
const exports = createSymbolTable();
@@ -5303,22 +5336,14 @@ namespace ts {
function getTypeOfInstantiatedSymbol(symbol: Symbol): Type {
const links = getSymbolLinks(symbol);
if (!links.type) {
if (symbolInstantiationDepth === 100) {
error(symbol.valueDeclaration, Diagnostics.Generic_type_instantiation_is_excessively_deep_and_possibly_infinite);
links.type = errorType;
if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {
return links.type = errorType;
}
else {
if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {
return links.type = errorType;
}
symbolInstantiationDepth++;
let type = instantiateType(getTypeOfSymbol(links.target!), links.mapper!);
symbolInstantiationDepth--;
if (!popTypeResolution()) {
type = reportCircularityError(symbol);
}
links.type = type;
let type = instantiateType(getTypeOfSymbol(links.target!), links.mapper!);
if (!popTypeResolution()) {
type = reportCircularityError(symbol);
}
links.type = type;
}
return links.type;
}
@@ -6906,10 +6931,15 @@ namespace ts {
}
function getConstraintOfIndexedAccess(type: IndexedAccessType) {
const objectType = getBaseConstraintOfType(type.objectType) || type.objectType;
const indexType = getBaseConstraintOfType(type.indexType) || type.indexType;
const constraint = !isGenericObjectType(objectType) && !isGenericIndexType(indexType) ? getIndexedAccessType(objectType, indexType) : undefined;
return constraint && constraint !== errorType ? constraint : undefined;
const objectType = getConstraintOfType(type.objectType) || type.objectType;
if (objectType !== type.objectType) {
const constraint = getIndexedAccessType(objectType, type.indexType, /*accessNode*/ undefined, errorType);
if (constraint && constraint !== errorType) {
return constraint;
}
}
const baseConstraint = getBaseConstraintOfType(type);
return baseConstraint && baseConstraint !== type ? baseConstraint : undefined;
}
function getDefaultConstraintOfConditionalType(type: ConditionalType) {
@@ -6928,7 +6958,8 @@ namespace ts {
// over the conditional type and possibly reduced. For example, 'T extends undefined ? never : T'
// removes 'undefined' from T.
if (type.root.isDistributive) {
const constraint = getConstraintOfType(getSimplifiedType(type.checkType));
const simplified = getSimplifiedType(type.checkType);
const constraint = simplified === type.checkType ? getConstraintOfType(simplified) : simplified;
if (constraint) {
const mapper = makeUnaryTypeMapper(type.root.checkType, constraint);
const instantiated = getConditionalTypeInstantiation(type, combineTypeMappers(mapper, type.mapper));
@@ -7011,6 +7042,7 @@ namespace ts {
* circularly references the type variable.
*/
function getResolvedBaseConstraint(type: InstantiableType | UnionOrIntersectionType): Type {
let nonTerminating = false;
return type.resolvedBaseConstraint ||
(type.resolvedBaseConstraint = getTypeWithThisArgument(getImmediateBaseConstraint(type), type));
@@ -7019,8 +7051,18 @@ namespace ts {
if (!pushTypeResolution(t, TypeSystemPropertyName.ImmediateBaseConstraint)) {
return circularConstraintType;
}
if (constraintDepth === 50) {
// We have reached 50 recursive invocations of getImmediateBaseConstraint and there is a
// very high likelyhood we're dealing with an infinite generic type that perpetually generates
// new type identities as we descend into it. We stop the recursion here and mark this type
// and the outer types as having circular constraints.
nonTerminating = true;
return t.immediateBaseConstraint = noConstraintType;
}
constraintDepth++;
let result = computeBaseConstraint(getSimplifiedType(t));
if (!popTypeResolution()) {
constraintDepth--;
if (!popTypeResolution() || nonTerminating) {
result = circularConstraintType;
}
t.immediateBaseConstraint = result || noConstraintType;
@@ -7059,7 +7101,7 @@ namespace ts {
if (t.flags & TypeFlags.IndexedAccess) {
const baseObjectType = getBaseConstraint((<IndexedAccessType>t).objectType);
const baseIndexType = getBaseConstraint((<IndexedAccessType>t).indexType);
const baseIndexedAccess = baseObjectType && baseIndexType ? getIndexedAccessType(baseObjectType, baseIndexType) : undefined;
const baseIndexedAccess = baseObjectType && baseIndexType ? getIndexedAccessType(baseObjectType, baseIndexType, /*accessNode*/ undefined, errorType) : undefined;
return baseIndexedAccess && baseIndexedAccess !== errorType ? getBaseConstraint(baseIndexedAccess) : undefined;
}
if (t.flags & TypeFlags.Conditional) {
@@ -7069,9 +7111,6 @@ namespace ts {
if (t.flags & TypeFlags.Substitution) {
return getBaseConstraint((<SubstitutionType>t).substitute);
}
if (isGenericMappedType(t)) {
return emptyObjectType;
}
return t;
}
}
@@ -9144,7 +9183,7 @@ namespace ts {
return false;
}
function getPropertyTypeForIndexType(objectType: Type, indexType: Type, accessNode: ElementAccessExpression | IndexedAccessTypeNode | undefined, cacheSymbol: boolean) {
function getPropertyTypeForIndexType(objectType: Type, indexType: Type, accessNode: ElementAccessExpression | IndexedAccessTypeNode | undefined, cacheSymbol: boolean, missingType: Type) {
const accessExpression = accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression ? accessNode : undefined;
const propName = isTypeUsableAsLateBoundName(indexType) ? getLateBoundNameFromType(indexType) :
accessExpression && checkThatExpressionIsProperSymbolReference(accessExpression.argumentExpression, indexType, /*reportError*/ false) ?
@@ -9157,7 +9196,7 @@ namespace ts {
markPropertyAsReferenced(prop, accessExpression, /*isThisAccess*/ accessExpression.expression.kind === SyntaxKind.ThisKeyword);
if (isAssignmentTarget(accessExpression) && (isReferenceToReadonlyEntity(accessExpression, prop) || isReferenceThroughNamespaceImport(accessExpression))) {
error(accessExpression.argumentExpression, Diagnostics.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, symbolToString(prop));
return errorType;
return missingType;
}
if (cacheSymbol) {
getNodeLinks(accessNode!).resolvedSymbol = prop;
@@ -9216,7 +9255,7 @@ namespace ts {
}
}
}
return anyType;
return missingType;
}
}
if (isJSLiteralType(objectType)) {
@@ -9234,7 +9273,10 @@ namespace ts {
error(indexNode, Diagnostics.Type_0_cannot_be_used_as_an_index_type, typeToString(indexType));
}
}
return errorType;
if (isTypeAny(indexType)) {
return indexType;
}
return missingType;
}
function isGenericObjectType(type: Type): boolean {
@@ -9245,22 +9287,6 @@ namespace ts {
return maybeTypeOfKind(type, TypeFlags.InstantiableNonPrimitive | TypeFlags.Index);
}
// Return true if the given type is a non-generic object type with a string index signature and no
// other members.
function isStringIndexOnlyType(type: Type): boolean {
if (type.flags & TypeFlags.Object && !isGenericMappedType(type)) {
const t = resolveStructuredTypeMembers(<ObjectType>type);
return t.properties.length === 0 &&
t.callSignatures.length === 0 && t.constructSignatures.length === 0 &&
!!t.stringIndexInfo && !t.numberIndexInfo;
}
return false;
}
function isMappedTypeToNever(type: Type): boolean {
return !!(getObjectFlags(type) & ObjectFlags.Mapped) && getTemplateTypeFromMappedType(type as MappedType) === neverType;
}
function getSimplifiedType(type: Type): Type {
return type.flags & TypeFlags.IndexedAccess ? getSimplifiedIndexedAccessType(<IndexedAccessType>type) : type;
}
@@ -9275,35 +9301,12 @@ namespace ts {
// We recursively simplify the object type as it may in turn be an indexed access type. For example, with
// '{ [P in T]: { [Q in U]: number } }[T][U]' we want to first simplify the inner indexed access type.
const objectType = getSimplifiedType(type.objectType);
if (objectType.flags & TypeFlags.Intersection && isGenericObjectType(objectType)) {
// Given an indexed access type T[K], if T is an intersection containing one or more generic types and one or
// more object types with only a string index signature, e.g. '(U & V & { [x: string]: D })[K]', return a
// transformed type of the form '(U & V)[K] | D'. This allows us to properly reason about higher order indexed
// access types with default property values as expressed by D.
if (some((<IntersectionType>objectType).types, isStringIndexOnlyType)) {
const regularTypes: Type[] = [];
const stringIndexTypes: Type[] = [];
for (const t of (<IntersectionType>objectType).types) {
if (isStringIndexOnlyType(t)) {
stringIndexTypes.push(getIndexTypeOfType(t, IndexKind.String)!);
}
else {
regularTypes.push(t);
}
}
return type.simplified = getUnionType([
getSimplifiedType(getIndexedAccessType(getIntersectionType(regularTypes), type.indexType)),
getIntersectionType(stringIndexTypes)
]);
}
// Given an indexed access type T[K], if T is an intersection containing one or more generic types and one or
// more mapped types with a template type `never`, '(U & V & { [P in T]: never })[K]', return a
// transformed type that removes the never-mapped type: '(U & V)[K]'. This mirrors what would happen
// eventually anyway, but it easier to reason about.
if (some((<IntersectionType>objectType).types, isMappedTypeToNever)) {
const nonNeverTypes = filter((<IntersectionType>objectType).types, t => !isMappedTypeToNever(t));
return type.simplified = getSimplifiedType(getIndexedAccessType(getIntersectionType(nonNeverTypes), type.indexType));
}
const indexType = getSimplifiedType(type.indexType);
if (objectType.flags & TypeFlags.Union) {
return type.simplified = mapType(objectType, t => getSimplifiedType(getIndexedAccessType(t, indexType)));
}
if (objectType.flags & TypeFlags.Intersection) {
return type.simplified = getIntersectionType(map((objectType as IntersectionType).types, t => getSimplifiedType(getIndexedAccessType(t, indexType))));
}
// If the object type is a mapped type { [P in K]: E }, where K is generic, instantiate E using a mapper
// that substitutes the index type for P. For example, for an index access { [P in K]: Box<T[P]> }[X], we
@@ -9327,7 +9330,7 @@ namespace ts {
return instantiateType(getTemplateTypeFromMappedType(objectType), templateMapper);
}
function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode): Type {
function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode, missingType = accessNode ? errorType : unknownType): Type {
if (objectType === wildcardType || indexType === wildcardType) {
return wildcardType;
}
@@ -9355,15 +9358,15 @@ namespace ts {
if (indexType.flags & TypeFlags.Union && !(indexType.flags & TypeFlags.Boolean)) {
const propTypes: Type[] = [];
for (const t of (<UnionType>indexType).types) {
const propType = getPropertyTypeForIndexType(apparentObjectType, t, accessNode, /*cacheSymbol*/ false);
if (propType === errorType) {
return errorType;
const propType = getPropertyTypeForIndexType(apparentObjectType, t, accessNode, /*cacheSymbol*/ false, missingType);
if (propType === missingType) {
return missingType;
}
propTypes.push(propType);
}
return getUnionType(propTypes);
}
return getPropertyTypeForIndexType(apparentObjectType, indexType, accessNode, /*cacheSymbol*/ true);
return getPropertyTypeForIndexType(apparentObjectType, indexType, accessNode, /*cacheSymbol*/ true, missingType);
}
function getTypeFromIndexedAccessTypeNode(node: IndexedAccessTypeNode) {
@@ -10293,49 +10296,66 @@ namespace ts {
function instantiateType(type: Type, mapper: TypeMapper | undefined): Type;
function instantiateType(type: Type | undefined, mapper: TypeMapper | undefined): Type | undefined;
function instantiateType(type: Type | undefined, mapper: TypeMapper | undefined): Type | undefined {
if (type && mapper && mapper !== identityMapper) {
if (type.flags & TypeFlags.TypeParameter) {
return mapper(<TypeParameter>type);
if (!type || !mapper || mapper === identityMapper) {
return type;
}
if (instantiationDepth === 50) {
// We have reached 50 recursive type instantiations and there is a very high likelyhood we're dealing
// with a combination of infinite generic types that perpetually generate new type identities. We stop
// the recursion here by yielding the error type.
return errorType;
}
instantiationDepth++;
const result = instantiateTypeWorker(type, mapper);
instantiationDepth--;
return result;
}
function instantiateTypeWorker(type: Type, mapper: TypeMapper): Type {
const flags = type.flags;
if (flags & TypeFlags.TypeParameter) {
return mapper(type);
}
if (flags & TypeFlags.Object) {
const objectFlags = (<ObjectType>type).objectFlags;
if (objectFlags & ObjectFlags.Anonymous) {
// If the anonymous type originates in a declaration of a function, method, class, or
// interface, in an object type literal, or in an object literal expression, we may need
// to instantiate the type because it might reference a type parameter.
return type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) && type.symbol.declarations ?
getAnonymousTypeInstantiation(<AnonymousType>type, mapper) : type;
}
if (type.flags & TypeFlags.Object) {
if ((<ObjectType>type).objectFlags & ObjectFlags.Anonymous) {
// If the anonymous type originates in a declaration of a function, method, class, or
// interface, in an object type literal, or in an object literal expression, we may need
// to instantiate the type because it might reference a type parameter.
return type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) && type.symbol.declarations ?
getAnonymousTypeInstantiation(<AnonymousType>type, mapper) : type;
}
if ((<ObjectType>type).objectFlags & ObjectFlags.Mapped) {
return getAnonymousTypeInstantiation(<MappedType>type, mapper);
}
if ((<ObjectType>type).objectFlags & ObjectFlags.Reference) {
const typeArguments = (<TypeReference>type).typeArguments;
const newTypeArguments = instantiateTypes(typeArguments, mapper);
return newTypeArguments !== typeArguments ? createTypeReference((<TypeReference>type).target, newTypeArguments) : type;
}
if (objectFlags & ObjectFlags.Mapped) {
return getAnonymousTypeInstantiation(<AnonymousType>type, mapper);
}
if (type.flags & TypeFlags.Union && !(type.flags & TypeFlags.Primitive)) {
const types = (<UnionType>type).types;
const newTypes = instantiateTypes(types, mapper);
return newTypes !== types ? getUnionType(newTypes, UnionReduction.Literal, type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper)) : type;
}
if (type.flags & TypeFlags.Intersection) {
const types = (<IntersectionType>type).types;
const newTypes = instantiateTypes(types, mapper);
return newTypes !== types ? getIntersectionType(newTypes, type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper)) : type;
}
if (type.flags & TypeFlags.Index) {
return getIndexType(instantiateType((<IndexType>type).type, mapper));
}
if (type.flags & TypeFlags.IndexedAccess) {
return getIndexedAccessType(instantiateType((<IndexedAccessType>type).objectType, mapper), instantiateType((<IndexedAccessType>type).indexType, mapper));
}
if (type.flags & TypeFlags.Conditional) {
return getConditionalTypeInstantiation(<ConditionalType>type, combineTypeMappers((<ConditionalType>type).mapper, mapper));
}
if (type.flags & TypeFlags.Substitution) {
return instantiateType((<SubstitutionType>type).typeVariable, mapper);
if (objectFlags & ObjectFlags.Reference) {
const typeArguments = (<TypeReference>type).typeArguments;
const newTypeArguments = instantiateTypes(typeArguments, mapper);
return newTypeArguments !== typeArguments ? createTypeReference((<TypeReference>type).target, newTypeArguments) : type;
}
return type;
}
if (flags & TypeFlags.Union && !(flags & TypeFlags.Primitive)) {
const types = (<UnionType>type).types;
const newTypes = instantiateTypes(types, mapper);
return newTypes !== types ? getUnionType(newTypes, UnionReduction.Literal, type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper)) : type;
}
if (flags & TypeFlags.Intersection) {
const types = (<IntersectionType>type).types;
const newTypes = instantiateTypes(types, mapper);
return newTypes !== types ? getIntersectionType(newTypes, type.aliasSymbol, instantiateTypes(type.aliasTypeArguments, mapper)) : type;
}
if (flags & TypeFlags.Index) {
return getIndexType(instantiateType((<IndexType>type).type, mapper));
}
if (flags & TypeFlags.IndexedAccess) {
return getIndexedAccessType(instantiateType((<IndexedAccessType>type).objectType, mapper), instantiateType((<IndexedAccessType>type).indexType, mapper));
}
if (flags & TypeFlags.Conditional) {
return getConditionalTypeInstantiation(<ConditionalType>type, combineTypeMappers((<ConditionalType>type).mapper, mapper));
}
if (flags & TypeFlags.Substitution) {
return instantiateType((<SubstitutionType>type).typeVariable, mapper);
}
return type;
}
@@ -10510,8 +10530,12 @@ namespace ts {
return false;
}
function isOrHasGenericConditional(type: Type): boolean {
return !!(type.flags & TypeFlags.Conditional || (type.flags & TypeFlags.Intersection && some((type as IntersectionType).types, isOrHasGenericConditional)));
}
function elaborateError(node: Expression | undefined, source: Type, target: Type): boolean {
if (!node) return false;
if (!node || isOrHasGenericConditional(target)) return false;
switch (node.kind) {
case SyntaxKind.JsxExpression:
case SyntaxKind.ParenthesizedExpression:
@@ -10544,9 +10568,9 @@ namespace ts {
let reportedError = false;
for (let status = iterator.next(); !status.done; status = iterator.next()) {
const { errorNode: prop, innerExpression: next, nameType, errorMessage } = status.value;
const sourcePropType = getIndexedAccessType(source, nameType);
const targetPropType = getIndexedAccessType(target, nameType);
if (!isTypeAssignableTo(sourcePropType, targetPropType)) {
const sourcePropType = getIndexedAccessType(source, nameType, /*accessNode*/ undefined, errorType);
const targetPropType = getIndexedAccessType(target, nameType, /*accessNode*/ undefined, errorType);
if (sourcePropType !== errorType && targetPropType !== errorType && !isTypeAssignableTo(sourcePropType, targetPropType)) {
const elaborated = next && elaborateError(next, sourcePropType, targetPropType);
if (elaborated) {
reportedError = true;
@@ -11156,7 +11180,7 @@ namespace ts {
}
if (relation !== comparableRelation &&
!(source.flags & TypeFlags.UnionOrIntersection) &&
!(source.flags & TypeFlags.Union) &&
!(target.flags & TypeFlags.Union) &&
!isIntersectionConstituent &&
source !== globalObjectType &&
@@ -11265,7 +11289,7 @@ namespace ts {
function isIdenticalTo(source: Type, target: Type): Ternary {
let result: Ternary;
const flags = source.flags & target.flags;
if (flags & TypeFlags.Object) {
if (flags & TypeFlags.Object || flags & TypeFlags.IndexedAccess || flags & TypeFlags.Conditional || flags & TypeFlags.Index || flags & TypeFlags.Substitution) {
return recursiveTypeRelatedTo(source, target, /*reportErrors*/ false);
}
if (flags & (TypeFlags.Union | TypeFlags.Intersection)) {
@@ -11275,32 +11299,6 @@ namespace ts {
}
}
}
if (flags & TypeFlags.Index) {
return isRelatedTo((<IndexType>source).type, (<IndexType>target).type, /*reportErrors*/ false);
}
if (flags & TypeFlags.IndexedAccess) {
if (result = isRelatedTo((<IndexedAccessType>source).objectType, (<IndexedAccessType>target).objectType, /*reportErrors*/ false)) {
if (result &= isRelatedTo((<IndexedAccessType>source).indexType, (<IndexedAccessType>target).indexType, /*reportErrors*/ false)) {
return result;
}
}
}
if (flags & TypeFlags.Conditional) {
if ((<ConditionalType>source).root.isDistributive === (<ConditionalType>target).root.isDistributive) {
if (result = isRelatedTo((<ConditionalType>source).checkType, (<ConditionalType>target).checkType, /*reportErrors*/ false)) {
if (result &= isRelatedTo((<ConditionalType>source).extendsType, (<ConditionalType>target).extendsType, /*reportErrors*/ false)) {
if (result &= isRelatedTo(getTrueTypeFromConditionalType(<ConditionalType>source), getTrueTypeFromConditionalType(<ConditionalType>target), /*reportErrors*/ false)) {
if (result &= isRelatedTo(getFalseTypeFromConditionalType(<ConditionalType>source), getFalseTypeFromConditionalType(<ConditionalType>target), /*reportErrors*/ false)) {
return result;
}
}
}
}
}
}
if (flags & TypeFlags.Substitution) {
return isRelatedTo((<SubstitutionType>source).substitute, (<SubstitutionType>target).substitute, /*reportErrors*/ false);
}
return Ternary.False;
}
@@ -11612,6 +11610,37 @@ namespace ts {
}
function structuredTypeRelatedTo(source: Type, target: Type, reportErrors: boolean): Ternary {
const flags = source.flags & target.flags;
if (relation === identityRelation && !(flags & TypeFlags.Object)) {
if (flags & TypeFlags.Index) {
return isRelatedTo((<IndexType>source).type, (<IndexType>target).type, /*reportErrors*/ false);
}
let result = Ternary.False;
if (flags & TypeFlags.IndexedAccess) {
if (result = isRelatedTo((<IndexedAccessType>source).objectType, (<IndexedAccessType>target).objectType, /*reportErrors*/ false)) {
if (result &= isRelatedTo((<IndexedAccessType>source).indexType, (<IndexedAccessType>target).indexType, /*reportErrors*/ false)) {
return result;
}
}
}
if (flags & TypeFlags.Conditional) {
if ((<ConditionalType>source).root.isDistributive === (<ConditionalType>target).root.isDistributive) {
if (result = isRelatedTo((<ConditionalType>source).checkType, (<ConditionalType>target).checkType, /*reportErrors*/ false)) {
if (result &= isRelatedTo((<ConditionalType>source).extendsType, (<ConditionalType>target).extendsType, /*reportErrors*/ false)) {
if (result &= isRelatedTo(getTrueTypeFromConditionalType(<ConditionalType>source), getTrueTypeFromConditionalType(<ConditionalType>target), /*reportErrors*/ false)) {
if (result &= isRelatedTo(getFalseTypeFromConditionalType(<ConditionalType>source), getFalseTypeFromConditionalType(<ConditionalType>target), /*reportErrors*/ false)) {
return result;
}
}
}
}
}
}
if (flags & TypeFlags.Substitution) {
return isRelatedTo((<SubstitutionType>source).substitute, (<SubstitutionType>target).substitute, /*reportErrors*/ false);
}
return Ternary.False;
}
let result: Ternary;
let originalErrorInfo: DiagnosticMessageChain | undefined;
const saveErrorInfo = errorInfo;
@@ -11647,12 +11676,13 @@ namespace ts {
}
}
else if (target.flags & TypeFlags.IndexedAccess) {
// A type S is related to a type T[K] if S is related to C, where C is the
// constraint of T[K]
const constraint = getConstraintForRelation(target);
if (constraint) {
if (result = isRelatedTo(source, constraint, reportErrors)) {
return result;
// A type S is related to a type T[K] if S is related to C, where C is the base constraint of T[K]
if (relation !== identityRelation) {
const constraint = getBaseConstraintOfType(target);
if (constraint && constraint !== target) {
if (result = isRelatedTo(source, constraint, reportErrors)) {
return result;
}
}
}
}
@@ -11756,6 +11786,9 @@ namespace ts {
}
return Ternary.False;
}
if (relation === definitelyAssignableRelation && isGenericMappedType(source)) {
return Ternary.False;
}
const sourceIsPrimitive = !!(source.flags & TypeFlags.Primitive);
if (relation !== identityRelation) {
source = getApparentType(source);
@@ -13524,14 +13557,16 @@ namespace ts {
const sourceLen = sourceSignatures.length;
const targetLen = targetSignatures.length;
const len = sourceLen < targetLen ? sourceLen : targetLen;
const skipParameters = !!(source.flags & TypeFlags.ContainsAnyFunctionType);
for (let i = 0; i < len; i++) {
inferFromSignature(getBaseSignature(sourceSignatures[sourceLen - len + i]), getBaseSignature(targetSignatures[targetLen - len + i]));
inferFromSignature(getBaseSignature(sourceSignatures[sourceLen - len + i]), getBaseSignature(targetSignatures[targetLen - len + i]), skipParameters);
}
}
function inferFromSignature(source: Signature, target: Signature) {
forEachMatchingParameterType(source, target, inferFromContravariantTypes);
function inferFromSignature(source: Signature, target: Signature, skipParameters: boolean) {
if (!skipParameters) {
forEachMatchingParameterType(source, target, inferFromContravariantTypes);
}
const sourceTypePredicate = getTypePredicateOfSignature(source);
const targetTypePredicate = getTypePredicateOfSignature(target);
if (sourceTypePredicate && targetTypePredicate && sourceTypePredicate.kind === targetTypePredicate.kind) {
@@ -13638,10 +13673,11 @@ namespace ts {
if (!inferredType) {
const signature = context.signature;
if (signature) {
if (inference.contraCandidates) {
// If we have contravariant inferences we find the best common subtype and treat
// that as a single covariant candidate.
inference.candidates = append(inference.candidates, getContravariantInference(inference));
if (inference.contraCandidates && (!inference.candidates || inference.candidates.length === 1 && inference.candidates[0].flags & TypeFlags.Never)) {
// If we have contravariant inferences, but no covariant inferences or a single
// covariant inference of 'never', we find the best common subtype and treat that
// as a single covariant candidate.
inference.candidates = [getContravariantInference(inference)];
inference.contraCandidates = undefined;
}
if (inference.candidates) {
@@ -14123,10 +14159,10 @@ namespace ts {
getInitialTypeOfBindingElement(node);
}
function getInitialOrAssignedType(node: VariableDeclaration | BindingElement | Expression) {
return node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.BindingElement ?
function getInitialOrAssignedType(node: VariableDeclaration | BindingElement | Expression, reference: Node) {
return getConstraintForLocation(node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.BindingElement ?
getInitialType(<VariableDeclaration | BindingElement>node) :
getAssignedType(node);
getAssignedType(node), reference);
}
function isEmptyArrayAssignment(node: VariableDeclaration | BindingElement | Expression) {
@@ -14407,8 +14443,8 @@ namespace ts {
return resultType;
function getTypeAtFlowNode(flow: FlowNode): FlowType {
if (flowDepth === 2500) {
// We have made 2500 recursive invocations. To avoid overflowing the call stack we report an error
if (flowDepth === 2000) {
// We have made 2000 recursive invocations. To avoid overflowing the call stack we report an error
// and disable further control flow analysis in the containing function or module body.
flowAnalysisDisabled = true;
reportFlowControlError(reference);
@@ -14512,11 +14548,11 @@ namespace ts {
if (isEmptyArrayAssignment(node)) {
return getEvolvingArrayType(neverType);
}
const assignedType = getBaseTypeOfLiteralType(getInitialOrAssignedType(node));
const assignedType = getBaseTypeOfLiteralType(getInitialOrAssignedType(node, reference));
return isTypeAssignableTo(assignedType, declaredType) ? assignedType : anyArrayType;
}
if (declaredType.flags & TypeFlags.Union) {
return getAssignmentReducedType(<UnionType>declaredType, getInitialOrAssignedType(node));
return getAssignmentReducedType(<UnionType>declaredType, getInitialOrAssignedType(node, reference));
}
return declaredType;
}
@@ -14548,7 +14584,8 @@ namespace ts {
}
}
else {
const indexType = getTypeOfExpression((<ElementAccessExpression>node.left).argumentExpression);
// We must get the context free expression type so as to not recur in an uncached fashion on the LHS (which causes exponential blowup in compile time)
const indexType = getContextFreeTypeOfExpression((<ElementAccessExpression>node.left).argumentExpression);
if (isTypeAssignableToKind(indexType, TypeFlags.NumberLike)) {
evolvedType = addEvolvingArrayElementType(evolvedType, node.right);
}
@@ -18356,7 +18393,7 @@ namespace ts {
return errorType;
}
const indexType = isForInVariableForNumericPropertyNames(indexExpression) ? numberType : checkExpression(indexExpression);
const indexType = checkExpression(indexExpression);
if (objectType === errorType || objectType === silentNeverType) {
return objectType;
@@ -18367,7 +18404,7 @@ namespace ts {
return errorType;
}
return checkIndexedAccessIndexType(getIndexedAccessType(objectType, indexType, node), node);
return checkIndexedAccessIndexType(getIndexedAccessType(objectType, isForInVariableForNumericPropertyNames(indexExpression) ? numberType : indexType, node), node);
}
function checkThatExpressionIsProperSymbolReference(expression: Expression, expressionType: Type, reportError: boolean): boolean {
@@ -18609,7 +18646,7 @@ namespace ts {
return getInferredTypes(context);
}
function inferTypeArguments(node: CallLikeExpression, signature: Signature, args: ReadonlyArray<Expression>, excludeArgument: boolean[] | undefined, context: InferenceContext): Type[] {
function inferTypeArguments(node: CallLikeExpression, signature: Signature, args: ReadonlyArray<Expression>, excludeArgument: ReadonlyArray<boolean> | undefined, context: InferenceContext): Type[] {
// Clear out all the inference results from the last time inferTypeArguments was called on this context
for (const inference of context.inferences) {
// As an optimization, we don't have to clear (and later recompute) inferred types
@@ -19023,19 +19060,7 @@ namespace ts {
// For a decorator, no arguments are susceptible to contextual typing due to the fact
// decorators are applied to a declaration by the emitter, and not to an expression.
const isSingleNonGenericCandidate = candidates.length === 1 && !candidates[0].typeParameters;
let excludeArgument: boolean[] | undefined;
if (!isDecorator && !isSingleNonGenericCandidate) {
// We do not need to call `getEffectiveArgumentCount` here as it only
// applies when calculating the number of arguments for a decorator.
for (let i = 0; i < args.length; i++) {
if (isContextSensitive(args[i])) {
if (!excludeArgument) {
excludeArgument = new Array(args.length);
}
excludeArgument[i] = true;
}
}
}
let excludeArgument = !isDecorator && !isSingleNonGenericCandidate ? getExcludeArgument(args) : undefined;
// The following variables are captured and modified by calls to chooseOverload.
// If overload resolution or type argument inference fails, we want to report the
@@ -19198,6 +19223,21 @@ namespace ts {
}
}
function getExcludeArgument(args: ReadonlyArray<Expression>): boolean[] | undefined {
let excludeArgument: boolean[] | undefined;
// We do not need to call `getEffectiveArgumentCount` here as it only
// applies when calculating the number of arguments for a decorator.
for (let i = 0; i < args.length; i++) {
if (isContextSensitive(args[i])) {
if (!excludeArgument) {
excludeArgument = new Array(args.length);
}
excludeArgument[i] = true;
}
}
return excludeArgument;
}
// No signature was applicable. We have already reported the errors for the invalid signature.
// If this is a type resolution session, e.g. Language Service, try to get better information than anySignature.
function getCandidateForOverloadFailure(
@@ -19276,17 +19316,29 @@ namespace ts {
return candidate;
}
const typeArgumentNodes: ReadonlyArray<TypeNode> = callLikeExpressionMayHaveTypeArguments(node) ? node.typeArguments || emptyArray : emptyArray;
const typeArgumentNodes: ReadonlyArray<TypeNode> | undefined = callLikeExpressionMayHaveTypeArguments(node) ? node.typeArguments : undefined;
const instantiated = typeArgumentNodes
? createSignatureInstantiation(candidate, getTypeArgumentsFromNodes(typeArgumentNodes, typeParameters, isInJavaScriptFile(node)))
: inferSignatureInstantiationForOverloadFailure(node, typeParameters, candidate, args);
candidates[bestIndex] = instantiated;
return instantiated;
}
function getTypeArgumentsFromNodes(typeArgumentNodes: ReadonlyArray<TypeNode>, typeParameters: ReadonlyArray<TypeParameter>, isJs: boolean): ReadonlyArray<Type> {
const typeArguments = typeArgumentNodes.map(getTypeOfNode);
while (typeArguments.length > typeParameters.length) {
typeArguments.pop();
}
while (typeArguments.length < typeParameters.length) {
typeArguments.push(getConstraintOfTypeParameter(typeParameters[typeArguments.length]) || getDefaultTypeArgumentType(isInJavaScriptFile(node)));
typeArguments.push(getConstraintOfTypeParameter(typeParameters[typeArguments.length]) || getDefaultTypeArgumentType(isJs));
}
const instantiated = createSignatureInstantiation(candidate, typeArguments);
candidates[bestIndex] = instantiated;
return instantiated;
return typeArguments;
}
function inferSignatureInstantiationForOverloadFailure(node: CallLikeExpression, typeParameters: ReadonlyArray<TypeParameter>, candidate: Signature, args: ReadonlyArray<Expression>): Signature {
const inferenceContext = createInferenceContext(typeParameters, candidate, /*flags*/ isInJavaScriptFile(node) ? InferenceFlags.AnyDefault : InferenceFlags.None);
const typeArgumentTypes = inferTypeArguments(node, candidate, args, getExcludeArgument(args), inferenceContext);
return createSignatureInstantiation(candidate, typeArgumentTypes);
}
function getLongestCandidateIndex(candidates: Signature[], argsCount: number): number {
@@ -20549,8 +20601,10 @@ namespace ts {
return links.contextFreeType;
}
const returnType = getReturnTypeFromBody(node, checkMode);
const singleReturnSignature = createSignature(undefined, undefined, undefined, emptyArray, returnType, /*resolvedTypePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
return links.contextFreeType = createAnonymousType(node.symbol, emptySymbols, [singleReturnSignature], emptyArray, undefined, undefined);
const returnOnlySignature = createSignature(undefined, undefined, undefined, emptyArray, returnType, /*resolvedTypePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
const returnOnlyType = createAnonymousType(node.symbol, emptySymbols, [returnOnlySignature], emptyArray, undefined, undefined);
returnOnlyType.flags |= TypeFlags.ContainsAnyFunctionType;
return links.contextFreeType = returnOnlyType;
}
return anyFunctionType;
}
@@ -21671,15 +21725,19 @@ namespace ts {
* to cache the result.
*/
function getTypeOfExpression(node: Expression, cache?: boolean) {
const expr = skipParentheses(node);
// Optimize for the common case of a call to a function with a single non-generic call
// signature where we can just fetch the return type without checking the arguments.
if (node.kind === SyntaxKind.CallExpression && (<CallExpression>node).expression.kind !== SyntaxKind.SuperKeyword && !isRequireCall(node, /*checkArgumentIsStringLiteralLike*/ true) && !isSymbolOrSymbolForCall(node)) {
const funcType = checkNonNullExpression((<CallExpression>node).expression);
if (expr.kind === SyntaxKind.CallExpression && (<CallExpression>expr).expression.kind !== SyntaxKind.SuperKeyword && !isRequireCall(expr, /*checkArgumentIsStringLiteralLike*/ true) && !isSymbolOrSymbolForCall(expr)) {
const funcType = checkNonNullExpression((<CallExpression>expr).expression);
const signature = getSingleCallSignature(funcType);
if (signature && !signature.typeParameters) {
return getReturnTypeOfSignature(signature);
}
}
else if (expr.kind === SyntaxKind.TypeAssertionExpression || expr.kind === SyntaxKind.AsExpression) {
return getTypeFromTypeNode((<TypeAssertion>expr).type);
}
// Otherwise simply call checkExpression. Ideally, the entire family of checkXXX functions
// should have a parameter that indicates whether full error checking is required such that
// we can perform the optimizations locally.
@@ -21694,9 +21752,13 @@ namespace ts {
* It sets the contextual type of the node to any before calling getTypeOfExpression.
*/
function getContextFreeTypeOfExpression(node: Expression) {
const links = getNodeLinks(node);
if (links.contextFreeType) {
return links.contextFreeType;
}
const saveContextualType = node.contextualType;
node.contextualType = anyType;
const type = getTypeOfExpression(node);
const type = links.contextFreeType = checkExpression(node, CheckMode.SkipContextSensitive);
node.contextualType = saveContextualType;
return type;
}
@@ -24219,7 +24281,7 @@ namespace ts {
if (nameText) {
const property = getPropertyOfType(parentType!, nameText)!; // TODO: GH#18217
markPropertyAsReferenced(property, /*nodeForCheckWriteOnly*/ undefined, /*isThisAccess*/ false); // A destructuring is never a write-only reference.
if (parent.initializer && property && !isComputedPropertyName(name)) {
if (parent.initializer && property) {
checkPropertyAccessibility(parent, parent.initializer.kind === SyntaxKind.SuperKeyword, parentType!, property);
}
}
@@ -27238,6 +27300,7 @@ namespace ts {
case SyntaxKind.DefaultKeyword:
case SyntaxKind.FunctionKeyword:
case SyntaxKind.EqualsGreaterThanToken:
case SyntaxKind.ClassKeyword:
return getSymbolOfNode(node.parent);
case SyntaxKind.ImportType:
return isLiteralImportTypeNode(node) ? getSymbolAtLocation(node.argument.literal) : undefined;
@@ -27731,6 +27794,27 @@ namespace ts {
hasModifier(parameter, ModifierFlags.ParameterPropertyModifier);
}
function isJSContainerFunctionDeclaration(node: Declaration): boolean {
const declaration = getParseTreeNode(node, isFunctionDeclaration);
if (!declaration) {
return false;
}
const symbol = getSymbolOfNode(declaration);
if (!symbol || !(symbol.flags & SymbolFlags.Function)) {
return false;
}
return !!forEachEntry(getExportsOfSymbol(symbol), p => isPropertyAccessExpression(p.valueDeclaration));
}
function getPropertiesOfContainerFunction(node: Declaration): Symbol[] {
const declaration = getParseTreeNode(node, isFunctionDeclaration);
if (!declaration) {
return emptyArray;
}
const symbol = getSymbolOfNode(declaration);
return symbol && getPropertiesOfType(getTypeOfSymbol(symbol)) || emptyArray;
}
function getNodeCheckFlags(node: Node): NodeCheckFlags {
return getNodeLinks(node).flags || 0;
}
@@ -27838,7 +27922,7 @@ namespace ts {
}
}
function createTypeOfDeclaration(declarationIn: AccessorDeclaration | VariableLikeDeclaration, enclosingDeclaration: Node, flags: NodeBuilderFlags, tracker: SymbolTracker, addUndefined?: boolean) {
function createTypeOfDeclaration(declarationIn: AccessorDeclaration | VariableLikeDeclaration | PropertyAccessExpression, enclosingDeclaration: Node, flags: NodeBuilderFlags, tracker: SymbolTracker, addUndefined?: boolean) {
const declaration = getParseTreeNode(declarationIn, isVariableLikeOrAccessor);
if (!declaration) {
return createToken(SyntaxKind.AnyKeyword) as KeywordTypeNode;
@@ -27971,6 +28055,8 @@ namespace ts {
isImplementationOfOverload,
isRequiredInitializedParameter,
isOptionalUninitializedParameterProperty,
isJSContainerFunctionDeclaration,
getPropertiesOfContainerFunction,
createTypeOfDeclaration,
createReturnTypeOfSignatureDeclaration,
createTypeOfExpression,
+11 -6
View File
@@ -815,10 +815,11 @@ namespace ts {
}
function getOptionNameMap(): OptionNameMap {
if (optionNameMapCache) {
return optionNameMapCache;
}
return optionNameMapCache || (optionNameMapCache = createOptionNameMap(optionDeclarations));
}
/*@internal*/
export function createOptionNameMap(optionDeclarations: ReadonlyArray<CommandLineOption>): OptionNameMap {
const optionNameMap = createMap<CommandLineOption>();
const shortOptionNames = createMap<string>();
forEach(optionDeclarations, option => {
@@ -828,8 +829,7 @@ namespace ts {
}
});
optionNameMapCache = { optionNameMap, shortOptionNames };
return optionNameMapCache;
return { optionNameMap, shortOptionNames };
}
/* @internal */
@@ -979,7 +979,12 @@ namespace ts {
}
/** @internal */
export function getOptionFromName(optionName: string, allowShort = false): CommandLineOption | undefined {
export function getOptionFromName(optionName: string, allowShort?: boolean): CommandLineOption | undefined {
return getOptionDeclarationFromName(getOptionNameMap, optionName, allowShort);
}
/*@internal*/
export function getOptionDeclarationFromName(getOptionNameMap: () => OptionNameMap, optionName: string, allowShort = false): CommandLineOption | undefined {
optionName = optionName.toLowerCase();
const { optionNameMap, shortOptionNames } = getOptionNameMap();
// Try to translate short option names to their full equivalents.
+5 -4
View File
@@ -1964,10 +1964,6 @@
"category": "Error",
"code": 2549
},
"Generic type instantiation is excessively deep and possibly infinite.": {
"category": "Error",
"code": 2550
},
"Property '{0}' does not exist on type '{1}'. Did you mean '{2}'?": {
"category": "Error",
"code": 2551
@@ -2900,6 +2896,11 @@
"category": "Error",
"code": 5071
},
"Unknown build option '{0}'.": {
"category": "Error",
"code": 5072
},
"Generates a sourcemap for each corresponding '.d.ts' file.": {
"category": "Message",
+106 -95
View File
@@ -1,8 +1,52 @@
// Used by importFixes, getEditsForFileRename, and declaration emit to synthesize import module specifiers.
/* @internal */
namespace ts.moduleSpecifiers {
export interface ModuleSpecifierPreferences {
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
const enum RelativePreference { Relative, NonRelative, Auto }
// See UserPreferences#importPathEnding
const enum Ending { Minimal, Index, JsExtension }
// Processed preferences
interface Preferences {
readonly relativePreference: RelativePreference;
readonly ending: Ending;
}
function getPreferences({ importModuleSpecifierPreference, importModuleSpecifierEnding }: UserPreferences, compilerOptions: CompilerOptions, importingSourceFile: SourceFile): Preferences {
return {
relativePreference: importModuleSpecifierPreference === "relative" ? RelativePreference.Relative : importModuleSpecifierPreference === "non-relative" ? RelativePreference.NonRelative : RelativePreference.Auto,
ending: getEnding(),
};
function getEnding(): Ending {
switch (importModuleSpecifierEnding) {
case "minimal": return Ending.Minimal;
case "index": return Ending.Index;
case "js": return Ending.JsExtension;
default: return usesJsExtensionOnImports(importingSourceFile) ? Ending.JsExtension
: getEmitModuleResolutionKind(compilerOptions) !== ModuleResolutionKind.NodeJs ? Ending.Index : Ending.Minimal;
}
}
}
function getPreferencesForUpdate(compilerOptions: CompilerOptions, oldImportSpecifier: string): Preferences {
return {
relativePreference: isExternalModuleNameRelative(oldImportSpecifier) ? RelativePreference.Relative : RelativePreference.NonRelative,
ending: hasJavaScriptOrJsonFileExtension(oldImportSpecifier) ? Ending.JsExtension
: getEmitModuleResolutionKind(compilerOptions) !== ModuleResolutionKind.NodeJs || endsWith(oldImportSpecifier, "index") ? Ending.Index : Ending.Minimal,
};
}
export function updateModuleSpecifier(
compilerOptions: CompilerOptions,
importingSourceFileName: Path,
toFileName: string,
host: ModuleSpecifierResolutionHost,
files: ReadonlyArray<SourceFile>,
redirectTargetsMap: RedirectTargetsMap,
oldImportSpecifier: string,
): string | undefined {
const res = getModuleSpecifierWorker(compilerOptions, importingSourceFileName, toFileName, host, files, redirectTargetsMap, getPreferencesForUpdate(compilerOptions, oldImportSpecifier));
if (res === oldImportSpecifier) return undefined;
return res;
}
// Note: importingSourceFile is just for usesJsExtensionOnImports
@@ -13,35 +57,25 @@ namespace ts.moduleSpecifiers {
toFileName: string,
host: ModuleSpecifierResolutionHost,
files: ReadonlyArray<SourceFile>,
preferences: ModuleSpecifierPreferences = {},
preferences: UserPreferences = {},
redirectTargetsMap: RedirectTargetsMap,
): string {
const info = getInfo(compilerOptions, importingSourceFile, importingSourceFileName, host);
const modulePaths = getAllModulePaths(files, importingSourceFileName, toFileName, info.getCanonicalFileName, host, redirectTargetsMap);
return firstDefined(modulePaths, moduleFileName => getGlobalModuleSpecifier(moduleFileName, info, host, compilerOptions)) ||
first(getLocalModuleSpecifiers(toFileName, info, compilerOptions, preferences));
return getModuleSpecifierWorker(compilerOptions, importingSourceFileName, toFileName, host, files, redirectTargetsMap, getPreferences(preferences, compilerOptions, importingSourceFile));
}
export function getModuleSpecifierForDeclarationFile(
moduleSymbol: Symbol,
function getModuleSpecifierWorker(
compilerOptions: CompilerOptions,
importingSourceFile: SourceFile,
importingSourceFileName: Path,
toFileName: string,
host: ModuleSpecifierResolutionHost,
files: ReadonlyArray<SourceFile>,
redirectTargetsMap: RedirectTargetsMap,
preferences: Preferences
): string {
const isBundle = (compilerOptions.out || compilerOptions.outFile);
if (isBundle && host.getCommonSourceDirectory) {
// For declaration bundles, we need to generate absolute paths relative to the common source dir for imports,
// just like how the declaration emitter does for the ambient module declarations - we can easily accomplish this
// using the `baseUrl` compiler option (which we would otherwise never use in declaration emit) and a non-relative
// specifier preference
compilerOptions = {
...compilerOptions,
baseUrl: host.getCommonSourceDirectory(),
};
}
const preferences: ModuleSpecifierPreferences = { importModuleSpecifierPreference: isBundle ? "non-relative" : "relative" };
return first(first(getModuleSpecifiers(moduleSymbol, compilerOptions, importingSourceFile, host, host.getSourceFiles ? host.getSourceFiles() : [importingSourceFile], preferences, redirectTargetsMap)));
const info = getInfo(importingSourceFileName, host);
const modulePaths = getAllModulePaths(files, importingSourceFileName, toFileName, info.getCanonicalFileName, host, redirectTargetsMap);
return firstDefined(modulePaths, moduleFileName => tryGetModuleNameAsNodeModule(moduleFileName, info, host, compilerOptions)) ||
first(getLocalModuleSpecifiers(toFileName, info, compilerOptions, preferences));
}
// For each symlink/original for a module, returns a list of ways to import that file.
@@ -51,60 +85,39 @@ namespace ts.moduleSpecifiers {
importingSourceFile: SourceFile,
host: ModuleSpecifierResolutionHost,
files: ReadonlyArray<SourceFile>,
preferences: ModuleSpecifierPreferences,
userPreferences: UserPreferences,
redirectTargetsMap: RedirectTargetsMap,
): ReadonlyArray<ReadonlyArray<string>> {
const ambient = tryGetModuleNameFromAmbientModule(moduleSymbol);
if (ambient) return [[ambient]];
const info = getInfo(compilerOptions, importingSourceFile, importingSourceFile.path, host);
if (!files) {
return Debug.fail("Files list must be present to resolve symlinks in specifier resolution");
}
const info = getInfo(importingSourceFile.path, host);
const moduleSourceFile = getSourceFileOfNode(moduleSymbol.valueDeclaration || getNonAugmentationDeclaration(moduleSymbol));
const modulePaths = getAllModulePaths(files, importingSourceFile.path, moduleSourceFile.fileName, info.getCanonicalFileName, host, redirectTargetsMap);
const global = mapDefined(modulePaths, moduleFileName => getGlobalModuleSpecifier(moduleFileName, info, host, compilerOptions));
const preferences = getPreferences(userPreferences, compilerOptions, importingSourceFile);
const global = mapDefined(modulePaths, moduleFileName => tryGetModuleNameAsNodeModule(moduleFileName, info, host, compilerOptions));
return global.length ? global.map(g => [g]) : modulePaths.map(moduleFileName =>
getLocalModuleSpecifiers(moduleFileName, info, compilerOptions, preferences));
}
interface Info {
readonly moduleResolutionKind: ModuleResolutionKind;
readonly addJsExtension: boolean;
readonly getCanonicalFileName: GetCanonicalFileName;
readonly sourceDirectory: Path;
}
// importingSourceFileName is separate because getEditsForFileRename may need to specify an updated path
function getInfo(compilerOptions: CompilerOptions, importingSourceFile: SourceFile, importingSourceFileName: Path, host: ModuleSpecifierResolutionHost): Info {
const moduleResolutionKind = getEmitModuleResolutionKind(compilerOptions);
const addJsExtension = usesJsExtensionOnImports(importingSourceFile);
function getInfo(importingSourceFileName: Path, host: ModuleSpecifierResolutionHost): Info {
const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames ? host.useCaseSensitiveFileNames() : true);
const sourceDirectory = getDirectoryPath(importingSourceFileName);
return { moduleResolutionKind, addJsExtension, getCanonicalFileName, sourceDirectory };
return { getCanonicalFileName, sourceDirectory };
}
function getGlobalModuleSpecifier(
moduleFileName: string,
{ addJsExtension, getCanonicalFileName, sourceDirectory }: Info,
host: ModuleSpecifierResolutionHost,
compilerOptions: CompilerOptions,
) {
return tryGetModuleNameFromTypeRoots(compilerOptions, host, getCanonicalFileName, moduleFileName, addJsExtension)
|| tryGetModuleNameAsNodeModule(compilerOptions, moduleFileName, host, getCanonicalFileName, sourceDirectory);
}
function getLocalModuleSpecifiers(
moduleFileName: string,
{ moduleResolutionKind, addJsExtension, getCanonicalFileName, sourceDirectory }: Info,
compilerOptions: CompilerOptions,
preferences: ModuleSpecifierPreferences,
): ReadonlyArray<string> {
function getLocalModuleSpecifiers(moduleFileName: string, { getCanonicalFileName, sourceDirectory }: Info, compilerOptions: CompilerOptions, { ending, relativePreference }: Preferences): ReadonlyArray<string> {
const { baseUrl, paths, rootDirs } = compilerOptions;
const relativePath = rootDirs && tryGetModuleNameFromRootDirs(rootDirs, moduleFileName, sourceDirectory, getCanonicalFileName) ||
removeExtensionAndIndexPostFix(ensurePathIsNonModuleName(getRelativePathFromDirectory(sourceDirectory, moduleFileName, getCanonicalFileName)), moduleResolutionKind, addJsExtension);
if (!baseUrl || preferences.importModuleSpecifierPreference === "relative") {
removeExtensionAndIndexPostFix(ensurePathIsNonModuleName(getRelativePathFromDirectory(sourceDirectory, moduleFileName, getCanonicalFileName)), ending, compilerOptions);
if (!baseUrl || relativePreference === RelativePreference.Relative) {
return [relativePath];
}
@@ -113,7 +126,7 @@ namespace ts.moduleSpecifiers {
return [relativePath];
}
const importRelativeToBaseUrl = removeExtensionAndIndexPostFix(relativeToBaseUrl, moduleResolutionKind, addJsExtension);
const importRelativeToBaseUrl = removeExtensionAndIndexPostFix(relativeToBaseUrl, ending, compilerOptions);
if (paths) {
const fromPaths = tryGetModuleNameFromPaths(removeFileExtension(relativeToBaseUrl), importRelativeToBaseUrl, paths);
if (fromPaths) {
@@ -121,11 +134,11 @@ namespace ts.moduleSpecifiers {
}
}
if (preferences.importModuleSpecifierPreference === "non-relative") {
if (relativePreference === RelativePreference.NonRelative) {
return [importRelativeToBaseUrl];
}
if (preferences.importModuleSpecifierPreference !== undefined) Debug.assertNever(preferences.importModuleSpecifierPreference);
if (relativePreference !== RelativePreference.Auto) Debug.assertNever(relativePreference);
if (isPathRelativeToParent(relativeToBaseUrl)) {
return [relativePath];
@@ -164,7 +177,7 @@ namespace ts.moduleSpecifiers {
}
function usesJsExtensionOnImports({ imports }: SourceFile): boolean {
return firstDefined(imports, ({ text }) => pathIsRelative(text) ? fileExtensionIs(text, Extension.Js) : undefined) || false;
return firstDefined(imports, ({ text }) => pathIsRelative(text) ? hasJavaScriptOrJsonFileExtension(text) : undefined) || false;
}
function stringsEqual(a: string, b: string, getCanonicalFileName: GetCanonicalFileName): boolean {
@@ -284,37 +297,8 @@ namespace ts.moduleSpecifiers {
return removeFileExtension(relativePath);
}
function tryGetModuleNameFromTypeRoots(
options: CompilerOptions,
host: GetEffectiveTypeRootsHost,
getCanonicalFileName: (file: string) => string,
moduleFileName: string,
addJsExtension: boolean,
): string | undefined {
const roots = getEffectiveTypeRoots(options, host);
return firstDefined(roots, unNormalizedTypeRoot => {
const typeRoot = toPath(unNormalizedTypeRoot, /*basePath*/ undefined, getCanonicalFileName);
if (startsWith(moduleFileName, typeRoot)) {
// For a type definition, we can strip `/index` even with classic resolution.
return removeExtensionAndIndexPostFix(moduleFileName.substring(typeRoot.length + 1), ModuleResolutionKind.NodeJs, addJsExtension);
}
});
}
function tryGetModuleNameAsNodeModule(
options: CompilerOptions,
moduleFileName: string,
host: ModuleSpecifierResolutionHost,
getCanonicalFileName: (file: string) => string,
sourceDirectory: Path,
): string | undefined {
if (getEmitModuleResolutionKind(options) !== ModuleResolutionKind.NodeJs) {
// nothing to do here
return undefined;
}
function tryGetModuleNameAsNodeModule(moduleFileName: string, { getCanonicalFileName, sourceDirectory }: Info, host: ModuleSpecifierResolutionHost, options: CompilerOptions): string | undefined {
const parts: NodeModulePathParts = getNodeModulePathParts(moduleFileName)!;
if (!parts) {
return undefined;
}
@@ -331,7 +315,7 @@ namespace ts.moduleSpecifiers {
const subModuleName = moduleFileName.slice(parts.packageRootIndex + 1);
const fromPaths = tryGetModuleNameFromPaths(
removeFileExtension(subModuleName),
removeExtensionAndIndexPostFix(subModuleName, ModuleResolutionKind.NodeJs, /*addJsExtension*/ false),
removeExtensionAndIndexPostFix(subModuleName, Ending.Minimal, options),
versionPaths.paths
);
if (fromPaths !== undefined) {
@@ -346,8 +330,12 @@ namespace ts.moduleSpecifiers {
// Get a path that's relative to node_modules or the importing file's path
// if node_modules folder is in this folder or any of its parent folders, no need to keep it.
if (!startsWith(sourceDirectory, getCanonicalFileName(moduleSpecifier.substring(0, parts.topLevelNodeModulesIndex)))) return undefined;
// If the module was found in @types, get the actual Node package name
return getPackageNameFromTypesPackageName(moduleSpecifier.substring(parts.topLevelPackageNameIndex + 1));
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;
function getDirectoryOrExtensionlessFileName(path: string): string {
// If the file is the main module, it can be imported by the package name
@@ -456,13 +444,36 @@ namespace ts.moduleSpecifiers {
});
}
function removeExtensionAndIndexPostFix(fileName: string, moduleResolutionKind: ModuleResolutionKind, addJsExtension: boolean): string {
function removeExtensionAndIndexPostFix(fileName: string, ending: Ending, options: CompilerOptions): string {
if (fileExtensionIs(fileName, Extension.Json)) return fileName;
const noExtension = removeFileExtension(fileName);
return addJsExtension
? noExtension + ".js"
: moduleResolutionKind === ModuleResolutionKind.NodeJs
? removeSuffix(noExtension, "/index")
: noExtension;
switch (ending) {
case Ending.Minimal:
return removeSuffix(noExtension, "/index");
case Ending.Index:
return noExtension;
case Ending.JsExtension:
return noExtension + getJavaScriptExtensionForFile(fileName, options);
default:
return Debug.assertNever(ending);
}
}
function getJavaScriptExtensionForFile(fileName: string, options: CompilerOptions): Extension {
const ext = extensionFromPath(fileName);
switch (ext) {
case Extension.Ts:
case Extension.Dts:
return Extension.Js;
case Extension.Tsx:
return options.jsx === JsxEmit.Preserve ? Extension.Jsx : Extension.Js;
case Extension.Js:
case Extension.Jsx:
case Extension.Json:
return ext;
default:
return Debug.assertNever(ext);
}
}
function getRelativePathIfInDirectory(path: string, directoryPath: string, getCanonicalFileName: GetCanonicalFileName): string | undefined {
+5 -2
View File
@@ -515,7 +515,7 @@ namespace ts {
performance.mark("beforeParse");
let result: SourceFile;
if (languageVersion === ScriptTarget.JSON) {
result = Parser.parseJsonText(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes);
result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes, ScriptKind.JSON);
}
else {
result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes, scriptKind);
@@ -689,8 +689,12 @@ namespace ts {
if (scriptKind === ScriptKind.JSON) {
const result = parseJsonText(fileName, sourceText, languageVersion, syntaxCursor, setParentNodes);
convertToObjectWorker(result, result.parseDiagnostics, /*returnValue*/ false, /*knownRootOptions*/ undefined, /*jsonConversionNotifier*/ undefined);
result.referencedFiles = emptyArray;
result.typeReferenceDirectives = emptyArray;
result.libReferenceDirectives = emptyArray;
result.amdDependencies = emptyArray;
result.hasNoDefaultLib = false;
result.pragmas = emptyMap;
return result;
}
@@ -7754,7 +7758,6 @@ namespace ts {
}
}
/*@internal*/
type PragmaDiagnosticReporter = (pos: number, length: number, message: DiagnosticMessage) => void;
/*@internal*/
+36 -41
View File
@@ -67,19 +67,24 @@ namespace ts {
}
export function createCompilerHost(options: CompilerOptions, setParentNodes?: boolean): CompilerHost {
return createCompilerHostWorker(options, setParentNodes);
}
/*@internal*/
// TODO(shkamat): update this after reworking ts build API
export function createCompilerHostWorker(options: CompilerOptions, setParentNodes?: boolean, system = sys): CompilerHost {
const existingDirectories = createMap<boolean>();
function getCanonicalFileName(fileName: string): string {
// if underlying system can distinguish between two files whose names differs only in cases then file name already in canonical form.
// otherwise use toLowerCase as a canonical form.
return sys.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase();
return system.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase();
}
function getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile | undefined {
let text: string | undefined;
try {
performance.mark("beforeIORead");
text = sys.readFile(fileName, options.charset);
text = system.readFile(fileName, options.charset);
performance.mark("afterIORead");
performance.measure("I/O Read", "beforeIORead", "afterIORead");
}
@@ -97,7 +102,7 @@ namespace ts {
if (existingDirectories.has(directoryPath)) {
return true;
}
if (sys.directoryExists(directoryPath)) {
if (system.directoryExists(directoryPath)) {
existingDirectories.set(directoryPath, true);
return true;
}
@@ -108,7 +113,7 @@ namespace ts {
if (directoryPath.length > getRootLength(directoryPath) && !directoryExists(directoryPath)) {
const parentDirectory = getDirectoryPath(directoryPath);
ensureDirectoriesExist(parentDirectory);
sys.createDirectory(directoryPath);
system.createDirectory(directoryPath);
}
}
@@ -119,8 +124,8 @@ namespace ts {
outputFingerprints = createMap<OutputFingerprint>();
}
const hash = sys.createHash!(data); // TODO: GH#18217
const mtimeBefore = sys.getModifiedTime!(fileName); // TODO: GH#18217
const hash = system.createHash!(data); // TODO: GH#18217
const mtimeBefore = system.getModifiedTime!(fileName); // TODO: GH#18217
if (mtimeBefore) {
const fingerprint = outputFingerprints.get(fileName);
@@ -133,9 +138,9 @@ namespace ts {
}
}
sys.writeFile(fileName, data, writeByteOrderMark);
system.writeFile(fileName, data, writeByteOrderMark);
const mtimeAfter = sys.getModifiedTime!(fileName) || missingFileModifiedTime; // TODO: GH#18217
const mtimeAfter = system.getModifiedTime!(fileName) || missingFileModifiedTime; // TODO: GH#18217
outputFingerprints.set(fileName, {
hash,
@@ -149,11 +154,11 @@ namespace ts {
performance.mark("beforeIOWrite");
ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName)));
if (isWatchSet(options) && sys.createHash && sys.getModifiedTime) {
if (isWatchSet(options) && system.createHash && system.getModifiedTime) {
writeFileIfUpdated(fileName, data, writeByteOrderMark);
}
else {
sys.writeFile(fileName, data, writeByteOrderMark);
system.writeFile(fileName, data, writeByteOrderMark);
}
performance.mark("afterIOWrite");
@@ -167,32 +172,29 @@ namespace ts {
}
function getDefaultLibLocation(): string {
return getDirectoryPath(normalizePath(sys.getExecutingFilePath()));
return getDirectoryPath(normalizePath(system.getExecutingFilePath()));
}
const newLine = getNewLineCharacter(options);
const realpath = sys.realpath && ((path: string) => sys.realpath!(path));
const newLine = getNewLineCharacter(options, () => system.newLine);
const realpath = system.realpath && ((path: string) => system.realpath!(path));
return {
getSourceFile,
getDefaultLibLocation,
getDefaultLibFileName: options => combinePaths(getDefaultLibLocation(), getDefaultLibFileName(options)),
writeFile,
getCurrentDirectory: memoize(() => sys.getCurrentDirectory()),
useCaseSensitiveFileNames: () => sys.useCaseSensitiveFileNames,
getCurrentDirectory: memoize(() => system.getCurrentDirectory()),
useCaseSensitiveFileNames: () => system.useCaseSensitiveFileNames,
getCanonicalFileName,
getNewLine: () => newLine,
fileExists: fileName => sys.fileExists(fileName),
readFile: fileName => sys.readFile(fileName),
trace: (s: string) => sys.write(s + newLine),
directoryExists: directoryName => sys.directoryExists(directoryName),
getEnvironmentVariable: name => sys.getEnvironmentVariable ? sys.getEnvironmentVariable(name) : "",
getDirectories: (path: string) => sys.getDirectories(path),
fileExists: fileName => system.fileExists(fileName),
readFile: fileName => system.readFile(fileName),
trace: (s: string) => system.write(s + newLine),
directoryExists: directoryName => system.directoryExists(directoryName),
getEnvironmentVariable: name => system.getEnvironmentVariable ? system.getEnvironmentVariable(name) : "",
getDirectories: (path: string) => system.getDirectories(path),
realpath,
readDirectory: (path, extensions, include, exclude, depth) => sys.readDirectory(path, extensions, include, exclude, depth),
getModifiedTime: sys.getModifiedTime && (path => sys.getModifiedTime!(path)),
setModifiedTime: sys.setModifiedTime && ((path, date) => sys.setModifiedTime!(path, date)),
deleteFile: sys.deleteFile && (path => sys.deleteFile!(path))
readDirectory: (path, extensions, include, exclude, depth) => system.readDirectory(path, extensions, include, exclude, depth)
};
}
@@ -2319,27 +2321,20 @@ namespace ts {
}
function computeCommonSourceDirectory(sourceFiles: SourceFile[]): string {
const fileNames: string[] = [];
for (const file of sourceFiles) {
if (!file.isDeclarationFile) {
fileNames.push(file.fileName);
}
}
const fileNames = mapDefined(sourceFiles, file => file.isDeclarationFile ? undefined : file.fileName);
return computeCommonSourceDirectoryOfFilenames(fileNames, currentDirectory, getCanonicalFileName);
}
function checkSourceFilesBelongToPath(sourceFiles: SourceFile[], rootDirectory: string): boolean {
function checkSourceFilesBelongToPath(sourceFiles: ReadonlyArray<SourceFile>, rootDirectory: string): boolean {
let allFilesBelongToPath = true;
if (sourceFiles) {
const absoluteRootDirectoryPath = host.getCanonicalFileName(getNormalizedAbsolutePath(rootDirectory, currentDirectory));
const absoluteRootDirectoryPath = host.getCanonicalFileName(getNormalizedAbsolutePath(rootDirectory, currentDirectory));
for (const sourceFile of sourceFiles) {
if (!sourceFile.isDeclarationFile) {
const absoluteSourceFilePath = host.getCanonicalFileName(getNormalizedAbsolutePath(sourceFile.fileName, currentDirectory));
if (absoluteSourceFilePath.indexOf(absoluteRootDirectoryPath) !== 0) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, sourceFile.fileName, rootDirectory));
allFilesBelongToPath = false;
}
for (const sourceFile of sourceFiles) {
if (!sourceFile.isDeclarationFile) {
const absoluteSourceFilePath = host.getCanonicalFileName(getNormalizedAbsolutePath(sourceFile.fileName, currentDirectory));
if (absoluteSourceFilePath.indexOf(absoluteRootDirectoryPath) !== 0) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, sourceFile.fileName, rootDirectory));
allFilesBelongToPath = false;
}
}
}
+16 -1
View File
@@ -978,7 +978,7 @@ namespace ts {
}
case SyntaxKind.FunctionDeclaration: {
// Generators lose their generator-ness, excepting their return type
return cleanup(updateFunctionDeclaration(
const clean = cleanup(updateFunctionDeclaration(
input,
/*decorators*/ undefined,
ensureModifiers(input, isPrivate),
@@ -989,6 +989,21 @@ namespace ts {
ensureType(input, input.type),
/*body*/ undefined
));
if (clean && resolver.isJSContainerFunctionDeclaration(input)) {
const declarations = mapDefined(resolver.getPropertiesOfContainerFunction(input), p => {
if (!isPropertyAccessExpression(p.valueDeclaration)) {
return undefined;
}
const type = resolver.createTypeOfDeclaration(p.valueDeclaration, enclosingDeclaration, declarationEmitNodeBuilderFlags, symbolTracker);
const varDecl = createVariableDeclaration(unescapeLeadingUnderscores(p.escapedName), type, /*initializer*/ undefined);
return createVariableStatement(/*modifiers*/ undefined, createVariableDeclarationList([varDecl]));
});
const namespaceDecl = createModuleDeclaration(/*decorators*/ undefined, ensureModifiers(input, isPrivate), input.name!, createModuleBlock(declarations), NodeFlags.Namespace);
return [clean, namespaceDecl];
}
else {
return clean;
}
}
case SyntaxKind.ModuleDeclaration: {
needsDeclare = false;
+397 -404
View File
File diff suppressed because it is too large Load Diff
+29 -20
View File
@@ -2632,7 +2632,7 @@ namespace ts {
/* @internal */ ambientModuleNames: ReadonlyArray<string>;
/* @internal */ checkJsDirective?: CheckJsDirective;
/* @internal */ version: string;
/* @internal */ pragmas: PragmaMap;
/* @internal */ pragmas: ReadonlyPragmaMap;
/* @internal */ localJsxNamespace?: __String;
/* @internal */ localJsxFactory?: EntityName;
@@ -3097,6 +3097,10 @@ namespace ts {
*/
/* @internal */ getAccessibleSymbolChain(symbol: Symbol, enclosingDeclaration: Node | undefined, meaning: SymbolFlags, useOnlyExternalAliasing: boolean): Symbol[] | undefined;
/* @internal */ getTypePredicateOfSignature(signature: Signature): TypePredicate;
/**
* An external module with an 'export =' declaration resolves to the target of the 'export =' declaration,
* and an external module with no 'export =' declaration resolves to the module itself.
*/
/* @internal */ resolveExternalModuleSymbol(symbol: Symbol): Symbol;
/** @param node A location where we might consider accessing `this`. Not necessarily a ThisExpression. */
/* @internal */ tryGetThisTypeAt(node: Node): Type | undefined;
@@ -3114,6 +3118,8 @@ namespace ts {
* and the operation is cancelled, then it should be discarded, otherwise it is safe to keep.
*/
runWithCancellationToken<T>(token: CancellationToken, cb: (checker: TypeChecker) => T): T;
/* @internal */ getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol: Symbol): ReadonlyArray<TypeParameter> | undefined;
}
/* @internal */
@@ -3374,7 +3380,9 @@ namespace ts {
isImplementationOfOverload(node: FunctionLike): boolean | undefined;
isRequiredInitializedParameter(node: ParameterDeclaration): boolean;
isOptionalUninitializedParameterProperty(node: ParameterDeclaration): boolean;
createTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration, enclosingDeclaration: Node, flags: NodeBuilderFlags, tracker: SymbolTracker, addUndefined?: boolean): TypeNode | undefined;
isJSContainerFunctionDeclaration(node: FunctionDeclaration): boolean;
getPropertiesOfContainerFunction(node: Declaration): Symbol[];
createTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration | PropertyAccessExpression, enclosingDeclaration: Node, flags: NodeBuilderFlags, tracker: SymbolTracker, addUndefined?: boolean): TypeNode | undefined;
createReturnTypeOfSignatureDeclaration(signatureDeclaration: SignatureDeclaration, enclosingDeclaration: Node, flags: NodeBuilderFlags, tracker: SymbolTracker): TypeNode | undefined;
createTypeOfExpression(expr: Expression, enclosingDeclaration: Node, flags: NodeBuilderFlags, tracker: SymbolTracker): TypeNode | undefined;
createLiteralConstValue(node: VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration): Expression;
@@ -4715,16 +4723,6 @@ namespace ts {
verticalTab = 0x0B, // \v
}
/*@internal*/
export interface UpToDateHost {
fileExists(fileName: string): boolean;
getModifiedTime(fileName: string): Date | undefined;
getUnchangedTime?(fileName: string): Date | undefined;
getLastStatus?(fileName: string): UpToDateStatus | undefined;
setLastStatus?(fileName: string, status: UpToDateStatus): void;
parseConfigFile?(configFilePath: ResolvedConfigFileName): ParsedCommandLine | undefined;
}
export interface ModuleResolutionHost {
// TODO: GH#18217 Optional methods frequently used as non-optional
@@ -4855,10 +4853,6 @@ namespace ts {
/* @internal */ hasInvalidatedResolution?: HasInvalidatedResolution;
/* @internal */ hasChangedAutomaticTypeDirectiveNames?: boolean;
createHash?(data: string): string;
getModifiedTime?(fileName: string): Date | undefined;
setModifiedTime?(fileName: string, date: Date): void;
deleteFile?(fileName: string): void;
}
/* @internal */
@@ -5343,8 +5337,6 @@ namespace ts {
useCaseSensitiveFileNames?(): boolean;
fileExists?(path: string): boolean;
readFile?(path: string): string | undefined;
getSourceFiles?(): ReadonlyArray<SourceFile>; // Used for cached resolutions to find symlinks without traversing the fs (again)
getCommonSourceDirectory?(): string;
}
// Note: this used to be deprecated in our public API, but is still used internally
@@ -5357,7 +5349,7 @@ namespace ts {
reportInaccessibleThisError?(): void;
reportPrivateInBaseOfClassExpression?(propertyName: string): void;
reportInaccessibleUniqueSymbolError?(): void;
moduleResolverHost?: ModuleSpecifierResolutionHost;
moduleResolverHost?: EmitHost;
trackReferencedAmbientModule?(decl: ModuleDeclaration, symbol: Symbol): void;
trackExternalModuleSymbolOfImportTypeNode?(symbol: Symbol): void;
}
@@ -5596,15 +5588,32 @@ namespace ts {
/* @internal */
export type PragmaPsuedoMapEntry = {[K in keyof PragmaPsuedoMap]: {name: K, args: PragmaPsuedoMap[K]}}[keyof PragmaPsuedoMap];
/* @internal */
export interface ReadonlyPragmaMap extends ReadonlyMap<PragmaPsuedoMap[keyof PragmaPsuedoMap] | PragmaPsuedoMap[keyof PragmaPsuedoMap][]> {
get<TKey extends keyof PragmaPsuedoMap>(key: TKey): PragmaPsuedoMap[TKey] | PragmaPsuedoMap[TKey][];
forEach(action: <TKey extends keyof PragmaPsuedoMap>(value: PragmaPsuedoMap[TKey] | PragmaPsuedoMap[TKey][], key: TKey) => void): void;
}
/**
* A strongly-typed es6 map of pragma entries, the values of which are either a single argument
* value (if only one was found), or an array of multiple argument values if the pragma is present
* in multiple places
*/
/* @internal */
export interface PragmaMap extends Map<PragmaPsuedoMap[keyof PragmaPsuedoMap] | PragmaPsuedoMap[keyof PragmaPsuedoMap][]> {
export interface PragmaMap extends Map<PragmaPsuedoMap[keyof PragmaPsuedoMap] | PragmaPsuedoMap[keyof PragmaPsuedoMap][]>, ReadonlyPragmaMap {
set<TKey extends keyof PragmaPsuedoMap>(key: TKey, value: PragmaPsuedoMap[TKey] | PragmaPsuedoMap[TKey][]): this;
get<TKey extends keyof PragmaPsuedoMap>(key: TKey): PragmaPsuedoMap[TKey] | PragmaPsuedoMap[TKey][];
forEach(action: <TKey extends keyof PragmaPsuedoMap>(value: PragmaPsuedoMap[TKey] | PragmaPsuedoMap[TKey][], key: TKey) => void): void;
}
export interface UserPreferences {
readonly disableSuggestions?: boolean;
readonly quotePreference?: "double" | "single";
readonly includeCompletionsForModuleExports?: boolean;
readonly includeCompletionsWithInsertText?: boolean;
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
/** Determines whether we import `foo/index.ts` as "foo", "foo/index", or "foo/index.js" */
readonly importModuleSpecifierEnding?: "minimal" | "index" | "js";
readonly allowTextChangesInNewFiles?: boolean;
}
}
+56 -13
View File
@@ -15,7 +15,7 @@ namespace ts {
/* @internal */
namespace ts {
export const resolvingEmptyArray: never[] = [] as never[];
export const emptyMap: ReadonlyMap<never> = createMap<never>();
export const emptyMap = createMap<never>() as ReadonlyMap<never> & ReadonlyPragmaMap;
export const emptyUnderscoreEscapedMap: ReadonlyUnderscoreEscapedMap<never> = emptyMap as ReadonlyUnderscoreEscapedMap<never>;
export const externalHelpersModuleNameText = "tslib";
@@ -2330,6 +2330,13 @@ namespace ts {
return node;
}
function skipParenthesesUp(node: Node): Node {
while (node.kind === SyntaxKind.ParenthesizedExpression) {
node = node.parent;
}
return node;
}
// a node is delete target iff. it is PropertyAccessExpression/ElementAccessExpression with parentheses skipped
export function isDeleteTarget(node: Node): boolean {
if (node.kind !== SyntaxKind.PropertyAccessExpression && node.kind !== SyntaxKind.ElementAccessExpression) {
@@ -4226,6 +4233,8 @@ namespace ts {
if (!parent) return AccessKind.Read;
switch (parent.kind) {
case SyntaxKind.ParenthesizedExpression:
return accessKind(parent);
case SyntaxKind.PostfixUnaryExpression:
case SyntaxKind.PrefixUnaryExpression:
const { operator } = parent as PrefixUnaryExpression | PostfixUnaryExpression;
@@ -4237,13 +4246,35 @@ namespace ts {
: AccessKind.Read;
case SyntaxKind.PropertyAccessExpression:
return (parent as PropertyAccessExpression).name !== node ? AccessKind.Read : accessKind(parent);
case SyntaxKind.PropertyAssignment: {
const parentAccess = accessKind(parent.parent);
// In `({ x: varname }) = { x: 1 }`, the left `x` is a read, the right `x` is a write.
return node === (parent as PropertyAssignment).name ? reverseAccessKind(parentAccess) : parentAccess;
}
case SyntaxKind.ShorthandPropertyAssignment:
// Assume it's the local variable being accessed, since we don't check public properties for --noUnusedLocals.
return node === (parent as ShorthandPropertyAssignment).objectAssignmentInitializer ? AccessKind.Read : accessKind(parent.parent);
case SyntaxKind.ArrayLiteralExpression:
return accessKind(parent);
default:
return AccessKind.Read;
}
function writeOrReadWrite(): AccessKind {
// If grandparent is not an ExpressionStatement, this is used as an expression in addition to having a side effect.
return parent.parent && parent.parent.kind === SyntaxKind.ExpressionStatement ? AccessKind.Write : AccessKind.ReadWrite;
return parent.parent && skipParenthesesUp(parent.parent).kind === SyntaxKind.ExpressionStatement ? AccessKind.Write : AccessKind.ReadWrite;
}
}
function reverseAccessKind(a: AccessKind): AccessKind {
switch (a) {
case AccessKind.Read:
return AccessKind.Write;
case AccessKind.Write:
return AccessKind.Read;
case AccessKind.ReadWrite:
return AccessKind.ReadWrite;
default:
return Debug.assertNever(a);
}
}
@@ -4873,13 +4904,13 @@ namespace ts {
if (isDeclaration(hostNode)) {
return getDeclarationIdentifier(hostNode);
}
// Covers remaining cases
// Covers remaining cases (returning undefined if none match).
switch (hostNode.kind) {
case SyntaxKind.VariableStatement:
if (hostNode.declarationList && hostNode.declarationList.declarations[0]) {
return getDeclarationIdentifier(hostNode.declarationList.declarations[0]);
}
return undefined;
break;
case SyntaxKind.ExpressionStatement:
const expr = hostNode.expression;
switch (expr.kind) {
@@ -4891,9 +4922,7 @@ namespace ts {
return arg;
}
}
return undefined;
case SyntaxKind.EndOfFileToken:
return undefined;
break;
case SyntaxKind.ParenthesizedExpression: {
return getDeclarationIdentifier(hostNode.expression);
}
@@ -4901,10 +4930,8 @@ namespace ts {
if (isDeclaration(hostNode.statement) || isExpression(hostNode.statement)) {
return getDeclarationIdentifier(hostNode.statement);
}
return undefined;
break;
}
default:
Debug.assertNever(hostNode, "Found typedef tag attached to node which it should not be!");
}
}
@@ -5140,7 +5167,20 @@ namespace ts {
Debug.assert(node.parent.kind === SyntaxKind.JSDocComment);
return flatMap(node.parent.tags, tag => isJSDocTemplateTag(tag) ? tag.typeParameters : undefined) as ReadonlyArray<TypeParameterDeclaration>;
}
return node.typeParameters || (isInJavaScriptFile(node) ? getJSDocTypeParameterDeclarations(node) : emptyArray);
if (node.typeParameters) {
return node.typeParameters;
}
if (isInJavaScriptFile(node)) {
const decls = getJSDocTypeParameterDeclarations(node);
if (decls.length) {
return decls;
}
const typeTag = getJSDocType(node);
if (typeTag && isFunctionTypeNode(typeTag) && typeTag.typeParameters) {
return typeTag.typeParameters;
}
}
return emptyArray;
}
export function getEffectiveConstraintOfTypeParameter(node: TypeParameterDeclaration): TypeNode | undefined {
@@ -7313,8 +7353,6 @@ namespace ts {
if (pathComponents.length === 0) return "";
const root = pathComponents[0] && ensureTrailingDirectorySeparator(pathComponents[0]);
if (pathComponents.length === 1) return root;
return root + pathComponents.slice(1).join(directorySeparator);
}
@@ -7918,6 +7956,7 @@ namespace ts {
/** Must have ".d.ts" first because if ".ts" goes first, that will be detected as the extension instead of ".d.ts". */
export const supportedTypescriptExtensionsForExtractExtension: ReadonlyArray<Extension> = [Extension.Dts, Extension.Ts, Extension.Tsx];
export const supportedJavascriptExtensions: ReadonlyArray<Extension> = [Extension.Js, Extension.Jsx];
export const supportedJavaScriptAndJsonExtensions: ReadonlyArray<Extension> = [Extension.Js, Extension.Jsx, Extension.Json];
const allSupportedExtensions: ReadonlyArray<Extension> = [...supportedTypeScriptExtensions, ...supportedJavascriptExtensions];
export function getSupportedExtensions(options?: CompilerOptions, extraFileExtensions?: ReadonlyArray<FileExtensionInfo>): ReadonlyArray<string> {
@@ -7943,6 +7982,10 @@ namespace ts {
return some(supportedJavascriptExtensions, extension => fileExtensionIs(fileName, extension));
}
export function hasJavaScriptOrJsonFileExtension(fileName: string): boolean {
return supportedJavaScriptAndJsonExtensions.some(ext => fileExtensionIs(fileName, ext));
}
export function hasTypeScriptFileExtension(fileName: string): boolean {
return some(supportedTypeScriptExtensions, extension => fileExtensionIs(fileName, extension));
}
+34 -26
View File
@@ -27,12 +27,6 @@ namespace ts {
};
}
/** @internal */
export const nonClearingMessageCodes: number[] = [
Diagnostics.Found_1_error_Watching_for_file_changes.code,
Diagnostics.Found_0_errors_Watching_for_file_changes.code
];
/**
* @returns Whether the screen was cleared.
*/
@@ -41,7 +35,7 @@ namespace ts {
!options.preserveWatchOutput &&
!options.extendedDiagnostics &&
!options.diagnostics &&
!contains(nonClearingMessageCodes, diagnostic.code)) {
contains(screenStartingMessageCodes, diagnostic.code)) {
system.clearScreen();
return true;
}
@@ -174,6 +168,17 @@ namespace ts {
const noopFileWatcher: FileWatcher = { close: noop };
export function createWatchHost(system = sys, reportWatchStatus?: WatchStatusReporter): WatchHost {
const onWatchStatusChange = reportWatchStatus || createWatchStatusReporter(system);
return {
onWatchStatusChange,
watchFile: system.watchFile ? ((path, callback, pollingInterval) => system.watchFile!(path, callback, pollingInterval)) : () => noopFileWatcher,
watchDirectory: system.watchDirectory ? ((path, callback, recursive) => system.watchDirectory!(path, callback, recursive)) : () => noopFileWatcher,
setTimeout: system.setTimeout ? ((callback, ms, ...args: any[]) => system.setTimeout!.call(system, callback, ms, ...args)) : noop,
clearTimeout: system.clearTimeout ? (timeoutId => system.clearTimeout!(timeoutId)) : noop
};
}
/**
* Creates the watch compiler host that can be extended with config file or root file names and options host
*/
@@ -186,7 +191,7 @@ namespace ts {
host; // tslint:disable-line no-unused-expression (TODO: `host` is unused!)
const useCaseSensitiveFileNames = () => system.useCaseSensitiveFileNames;
const writeFileName = (s: string) => system.write(s + system.newLine);
const onWatchStatusChange = reportWatchStatus || createWatchStatusReporter(system);
const { onWatchStatusChange, watchFile, watchDirectory, setTimeout, clearTimeout } = createWatchHost(system, reportWatchStatus);
return {
useCaseSensitiveFileNames,
getNewLine: () => system.newLine,
@@ -200,10 +205,10 @@ namespace ts {
readDirectory: (path, extensions, exclude, include, depth) => system.readDirectory(path, extensions, exclude, include, depth),
realpath: system.realpath && (path => system.realpath!(path)),
getEnvironmentVariable: system.getEnvironmentVariable && (name => system.getEnvironmentVariable(name)),
watchFile: system.watchFile ? ((path, callback, pollingInterval) => system.watchFile!(path, callback, pollingInterval)) : () => noopFileWatcher,
watchDirectory: system.watchDirectory ? ((path, callback, recursive) => system.watchDirectory!(path, callback, recursive)) : () => noopFileWatcher,
setTimeout: system.setTimeout ? ((callback, ms, ...args: any[]) => system.setTimeout!.call(system, callback, ms, ...args)) : noop,
clearTimeout: system.clearTimeout ? (timeoutId => system.clearTimeout!(timeoutId)) : noop,
watchFile,
watchDirectory,
setTimeout,
clearTimeout,
trace: s => system.write(s),
onWatchStatusChange,
createDirectory: path => system.createDirectory(path),
@@ -224,10 +229,10 @@ namespace ts {
const reportSummary = (errorCount: number) => {
if (errorCount === 1) {
onWatchStatusChange(createCompilerDiagnostic(Diagnostics.Found_1_error_Watching_for_file_changes, errorCount), newLine, compilerOptions);
onWatchStatusChange!(createCompilerDiagnostic(Diagnostics.Found_1_error_Watching_for_file_changes, errorCount), newLine, compilerOptions);
}
else {
onWatchStatusChange(createCompilerDiagnostic(Diagnostics.Found_0_errors_Watching_for_file_changes, errorCount, errorCount), newLine, compilerOptions);
onWatchStatusChange!(createCompilerDiagnostic(Diagnostics.Found_0_errors_Watching_for_file_changes, errorCount, errorCount), newLine, compilerOptions);
}
};
@@ -270,7 +275,21 @@ namespace ts {
export type WatchStatusReporter = (diagnostic: Diagnostic, newLine: string, options: CompilerOptions) => void;
/** Create the program with rootNames and options, if they are undefined, oldProgram and new configFile diagnostics create new program */
export type CreateProgram<T extends BuilderProgram> = (rootNames: ReadonlyArray<string> | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: T, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>) => T;
export interface WatchCompilerHost<T extends BuilderProgram> {
/** Host that has watch functionality used in --watch mode */
export interface WatchHost {
/** If provided, called with Diagnostic message that informs about change in watch status */
onWatchStatusChange?(diagnostic: Diagnostic, newLine: string, options: CompilerOptions): void;
/** Used to watch changes in source files, missing files needed to update the program or config file */
watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher;
/** Used to watch resolved module's failed lookup locations, config file specs, type roots where auto type reference directives are added */
watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher;
/** If provided, will be used to set delayed compilation, so that multiple changes in short span are compiled together */
setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any;
/** If provided, will be used to reset existing delayed compilation */
clearTimeout?(timeoutId: any): void;
}
export interface WatchCompilerHost<T extends BuilderProgram> extends WatchHost {
// TODO: GH#18217 Optional methods are frequently asserted
/**
@@ -279,8 +298,6 @@ namespace ts {
createProgram: CreateProgram<T>;
/** If provided, callback to invoke after every new program creation */
afterProgramCreate?(program: T): void;
/** If provided, called with Diagnostic message that informs about change in watch status */
onWatchStatusChange?(diagnostic: Diagnostic, newLine: string, options: CompilerOptions): void;
// Only for testing
/*@internal*/
@@ -323,15 +340,6 @@ namespace ts {
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[];
/** If provided, used to resolve type reference directives, otherwise typescript's default resolution */
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
/** Used to watch changes in source files, missing files needed to update the program or config file */
watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher;
/** Used to watch resolved module's failed lookup locations, config file specs, type roots where auto type reference directives are added */
watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher;
/** If provided, will be used to set delayed compilation, so that multiple changes in short span are compiled together */
setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any;
/** If provided, will be used to reset existing delayed compilation */
clearTimeout?(timeoutId: any): void;
}
/** Internal interface used to wire emit through same host */
+36
View File
@@ -374,5 +374,41 @@ namespace fakes {
return parsed;
}
}
export class SolutionBuilderHost extends CompilerHost implements ts.SolutionBuilderHost {
diagnostics: ts.Diagnostic[] = [];
reportDiagnostic(diagnostic: ts.Diagnostic) {
this.diagnostics.push(diagnostic);
}
reportSolutionBuilderStatus(diagnostic: ts.Diagnostic) {
this.diagnostics.push(diagnostic);
}
clearDiagnostics() {
this.diagnostics.length = 0;
}
assertDiagnosticMessages(...expected: ts.DiagnosticMessage[]) {
const actual = this.diagnostics.slice();
if (actual.length !== expected.length) {
assert.fail<any>(actual, expected, `Diagnostic arrays did not match - got\r\n${actual.map(a => " " + a.messageText).join("\r\n")}\r\nexpected\r\n${expected.map(e => " " + e.message).join("\r\n")}`);
}
for (let i = 0; i < actual.length; i++) {
if (actual[i].code !== expected[i].code) {
assert.fail(actual[i].messageText, expected[i].message, `Mismatched error code - expected diagnostic ${i} "${actual[i].messageText}" to match ${expected[i].message}`);
}
}
}
printDiagnostics(header = "== Diagnostics ==") {
const out = ts.createDiagnosticReporter(ts.sys);
ts.sys.write(header + "\r\n");
for (const d of this.diagnostics) {
out(d);
}
}
}
}
+110 -75
View File
@@ -50,10 +50,8 @@ namespace FourSlash {
data?: {};
}
export interface Range {
export interface Range extends ts.TextRange {
fileName: string;
pos: number;
end: number;
marker?: Marker;
}
@@ -870,8 +868,7 @@ namespace FourSlash {
const actualByName = ts.createMap<ts.CompletionEntry>();
for (const entry of actualCompletions.entries) {
if (actualByName.has(entry.name)) {
// TODO: GH#23587
if (entry.name !== "undefined" && entry.name !== "require") this.raiseError(`Duplicate (${actualCompletions.entries.filter(a => a.name === entry.name).length}) completions for ${entry.name}`);
this.raiseError(`Duplicate (${actualCompletions.entries.filter(a => a.name === entry.name).length}) completions for ${entry.name}`);
}
else {
actualByName.set(entry.name, entry);
@@ -911,8 +908,8 @@ namespace FourSlash {
}
private verifyCompletionEntry(actual: ts.CompletionEntry, expected: FourSlashInterface.ExpectedCompletionEntry) {
const { insertText, replacementSpan, hasAction, isRecommended, kind, text, documentation, sourceDisplay } = typeof expected === "string"
? { insertText: undefined, replacementSpan: undefined, hasAction: undefined, isRecommended: undefined, kind: undefined, text: undefined, documentation: undefined, sourceDisplay: undefined }
const { insertText, replacementSpan, hasAction, isRecommended, kind, text, documentation, source, sourceDisplay } = typeof expected === "string"
? { insertText: undefined, replacementSpan: undefined, hasAction: undefined, isRecommended: undefined, kind: undefined, text: undefined, documentation: undefined, source: undefined, sourceDisplay: undefined }
: expected;
if (actual.insertText !== insertText) {
@@ -930,6 +927,7 @@ namespace FourSlash {
assert.equal(actual.hasAction, hasAction);
assert.equal(actual.isRecommended, isRecommended);
assert.equal(actual.source, source);
if (text) {
const actualDetails = this.getCompletionEntryDetails(actual.name, actual.source)!;
@@ -1103,7 +1101,7 @@ namespace FourSlash {
return node;
}
private verifyRange(desc: string, expected: Range, actual: ts.Node) {
private verifyRange(desc: string, expected: ts.TextRange, actual: ts.Node) {
const actualStart = actual.getStart();
const actualEnd = actual.getEnd();
if (actualStart !== expected.pos || actualEnd !== expected.end) {
@@ -1713,11 +1711,8 @@ Actual: ${stringify(fullActual)}`);
}
public baselineQuickInfo() {
let baselineFile = this.testData.globalOptions[MetadataOptionNames.baselineFile];
if (!baselineFile) {
baselineFile = ts.getBaseFileName(this.activeFile.fileName).replace(ts.Extension.Ts, ".baseline");
}
const baselineFile = this.testData.globalOptions[MetadataOptionNames.baselineFile] ||
ts.getBaseFileName(this.activeFile.fileName).replace(ts.Extension.Ts, ".baseline");
Harness.Baseline.runBaseline(
baselineFile,
stringify(
@@ -1958,18 +1953,11 @@ Actual: ${stringify(fullActual)}`);
* May be negative.
*/
private applyEdits(fileName: string, edits: ReadonlyArray<ts.TextChange>, isFormattingEdit: boolean): number {
// We get back a set of edits, but langSvc.editScript only accepts one at a time. Use this to keep track
// of the incremental offset from each edit to the next. We assume these edit ranges don't overlap
// Copy this so we don't ruin someone else's copy
edits = JSON.parse(JSON.stringify(edits));
// Get a snapshot of the content of the file so we can make sure any formatting edits didn't destroy non-whitespace characters
const oldContent = this.getFileContent(fileName);
let runningOffset = 0;
for (let i = 0; i < edits.length; i++) {
const edit = edits[i];
forEachTextChange(edits, edit => {
const offsetStart = edit.span.start;
const offsetEnd = offsetStart + edit.span.length;
this.editScriptAndUpdateMarkers(fileName, offsetStart, offsetEnd, edit.newText);
@@ -1985,14 +1973,7 @@ Actual: ${stringify(fullActual)}`);
}
}
runningOffset += editDelta;
// Update positions of any future edits affected by this change
for (let j = i + 1; j < edits.length; j++) {
if (edits[j].span.start >= edits[i].span.start) {
edits[j].span.start += editDelta;
}
}
}
});
if (isFormattingEdit) {
const newContent = this.getFileContent(fileName);
@@ -2034,30 +2015,14 @@ Actual: ${stringify(fullActual)}`);
this.languageServiceAdapterHost.editScript(fileName, editStart, editEnd, newText);
for (const marker of this.testData.markers) {
if (marker.fileName === fileName) {
marker.position = updatePosition(marker.position);
marker.position = updatePosition(marker.position, editStart, editEnd, newText);
}
}
for (const range of this.testData.ranges) {
if (range.fileName === fileName) {
range.pos = updatePosition(range.pos);
range.end = updatePosition(range.end);
}
}
function updatePosition(position: number) {
if (position > editStart) {
if (position < editEnd) {
// Inside the edit - mark it as invalidated (?)
return -1;
}
else {
// Move marker back/forward by the appropriate amount
return position + (editStart - editEnd) + newText.length;
}
}
else {
return position;
range.pos = updatePosition(range.pos, editStart, editEnd, newText);
range.end = updatePosition(range.end, editStart, editEnd, newText);
}
}
}
@@ -2488,22 +2453,24 @@ Actual: ${stringify(fullActual)}`);
this.applyCodeActions(codeActions);
this.verifyNewContent(options, ts.flatMap(codeActions, a => a.changes.map(c => c.fileName)));
this.verifyNewContentAfterChange(options, ts.flatMap(codeActions, a => a.changes.map(c => c.fileName)));
}
public verifyRangeIs(expectedText: string, includeWhiteSpace?: boolean) {
this.verifyTextMatches(this.rangeText(this.getOnlyRange()), !!includeWhiteSpace, expectedText);
}
private getOnlyRange() {
const ranges = this.getRanges();
if (ranges.length !== 1) {
this.raiseError("Exactly one range should be specified in the testfile.");
}
return ts.first(ranges);
}
const actualText = this.rangeText(ranges[0]);
const result = includeWhiteSpace
? actualText === expectedText
: this.removeWhitespace(actualText) === this.removeWhitespace(expectedText);
if (!result) {
private verifyTextMatches(actualText: string, includeWhitespace: boolean, expectedText: string) {
const removeWhitespace = (s: string): string => includeWhitespace ? s : this.removeWhitespace(s);
if (removeWhitespace(actualText) !== removeWhitespace(expectedText)) {
this.raiseError(`Actual range text doesn't match expected text.\n${showTextDiff(expectedText, actualText)}`);
}
}
@@ -2570,33 +2537,68 @@ Actual: ${stringify(fullActual)}`);
const action = actions[index];
assert.equal(action.description, options.description);
assert.deepEqual(action.commands, options.commands);
for (const change of action.changes) {
this.applyEdits(change.fileName, change.textChanges, /*isFormattingEdit*/ false);
if (options.applyChanges) {
for (const change of action.changes) {
this.applyEdits(change.fileName, change.textChanges, /*isFormattingEdit*/ false);
}
this.verifyNewContentAfterChange(options, action.changes.map(c => c.fileName));
}
else {
this.verifyNewContent(options, action.changes);
}
this.verifyNewContent(options, action.changes.map(c => c.fileName));
}
private verifyNewContent(options: FourSlashInterface.NewContentOptions, changedFiles: ReadonlyArray<string>) {
const assertedChangedFiles = !options.newFileContent || typeof options.newFileContent === "string"
private verifyNewContent({ newFileContent, newRangeContent }: FourSlashInterface.NewContentOptions, changes: ReadonlyArray<ts.FileTextChanges>): void {
if (newRangeContent !== undefined) {
assert(newFileContent === undefined);
assert(changes.length === 1, "Affected 0 or more than 1 file, must use 'newFileContent' instead of 'newRangeContent'");
const change = ts.first(changes);
assert(change.fileName = this.activeFile.fileName);
const newText = ts.textChanges.applyChanges(this.getFileContent(this.activeFile.fileName), change.textChanges);
const newRange = updateTextRangeForTextChanges(this.getOnlyRange(), change.textChanges);
const actualText = newText.slice(newRange.pos, newRange.end);
this.verifyTextMatches(actualText, /*includeWhitespace*/ true, newRangeContent);
}
else {
if (newFileContent === undefined) throw ts.Debug.fail();
if (typeof newFileContent !== "object") newFileContent = { [this.activeFile.fileName]: newFileContent };
for (const change of changes) {
const expectedNewContent = newFileContent[change.fileName];
if (expectedNewContent === undefined) {
ts.Debug.fail(`Did not expect a change in ${change.fileName}`);
}
const oldText = this.tryGetFileContent(change.fileName);
ts.Debug.assert(!!change.isNewFile === (oldText === undefined));
const newContent = change.isNewFile ? ts.first(change.textChanges).newText : ts.textChanges.applyChanges(oldText!, change.textChanges);
assert.equal(newContent, expectedNewContent);
}
for (const newFileName in newFileContent) {
ts.Debug.assert(changes.some(c => c.fileName === newFileName), "No change in file", () => newFileName);
}
}
}
private verifyNewContentAfterChange({ newFileContent, newRangeContent }: FourSlashInterface.NewContentOptions, changedFiles: ReadonlyArray<string>) {
const assertedChangedFiles = !newFileContent || typeof newFileContent === "string"
? [this.activeFile.fileName]
: ts.getOwnKeys(options.newFileContent);
: ts.getOwnKeys(newFileContent);
assert.deepEqual(assertedChangedFiles, changedFiles);
if (options.newFileContent !== undefined) {
assert(!options.newRangeContent);
if (typeof options.newFileContent === "string") {
this.verifyCurrentFileContent(options.newFileContent);
if (newFileContent !== undefined) {
assert(!newRangeContent);
if (typeof newFileContent === "string") {
this.verifyCurrentFileContent(newFileContent);
}
else {
for (const fileName in options.newFileContent) {
this.verifyFileContent(fileName, options.newFileContent[fileName]);
for (const fileName in newFileContent) {
this.verifyFileContent(fileName, newFileContent[fileName]);
}
}
}
else {
this.verifyRangeIs(options.newRangeContent!, /*includeWhitespace*/ true);
this.verifyRangeIs(newRangeContent!, /*includeWhitespace*/ true);
}
}
@@ -3114,7 +3116,7 @@ Actual: ${stringify(fullActual)}`);
assert(action.name === "Move to a new file" && action.description === "Move to a new file");
const editInfo = this.languageService.getEditsForRefactor(range.fileName, this.formatCodeSettings, range, refactor.name, action.name, options.preferences || ts.emptyOptions)!;
this.testNewFileContents(editInfo.edits, options.newFileContents, "move to new file");
this.verifyNewContent({ newFileContent: options.newFileContents }, editInfo.edits);
}
private testNewFileContents(edits: ReadonlyArray<ts.FileTextChanges>, newFileContents: { [fileName: string]: string }, description: string): void {
@@ -3380,6 +3382,36 @@ Actual: ${stringify(fullActual)}`);
}
}
function updateTextRangeForTextChanges({ pos, end }: ts.TextRange, textChanges: ReadonlyArray<ts.TextChange>): ts.TextRange {
forEachTextChange(textChanges, change => {
const update = (p: number): number => updatePosition(p, change.span.start, ts.textSpanEnd(change.span), change.newText);
pos = update(pos);
end = update(end);
});
return { pos, end };
}
/** Apply each textChange in order, updating future changes to account for the text offset of previous changes. */
function forEachTextChange(changes: ReadonlyArray<ts.TextChange>, cb: (change: ts.TextChange) => void): void {
// Copy this so we don't ruin someone else's copy
changes = JSON.parse(JSON.stringify(changes));
for (let i = 0; i < changes.length; i++) {
const change = changes[i];
cb(change);
const changeDelta = change.newText.length - change.span.length;
for (let j = i + 1; j < changes.length; j++) {
if (changes[j].span.start >= change.span.start) {
changes[j].span.start += changeDelta;
}
}
}
}
function updatePosition(position: number, editStart: number, editEnd: number, { length }: string): number {
// If inside the edit, return -1 to mark as invalid
return position <= editStart ? position : position < editEnd ? -1 : position + length - + (editEnd - editStart);
}
function renameKeys<T>(obj: { readonly [key: string]: T }, renameKey: (key: string) => string): { readonly [key: string]: T } {
const res: { [key: string]: T } = {};
for (const key in obj) {
@@ -4758,6 +4790,7 @@ namespace FourSlashInterface {
export type ExpectedCompletionEntry = string | {
readonly name: string,
readonly source?: string,
readonly insertText?: string,
readonly replacementSpan?: FourSlash.Range,
readonly hasAction?: boolean, // If not specified, will assert that this is false.
@@ -4842,10 +4875,12 @@ namespace FourSlashInterface {
}
export interface VerifyCodeFixOptions extends NewContentOptions {
description: string;
errorCode?: number;
index?: number;
preferences?: ts.UserPreferences;
readonly description: string;
readonly errorCode?: number;
readonly index?: number;
readonly preferences?: ts.UserPreferences;
readonly applyChanges?: boolean;
readonly commands?: ReadonlyArray<ts.CodeActionCommand>;
}
export interface VerifyCodeFixAvailableOptions {
+11 -3
View File
@@ -620,14 +620,14 @@ interface Array<T> {}`
}
}
removeFile(filePath: string) {
deleteFile(filePath: string) {
const path = this.toFullPath(filePath);
const currentEntry = this.fs.get(path) as FsFile;
Debug.assert(isFsFile(currentEntry));
this.removeFileOrFolder(currentEntry, returnFalse);
}
removeFolder(folderPath: string, recursive?: boolean) {
deleteFolder(folderPath: string, recursive?: boolean) {
const path = this.toFullPath(folderPath);
const currentEntry = this.fs.get(path) as FsFolder;
Debug.assert(isFsFolder(currentEntry));
@@ -635,7 +635,7 @@ interface Array<T> {}`
const subEntries = currentEntry.entries.slice();
subEntries.forEach(fsEntry => {
if (isFsFolder(fsEntry)) {
this.removeFolder(fsEntry.fullPath, recursive);
this.deleteFolder(fsEntry.fullPath, recursive);
}
else {
this.removeFileOrFolder(fsEntry, returnFalse);
@@ -766,6 +766,14 @@ interface Array<T> {}`
return (fsEntry && fsEntry.modifiedTime)!; // TODO: GH#18217
}
setModifiedTime(s: string, date: Date) {
const path = this.toFullPath(s);
const fsEntry = this.fs.get(path);
if (fsEntry) {
fsEntry.modifiedTime = date;
}
}
readFile(s: string): string | undefined {
const fsEntry = this.getRealFile(this.toFullPath(s));
return fsEntry ? fsEntry.content : undefined;
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+32 -6
View File
@@ -218,9 +218,15 @@ namespace ts.server {
}
}
/*@internal*/
export function convertUserPreferences(preferences: protocol.UserPreferences): UserPreferences {
const { lazyConfiguredProjectsFromExternalProject, ...userPreferences } = preferences;
return userPreferences;
}
export interface HostConfiguration {
formatCodeOptions: FormatCodeSettings;
preferences: UserPreferences;
preferences: protocol.UserPreferences;
hostInfo: string;
extraFileExtensions?: FileExtensionInfo[];
}
@@ -802,7 +808,7 @@ namespace ts.server {
return info && info.getFormatCodeSettings() || this.hostConfiguration.formatCodeOptions;
}
getPreferences(file: NormalizedPath): UserPreferences {
getPreferences(file: NormalizedPath): protocol.UserPreferences {
const info = this.getScriptInfoForNormalizedPath(file);
return info && info.getPreferences() || this.hostConfiguration.preferences;
}
@@ -811,7 +817,7 @@ namespace ts.server {
return this.hostConfiguration.formatCodeOptions;
}
getHostPreferences(): UserPreferences {
getHostPreferences(): protocol.UserPreferences {
return this.hostConfiguration.preferences;
}
@@ -1561,6 +1567,13 @@ namespace ts.server {
return project;
}
/* @internal */
private createLoadAndUpdateConfiguredProject(configFileName: NormalizedPath) {
const project = this.createAndLoadConfiguredProject(configFileName);
project.updateGraph();
return project;
}
/**
* Read the config file of the project, and update the project root file names.
*/
@@ -1979,7 +1992,19 @@ namespace ts.server {
this.logger.info("Format host information updated");
}
if (args.preferences) {
const { lazyConfiguredProjectsFromExternalProject } = this.hostConfiguration.preferences;
this.hostConfiguration.preferences = { ...this.hostConfiguration.preferences, ...args.preferences };
if (lazyConfiguredProjectsFromExternalProject && !this.hostConfiguration.preferences.lazyConfiguredProjectsFromExternalProject) {
// Load configured projects for external projects that are pending reload
this.configuredProjects.forEach(project => {
if (project.hasExternalProjectRef() &&
project.pendingReload === ConfigFileProgramReloadLevel.Full &&
!this.pendingProjectUpdates.has(project.getProjectName())) {
this.loadConfiguredProject(project);
project.updateGraph();
}
});
}
}
if (args.extraFileExtensions) {
this.hostConfiguration.extraFileExtensions = args.extraFileExtensions;
@@ -2192,8 +2217,7 @@ namespace ts.server {
if (configFileName) {
project = this.findConfiguredProjectByProjectName(configFileName);
if (!project) {
project = this.createAndLoadConfiguredProject(configFileName);
project.updateGraph();
project = this.createLoadAndUpdateConfiguredProject(configFileName);
// Send the event only if the project got created as part of this open request and info is part of the project
if (info.isOrphan()) {
// Since the file isnt part of configured project, do not send config file info
@@ -2633,7 +2657,9 @@ namespace ts.server {
let project = this.findConfiguredProjectByProjectName(tsconfigFile);
if (!project) {
// errors are stored in the project, do not need to update the graph
project = this.createConfiguredProjectWithDelayLoad(tsconfigFile);
project = this.getHostPreferences().lazyConfiguredProjectsFromExternalProject ?
this.createConfiguredProjectWithDelayLoad(tsconfigFile) :
this.createLoadAndUpdateConfiguredProject(tsconfigFile);
}
if (project && !contains(exisingConfigFiles, tsconfigFile)) {
// keep project alive even if no documents are opened - its lifetime is bound to the lifetime of containing external project
+5
View File
@@ -1520,6 +1520,11 @@ namespace ts.server {
) || false;
}
/*@internal*/
hasExternalProjectRef() {
return !!this.externalProjectRefCount;
}
getEffectiveTypeRoots() {
return getEffectiveTypeRoots(this.getCompilationSettings(), this.directoryStructureHost) || [];
}
+2 -1
View File
@@ -1839,7 +1839,7 @@ namespace ts.server.protocol {
* begin with prefix.
*/
export interface CompletionsRequest extends FileLocationRequest {
command: CommandTypes.Completions;
command: CommandTypes.Completions | CommandTypes.CompletionInfo;
arguments: CompletionsRequestArgs;
}
@@ -2823,6 +2823,7 @@ namespace ts.server.protocol {
readonly includeCompletionsWithInsertText?: boolean;
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
readonly allowTextChangesInNewFiles?: boolean;
readonly lazyConfiguredProjectsFromExternalProject?: boolean;
}
export interface CompilerOptions {
+3 -3
View File
@@ -234,7 +234,7 @@ namespace ts.server {
*/
readonly containingProjects: Project[] = [];
private formatSettings: FormatCodeSettings | undefined;
private preferences: UserPreferences | undefined;
private preferences: protocol.UserPreferences | undefined;
/* @internal */
fileWatcher: FileWatcher | undefined;
@@ -333,7 +333,7 @@ namespace ts.server {
}
getFormatCodeSettings(): FormatCodeSettings | undefined { return this.formatSettings; }
getPreferences(): UserPreferences | undefined { return this.preferences; }
getPreferences(): protocol.UserPreferences | undefined { return this.preferences; }
attachToProject(project: Project): boolean {
const isNew = !this.isAttached(project);
@@ -432,7 +432,7 @@ namespace ts.server {
}
}
setOptions(formatSettings: FormatCodeSettings, preferences: UserPreferences | undefined): void {
setOptions(formatSettings: FormatCodeSettings, preferences: protocol.UserPreferences | undefined): void {
if (formatSettings) {
if (!this.formatSettings) {
this.formatSettings = getDefaultFormatCodeSettings(this.host);
+3 -3
View File
@@ -1423,7 +1423,7 @@ namespace ts.server {
const position = this.getPosition(args, scriptInfo);
const completions = project.getLanguageService().getCompletionsAtPosition(file, position, {
...this.getPreferences(file),
...convertUserPreferences(this.getPreferences(file)),
triggerCharacter: args.triggerCharacter,
includeExternalModuleExports: args.includeExternalModuleExports,
includeInsertTextCompletions: args.includeInsertTextCompletions
@@ -2352,7 +2352,7 @@ namespace ts.server {
return this.projectService.getFormatCodeOptions(file);
}
private getPreferences(file: NormalizedPath): UserPreferences {
private getPreferences(file: NormalizedPath): protocol.UserPreferences {
return this.projectService.getPreferences(file);
}
@@ -2360,7 +2360,7 @@ namespace ts.server {
return this.projectService.getHostFormatCodeOptions();
}
private getHostPreferences(): UserPreferences {
private getHostPreferences(): protocol.UserPreferences {
return this.projectService.getHostPreferences();
}
}
+9 -13
View File
@@ -112,27 +112,23 @@ namespace ts.codefix {
export function createMethodFromCallExpression(
context: CodeFixContextBase,
{ typeArguments, arguments: args, parent: parent }: CallExpression,
call: CallExpression,
methodName: string,
inJs: boolean,
makeStatic: boolean,
preferences: UserPreferences,
body: boolean,
): MethodDeclaration {
const { typeArguments, arguments: args, parent } = call;
const checker = context.program.getTypeChecker();
const types = map(args,
arg => {
let type = checker.getTypeAtLocation(arg);
if (type === undefined) {
return undefined;
}
// Widen the type so we don't emit nonsense annotations like "function fn(x: 3) {"
type = checker.getBaseTypeOfLiteralType(type);
return checker.typeToTypeNode(type);
});
const types = map(args, arg =>
// Widen the type so we don't emit nonsense annotations like "function fn(x: 3) {"
checker.typeToTypeNode(checker.getBaseTypeOfLiteralType(checker.getTypeAtLocation(arg))));
const names = map(args, arg =>
isIdentifier(arg) ? arg.text :
isPropertyAccessExpression(arg) ? arg.name.text : undefined);
isPropertyAccessExpression(arg) ? arg.name.text : undefined);
const contextualType = checker.getContextualType(call);
const returnType = inJs ? undefined : contextualType && checker.typeToTypeNode(contextualType, call) || createKeywordTypeNode(SyntaxKind.AnyKeyword);
return createMethod(
/*decorators*/ undefined,
/*modifiers*/ makeStatic ? [createToken(SyntaxKind.StaticKeyword)] : undefined,
@@ -142,7 +138,7 @@ namespace ts.codefix {
/*typeParameters*/ inJs ? undefined : map(typeArguments, (_, i) =>
createTypeParameterDeclaration(CharacterCodes.T + typeArguments!.length - 1 <= CharacterCodes.Z ? String.fromCharCode(CharacterCodes.T + i) : `T${i}`)),
/*parameters*/ createDummyParameters(args.length, names, types, /*minArgumentCount*/ undefined, inJs),
/*type*/ inJs ? undefined : createKeywordTypeNode(SyntaxKind.AnyKeyword),
/*type*/ returnType,
body ? createStubbedMethodBody(preferences) : undefined);
}
+39 -17
View File
@@ -163,14 +163,19 @@ namespace ts.codefix {
position: number,
preferences: UserPreferences,
): { readonly moduleSpecifier: string, readonly codeAction: CodeAction } {
const exportInfos = getAllReExportingModules(exportedSymbol, moduleSymbol, symbolName, sourceFile, program.getTypeChecker(), program.getSourceFiles());
const exportInfos = getAllReExportingModules(exportedSymbol, moduleSymbol, symbolName, sourceFile, program.getCompilerOptions(), program.getTypeChecker(), program.getSourceFiles());
Debug.assert(exportInfos.some(info => info.moduleSymbol === moduleSymbol));
// We sort the best codefixes first, so taking `first` is best for completions.
const moduleSpecifier = first(getNewImportInfos(program, sourceFile, position, exportInfos, host, preferences)).moduleSpecifier;
const fix = first(getFixForImport(exportInfos, symbolName, position, program, sourceFile, host, preferences));
return { moduleSpecifier, codeAction: codeActionForFix({ host, formatContext }, sourceFile, symbolName, fix, getQuotePreference(sourceFile, preferences)) };
return { moduleSpecifier, codeAction: codeFixActionToCodeAction(codeActionForFix({ host, formatContext }, sourceFile, symbolName, fix, getQuotePreference(sourceFile, preferences))) };
}
function getAllReExportingModules(exportedSymbol: Symbol, exportingModuleSymbol: Symbol, symbolName: string, sourceFile: SourceFile, checker: TypeChecker, allSourceFiles: ReadonlyArray<SourceFile>): ReadonlyArray<SymbolExportInfo> {
function codeFixActionToCodeAction({ description, changes, commands }: CodeFixAction): CodeAction {
return { description, changes, commands };
}
function getAllReExportingModules(exportedSymbol: Symbol, exportingModuleSymbol: Symbol, symbolName: string, sourceFile: SourceFile, compilerOptions: CompilerOptions, checker: TypeChecker, allSourceFiles: ReadonlyArray<SourceFile>): ReadonlyArray<SymbolExportInfo> {
const result: SymbolExportInfo[] = [];
forEachExternalModule(checker, allSourceFiles, (moduleSymbol, moduleFile) => {
// Don't import from a re-export when looking "up" like to `./index` or `../index`.
@@ -178,10 +183,14 @@ namespace ts.codefix {
return;
}
const defaultInfo = getDefaultLikeExportInfo(moduleSymbol, checker, compilerOptions);
if (defaultInfo && defaultInfo.name === symbolName && skipAlias(defaultInfo.symbol, checker) === exportedSymbol) {
result.push({ moduleSymbol, importKind: defaultInfo.kind, exportedSymbolIsTypeOnly: isTypeOnlySymbol(defaultInfo.symbol) });
}
for (const exported of checker.getExportsOfModule(moduleSymbol)) {
if ((exported.escapedName === InternalSymbolName.Default || exported.name === symbolName) && skipAlias(exported, checker) === exportedSymbol) {
const isDefaultExport = checker.tryGetMemberInModuleExports(InternalSymbolName.Default, moduleSymbol) === exported;
result.push({ moduleSymbol, importKind: isDefaultExport ? ImportKind.Default : ImportKind.Named, exportedSymbolIsTypeOnly: isTypeOnlySymbol(exported) });
if (exported.name === symbolName && skipAlias(exported, checker) === exportedSymbol) {
result.push({ moduleSymbol, importKind: ImportKind.Named, exportedSymbolIsTypeOnly: isTypeOnlySymbol(exported) });
}
}
});
@@ -395,13 +404,9 @@ namespace ts.codefix {
forEachExternalModuleToImportFrom(checker, sourceFile, program.getSourceFiles(), moduleSymbol => {
cancellationToken.throwIfCancellationRequested();
// check the default export
const defaultExport = checker.tryGetMemberInModuleExports(InternalSymbolName.Default, moduleSymbol);
if (defaultExport) {
const info = getDefaultExportInfo(defaultExport, moduleSymbol, program);
if (info && info.name === symbolName && symbolHasMeaning(info.symbolForMeaning, currentTokenMeaning)) {
addSymbol(moduleSymbol, defaultExport, ImportKind.Default);
}
const defaultInfo = getDefaultLikeExportInfo(moduleSymbol, checker, program.getCompilerOptions());
if (defaultInfo && defaultInfo.name === symbolName && symbolHasMeaning(defaultInfo.symbolForMeaning, currentTokenMeaning)) {
addSymbol(moduleSymbol, defaultInfo.symbol, defaultInfo.kind);
}
// check exports with the same name
@@ -413,7 +418,24 @@ namespace ts.codefix {
return originalSymbolToExportInfos;
}
function getDefaultExportInfo(defaultExport: Symbol, moduleSymbol: Symbol, program: Program): { readonly symbolForMeaning: Symbol, readonly name: string } | undefined {
function getDefaultLikeExportInfo(
moduleSymbol: Symbol, checker: TypeChecker, compilerOptions: CompilerOptions,
): { readonly symbol: Symbol, readonly symbolForMeaning: Symbol, readonly name: string, readonly kind: ImportKind.Default | ImportKind.Equals } | undefined {
const exported = getDefaultLikeExportWorker(moduleSymbol, checker);
if (!exported) return undefined;
const { symbol, kind } = exported;
const info = getDefaultExportInfoWorker(symbol, moduleSymbol, checker, compilerOptions);
return info && { symbol, symbolForMeaning: info.symbolForMeaning, name: info.name, kind };
}
function getDefaultLikeExportWorker(moduleSymbol: Symbol, checker: TypeChecker): { readonly symbol: Symbol, readonly kind: ImportKind.Default | ImportKind.Equals } | undefined {
const defaultExport = checker.tryGetMemberInModuleExports(InternalSymbolName.Default, moduleSymbol);
if (defaultExport) return { symbol: defaultExport, kind: ImportKind.Default };
const exportEquals = checker.resolveExternalModuleSymbol(moduleSymbol);
return exportEquals === moduleSymbol ? undefined : { symbol: exportEquals, kind: ImportKind.Equals };
}
function getDefaultExportInfoWorker(defaultExport: Symbol, moduleSymbol: Symbol, checker: TypeChecker, compilerOptions: CompilerOptions): { readonly symbolForMeaning: Symbol, readonly name: string } | undefined {
const localSymbol = getLocalSymbolForExportDefault(defaultExport);
if (localSymbol) return { symbolForMeaning: localSymbol, name: localSymbol.name };
@@ -421,11 +443,11 @@ namespace ts.codefix {
if (name !== undefined) return { symbolForMeaning: defaultExport, name };
if (defaultExport.flags & SymbolFlags.Alias) {
const aliased = program.getTypeChecker().getImmediateAliasedSymbol(defaultExport);
return aliased && getDefaultExportInfo(aliased, Debug.assertDefined(aliased.parent), program);
const aliased = checker.getImmediateAliasedSymbol(defaultExport);
return aliased && getDefaultExportInfoWorker(aliased, Debug.assertDefined(aliased.parent), checker, compilerOptions);
}
else {
return { symbolForMeaning: defaultExport, name: moduleSymbolToValidIdentifier(moduleSymbol, program.getCompilerOptions().target!) };
return { symbolForMeaning: defaultExport, name: moduleSymbolToValidIdentifier(moduleSymbol, compilerOptions.target!) };
}
}
+11 -3
View File
@@ -1378,6 +1378,14 @@ namespace ts.Completions {
return;
}
if (resolvedModuleSymbol !== moduleSymbol &&
// Don't add another completion for `export =` of a symbol that's already global.
// So in `declare namespace foo {} declare module "foo" { export = foo; }`, there will just be the global completion for `foo`.
resolvedModuleSymbol.declarations.some(d => !!d.getSourceFile().externalModuleIndicator)) {
symbols.push(resolvedModuleSymbol);
symbolToOriginInfoMap[getSymbolId(resolvedModuleSymbol)] = { kind: SymbolOriginInfoKind.Export, moduleSymbol, isDefaultExport: false };
}
for (let symbol of typeChecker.getExportsOfModule(moduleSymbol)) {
// Don't add a completion for a re-export, only for the original.
// The actual import fix might end up coming from a re-export -- we don't compute that until getting completion details.
@@ -2116,8 +2124,8 @@ namespace ts.Completions {
const kind = stringToToken(entry.name)!;
switch (keywordFilter) {
case KeywordCompletionFilters.None:
// "undefined" is a global variable, so don't need a keyword completion for it.
return kind !== SyntaxKind.UndefinedKeyword;
return kind === SyntaxKind.AsyncKeyword || !isContextualKeyword(kind) && !isClassMemberCompletionKeyword(kind) || kind === SyntaxKind.DeclareKeyword || kind === SyntaxKind.ModuleKeyword
|| isTypeKeyword(kind) && kind !== SyntaxKind.UndefinedKeyword;
case KeywordCompletionFilters.ClassElementKeywords:
return isClassMemberCompletionKeyword(kind);
case KeywordCompletionFilters.InterfaceElementKeywords:
@@ -2152,7 +2160,7 @@ namespace ts.Completions {
}
function isFunctionLikeBodyKeyword(kind: SyntaxKind) {
return kind === SyntaxKind.AsyncKeyword || !isClassMemberCompletionKeyword(kind);
return kind === SyntaxKind.AsyncKeyword || !isContextualKeyword(kind) && !isClassMemberCompletionKeyword(kind);
}
function keywordForNode(node: Node): SyntaxKind {
+4 -5
View File
@@ -6,7 +6,7 @@ namespace ts {
newFileOrDirPath: string,
host: LanguageServiceHost,
formatContext: formatting.FormatContext,
preferences: UserPreferences,
_preferences: UserPreferences,
sourceMapper: SourceMapper,
): ReadonlyArray<FileTextChanges> {
const useCaseSensitiveFileNames = hostUsesCaseSensitiveFileNames(host);
@@ -15,7 +15,7 @@ namespace ts {
const newToOld = getPathUpdater(newFileOrDirPath, oldFileOrDirPath, getCanonicalFileName, sourceMapper);
return textChanges.ChangeTracker.with({ host, formatContext }, changeTracker => {
updateTsconfigFiles(program, changeTracker, oldToNew, newFileOrDirPath, host.getCurrentDirectory(), useCaseSensitiveFileNames);
updateImports(program, changeTracker, oldToNew, newToOld, host, getCanonicalFileName, preferences);
updateImports(program, changeTracker, oldToNew, newToOld, host, getCanonicalFileName);
});
}
@@ -122,7 +122,6 @@ namespace ts {
newToOld: PathUpdater,
host: LanguageServiceHost,
getCanonicalFileName: GetCanonicalFileName,
preferences: UserPreferences,
): void {
const allFiles = program.getSourceFiles();
for (const sourceFile of allFiles) {
@@ -156,7 +155,7 @@ namespace ts {
// Need an update if the imported file moved, or the importing file moved and was using a relative path.
return toImport !== undefined && (toImport.updated || (importingSourceFileMoved && pathIsRelative(importLiteral.text)))
? moduleSpecifiers.getModuleSpecifier(program.getCompilerOptions(), sourceFile, newImportFromPath, toImport.newFileName, host, allFiles, preferences, program.redirectTargetsMap)
? moduleSpecifiers.updateModuleSpecifier(program.getCompilerOptions(), newImportFromPath, toImport.newFileName, host, allFiles, program.redirectTargetsMap, importLiteral.text)
: undefined;
});
}
@@ -210,7 +209,7 @@ namespace ts {
}
function updateImportsWorker(sourceFile: SourceFile, changeTracker: textChanges.ChangeTracker, updateRef: (refText: string) => string | undefined, updateImport: (importLiteral: StringLiteralLike) => string | undefined) {
for (const ref of sourceFile.referencedFiles) {
for (const ref of sourceFile.referencedFiles || emptyArray) { // TODO: GH#26162
const updated = updateRef(ref.fileName);
if (updated !== undefined && updated !== sourceFile.text.slice(ref.pos, ref.end)) changeTracker.replaceRangeWithText(sourceFile, ref, updated);
}
+12 -1
View File
@@ -29,7 +29,7 @@ namespace ts.GoToDefinition {
const calledDeclaration = tryGetSignatureDeclaration(typeChecker, node);
// Don't go to the component constructor definition for a JSX element, just go to the component definition.
if (calledDeclaration && !(isJsxOpeningLikeElement(node.parent) && isConstructorDeclaration(calledDeclaration))) {
if (calledDeclaration && !(isJsxOpeningLikeElement(node.parent) && isConstructorLike(calledDeclaration))) {
const sigInfo = createDefinitionFromSignatureDeclaration(typeChecker, calledDeclaration);
// For a function, if this is the original function definition, return just sigInfo.
// If this is the original constructor definition, parent is the class.
@@ -319,4 +319,15 @@ namespace ts.GoToDefinition {
// Don't go to a function type, go to the value having that type.
return tryCast(signature && signature.declaration, (d): d is SignatureDeclaration => isFunctionLike(d) && !isFunctionTypeNode(d));
}
function isConstructorLike(node: Node): boolean {
switch (node.kind) {
case SyntaxKind.Constructor:
case SyntaxKind.ConstructorType:
case SyntaxKind.ConstructSignature:
return true;
default:
return false;
}
}
}
+61 -15
View File
@@ -2,7 +2,7 @@
namespace ts.SignatureHelp {
const enum InvocationKind { Call, TypeArgs, Contextual }
interface CallInvocation { readonly kind: InvocationKind.Call; readonly node: CallLikeExpression; }
interface TypeArgsInvocation { readonly kind: InvocationKind.TypeArgs; readonly called: Expression; }
interface TypeArgsInvocation { readonly kind: InvocationKind.TypeArgs; readonly called: Identifier; }
interface ContextualInvocation {
readonly kind: InvocationKind.Contextual;
readonly signature: Signature;
@@ -44,7 +44,7 @@ namespace ts.SignatureHelp {
cancellationToken.throwIfCancellationRequested();
// Extra syntactic and semantic filtering of signature help
const candidateInfo = getCandidateInfo(argumentInfo, typeChecker, sourceFile, startingToken, onlyUseSyntacticOwners);
const candidateInfo = getCandidateOrTypeInfo(argumentInfo, typeChecker, sourceFile, startingToken, onlyUseSyntacticOwners);
cancellationToken.throwIfCancellationRequested();
if (!candidateInfo) {
@@ -53,11 +53,24 @@ namespace ts.SignatureHelp {
return isSourceFileJavaScript(sourceFile) ? createJavaScriptSignatureHelpItems(argumentInfo, program, cancellationToken) : undefined;
}
return typeChecker.runWithCancellationToken(cancellationToken, typeChecker => createSignatureHelpItems(candidateInfo.candidates, candidateInfo.resolvedSignature, argumentInfo, sourceFile, typeChecker));
return typeChecker.runWithCancellationToken(cancellationToken, typeChecker =>
candidateInfo.kind === CandidateOrTypeKind.Candidate
? createSignatureHelpItems(candidateInfo.candidates, candidateInfo.resolvedSignature, argumentInfo, sourceFile, typeChecker)
: createTypeHelpItems(candidateInfo.symbol, argumentInfo, sourceFile, typeChecker));
}
interface CandidateInfo { readonly candidates: ReadonlyArray<Signature>; readonly resolvedSignature: Signature; }
function getCandidateInfo({ invocation, argumentCount }: ArgumentListInfo, checker: TypeChecker, sourceFile: SourceFile, startingToken: Node, onlyUseSyntacticOwners: boolean): CandidateInfo | undefined {
const enum CandidateOrTypeKind { Candidate, Type }
interface CandidateInfo {
readonly kind: CandidateOrTypeKind.Candidate;
readonly candidates: ReadonlyArray<Signature>;
readonly resolvedSignature: Signature;
}
interface TypeInfo {
readonly kind: CandidateOrTypeKind.Type;
readonly symbol: Symbol;
}
function getCandidateOrTypeInfo({ invocation, argumentCount }: ArgumentListInfo, checker: TypeChecker, sourceFile: SourceFile, startingToken: Node, onlyUseSyntacticOwners: boolean): CandidateInfo | TypeInfo | undefined {
switch (invocation.kind) {
case InvocationKind.Call: {
if (onlyUseSyntacticOwners && !isSyntacticOwner(startingToken, invocation.node, sourceFile)) {
@@ -65,17 +78,21 @@ namespace ts.SignatureHelp {
}
const candidates: Signature[] = [];
const resolvedSignature = checker.getResolvedSignatureForSignatureHelp(invocation.node, candidates, argumentCount)!; // TODO: GH#18217
return candidates.length === 0 ? undefined : { candidates, resolvedSignature };
return candidates.length === 0 ? undefined : { kind: CandidateOrTypeKind.Candidate, candidates, resolvedSignature };
}
case InvocationKind.TypeArgs: {
if (onlyUseSyntacticOwners && !lessThanFollowsCalledExpression(startingToken, sourceFile, invocation.called)) {
const { called } = invocation;
if (onlyUseSyntacticOwners && !containsPrecedingToken(startingToken, sourceFile, isIdentifier(called) ? called.parent : called)) {
return undefined;
}
const candidates = getPossibleGenericSignatures(invocation.called, argumentCount, checker);
return candidates.length === 0 ? undefined : { candidates, resolvedSignature: first(candidates) };
const candidates = getPossibleGenericSignatures(called, argumentCount, checker);
if (candidates.length !== 0) return { kind: CandidateOrTypeKind.Candidate, candidates, resolvedSignature: first(candidates) };
const symbol = checker.getSymbolAtLocation(called);
return symbol && { kind: CandidateOrTypeKind.Type, symbol };
}
case InvocationKind.Contextual:
return { candidates: [invocation.signature], resolvedSignature: invocation.signature };
return { kind: CandidateOrTypeKind.Candidate, candidates: [invocation.signature], resolvedSignature: invocation.signature };
default:
return Debug.assertNever(invocation);
}
@@ -92,7 +109,7 @@ namespace ts.SignatureHelp {
return !!containingList && contains(invocationChildren, containingList);
}
case SyntaxKind.LessThanToken:
return lessThanFollowsCalledExpression(startingToken, sourceFile, node.expression);
return containsPrecedingToken(startingToken, sourceFile, node.expression);
default:
return false;
}
@@ -114,12 +131,12 @@ namespace ts.SignatureHelp {
}));
}
function lessThanFollowsCalledExpression(startingToken: Node, sourceFile: SourceFile, calledExpression: Expression) {
function containsPrecedingToken(startingToken: Node, sourceFile: SourceFile, container: Node) {
const precedingToken = Debug.assertDefined(
findPrecedingToken(startingToken.getFullStart(), sourceFile, startingToken.parent, /*excludeJsdoc*/ true)
);
return rangeContainsRange(calledExpression, precedingToken);
return rangeContainsRange(container, precedingToken);
}
export interface ArgumentInfoForCompletions {
@@ -457,6 +474,10 @@ namespace ts.SignatureHelp {
return invocation.kind === InvocationKind.Call ? getInvokedExpression(invocation.node) : invocation.called;
}
function getEnclosingDeclarationFromInvocation(invocation: Invocation): Node {
return invocation.kind === InvocationKind.Call ? invocation.node : invocation.kind === InvocationKind.TypeArgs ? invocation.called : invocation.node;
}
const signatureHelpNodeBuilderFlags = NodeBuilderFlags.OmitParameterModifiers | NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope;
function createSignatureHelpItems(
candidates: ReadonlyArray<Signature>,
@@ -465,7 +486,7 @@ namespace ts.SignatureHelp {
sourceFile: SourceFile,
typeChecker: TypeChecker,
): SignatureHelpItems {
const enclosingDeclaration = invocation.kind === InvocationKind.Call ? invocation.node : invocation.kind === InvocationKind.TypeArgs ? invocation.called : invocation.node;
const enclosingDeclaration = getEnclosingDeclarationFromInvocation(invocation);
const callTargetSymbol = invocation.kind === InvocationKind.Contextual ? invocation.symbol : typeChecker.getSymbolAtLocation(getExpressionFromInvocation(invocation));
const callTargetDisplayParts = callTargetSymbol ? symbolToDisplayParts(typeChecker, callTargetSymbol, /*enclosingDeclaration*/ undefined, /*meaning*/ undefined) : emptyArray;
const items = candidates.map(candidateSignature => getSignatureHelpItem(candidateSignature, callTargetDisplayParts, isTypeParameterList, typeChecker, enclosingDeclaration, sourceFile));
@@ -480,11 +501,36 @@ namespace ts.SignatureHelp {
return { items, applicableSpan, selectedItemIndex, argumentIndex, argumentCount };
}
function createTypeHelpItems(
symbol: Symbol,
{ argumentCount, argumentsSpan: applicableSpan, invocation, argumentIndex }: ArgumentListInfo,
sourceFile: SourceFile,
checker: TypeChecker
): SignatureHelpItems | undefined {
const typeParameters = checker.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol);
if (!typeParameters) return undefined;
const items = [getTypeHelpItem(symbol, typeParameters, checker, getEnclosingDeclarationFromInvocation(invocation), sourceFile)];
return { items, applicableSpan, selectedItemIndex: 0, argumentIndex, argumentCount };
}
function getTypeHelpItem(symbol: Symbol, typeParameters: ReadonlyArray<TypeParameter>, checker: TypeChecker, enclosingDeclaration: Node, sourceFile: SourceFile): SignatureHelpItem {
const typeSymbolDisplay = symbolToDisplayParts(checker, symbol);
const printer = createPrinter({ removeComments: true });
const parameters = typeParameters.map(t => createSignatureHelpParameterForTypeParameter(t, checker, enclosingDeclaration, sourceFile, printer));
const documentation = symbol.getDocumentationComment(checker);
const tags = symbol.getJsDocTags();
const prefixDisplayParts = [...typeSymbolDisplay, punctuationPart(SyntaxKind.LessThanToken)];
return { isVariadic: false, prefixDisplayParts, suffixDisplayParts: [punctuationPart(SyntaxKind.GreaterThanToken)], separatorDisplayParts, parameters, documentation, tags };
}
const separatorDisplayParts: SymbolDisplayPart[] = [punctuationPart(SyntaxKind.CommaToken), spacePart()];
function getSignatureHelpItem(candidateSignature: Signature, callTargetDisplayParts: ReadonlyArray<SymbolDisplayPart>, isTypeParameterList: boolean, checker: TypeChecker, enclosingDeclaration: Node, sourceFile: SourceFile): SignatureHelpItem {
const { isVariadic, parameters, prefix, suffix } = (isTypeParameterList ? itemInfoForTypeParameters : itemInfoForParameters)(candidateSignature, checker, enclosingDeclaration, sourceFile);
const prefixDisplayParts = [...callTargetDisplayParts, ...prefix];
const suffixDisplayParts = [...suffix, ...returnTypeToDisplayParts(candidateSignature, enclosingDeclaration, checker)];
const separatorDisplayParts = [punctuationPart(SyntaxKind.CommaToken), spacePart()];
const documentation = candidateSignature.getDocumentationComment(checker);
const tags = candidateSignature.getJsDocTags();
return { isVariadic, prefixDisplayParts, suffixDisplayParts, separatorDisplayParts, parameters, documentation, tags };
+2 -1
View File
@@ -611,7 +611,8 @@ namespace ts.SymbolDisplay {
displayParts.push(textPart(allSignatures.length === 2 ? "overload" : "overloads"));
displayParts.push(punctuationPart(SyntaxKind.CloseParenToken));
}
documentation = signature.getDocumentationComment(typeChecker);
const docComment = signature.getDocumentationComment(typeChecker);
documentation = docComment.length === 0 ? undefined : docComment;
tags = signature.getJsDocTags();
}
-8
View File
@@ -233,14 +233,6 @@ namespace ts {
installPackage?(options: InstallPackageOptions): Promise<ApplyCodeActionCommandResult>;
}
export interface UserPreferences {
readonly disableSuggestions?: boolean;
readonly quotePreference?: "double" | "single";
readonly includeCompletionsForModuleExports?: boolean;
readonly includeCompletionsWithInsertText?: boolean;
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
readonly allowTextChangesInNewFiles?: boolean;
}
/* @internal */
export const emptyOptions = {};
+1
View File
@@ -81,6 +81,7 @@
"unittests/transform.ts",
"unittests/transpile.ts",
"unittests/tsbuild.ts",
"unittests/tsbuildWatchMode.ts",
"unittests/tsconfigParsing.ts",
"unittests/tscWatchMode.ts",
"unittests/versionCache.ts",
+48 -90
View File
@@ -1,15 +1,5 @@
namespace ts {
let currentTime = 100;
let lastDiagnostics: Diagnostic[] = [];
const reportDiagnostic: DiagnosticReporter = diagnostic => lastDiagnostics.push(diagnostic);
const report = (message: DiagnosticMessage, ...args: string[]) => reportDiagnostic(createCompilerDiagnostic(message, ...args));
const buildHost: BuildHost = {
error: report,
verbose: report,
message: report,
errorDiagnostic: d => reportDiagnostic(d)
};
export namespace Sample1 {
tick();
const projFs = loadProjectFromDisk("tests/projects/sample1");
@@ -21,12 +11,12 @@ namespace ts {
describe("tsbuild - sanity check of clean build of 'sample1' project", () => {
it("can build the sample project 'sample1' without error", () => {
const fs = projFs.shadow();
const host = new fakes.CompilerHost(fs);
const builder = createSolutionBuilder(host, buildHost, ["/src/tests"], { dry: false, force: false, verbose: false });
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: false, verbose: false });
clearDiagnostics();
host.clearDiagnostics();
builder.buildAllProjects();
assertDiagnosticMessages(/*empty*/);
host.assertDiagnosticMessages(/*empty*/);
// Check for outputs. Not an exhaustive list
for (const output of allExpectedOutputs) {
@@ -37,12 +27,11 @@ namespace ts {
describe("tsbuild - dry builds", () => {
it("doesn't write any files in a dry build", () => {
clearDiagnostics();
const fs = projFs.shadow();
const host = new fakes.CompilerHost(fs);
const builder = createSolutionBuilder(host, buildHost, ["/src/tests"], { dry: true, force: false, verbose: false });
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: true, force: false, verbose: false });
builder.buildAllProjects();
assertDiagnosticMessages(Diagnostics.A_non_dry_build_would_build_project_0, Diagnostics.A_non_dry_build_would_build_project_0, Diagnostics.A_non_dry_build_would_build_project_0);
host.assertDiagnosticMessages(Diagnostics.A_non_dry_build_would_build_project_0, Diagnostics.A_non_dry_build_would_build_project_0, Diagnostics.A_non_dry_build_would_build_project_0);
// Check for outputs to not be written. Not an exhaustive list
for (const output of allExpectedOutputs) {
@@ -51,28 +40,26 @@ namespace ts {
});
it("indicates that it would skip builds during a dry build", () => {
clearDiagnostics();
const fs = projFs.shadow();
const host = new fakes.CompilerHost(fs);
const host = new fakes.SolutionBuilderHost(fs);
let builder = createSolutionBuilder(host, buildHost, ["/src/tests"], { dry: false, force: false, verbose: false });
let builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: false, verbose: false });
builder.buildAllProjects();
tick();
clearDiagnostics();
builder = createSolutionBuilder(host, buildHost, ["/src/tests"], { dry: true, force: false, verbose: false });
host.clearDiagnostics();
builder = createSolutionBuilder(host, ["/src/tests"], { dry: true, force: false, verbose: false });
builder.buildAllProjects();
assertDiagnosticMessages(Diagnostics.Project_0_is_up_to_date, Diagnostics.Project_0_is_up_to_date, Diagnostics.Project_0_is_up_to_date);
host.assertDiagnosticMessages(Diagnostics.Project_0_is_up_to_date, Diagnostics.Project_0_is_up_to_date, Diagnostics.Project_0_is_up_to_date);
});
});
describe("tsbuild - clean builds", () => {
it("removes all files it built", () => {
clearDiagnostics();
const fs = projFs.shadow();
const host = new fakes.CompilerHost(fs);
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, buildHost, ["/src/tests"], { dry: false, force: false, verbose: false });
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: false, verbose: false });
builder.buildAllProjects();
// Verify they exist
for (const output of allExpectedOutputs) {
@@ -91,9 +78,9 @@ namespace ts {
describe("tsbuild - force builds", () => {
it("always builds under --force", () => {
const fs = projFs.shadow();
const host = new fakes.CompilerHost(fs);
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, buildHost, ["/src/tests"], { dry: false, force: true, verbose: false });
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: true, verbose: false });
builder.buildAllProjects();
let currentTime = time();
checkOutputTimestamps(currentTime);
@@ -116,14 +103,14 @@ namespace ts {
describe("tsbuild - can detect when and what to rebuild", () => {
const fs = projFs.shadow();
const host = new fakes.CompilerHost(fs);
const builder = createSolutionBuilder(host, buildHost, ["/src/tests"], { dry: false, force: false, verbose: true });
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: false, verbose: true });
it("Builds the project", () => {
clearDiagnostics();
host.clearDiagnostics();
builder.resetBuildContext();
builder.buildAllProjects();
assertDiagnosticMessages(Diagnostics.Projects_in_this_build_Colon_0,
host.assertDiagnosticMessages(Diagnostics.Projects_in_this_build_Colon_0,
Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist,
Diagnostics.Building_project_0,
Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist,
@@ -135,10 +122,10 @@ namespace ts {
// All three projects are up to date
it("Detects that all projects are up to date", () => {
clearDiagnostics();
host.clearDiagnostics();
builder.resetBuildContext();
builder.buildAllProjects();
assertDiagnosticMessages(Diagnostics.Projects_in_this_build_Colon_0,
host.assertDiagnosticMessages(Diagnostics.Projects_in_this_build_Colon_0,
Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2,
Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2,
Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2);
@@ -147,12 +134,12 @@ namespace ts {
// Update a file in the leaf node (tests), only it should rebuild the last one
it("Only builds the leaf node project", () => {
clearDiagnostics();
host.clearDiagnostics();
fs.writeFileSync("/src/tests/index.ts", "const m = 10;");
builder.resetBuildContext();
builder.buildAllProjects();
assertDiagnosticMessages(Diagnostics.Projects_in_this_build_Colon_0,
host.assertDiagnosticMessages(Diagnostics.Projects_in_this_build_Colon_0,
Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2,
Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2,
Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2,
@@ -162,12 +149,12 @@ namespace ts {
// Update a file in the parent (without affecting types), should get fast downstream builds
it("Detects type-only changes in upstream projects", () => {
clearDiagnostics();
host.clearDiagnostics();
replaceText(fs, "/src/core/index.ts", "HELLO WORLD", "WELCOME PLANET");
builder.resetBuildContext();
builder.buildAllProjects();
assertDiagnosticMessages(Diagnostics.Projects_in_this_build_Colon_0,
host.assertDiagnosticMessages(Diagnostics.Projects_in_this_build_Colon_0,
Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2,
Diagnostics.Building_project_0,
Diagnostics.Project_0_is_up_to_date_with_d_ts_files_from_its_dependencies,
@@ -180,15 +167,13 @@ namespace ts {
describe("tsbuild - downstream-blocked compilations", () => {
it("won't build downstream projects if upstream projects have errors", () => {
const fs = projFs.shadow();
const host = new fakes.CompilerHost(fs);
const builder = createSolutionBuilder(host, buildHost, ["/src/tests"], { dry: false, force: false, verbose: true });
clearDiagnostics();
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: false, verbose: true });
// Induce an error in the middle project
replaceText(fs, "/src/logic/index.ts", "c.multiply(10, 15)", `c.muitply()`);
builder.buildAllProjects();
assertDiagnosticMessages(
host.assertDiagnosticMessages(
Diagnostics.Projects_in_this_build_Colon_0,
Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist,
Diagnostics.Building_project_0,
@@ -204,12 +189,11 @@ namespace ts {
describe("tsbuild - project invalidation", () => {
it("invalidates projects correctly", () => {
const fs = projFs.shadow();
const host = new fakes.CompilerHost(fs);
const builder = createSolutionBuilder(host, buildHost, ["/src/tests"], { dry: false, force: false, verbose: false });
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: false, verbose: false });
clearDiagnostics();
builder.buildAllProjects();
assertDiagnosticMessages(/*empty*/);
host.assertDiagnosticMessages(/*empty*/);
// Update a timestamp in the middle project
tick();
@@ -221,14 +205,14 @@ namespace ts {
// Rebuild this project
tick();
builder.invalidateProject("/src/logic");
builder.buildInvalidatedProjects();
builder.buildInvalidatedProject();
// The file should be updated
assert.equal(fs.statSync("/src/logic/index.js").mtimeMs, time(), "JS file should have been rebuilt");
assert.isBelow(fs.statSync("/src/tests/index.js").mtimeMs, time(), "Downstream JS file should *not* have been rebuilt");
// Build downstream projects should update 'tests', but not 'core'
tick();
builder.buildDependentInvalidatedProjects();
builder.buildInvalidatedProject();
assert.equal(fs.statSync("/src/tests/index.js").mtimeMs, time(), "Downstream JS file should have been rebuilt");
assert.isBelow(fs.statSync("/src/core/index.js").mtimeMs, time(), "Upstream JS file should not have been rebuilt");
});
@@ -240,11 +224,10 @@ namespace ts {
function verifyProjectWithResolveJsonModule(configFile: string, ...expectedDiagnosticMessages: DiagnosticMessage[]) {
const fs = projFs.shadow();
const host = new fakes.CompilerHost(fs);
const builder = createSolutionBuilder(host, buildHost, [configFile], { dry: false, force: false, verbose: false });
clearDiagnostics();
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, [configFile], { dry: false, force: false, verbose: false });
builder.buildAllProjects();
assertDiagnosticMessages(...expectedDiagnosticMessages);
host.assertDiagnosticMessages(...expectedDiagnosticMessages);
if (!expectedDiagnosticMessages.length) {
// Check for outputs. Not an exhaustive list
for (const output of allExpectedOutputs) {
@@ -274,11 +257,11 @@ namespace ts {
let fs: vfs.FileSystem | undefined;
before(() => {
fs = outFileFs.shadow();
const host = new fakes.CompilerHost(fs);
const builder = createSolutionBuilder(host, buildHost, ["/src/third"], { dry: false, force: false, verbose: false });
clearDiagnostics();
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/third"], { dry: false, force: false, verbose: false });
host.clearDiagnostics();
builder.buildAllProjects();
assertDiagnosticMessages(/*none*/);
host.assertDiagnosticMessages(/*none*/);
});
after(() => {
fs = undefined;
@@ -293,25 +276,24 @@ namespace ts {
describe("tsbuild - downstream prepend projects always get rebuilt", () => {
it("", () => {
const fs = outFileFs.shadow();
const host = new fakes.CompilerHost(fs);
const builder = createSolutionBuilder(host, buildHost, ["/src/third"], { dry: false, force: false, verbose: false });
clearDiagnostics();
const host = new fakes.SolutionBuilderHost(fs);
const builder = createSolutionBuilder(host, ["/src/third"], { dry: false, force: false, verbose: false });
builder.buildAllProjects();
assertDiagnosticMessages(/*none*/);
host.assertDiagnosticMessages(/*none*/);
assert.equal(fs.statSync("src/third/thirdjs/output/third-output.js").mtimeMs, time(), "First build timestamp is correct");
tick();
replaceText(fs, "src/first/first_PART1.ts", "Hello", "Hola");
tick();
builder.resetBuildContext();
builder.buildAllProjects();
assertDiagnosticMessages(/*none*/);
host.assertDiagnosticMessages(/*none*/);
assert.equal(fs.statSync("src/third/thirdjs/output/third-output.js").mtimeMs, time(), "Second build timestamp is correct");
});
});
}
describe("tsbuild - graph-ordering", () => {
let host: fakes.CompilerHost | undefined;
let host: fakes.SolutionBuilderHost | undefined;
const deps: [string, string][] = [
["A", "B"],
["B", "C"],
@@ -324,7 +306,7 @@ namespace ts {
before(() => {
const fs = new vfs.FileSystem(false);
host = new fakes.CompilerHost(fs);
host = new fakes.SolutionBuilderHost(fs);
writeProjects(fs, ["A", "B", "C", "D", "E", "F", "G"], deps);
});
@@ -349,7 +331,7 @@ namespace ts {
});
function checkGraphOrdering(rootNames: string[], expectedBuildSet: string[]) {
const builder = createSolutionBuilder(host!, buildHost, rootNames, { dry: true, force: false, verbose: false });
const builder = createSolutionBuilder(host!, rootNames, { dry: true, force: false, verbose: false });
const projFileNames = rootNames.map(getProjectFileName);
const graph = builder.getBuildGraph(projFileNames);
@@ -404,30 +386,6 @@ namespace ts {
fs.writeFileSync(path, newContent, "utf-8");
}
function assertDiagnosticMessages(...expected: DiagnosticMessage[]) {
const actual = lastDiagnostics.slice();
if (actual.length !== expected.length) {
assert.fail<any>(actual, expected, `Diagnostic arrays did not match - got\r\n${actual.map(a => " " + a.messageText).join("\r\n")}\r\nexpected\r\n${expected.map(e => " " + e.message).join("\r\n")}`);
}
for (let i = 0; i < actual.length; i++) {
if (actual[i].code !== expected[i].code) {
assert.fail(actual[i].messageText, expected[i].message, `Mismatched error code - expected diagnostic ${i} "${actual[i].messageText}" to match ${expected[i].message}`);
}
}
}
function clearDiagnostics() {
lastDiagnostics = [];
}
export function printDiagnostics(header = "== Diagnostics ==") {
const out = createDiagnosticReporter(sys);
sys.write(header + "\r\n");
for (const d of lastDiagnostics) {
out(d);
}
}
function tick() {
currentTime += 60_000;
}
@@ -0,0 +1,133 @@
namespace ts.tscWatch {
export import libFile = TestFSWithWatch.libFile;
function createSolutionBuilder(system: WatchedSystem, rootNames: ReadonlyArray<string>, defaultOptions?: BuildOptions) {
const host = createSolutionBuilderWithWatchHost(system);
return ts.createSolutionBuilder(host, rootNames, defaultOptions || { dry: false, force: false, verbose: false, watch: true });
}
function createSolutionBuilderWithWatch(host: WatchedSystem, rootNames: ReadonlyArray<string>, defaultOptions?: BuildOptions) {
const solutionBuilder = createSolutionBuilder(host, rootNames, defaultOptions);
solutionBuilder.buildAllProjects();
solutionBuilder.startWatching();
return solutionBuilder;
}
describe("tsbuild-watch program updates", () => {
const projectsLocation = "/user/username/projects";
const project = "sample1";
const enum SubProject {
core = "core",
logic = "logic",
tests = "tests",
ui = "ui"
}
type ReadonlyFile = Readonly<File>;
/** [tsconfig, index] | [tsconfig, index, anotherModule, someDecl] */
type SubProjectFiles = [ReadonlyFile, ReadonlyFile] | [ReadonlyFile, ReadonlyFile, ReadonlyFile, ReadonlyFile];
const root = Harness.IO.getWorkspaceRoot();
function projectFilePath(subProject: SubProject, baseFileName: string) {
return `${projectsLocation}/${project}/${subProject}/${baseFileName.toLowerCase()}`;
}
function projectFile(subProject: SubProject, baseFileName: string): File {
return {
path: projectFilePath(subProject, baseFileName),
content: Harness.IO.readFile(`${root}/tests/projects/${project}/${subProject}/${baseFileName}`)!
};
}
function subProjectFiles(subProject: SubProject, anotherModuleAndSomeDecl?: true): SubProjectFiles {
const tsconfig = projectFile(subProject, "tsconfig.json");
const index = projectFile(subProject, "index.ts");
if (!anotherModuleAndSomeDecl) {
return [tsconfig, index];
}
const anotherModule = projectFile(SubProject.core, "anotherModule.ts");
const someDecl = projectFile(SubProject.core, "some_decl.ts");
return [tsconfig, index, anotherModule, someDecl];
}
function getOutputFileNames(subProject: SubProject, baseFileNameWithoutExtension: string) {
const file = projectFilePath(subProject, baseFileNameWithoutExtension);
return [`${file}.js`, `${file}.d.ts`];
}
type OutputFileStamp = [string, Date | undefined];
function getOutputStamps(host: WatchedSystem, subProject: SubProject, baseFileNameWithoutExtension: string): OutputFileStamp[] {
return getOutputFileNames(subProject, baseFileNameWithoutExtension).map(f => [f, host.getModifiedTime(f)] as OutputFileStamp);
}
function getOutputFileStamps(host: WatchedSystem): OutputFileStamp[] {
return [
...getOutputStamps(host, SubProject.core, "anotherModule"),
...getOutputStamps(host, SubProject.core, "index"),
...getOutputStamps(host, SubProject.logic, "index"),
...getOutputStamps(host, SubProject.tests, "index"),
];
}
function verifyChangedFiles(actualStamps: OutputFileStamp[], oldTimeStamps: OutputFileStamp[], changedFiles: string[]) {
for (let i = 0; i < oldTimeStamps.length; i++) {
const actual = actualStamps[i];
const old = oldTimeStamps[i];
if (contains(changedFiles, actual[0])) {
assert.isTrue((actual[1] || 0) > (old[1] || 0), `${actual[0]} expected to written`);
}
else {
assert.equal(actual[1], old[1], `${actual[0]} expected to not change`);
}
}
}
const core = subProjectFiles(SubProject.core, /*anotherModuleAndSomeDecl*/ true);
const logic = subProjectFiles(SubProject.logic);
const tests = subProjectFiles(SubProject.tests);
const ui = subProjectFiles(SubProject.ui);
const allFiles: ReadonlyArray<File> = [libFile, ...core, ...logic, ...tests, ...ui];
const testProjectExpectedWatchedFiles = [core[0], core[1], core[2], ...logic, ...tests].map(f => f.path);
function createSolutionInWatchMode() {
const host = createWatchedSystem(allFiles, { currentDirectory: projectsLocation });
createSolutionBuilderWithWatch(host, [`${project}/${SubProject.tests}`]);
checkWatchedFiles(host, testProjectExpectedWatchedFiles);
checkWatchedDirectories(host, emptyArray, /*recursive*/ false);
checkWatchedDirectories(host, emptyArray, /*recursive*/ true); // TODO: #26524
checkOutputErrorsInitial(host, emptyArray);
const outputFileStamps = getOutputFileStamps(host);
for (const stamp of outputFileStamps) {
assert.isDefined(stamp[1], `${stamp[0]} expected to be present`);
}
return { host, outputFileStamps };
}
it("creates solution in watch mode", () => {
createSolutionInWatchMode();
});
it("change builds changes and reports found errors message", () => {
const { host, outputFileStamps } = createSolutionInWatchMode();
host.writeFile(core[1].path, `${core[1].content}
export class someClass { }`);
host.checkTimeoutQueueLengthAndRun(1); // Builds core
const changedCore = getOutputFileStamps(host);
verifyChangedFiles(changedCore, outputFileStamps, [
...getOutputFileNames(SubProject.core, "anotherModule"), // This should not be written really
...getOutputFileNames(SubProject.core, "index")
]);
host.checkTimeoutQueueLengthAndRun(1); // Builds tests
const changedTests = getOutputFileStamps(host);
verifyChangedFiles(changedTests, changedCore, [
...getOutputFileNames(SubProject.tests, "index") // Again these need not be written
]);
host.checkTimeoutQueueLengthAndRun(1); // Builds logic
const changedLogic = getOutputFileStamps(host);
verifyChangedFiles(changedLogic, changedTests, [
...getOutputFileNames(SubProject.logic, "index") // Again these need not be written
]);
host.checkTimeoutQueueLength(0);
checkOutputErrorsIncremental(host, emptyArray);
});
// TODO: write tests reporting errors but that will have more involved work since file
});
}
+15 -16
View File
@@ -1,17 +1,16 @@
namespace ts.tscWatch {
import WatchedSystem = TestFSWithWatch.TestServerHost;
type File = TestFSWithWatch.File;
type SymLink = TestFSWithWatch.SymLink;
import createWatchedSystem = TestFSWithWatch.createWatchedSystem;
import checkArray = TestFSWithWatch.checkArray;
import libFile = TestFSWithWatch.libFile;
import checkWatchedFiles = TestFSWithWatch.checkWatchedFiles;
import checkWatchedFilesDetailed = TestFSWithWatch.checkWatchedFilesDetailed;
import checkWatchedDirectories = TestFSWithWatch.checkWatchedDirectories;
import checkWatchedDirectoriesDetailed = TestFSWithWatch.checkWatchedDirectoriesDetailed;
import checkOutputContains = TestFSWithWatch.checkOutputContains;
import checkOutputDoesNotContain = TestFSWithWatch.checkOutputDoesNotContain;
import Tsc_WatchDirectory = TestFSWithWatch.Tsc_WatchDirectory;
export import WatchedSystem = TestFSWithWatch.TestServerHost;
export type File = TestFSWithWatch.File;
export type SymLink = TestFSWithWatch.SymLink;
export import createWatchedSystem = TestFSWithWatch.createWatchedSystem;
export import checkArray = TestFSWithWatch.checkArray;
export import checkWatchedFiles = TestFSWithWatch.checkWatchedFiles;
export import checkWatchedFilesDetailed = TestFSWithWatch.checkWatchedFilesDetailed;
export import checkWatchedDirectories = TestFSWithWatch.checkWatchedDirectories;
export import checkWatchedDirectoriesDetailed = TestFSWithWatch.checkWatchedDirectoriesDetailed;
export import checkOutputContains = TestFSWithWatch.checkOutputContains;
export import checkOutputDoesNotContain = TestFSWithWatch.checkOutputDoesNotContain;
export import Tsc_WatchDirectory = TestFSWithWatch.Tsc_WatchDirectory;
export function checkProgramActualFiles(program: Program, expectedFiles: string[]) {
checkArray(`Program actual files`, program.getSourceFiles().map(file => file.fileName), expectedFiles);
@@ -111,7 +110,7 @@ namespace ts.tscWatch {
function assertWatchDiagnostic(diagnostic: Diagnostic) {
const expected = getWatchDiagnosticWithoutDate(diagnostic);
if (!disableConsoleClears && !contains(nonClearingMessageCodes, diagnostic.code)) {
if (!disableConsoleClears && contains(screenStartingMessageCodes, diagnostic.code)) {
assert.equal(host.screenClears[screenClears], index, `Expected screen clear at this diagnostic: ${expected}`);
screenClears++;
}
@@ -137,7 +136,7 @@ namespace ts.tscWatch {
: createCompilerDiagnostic(Diagnostics.Found_0_errors_Watching_for_file_changes, errors.length);
}
function checkOutputErrorsInitial(host: WatchedSystem, errors: ReadonlyArray<Diagnostic>, disableConsoleClears?: boolean, logsBeforeErrors?: string[]) {
export function checkOutputErrorsInitial(host: WatchedSystem, errors: ReadonlyArray<Diagnostic>, disableConsoleClears?: boolean, logsBeforeErrors?: string[]) {
checkOutputErrors(
host,
/*logsBeforeWatchDiagnostic*/ undefined,
@@ -148,7 +147,7 @@ namespace ts.tscWatch {
createErrorsFoundCompilerDiagnostic(errors));
}
function checkOutputErrorsIncremental(host: WatchedSystem, errors: ReadonlyArray<Diagnostic>, disableConsoleClears?: boolean, logsBeforeWatchDiagnostic?: string[], logsBeforeErrors?: string[]) {
export function checkOutputErrorsIncremental(host: WatchedSystem, errors: ReadonlyArray<Diagnostic>, disableConsoleClears?: boolean, logsBeforeWatchDiagnostic?: string[], logsBeforeErrors?: string[]) {
checkOutputErrors(
host,
logsBeforeWatchDiagnostic,
+459 -229
View File
@@ -642,37 +642,54 @@ namespace ts.projectSystem {
checkWatchedDirectories(host, [combinePaths(getDirectoryPath(appFile.path), nodeModulesAtTypes)], /*recursive*/ true);
});
it("can handle tsconfig file name with difference casing", () => {
const f1 = {
path: "/a/b/app.ts",
content: "let x = 1"
};
const config = {
path: "/a/b/tsconfig.json",
content: JSON.stringify({
include: []
})
};
describe("can handle tsconfig file name with difference casing", () => {
function verifyConfigFileCasing(lazyConfiguredProjectsFromExternalProject: boolean) {
const f1 = {
path: "/a/b/app.ts",
content: "let x = 1"
};
const config = {
path: "/a/b/tsconfig.json",
content: JSON.stringify({
include: []
})
};
const host = createServerHost([f1, config], { useCaseSensitiveFileNames: false });
const service = createProjectService(host);
const upperCaseConfigFilePath = combinePaths(getDirectoryPath(config.path).toUpperCase(), getBaseFileName(config.path));
service.openExternalProject(<protocol.ExternalProject>{
projectFileName: "/a/b/project.csproj",
rootFiles: toExternalFiles([f1.path, upperCaseConfigFilePath]),
options: {}
const host = createServerHost([f1, config], { useCaseSensitiveFileNames: false });
const service = createProjectService(host);
service.setHostConfiguration({ preferences: { lazyConfiguredProjectsFromExternalProject } });
const upperCaseConfigFilePath = combinePaths(getDirectoryPath(config.path).toUpperCase(), getBaseFileName(config.path));
service.openExternalProject(<protocol.ExternalProject>{
projectFileName: "/a/b/project.csproj",
rootFiles: toExternalFiles([f1.path, upperCaseConfigFilePath]),
options: {}
});
service.checkNumberOfProjects({ configuredProjects: 1 });
const project = service.configuredProjects.get(config.path)!;
if (lazyConfiguredProjectsFromExternalProject) {
assert.equal(project.pendingReload, ConfigFileProgramReloadLevel.Full); // External project referenced configured project pending to be reloaded
checkProjectActualFiles(project, emptyArray);
}
else {
assert.equal(project.pendingReload, ConfigFileProgramReloadLevel.None); // External project referenced configured project loaded
checkProjectActualFiles(project, [upperCaseConfigFilePath]);
}
service.openClientFile(f1.path);
service.checkNumberOfProjects({ configuredProjects: 1, inferredProjects: 1 });
assert.equal(project.pendingReload, ConfigFileProgramReloadLevel.None); // External project referenced configured project is updated
checkProjectActualFiles(project, [upperCaseConfigFilePath]);
checkProjectActualFiles(service.inferredProjects[0], [f1.path]);
}
it("when lazyConfiguredProjectsFromExternalProject not set", () => {
verifyConfigFileCasing(/*lazyConfiguredProjectsFromExternalProject*/ false);
});
service.checkNumberOfProjects({ configuredProjects: 1 });
const project = service.configuredProjects.get(config.path)!;
assert.equal(project.pendingReload, ConfigFileProgramReloadLevel.Full); // External project referenced configured project pending to be reloaded
checkProjectActualFiles(project, emptyArray);
service.openClientFile(f1.path);
service.checkNumberOfProjects({ configuredProjects: 1, inferredProjects: 1 });
assert.equal(project.pendingReload, ConfigFileProgramReloadLevel.None); // External project referenced configured project is updated
checkProjectActualFiles(project, [upperCaseConfigFilePath]);
checkProjectActualFiles(service.inferredProjects[0], [f1.path]);
it("when lazyConfiguredProjectsFromExternalProject is set", () => {
verifyConfigFileCasing(/*lazyConfiguredProjectsFromExternalProject*/ true);
});
});
it("create configured project without file list", () => {
@@ -2950,45 +2967,56 @@ namespace ts.projectSystem {
assert.equal(navbar[0].spans[0].length, f1.content.length);
});
it("deleting config file opened from the external project works", () => {
const site = {
path: "/user/someuser/project/js/site.js",
content: ""
};
const configFile = {
path: "/user/someuser/project/tsconfig.json",
content: "{}"
};
const projectFileName = "/user/someuser/project/WebApplication6.csproj";
const host = createServerHost([libFile, site, configFile]);
const projectService = createProjectService(host);
describe("deleting config file opened from the external project works", () => {
function verifyDeletingConfigFile(lazyConfiguredProjectsFromExternalProject: boolean) {
const site = {
path: "/user/someuser/project/js/site.js",
content: ""
};
const configFile = {
path: "/user/someuser/project/tsconfig.json",
content: "{}"
};
const projectFileName = "/user/someuser/project/WebApplication6.csproj";
const host = createServerHost([libFile, site, configFile]);
const projectService = createProjectService(host);
projectService.setHostConfiguration({ preferences: { lazyConfiguredProjectsFromExternalProject } });
const externalProject: protocol.ExternalProject = {
projectFileName,
rootFiles: [toExternalFile(site.path), toExternalFile(configFile.path)],
options: { allowJs: false },
typeAcquisition: { include: [] }
};
const externalProject: protocol.ExternalProject = {
projectFileName,
rootFiles: [toExternalFile(site.path), toExternalFile(configFile.path)],
options: { allowJs: false },
typeAcquisition: { include: [] }
};
projectService.openExternalProjects([externalProject]);
projectService.openExternalProjects([externalProject]);
let knownProjects = projectService.synchronizeProjectList([]);
checkNumberOfProjects(projectService, { configuredProjects: 1, externalProjects: 0, inferredProjects: 0 });
let knownProjects = projectService.synchronizeProjectList([]);
checkNumberOfProjects(projectService, { configuredProjects: 1, externalProjects: 0, inferredProjects: 0 });
const configProject = configuredProjectAt(projectService, 0);
checkProjectActualFiles(configProject, []); // Since no files opened from this project, its not loaded
const configProject = configuredProjectAt(projectService, 0);
checkProjectActualFiles(configProject, lazyConfiguredProjectsFromExternalProject ?
emptyArray : // Since no files opened from this project, its not loaded
[libFile.path, configFile.path]);
host.reloadFS([libFile, site]);
host.checkTimeoutQueueLengthAndRun(1);
host.reloadFS([libFile, site]);
host.checkTimeoutQueueLengthAndRun(1);
knownProjects = projectService.synchronizeProjectList(map(knownProjects, proj => proj.info!)); // TODO: GH#18217 GH#20039
checkNumberOfProjects(projectService, { configuredProjects: 0, externalProjects: 0, inferredProjects: 0 });
knownProjects = projectService.synchronizeProjectList(map(knownProjects, proj => proj.info!)); // TODO: GH#18217 GH#20039
checkNumberOfProjects(projectService, { configuredProjects: 0, externalProjects: 0, inferredProjects: 0 });
externalProject.rootFiles.length = 1;
projectService.openExternalProjects([externalProject]);
externalProject.rootFiles.length = 1;
projectService.openExternalProjects([externalProject]);
checkNumberOfProjects(projectService, { configuredProjects: 0, externalProjects: 1, inferredProjects: 0 });
checkProjectActualFiles(projectService.externalProjects[0], [site.path, libFile.path]);
checkNumberOfProjects(projectService, { configuredProjects: 0, externalProjects: 1, inferredProjects: 0 });
checkProjectActualFiles(projectService.externalProjects[0], [site.path, libFile.path]);
}
it("when lazyConfiguredProjectsFromExternalProject not set", () => {
verifyDeletingConfigFile(/*lazyConfiguredProjectsFromExternalProject*/ false);
});
it("when lazyConfiguredProjectsFromExternalProject is set", () => {
verifyDeletingConfigFile(/*lazyConfiguredProjectsFromExternalProject*/ true);
});
});
it("Getting errors from closed script info does not throw exception (because of getting project from orphan script info)", () => {
@@ -3298,49 +3326,64 @@ namespace ts.projectSystem {
});
it("includes deferred files in the project context", () => {
const file1 = {
path: "/a.deferred",
content: "const a = 1;"
};
// Deferred extensions should not affect JS files.
const file2 = {
path: "/b.js",
content: "const b = 1;"
};
const tsconfig = {
path: "/tsconfig.json",
content: ""
};
describe("includes deferred files in the project context", () => {
function verifyDeferredContext(lazyConfiguredProjectsFromExternalProject: boolean) {
const file1 = {
path: "/a.deferred",
content: "const a = 1;"
};
// Deferred extensions should not affect JS files.
const file2 = {
path: "/b.js",
content: "const b = 1;"
};
const tsconfig = {
path: "/tsconfig.json",
content: ""
};
const host = createServerHost([file1, file2, tsconfig]);
const session = createSession(host);
const projectService = session.getProjectService();
const host = createServerHost([file1, file2, tsconfig]);
const session = createSession(host);
const projectService = session.getProjectService();
session.executeCommandSeq<protocol.ConfigureRequest>({
command: protocol.CommandTypes.Configure,
arguments: { preferences: { lazyConfiguredProjectsFromExternalProject } }
});
// Configure the deferred extension.
const extraFileExtensions = [{ extension: ".deferred", scriptKind: ScriptKind.Deferred, isMixedContent: true }];
const configureHostRequest = makeSessionRequest<protocol.ConfigureRequestArguments>(CommandNames.Configure, { extraFileExtensions });
session.executeCommand(configureHostRequest);
// Configure the deferred extension.
const extraFileExtensions = [{ extension: ".deferred", scriptKind: ScriptKind.Deferred, isMixedContent: true }];
const configureHostRequest = makeSessionRequest<protocol.ConfigureRequestArguments>(CommandNames.Configure, { extraFileExtensions });
session.executeCommand(configureHostRequest);
// Open external project
const projectName = "/proj1";
projectService.openExternalProject({
projectFileName: projectName,
rootFiles: toExternalFiles([file1.path, file2.path, tsconfig.path]),
options: {}
// Open external project
const projectName = "/proj1";
projectService.openExternalProject({
projectFileName: projectName,
rootFiles: toExternalFiles([file1.path, file2.path, tsconfig.path]),
options: {}
});
// Assert
checkNumberOfProjects(projectService, { configuredProjects: 1 });
const configuredProject = configuredProjectAt(projectService, 0);
if (lazyConfiguredProjectsFromExternalProject) {
// configured project is just created and not yet loaded
checkProjectActualFiles(configuredProject, emptyArray);
projectService.ensureInferredProjectsUpToDate_TestOnly();
}
checkProjectActualFiles(configuredProject, [file1.path, tsconfig.path]);
// Allow allowNonTsExtensions will be set to true for deferred extensions.
assert.isTrue(configuredProject.getCompilerOptions().allowNonTsExtensions);
}
it("when lazyConfiguredProjectsFromExternalProject not set", () => {
verifyDeferredContext(/*lazyConfiguredProjectsFromExternalProject*/ false);
});
it("when lazyConfiguredProjectsFromExternalProject is set", () => {
verifyDeferredContext(/*lazyConfiguredProjectsFromExternalProject*/ true);
});
// Assert
checkNumberOfProjects(projectService, { configuredProjects: 1 });
const configuredProject = configuredProjectAt(projectService, 0);
// configured project is just created and not yet loaded
checkProjectActualFiles(configuredProject, emptyArray);
projectService.ensureInferredProjectsUpToDate_TestOnly();
checkProjectActualFiles(configuredProject, [file1.path, tsconfig.path]);
// Allow allowNonTsExtensions will be set to true for deferred extensions.
assert.isTrue(configuredProject.getCompilerOptions().allowNonTsExtensions);
});
it("Orphan source files are handled correctly on watch trigger", () => {
@@ -3943,143 +3986,167 @@ namespace ts.projectSystem {
});
describe("tsserverProjectSystem external projects", () => {
it("correctly handling add/remove tsconfig - 1", () => {
const f1 = {
path: "/a/b/app.ts",
content: "let x = 1;"
};
const f2 = {
path: "/a/b/lib.ts",
content: ""
};
const tsconfig = {
path: "/a/b/tsconfig.json",
content: ""
};
const host = createServerHost([f1, f2]);
const projectService = createProjectService(host);
describe("correctly handling add/remove tsconfig - 1", () => {
function verifyAddRemoveConfig(lazyConfiguredProjectsFromExternalProject: boolean) {
const f1 = {
path: "/a/b/app.ts",
content: "let x = 1;"
};
const f2 = {
path: "/a/b/lib.ts",
content: ""
};
const tsconfig = {
path: "/a/b/tsconfig.json",
content: ""
};
const host = createServerHost([f1, f2]);
const projectService = createProjectService(host);
projectService.setHostConfiguration({ preferences: { lazyConfiguredProjectsFromExternalProject } });
// open external project
const projectName = "/a/b/proj1";
projectService.openExternalProject({
projectFileName: projectName,
rootFiles: toExternalFiles([f1.path, f2.path]),
options: {}
// open external project
const projectName = "/a/b/proj1";
projectService.openExternalProject({
projectFileName: projectName,
rootFiles: toExternalFiles([f1.path, f2.path]),
options: {}
});
projectService.openClientFile(f1.path);
projectService.checkNumberOfProjects({ externalProjects: 1 });
checkProjectActualFiles(projectService.externalProjects[0], [f1.path, f2.path]);
// rename lib.ts to tsconfig.json
host.reloadFS([f1, tsconfig]);
projectService.openExternalProject({
projectFileName: projectName,
rootFiles: toExternalFiles([f1.path, tsconfig.path]),
options: {}
});
projectService.checkNumberOfProjects({ configuredProjects: 1 });
if (lazyConfiguredProjectsFromExternalProject) {
checkProjectActualFiles(configuredProjectAt(projectService, 0), emptyArray); // Configured project created but not loaded till actually needed
projectService.ensureInferredProjectsUpToDate_TestOnly();
}
checkProjectActualFiles(configuredProjectAt(projectService, 0), [f1.path, tsconfig.path]);
// rename tsconfig.json back to lib.ts
host.reloadFS([f1, f2]);
projectService.openExternalProject({
projectFileName: projectName,
rootFiles: toExternalFiles([f1.path, f2.path]),
options: {}
});
projectService.checkNumberOfProjects({ externalProjects: 1 });
checkProjectActualFiles(projectService.externalProjects[0], [f1.path, f2.path]);
}
it("when lazyConfiguredProjectsFromExternalProject not set", () => {
verifyAddRemoveConfig(/*lazyConfiguredProjectsFromExternalProject*/ false);
});
projectService.openClientFile(f1.path);
projectService.checkNumberOfProjects({ externalProjects: 1 });
checkProjectActualFiles(projectService.externalProjects[0], [f1.path, f2.path]);
// rename lib.ts to tsconfig.json
host.reloadFS([f1, tsconfig]);
projectService.openExternalProject({
projectFileName: projectName,
rootFiles: toExternalFiles([f1.path, tsconfig.path]),
options: {}
it("when lazyConfiguredProjectsFromExternalProject is set", () => {
verifyAddRemoveConfig(/*lazyConfiguredProjectsFromExternalProject*/ true);
});
projectService.checkNumberOfProjects({ configuredProjects: 1 });
checkProjectActualFiles(configuredProjectAt(projectService, 0), emptyArray); // Configured project created but not loaded till actually needed
projectService.ensureInferredProjectsUpToDate_TestOnly();
checkProjectActualFiles(configuredProjectAt(projectService, 0), [f1.path, tsconfig.path]);
// rename tsconfig.json back to lib.ts
host.reloadFS([f1, f2]);
projectService.openExternalProject({
projectFileName: projectName,
rootFiles: toExternalFiles([f1.path, f2.path]),
options: {}
});
projectService.checkNumberOfProjects({ externalProjects: 1 });
checkProjectActualFiles(projectService.externalProjects[0], [f1.path, f2.path]);
});
describe("correctly handling add/remove tsconfig - 2", () => {
function verifyAddRemoveConfig(lazyConfiguredProjectsFromExternalProject: boolean) {
const f1 = {
path: "/a/b/app.ts",
content: "let x = 1;"
};
const cLib = {
path: "/a/b/c/lib.ts",
content: ""
};
const cTsconfig = {
path: "/a/b/c/tsconfig.json",
content: "{}"
};
const dLib = {
path: "/a/b/d/lib.ts",
content: ""
};
const dTsconfig = {
path: "/a/b/d/tsconfig.json",
content: "{}"
};
const host = createServerHost([f1, cLib, cTsconfig, dLib, dTsconfig]);
const projectService = createProjectService(host);
projectService.setHostConfiguration({ preferences: { lazyConfiguredProjectsFromExternalProject } });
it("correctly handling add/remove tsconfig - 2", () => {
const f1 = {
path: "/a/b/app.ts",
content: "let x = 1;"
};
const cLib = {
path: "/a/b/c/lib.ts",
content: ""
};
const cTsconfig = {
path: "/a/b/c/tsconfig.json",
content: "{}"
};
const dLib = {
path: "/a/b/d/lib.ts",
content: ""
};
const dTsconfig = {
path: "/a/b/d/tsconfig.json",
content: "{}"
};
const host = createServerHost([f1, cLib, cTsconfig, dLib, dTsconfig]);
const projectService = createProjectService(host);
// open external project
const projectName = "/a/b/proj1";
projectService.openExternalProject({
projectFileName: projectName,
rootFiles: toExternalFiles([f1.path]),
options: {}
});
// open external project
const projectName = "/a/b/proj1";
projectService.openExternalProject({
projectFileName: projectName,
rootFiles: toExternalFiles([f1.path]),
options: {}
projectService.checkNumberOfProjects({ externalProjects: 1 });
checkProjectActualFiles(projectService.externalProjects[0], [f1.path]);
// add two config file as root files
projectService.openExternalProject({
projectFileName: projectName,
rootFiles: toExternalFiles([f1.path, cTsconfig.path, dTsconfig.path]),
options: {}
});
projectService.checkNumberOfProjects({ configuredProjects: 2 });
if (lazyConfiguredProjectsFromExternalProject) {
checkProjectActualFiles(configuredProjectAt(projectService, 0), emptyArray); // Configured project created but not loaded till actually needed
checkProjectActualFiles(configuredProjectAt(projectService, 1), emptyArray); // Configured project created but not loaded till actually needed
projectService.ensureInferredProjectsUpToDate_TestOnly();
}
checkProjectActualFiles(configuredProjectAt(projectService, 0), [cLib.path, cTsconfig.path]);
checkProjectActualFiles(configuredProjectAt(projectService, 1), [dLib.path, dTsconfig.path]);
// remove one config file
projectService.openExternalProject({
projectFileName: projectName,
rootFiles: toExternalFiles([f1.path, dTsconfig.path]),
options: {}
});
projectService.checkNumberOfProjects({ configuredProjects: 1 });
checkProjectActualFiles(configuredProjectAt(projectService, 0), [dLib.path, dTsconfig.path]);
// remove second config file
projectService.openExternalProject({
projectFileName: projectName,
rootFiles: toExternalFiles([f1.path]),
options: {}
});
projectService.checkNumberOfProjects({ externalProjects: 1 });
checkProjectActualFiles(projectService.externalProjects[0], [f1.path]);
// open two config files
// add two config file as root files
projectService.openExternalProject({
projectFileName: projectName,
rootFiles: toExternalFiles([f1.path, cTsconfig.path, dTsconfig.path]),
options: {}
});
projectService.checkNumberOfProjects({ configuredProjects: 2 });
if (lazyConfiguredProjectsFromExternalProject) {
checkProjectActualFiles(configuredProjectAt(projectService, 0), emptyArray); // Configured project created but not loaded till actually needed
checkProjectActualFiles(configuredProjectAt(projectService, 1), emptyArray); // Configured project created but not loaded till actually needed
projectService.ensureInferredProjectsUpToDate_TestOnly();
}
checkProjectActualFiles(configuredProjectAt(projectService, 0), [cLib.path, cTsconfig.path]);
checkProjectActualFiles(configuredProjectAt(projectService, 1), [dLib.path, dTsconfig.path]);
// close all projects - no projects should be opened
projectService.closeExternalProject(projectName);
projectService.checkNumberOfProjects({});
}
it("when lazyConfiguredProjectsFromExternalProject not set", () => {
verifyAddRemoveConfig(/*lazyConfiguredProjectsFromExternalProject*/ false);
});
projectService.checkNumberOfProjects({ externalProjects: 1 });
checkProjectActualFiles(projectService.externalProjects[0], [f1.path]);
// add two config file as root files
projectService.openExternalProject({
projectFileName: projectName,
rootFiles: toExternalFiles([f1.path, cTsconfig.path, dTsconfig.path]),
options: {}
it("when lazyConfiguredProjectsFromExternalProject is set", () => {
verifyAddRemoveConfig(/*lazyConfiguredProjectsFromExternalProject*/ true);
});
projectService.checkNumberOfProjects({ configuredProjects: 2 });
checkProjectActualFiles(configuredProjectAt(projectService, 0), emptyArray); // Configured project created but not loaded till actually needed
checkProjectActualFiles(configuredProjectAt(projectService, 1), emptyArray); // Configured project created but not loaded till actually needed
projectService.ensureInferredProjectsUpToDate_TestOnly();
checkProjectActualFiles(configuredProjectAt(projectService, 0), [cLib.path, cTsconfig.path]);
checkProjectActualFiles(configuredProjectAt(projectService, 1), [dLib.path, dTsconfig.path]);
// remove one config file
projectService.openExternalProject({
projectFileName: projectName,
rootFiles: toExternalFiles([f1.path, dTsconfig.path]),
options: {}
});
projectService.checkNumberOfProjects({ configuredProjects: 1 });
checkProjectActualFiles(configuredProjectAt(projectService, 0), [dLib.path, dTsconfig.path]);
// remove second config file
projectService.openExternalProject({
projectFileName: projectName,
rootFiles: toExternalFiles([f1.path]),
options: {}
});
projectService.checkNumberOfProjects({ externalProjects: 1 });
checkProjectActualFiles(projectService.externalProjects[0], [f1.path]);
// open two config files
// add two config file as root files
projectService.openExternalProject({
projectFileName: projectName,
rootFiles: toExternalFiles([f1.path, cTsconfig.path, dTsconfig.path]),
options: {}
});
projectService.checkNumberOfProjects({ configuredProjects: 2 });
checkProjectActualFiles(configuredProjectAt(projectService, 0), emptyArray); // Configured project created but not loaded till actually needed
checkProjectActualFiles(configuredProjectAt(projectService, 1), emptyArray); // Configured project created but not loaded till actually needed
projectService.ensureInferredProjectsUpToDate_TestOnly();
checkProjectActualFiles(configuredProjectAt(projectService, 0), [cLib.path, cTsconfig.path]);
checkProjectActualFiles(configuredProjectAt(projectService, 1), [dLib.path, dTsconfig.path]);
// close all projects - no projects should be opened
projectService.closeExternalProject(projectName);
projectService.checkNumberOfProjects({});
});
it("correctly handles changes in lib section of config file", () => {
@@ -4174,6 +4241,47 @@ namespace ts.projectSystem {
assert.isTrue(project.hasOpenRef()); // f
assert.isFalse(project.isClosed());
});
it("handles loads existing configured projects of external projects when lazyConfiguredProjectsFromExternalProject is disabled", () => {
const f1 = {
path: "/a/b/app.ts",
content: "let x = 1"
};
const config = {
path: "/a/b/tsconfig.json",
content: JSON.stringify({})
};
const projectFileName = "/a/b/project.csproj";
const host = createServerHost([f1, config]);
const service = createProjectService(host);
service.setHostConfiguration({ preferences: { lazyConfiguredProjectsFromExternalProject: true } });
service.openExternalProject(<protocol.ExternalProject>{
projectFileName,
rootFiles: toExternalFiles([f1.path, config.path]),
options: {}
});
service.checkNumberOfProjects({ configuredProjects: 1 });
const project = service.configuredProjects.get(config.path)!;
assert.equal(project.pendingReload, ConfigFileProgramReloadLevel.Full); // External project referenced configured project pending to be reloaded
checkProjectActualFiles(project, emptyArray);
service.setHostConfiguration({ preferences: { lazyConfiguredProjectsFromExternalProject: false } });
assert.equal(project.pendingReload, ConfigFileProgramReloadLevel.None); // External project referenced configured project loaded
checkProjectActualFiles(project, [config.path, f1.path]);
service.closeExternalProject(projectFileName);
service.checkNumberOfProjects({});
service.openExternalProject(<protocol.ExternalProject>{
projectFileName,
rootFiles: toExternalFiles([f1.path, config.path]),
options: {}
});
service.checkNumberOfProjects({ configuredProjects: 1 });
const project2 = service.configuredProjects.get(config.path)!;
assert.equal(project2.pendingReload, ConfigFileProgramReloadLevel.None); // External project referenced configured project loaded
checkProjectActualFiles(project2, [config.path, f1.path]);
});
});
describe("tsserverProjectSystem prefer typings to js", () => {
@@ -8257,7 +8365,7 @@ new C();`
verifyProjectWithResolvedModule(session);
host.removeFolder(recognizersTextDist, /*recursive*/ true);
host.deleteFolder(recognizersTextDist, /*recursive*/ true);
host.runQueuedTimeoutCallbacks();
verifyProjectWithUnresolvedModule(session);
@@ -9173,7 +9281,7 @@ describe("Test Suite 1", () => {
checkProjectActualFiles(project, expectedFilesWithUnitTest1);
const navBarResultUnitTest1 = navBarFull(session, unitTest1);
host.removeFile(unitTest1.path);
host.deleteFile(unitTest1.path);
host.checkTimeoutQueueLengthAndRun(2);
checkProjectActualFiles(project, expectedFilesWithoutUnitTest1);
@@ -9210,6 +9318,128 @@ export function Test2() {
});
});
describe("tsserverProjectSystem completions", () => {
it("works", () => {
const aTs: File = {
path: "/a.ts",
content: "export const foo = 0;",
};
const bTs: File = {
path: "/b.ts",
content: "foo",
};
const tsconfig: File = {
path: "/tsconfig.json",
content: "{}",
};
const host = createServerHost([aTs, bTs, tsconfig]);
const session = createSession(host);
openFilesForSession([aTs, bTs], session);
const requestLocation: protocol.FileLocationRequestArgs = {
file: bTs.path,
line: 1,
offset: 3,
};
const response = executeSessionRequest<protocol.CompletionsRequest, protocol.CompletionInfoResponse>(session, protocol.CommandTypes.CompletionInfo, {
...requestLocation,
includeExternalModuleExports: true,
prefix: "foo",
});
const entry: protocol.CompletionEntry = {
hasAction: true,
insertText: undefined,
isRecommended: undefined,
kind: ScriptElementKind.constElement,
kindModifiers: ScriptElementKindModifier.exportedModifier,
name: "foo",
replacementSpan: undefined,
sortText: "0",
source: "/a",
};
assert.deepEqual<protocol.CompletionInfo | undefined>(response, {
isGlobalCompletion: true,
isMemberCompletion: false,
isNewIdentifierLocation: false,
entries: [entry],
});
const detailsRequestArgs: protocol.CompletionDetailsRequestArgs = {
...requestLocation,
entryNames: [{ name: "foo", source: "/a" }],
};
const detailsResponse = executeSessionRequest<protocol.CompletionDetailsRequest, protocol.CompletionDetailsResponse>(session, protocol.CommandTypes.CompletionDetails, detailsRequestArgs);
const detailsCommon: protocol.CompletionEntryDetails & CompletionEntryDetails = {
displayParts: [
keywordPart(SyntaxKind.ConstKeyword),
spacePart(),
displayPart("foo", SymbolDisplayPartKind.localName),
punctuationPart(SyntaxKind.ColonToken),
spacePart(),
displayPart("0", SymbolDisplayPartKind.stringLiteral),
],
documentation: emptyArray,
kind: ScriptElementKind.constElement,
kindModifiers: ScriptElementKindModifier.exportedModifier,
name: "foo",
source: [{ text: "./a", kind: "text" }],
tags: emptyArray,
};
assert.deepEqual<ReadonlyArray<protocol.CompletionEntryDetails> | undefined>(detailsResponse, [
{
codeActions: [
{
description: `Import 'foo' from module "./a"`,
changes: [
{
fileName: "/b.ts",
textChanges: [
{
start: { line: 1, offset: 1 },
end: { line: 1, offset: 1 },
newText: 'import { foo } from "./a";\n\n',
},
],
},
],
commands: undefined,
},
],
...detailsCommon,
},
]);
interface CompletionDetailsFullRequest extends protocol.FileLocationRequest {
readonly command: protocol.CommandTypes.CompletionDetailsFull;
readonly arguments: protocol.CompletionDetailsRequestArgs;
}
interface CompletionDetailsFullResponse extends protocol.Response {
readonly body?: ReadonlyArray<CompletionEntryDetails>;
}
const detailsFullResponse = executeSessionRequest<CompletionDetailsFullRequest, CompletionDetailsFullResponse>(session, protocol.CommandTypes.CompletionDetailsFull, detailsRequestArgs);
assert.deepEqual<ReadonlyArray<CompletionEntryDetails> | undefined>(detailsFullResponse, [
{
codeActions: [
{
description: `Import 'foo' from module "./a"`,
changes: [
{
fileName: "/b.ts",
textChanges: [createTextChange(createTextSpan(0, 0), 'import { foo } from "./a";\n\n')],
},
],
commands: undefined,
}
],
...detailsCommon,
}
]);
});
});
describe("tsserverProjectSystem project references", () => {
const aTs: File = {
path: "/a/a.ts",
@@ -9297,7 +9527,7 @@ export function Test2() {
checkDeclarationFiles(bTs, session, [bDtsMap, bDts]);
// Testing what happens if we delete the original sources.
host.removeFile(bTs.path);
host.deleteFile(bTs.path);
openFilesForSession([userTs], session);
const service = session.getProjectService();
+145 -13
View File
@@ -13,7 +13,7 @@ namespace ts {
}
let reportDiagnostic = createDiagnosticReporter(sys);
function updateReportDiagnostic(options: CompilerOptions) {
function updateReportDiagnostic(options?: CompilerOptions) {
if (shouldBePretty(options)) {
reportDiagnostic = createDiagnosticReporter(sys, /*pretty*/ true);
}
@@ -23,8 +23,8 @@ namespace ts {
return !!sys.writeOutputIsTTY && sys.writeOutputIsTTY();
}
function shouldBePretty(options: CompilerOptions) {
if (typeof options.pretty === "undefined") {
function shouldBePretty(options?: CompilerOptions) {
if (!options || typeof options.pretty === "undefined") {
return defaultIsPretty();
}
return options.pretty;
@@ -54,15 +54,7 @@ namespace ts {
export function executeCommandLine(args: string[]): void {
if (args.length > 0 && ((args[0].toLowerCase() === "--build") || (args[0].toLowerCase() === "-b"))) {
const reportDiag = createDiagnosticReporter(sys, defaultIsPretty());
const report = (message: DiagnosticMessage, ...args: string[]) => reportDiag(createCompilerDiagnostic(message, ...args));
const buildHost: BuildHost = {
error: report,
verbose: report,
message: report,
errorDiagnostic: d => reportDiag(d)
};
const result = performBuild(args.slice(1), createCompilerHost({}), buildHost, sys);
const result = performBuild(args.slice(1));
// undefined = in watch mode, do not exit
if (result !== undefined) {
return sys.exit(result);
@@ -172,6 +164,146 @@ namespace ts {
}
}
function performBuild(args: string[]): number | undefined {
const buildOpts: CommandLineOption[] = [
{
name: "help",
shortName: "h",
type: "boolean",
showInSimplifiedHelpView: true,
category: Diagnostics.Command_line_Options,
description: Diagnostics.Print_this_message,
},
{
name: "help",
shortName: "?",
type: "boolean"
},
{
name: "verbose",
shortName: "v",
category: Diagnostics.Command_line_Options,
description: Diagnostics.Enable_verbose_logging,
type: "boolean"
},
{
name: "dry",
shortName: "d",
category: Diagnostics.Command_line_Options,
description: Diagnostics.Show_what_would_be_built_or_deleted_if_specified_with_clean,
type: "boolean"
},
{
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"
},
{
name: "clean",
category: Diagnostics.Command_line_Options,
description: Diagnostics.Delete_the_outputs_of_all_projects,
type: "boolean"
},
{
name: "watch",
category: Diagnostics.Command_line_Options,
description: Diagnostics.Watch_input_files,
type: "boolean"
},
{
name: "preserveWatchOutput",
type: "boolean",
category: Diagnostics.Command_line_Options,
description: Diagnostics.Whether_to_keep_outdated_console_output_in_watch_mode_instead_of_clearing_the_screen,
},
];
let buildOptionNameMap: OptionNameMap | undefined;
const returnBuildOptionNameMap = () => (buildOptionNameMap || (buildOptionNameMap = createOptionNameMap(buildOpts)));
const buildOptions: BuildOptions = {};
const projects: string[] = [];
for (const arg of args) {
if (arg.charCodeAt(0) === CharacterCodes.minus) {
const opt = getOptionDeclarationFromName(returnBuildOptionNameMap, arg.slice(arg.charCodeAt(1) === CharacterCodes.minus ? 2 : 1), /*allowShort*/ true);
if (opt) {
buildOptions[opt.name as keyof BuildOptions] = true;
}
else {
reportDiagnostic(createCompilerDiagnostic(Diagnostics.Unknown_build_option_0, arg));
}
}
else {
// Not a flag, parse as filename
addProject(arg);
}
}
if (buildOptions.help) {
printVersion();
printHelp(buildOpts, "--build ");
return ExitStatus.Success;
}
// Update to pretty if host supports it
updateReportDiagnostic();
if (!sys.getModifiedTime || !sys.setModifiedTime || (buildOptions.clean && !sys.deleteFile)) {
reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_current_host_does_not_support_the_0_option, "--build"));
return ExitStatus.DiagnosticsPresent_OutputsSkipped;
}
if (buildOptions.watch) {
reportWatchModeWithoutSysSupport();
}
// Nonsensical combinations
if (buildOptions.clean && buildOptions.force) {
reportDiagnostic(createCompilerDiagnostic(Diagnostics.Options_0_and_1_cannot_be_combined, "clean", "force"));
return ExitStatus.DiagnosticsPresent_OutputsSkipped;
}
if (buildOptions.clean && buildOptions.verbose) {
reportDiagnostic(createCompilerDiagnostic(Diagnostics.Options_0_and_1_cannot_be_combined, "clean", "verbose"));
return ExitStatus.DiagnosticsPresent_OutputsSkipped;
}
if (buildOptions.clean && buildOptions.watch) {
reportDiagnostic(createCompilerDiagnostic(Diagnostics.Options_0_and_1_cannot_be_combined, "clean", "watch"));
return ExitStatus.DiagnosticsPresent_OutputsSkipped;
}
if (buildOptions.watch && buildOptions.dry) {
reportDiagnostic(createCompilerDiagnostic(Diagnostics.Options_0_and_1_cannot_be_combined, "watch", "dry"));
return ExitStatus.DiagnosticsPresent_OutputsSkipped;
}
if (projects.length === 0) {
// tsc -b invoked with no extra arguments; act as if invoked with "tsc -b ."
addProject(".");
}
// TODO: change this to host if watch => watchHost otherwiue without wathc
const builder = createSolutionBuilder(createSolutionBuilderWithWatchHost(sys, reportDiagnostic, createBuilderStatusReporter(sys, shouldBePretty()), createWatchStatusReporter()), projects, buildOptions);
if (buildOptions.clean) {
return builder.cleanAllProjects();
}
if (buildOptions.watch) {
builder.buildAllProjects();
builder.startWatching();
return undefined;
}
return builder.buildAllProjects();
function addProject(projectSpecification: string) {
const fileName = resolvePath(sys.getCurrentDirectory(), projectSpecification);
const refPath = resolveProjectReferencePath(sys, { path: fileName });
if (!sys.fileExists(refPath)) {
return reportDiagnostic(createCompilerDiagnostic(Diagnostics.File_0_does_not_exist, fileName));
}
projects.push(refPath);
}
}
function performCompilation(rootNames: string[], projectReferences: ReadonlyArray<ProjectReference> | undefined, options: CompilerOptions, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>) {
const host = createCompilerHost(options);
enableStatistics(options);
@@ -205,7 +337,7 @@ namespace ts {
};
}
function createWatchStatusReporter(options: CompilerOptions) {
function createWatchStatusReporter(options?: CompilerOptions) {
return ts.createWatchStatusReporter(sys, shouldBePretty(options));
}
+31 -28
View File
@@ -2690,9 +2690,6 @@ declare namespace ts {
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
getEnvironmentVariable?(name: string): string | undefined;
createHash?(data: string): string;
getModifiedTime?(fileName: string): Date | undefined;
setModifiedTime?(fileName: string, date: Date): void;
deleteFile?(fileName: string): void;
}
interface SourceMapRange extends TextRange {
source?: SourceMapSource;
@@ -2993,6 +2990,16 @@ declare namespace ts {
Parameters = 1296,
IndexSignatureParameters = 4432
}
interface UserPreferences {
readonly disableSuggestions?: boolean;
readonly quotePreference?: "double" | "single";
readonly includeCompletionsForModuleExports?: boolean;
readonly includeCompletionsWithInsertText?: boolean;
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
/** Determines whether we import `foo/index.ts` as "foo", "foo/index", or "foo/index.js" */
readonly importModuleSpecifierEnding?: "minimal" | "index" | "js";
readonly allowTextChangesInNewFiles?: boolean;
}
}
declare function setTimeout(handler: (...args: any[]) => void, timeout: number): any;
declare function clearTimeout(handle: any): void;
@@ -4312,15 +4319,26 @@ declare namespace ts {
type WatchStatusReporter = (diagnostic: Diagnostic, newLine: string, options: CompilerOptions) => void;
/** Create the program with rootNames and options, if they are undefined, oldProgram and new configFile diagnostics create new program */
type CreateProgram<T extends BuilderProgram> = (rootNames: ReadonlyArray<string> | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: T, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>) => T;
interface WatchCompilerHost<T extends BuilderProgram> {
/** Host that has watch functionality used in --watch mode */
interface WatchHost {
/** If provided, called with Diagnostic message that informs about change in watch status */
onWatchStatusChange?(diagnostic: Diagnostic, newLine: string, options: CompilerOptions): void;
/** Used to watch changes in source files, missing files needed to update the program or config file */
watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher;
/** Used to watch resolved module's failed lookup locations, config file specs, type roots where auto type reference directives are added */
watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher;
/** If provided, will be used to set delayed compilation, so that multiple changes in short span are compiled together */
setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any;
/** If provided, will be used to reset existing delayed compilation */
clearTimeout?(timeoutId: any): void;
}
interface WatchCompilerHost<T extends BuilderProgram> extends WatchHost {
/**
* Used to create the program when need for program creation or recreation detected
*/
createProgram: CreateProgram<T>;
/** If provided, callback to invoke after every new program creation */
afterProgramCreate?(program: T): void;
/** If provided, called with Diagnostic message that informs about change in watch status */
onWatchStatusChange?(diagnostic: Diagnostic, newLine: string, options: CompilerOptions): void;
useCaseSensitiveFileNames(): boolean;
getNewLine(): string;
getCurrentDirectory(): string;
@@ -4353,14 +4371,6 @@ declare namespace ts {
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[];
/** If provided, used to resolve type reference directives, otherwise typescript's default resolution */
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
/** Used to watch changes in source files, missing files needed to update the program or config file */
watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher;
/** Used to watch resolved module's failed lookup locations, config file specs, type roots where auto type reference directives are added */
watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher;
/** If provided, will be used to set delayed compilation, so that multiple changes in short span are compiled together */
setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any;
/** If provided, will be used to reset existing delayed compilation */
clearTimeout?(timeoutId: any): void;
}
/**
* Host to create watch with root files and options
@@ -4636,14 +4646,6 @@ declare namespace ts {
isKnownTypesPackageName?(name: string): boolean;
installPackage?(options: InstallPackageOptions): Promise<ApplyCodeActionCommandResult>;
}
interface UserPreferences {
readonly disableSuggestions?: boolean;
readonly quotePreference?: "double" | "single";
readonly includeCompletionsForModuleExports?: boolean;
readonly includeCompletionsWithInsertText?: boolean;
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
readonly allowTextChangesInNewFiles?: boolean;
}
interface LanguageService {
cleanupSemanticCache(): void;
getSyntacticDiagnostics(fileName: string): DiagnosticWithLocation[];
@@ -6948,7 +6950,7 @@ declare namespace ts.server.protocol {
* begin with prefix.
*/
interface CompletionsRequest extends FileLocationRequest {
command: CommandTypes.Completions;
command: CommandTypes.Completions | CommandTypes.CompletionInfo;
arguments: CompletionsRequestArgs;
}
/**
@@ -7795,6 +7797,7 @@ declare namespace ts.server.protocol {
readonly includeCompletionsWithInsertText?: boolean;
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
readonly allowTextChangesInNewFiles?: boolean;
readonly lazyConfiguredProjectsFromExternalProject?: boolean;
}
interface CompilerOptions {
allowJs?: boolean;
@@ -7926,14 +7929,14 @@ declare namespace ts.server {
getSnapshot(): IScriptSnapshot;
private ensureRealPath;
getFormatCodeSettings(): FormatCodeSettings | undefined;
getPreferences(): UserPreferences | undefined;
getPreferences(): protocol.UserPreferences | undefined;
attachToProject(project: Project): boolean;
isAttached(project: Project): boolean;
detachFromProject(project: Project): void;
detachAllProjects(): void;
getDefaultProject(): Project;
registerFileUpdate(): void;
setOptions(formatSettings: FormatCodeSettings, preferences: UserPreferences | undefined): void;
setOptions(formatSettings: FormatCodeSettings, preferences: protocol.UserPreferences | undefined): void;
getLatestVersion(): string;
saveTo(fileName: string): void;
reloadFromFile(tempFileName?: NormalizedPath): boolean;
@@ -8311,7 +8314,7 @@ declare namespace ts.server {
function convertScriptKindName(scriptKindName: protocol.ScriptKindName): ScriptKind.Unknown | ScriptKind.JS | ScriptKind.JSX | ScriptKind.TS | ScriptKind.TSX;
interface HostConfiguration {
formatCodeOptions: FormatCodeSettings;
preferences: UserPreferences;
preferences: protocol.UserPreferences;
hostInfo: string;
extraFileExtensions?: FileExtensionInfo[];
}
@@ -8430,9 +8433,9 @@ declare namespace ts.server {
*/
private ensureProjectStructuresUptoDate;
getFormatCodeOptions(file: NormalizedPath): FormatCodeSettings;
getPreferences(file: NormalizedPath): UserPreferences;
getPreferences(file: NormalizedPath): protocol.UserPreferences;
getHostFormatCodeOptions(): FormatCodeSettings;
getHostPreferences(): UserPreferences;
getHostPreferences(): protocol.UserPreferences;
private onSourceFileChanged;
private handleDeletedFile;
private onConfigChangedForConfiguredProject;
+24 -22
View File
@@ -2690,9 +2690,6 @@ declare namespace ts {
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
getEnvironmentVariable?(name: string): string | undefined;
createHash?(data: string): string;
getModifiedTime?(fileName: string): Date | undefined;
setModifiedTime?(fileName: string, date: Date): void;
deleteFile?(fileName: string): void;
}
interface SourceMapRange extends TextRange {
source?: SourceMapSource;
@@ -2993,6 +2990,16 @@ declare namespace ts {
Parameters = 1296,
IndexSignatureParameters = 4432
}
interface UserPreferences {
readonly disableSuggestions?: boolean;
readonly quotePreference?: "double" | "single";
readonly includeCompletionsForModuleExports?: boolean;
readonly includeCompletionsWithInsertText?: boolean;
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
/** Determines whether we import `foo/index.ts` as "foo", "foo/index", or "foo/index.js" */
readonly importModuleSpecifierEnding?: "minimal" | "index" | "js";
readonly allowTextChangesInNewFiles?: boolean;
}
}
declare function setTimeout(handler: (...args: any[]) => void, timeout: number): any;
declare function clearTimeout(handle: any): void;
@@ -4312,15 +4319,26 @@ declare namespace ts {
type WatchStatusReporter = (diagnostic: Diagnostic, newLine: string, options: CompilerOptions) => void;
/** Create the program with rootNames and options, if they are undefined, oldProgram and new configFile diagnostics create new program */
type CreateProgram<T extends BuilderProgram> = (rootNames: ReadonlyArray<string> | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: T, configFileParsingDiagnostics?: ReadonlyArray<Diagnostic>) => T;
interface WatchCompilerHost<T extends BuilderProgram> {
/** Host that has watch functionality used in --watch mode */
interface WatchHost {
/** If provided, called with Diagnostic message that informs about change in watch status */
onWatchStatusChange?(diagnostic: Diagnostic, newLine: string, options: CompilerOptions): void;
/** Used to watch changes in source files, missing files needed to update the program or config file */
watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher;
/** Used to watch resolved module's failed lookup locations, config file specs, type roots where auto type reference directives are added */
watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher;
/** If provided, will be used to set delayed compilation, so that multiple changes in short span are compiled together */
setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any;
/** If provided, will be used to reset existing delayed compilation */
clearTimeout?(timeoutId: any): void;
}
interface WatchCompilerHost<T extends BuilderProgram> extends WatchHost {
/**
* Used to create the program when need for program creation or recreation detected
*/
createProgram: CreateProgram<T>;
/** If provided, callback to invoke after every new program creation */
afterProgramCreate?(program: T): void;
/** If provided, called with Diagnostic message that informs about change in watch status */
onWatchStatusChange?(diagnostic: Diagnostic, newLine: string, options: CompilerOptions): void;
useCaseSensitiveFileNames(): boolean;
getNewLine(): string;
getCurrentDirectory(): string;
@@ -4353,14 +4371,6 @@ declare namespace ts {
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[];
/** If provided, used to resolve type reference directives, otherwise typescript's default resolution */
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
/** Used to watch changes in source files, missing files needed to update the program or config file */
watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher;
/** Used to watch resolved module's failed lookup locations, config file specs, type roots where auto type reference directives are added */
watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher;
/** If provided, will be used to set delayed compilation, so that multiple changes in short span are compiled together */
setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any;
/** If provided, will be used to reset existing delayed compilation */
clearTimeout?(timeoutId: any): void;
}
/**
* Host to create watch with root files and options
@@ -4636,14 +4646,6 @@ declare namespace ts {
isKnownTypesPackageName?(name: string): boolean;
installPackage?(options: InstallPackageOptions): Promise<ApplyCodeActionCommandResult>;
}
interface UserPreferences {
readonly disableSuggestions?: boolean;
readonly quotePreference?: "double" | "single";
readonly includeCompletionsForModuleExports?: boolean;
readonly includeCompletionsWithInsertText?: boolean;
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
readonly allowTextChangesInNewFiles?: boolean;
}
interface LanguageService {
cleanupSemanticCache(): void;
getSyntacticDiagnostics(fileName: string): DiagnosticWithLocation[];
@@ -23,7 +23,7 @@ function foo() {
var x: TokenType = list['one'];
>x : TokenType
>list['one'] : any
>list['one'] : error
>list : {}
>'one' : "one"
}
@@ -16,14 +16,14 @@ interface Function {
}
var a = {}[0]; // Should be Foo
>a : any
>{}[0] : any
>a : error
>{}[0] : error
>{} : {}
>0 : 0
var b = (() => { })[0]; // Should be Bar
>b : any
>(() => { })[0] : any
>b : error
>(() => { })[0] : error
>(() => { }) : () => void
>() => { } : () => void
>0 : 0
@@ -22,8 +22,8 @@ var r1 = o['data']; // Should be number
>'data' : "data"
var r2 = o['functionData']; // Should be any (no property found)
>r2 : any
>o['functionData'] : any
>r2 : error
>o['functionData'] : error
>o : {}
>'functionData' : "functionData"
@@ -1,4 +1,6 @@
//// [badInferenceLowerPriorityThanGoodInference.ts]
// Repro from #13118
interface Foo<A> {
a: A;
b: (x: A) => void;
@@ -11,11 +13,24 @@ const result = canYouInferThis(() => ({
b: x => { }
}))
result.BLAH;
result.BLAH;
// Repro from #26629
function goofus <ARGS extends any[]> (f: (...args: ARGS) => any ) {}
goofus((a: string) => ({ dog() { return a; } }));
goofus((a: string) => ({ dog: function() { return a; } }));
//// [badInferenceLowerPriorityThanGoodInference.js]
// Repro from #13118
var result = canYouInferThis(function () { return ({
a: { BLAH: 33 },
b: function (x) { }
}); });
result.BLAH;
// Repro from #26629
function goofus(f) { }
goofus(function (a) { return ({ dog: function () { return a; } }); });
goofus(function (a) { return ({ dog: function () { return a; } }); });
@@ -1,42 +1,65 @@
=== tests/cases/compiler/badInferenceLowerPriorityThanGoodInference.ts ===
// Repro from #13118
interface Foo<A> {
>Foo : Symbol(Foo, Decl(badInferenceLowerPriorityThanGoodInference.ts, 0, 0))
>A : Symbol(A, Decl(badInferenceLowerPriorityThanGoodInference.ts, 0, 14))
>A : Symbol(A, Decl(badInferenceLowerPriorityThanGoodInference.ts, 2, 14))
a: A;
>a : Symbol(Foo.a, Decl(badInferenceLowerPriorityThanGoodInference.ts, 0, 18))
>A : Symbol(A, Decl(badInferenceLowerPriorityThanGoodInference.ts, 0, 14))
>a : Symbol(Foo.a, Decl(badInferenceLowerPriorityThanGoodInference.ts, 2, 18))
>A : Symbol(A, Decl(badInferenceLowerPriorityThanGoodInference.ts, 2, 14))
b: (x: A) => void;
>b : Symbol(Foo.b, Decl(badInferenceLowerPriorityThanGoodInference.ts, 1, 9))
>x : Symbol(x, Decl(badInferenceLowerPriorityThanGoodInference.ts, 2, 8))
>A : Symbol(A, Decl(badInferenceLowerPriorityThanGoodInference.ts, 0, 14))
>b : Symbol(Foo.b, Decl(badInferenceLowerPriorityThanGoodInference.ts, 3, 9))
>x : Symbol(x, Decl(badInferenceLowerPriorityThanGoodInference.ts, 4, 8))
>A : Symbol(A, Decl(badInferenceLowerPriorityThanGoodInference.ts, 2, 14))
}
declare function canYouInferThis<A>(fn: () => Foo<A>): A;
>canYouInferThis : Symbol(canYouInferThis, Decl(badInferenceLowerPriorityThanGoodInference.ts, 3, 1))
>A : Symbol(A, Decl(badInferenceLowerPriorityThanGoodInference.ts, 5, 33))
>fn : Symbol(fn, Decl(badInferenceLowerPriorityThanGoodInference.ts, 5, 36))
>canYouInferThis : Symbol(canYouInferThis, Decl(badInferenceLowerPriorityThanGoodInference.ts, 5, 1))
>A : Symbol(A, Decl(badInferenceLowerPriorityThanGoodInference.ts, 7, 33))
>fn : Symbol(fn, Decl(badInferenceLowerPriorityThanGoodInference.ts, 7, 36))
>Foo : Symbol(Foo, Decl(badInferenceLowerPriorityThanGoodInference.ts, 0, 0))
>A : Symbol(A, Decl(badInferenceLowerPriorityThanGoodInference.ts, 5, 33))
>A : Symbol(A, Decl(badInferenceLowerPriorityThanGoodInference.ts, 5, 33))
>A : Symbol(A, Decl(badInferenceLowerPriorityThanGoodInference.ts, 7, 33))
>A : Symbol(A, Decl(badInferenceLowerPriorityThanGoodInference.ts, 7, 33))
const result = canYouInferThis(() => ({
>result : Symbol(result, Decl(badInferenceLowerPriorityThanGoodInference.ts, 7, 5))
>canYouInferThis : Symbol(canYouInferThis, Decl(badInferenceLowerPriorityThanGoodInference.ts, 3, 1))
>result : Symbol(result, Decl(badInferenceLowerPriorityThanGoodInference.ts, 9, 5))
>canYouInferThis : Symbol(canYouInferThis, Decl(badInferenceLowerPriorityThanGoodInference.ts, 5, 1))
a: { BLAH: 33 },
>a : Symbol(a, Decl(badInferenceLowerPriorityThanGoodInference.ts, 7, 39))
>BLAH : Symbol(BLAH, Decl(badInferenceLowerPriorityThanGoodInference.ts, 8, 8))
>a : Symbol(a, Decl(badInferenceLowerPriorityThanGoodInference.ts, 9, 39))
>BLAH : Symbol(BLAH, Decl(badInferenceLowerPriorityThanGoodInference.ts, 10, 8))
b: x => { }
>b : Symbol(b, Decl(badInferenceLowerPriorityThanGoodInference.ts, 8, 20))
>x : Symbol(x, Decl(badInferenceLowerPriorityThanGoodInference.ts, 9, 6))
>b : Symbol(b, Decl(badInferenceLowerPriorityThanGoodInference.ts, 10, 20))
>x : Symbol(x, Decl(badInferenceLowerPriorityThanGoodInference.ts, 11, 6))
}))
result.BLAH;
>result.BLAH : Symbol(BLAH, Decl(badInferenceLowerPriorityThanGoodInference.ts, 8, 8))
>result : Symbol(result, Decl(badInferenceLowerPriorityThanGoodInference.ts, 7, 5))
>BLAH : Symbol(BLAH, Decl(badInferenceLowerPriorityThanGoodInference.ts, 8, 8))
>result.BLAH : Symbol(BLAH, Decl(badInferenceLowerPriorityThanGoodInference.ts, 10, 8))
>result : Symbol(result, Decl(badInferenceLowerPriorityThanGoodInference.ts, 9, 5))
>BLAH : Symbol(BLAH, Decl(badInferenceLowerPriorityThanGoodInference.ts, 10, 8))
// Repro from #26629
function goofus <ARGS extends any[]> (f: (...args: ARGS) => any ) {}
>goofus : Symbol(goofus, Decl(badInferenceLowerPriorityThanGoodInference.ts, 14, 12))
>ARGS : Symbol(ARGS, Decl(badInferenceLowerPriorityThanGoodInference.ts, 18, 17))
>f : Symbol(f, Decl(badInferenceLowerPriorityThanGoodInference.ts, 18, 38))
>args : Symbol(args, Decl(badInferenceLowerPriorityThanGoodInference.ts, 18, 42))
>ARGS : Symbol(ARGS, Decl(badInferenceLowerPriorityThanGoodInference.ts, 18, 17))
goofus((a: string) => ({ dog() { return a; } }));
>goofus : Symbol(goofus, Decl(badInferenceLowerPriorityThanGoodInference.ts, 14, 12))
>a : Symbol(a, Decl(badInferenceLowerPriorityThanGoodInference.ts, 20, 8))
>dog : Symbol(dog, Decl(badInferenceLowerPriorityThanGoodInference.ts, 20, 24))
>a : Symbol(a, Decl(badInferenceLowerPriorityThanGoodInference.ts, 20, 8))
goofus((a: string) => ({ dog: function() { return a; } }));
>goofus : Symbol(goofus, Decl(badInferenceLowerPriorityThanGoodInference.ts, 14, 12))
>a : Symbol(a, Decl(badInferenceLowerPriorityThanGoodInference.ts, 21, 8))
>dog : Symbol(dog, Decl(badInferenceLowerPriorityThanGoodInference.ts, 21, 24))
>a : Symbol(a, Decl(badInferenceLowerPriorityThanGoodInference.ts, 21, 8))
@@ -1,4 +1,6 @@
=== tests/cases/compiler/badInferenceLowerPriorityThanGoodInference.ts ===
// Repro from #13118
interface Foo<A> {
a: A;
>a : A
@@ -38,3 +40,31 @@ result.BLAH;
>result : { BLAH: number; }
>BLAH : number
// Repro from #26629
function goofus <ARGS extends any[]> (f: (...args: ARGS) => any ) {}
>goofus : <ARGS extends any[]>(f: (...args: ARGS) => any) => void
>f : (...args: ARGS) => any
>args : ARGS
goofus((a: string) => ({ dog() { return a; } }));
>goofus((a: string) => ({ dog() { return a; } })) : void
>goofus : <ARGS extends any[]>(f: (...args: ARGS) => any) => void
>(a: string) => ({ dog() { return a; } }) : (a: string) => { dog(): string; }
>a : string
>({ dog() { return a; } }) : { dog(): string; }
>{ dog() { return a; } } : { dog(): string; }
>dog : () => string
>a : string
goofus((a: string) => ({ dog: function() { return a; } }));
>goofus((a: string) => ({ dog: function() { return a; } })) : void
>goofus : <ARGS extends any[]>(f: (...args: ARGS) => any) => void
>(a: string) => ({ dog: function() { return a; } }) : (a: string) => { dog: () => string; }
>a : string
>({ dog: function() { return a; } }) : { dog: () => string; }
>{ dog: function() { return a; } } : { dog: () => string; }
>dog : () => string
>function() { return a; } : () => string
>a : string
@@ -79,7 +79,7 @@ obj1["26"]; // string
>"26" : "26"
obj1["0b11010"]; // any
>obj1["0b11010"] : any
>obj1["0b11010"] : error
>obj1 : { 0b11010: string; a: number; bin1: number; b: number; 0B111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111101001010100000010111110001111111111: boolean; }
>"0b11010" : "0b11010"
@@ -119,7 +119,7 @@ obj2["26"]; // string
>"26" : "26"
obj2["0B11010"]; // any
>obj2["0B11010"] : any
>obj2["0B11010"] : error
>obj2 : { 0B11010: string; a: number; bin2: number; b: number; 0B11111111111111111111111111111111111111111111111101001010100000010111110001111111111: boolean; }
>"0B11010" : "0B11010"
@@ -149,7 +149,7 @@ obj2["9.671406556917009e+24"]; // boolean
>"9.671406556917009e+24" : "9.671406556917009e+24"
obj2["Infinity"]; // any
>obj2["Infinity"] : any
>obj2["Infinity"] : error
>obj2 : { 0B11010: string; a: number; bin2: number; b: number; 0B11111111111111111111111111111111111111111111111101001010100000010111110001111111111: boolean; }
>"Infinity" : "Infinity"
@@ -79,7 +79,7 @@ obj1["26"]; // string
>"26" : "26"
obj1["0b11010"]; // any
>obj1["0b11010"] : any
>obj1["0b11010"] : error
>obj1 : { 0b11010: string; a: number; bin1: number; b: number; 0B111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111101001010100000010111110001111111111: boolean; }
>"0b11010" : "0b11010"
@@ -119,7 +119,7 @@ obj2["26"]; // string
>"26" : "26"
obj2["0B11010"]; // any
>obj2["0B11010"] : any
>obj2["0B11010"] : error
>obj2 : { 0B11010: string; a: number; bin2: number; b: number; 0B11111111111111111111111111111111111111111111111101001010100000010111110001111111111: boolean; }
>"0B11010" : "0B11010"
@@ -149,7 +149,7 @@ obj2["9.671406556917009e+24"]; // boolean
>"9.671406556917009e+24" : "9.671406556917009e+24"
obj2["Infinity"]; // any
>obj2["Infinity"] : any
>obj2["Infinity"] : error
>obj2 : { 0B11010: string; a: number; bin2: number; b: number; 0B11111111111111111111111111111111111111111111111101001010100000010111110001111111111: boolean; }
>"Infinity" : "Infinity"
@@ -111,6 +111,16 @@ for (const y = 0; y < 1;) {
const x = 1;
(function() { return x + y});
(() => x + y);
}
// https://github.com/Microsoft/TypeScript/issues/20594
declare const sobj: { [x: string]: any };
for (let sx in sobj) {
(() => sobj[sx]);
}
declare const iobj: { [x: number]: any };
for (let ix in iobj) {
(() => iobj[ix]);
}
//// [capturedLetConstInLoop1.js]
@@ -270,3 +280,15 @@ var _loop_20 = function (y) {
for (var y = 0; y < 1;) {
_loop_20(y);
}
var _loop_21 = function (sx) {
(function () { return sobj[sx]; });
};
for (var sx in sobj) {
_loop_21(sx);
}
var _loop_22 = function (ix) {
(function () { return iobj[ix]; });
};
for (var ix in iobj) {
_loop_22(ix);
}
@@ -258,3 +258,29 @@ for (const y = 0; y < 1;) {
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 109, 9))
>y : Symbol(y, Decl(capturedLetConstInLoop1.ts, 108, 10))
}
// https://github.com/Microsoft/TypeScript/issues/20594
declare const sobj: { [x: string]: any };
>sobj : Symbol(sobj, Decl(capturedLetConstInLoop1.ts, 115, 13))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 115, 23))
for (let sx in sobj) {
>sx : Symbol(sx, Decl(capturedLetConstInLoop1.ts, 116, 8))
>sobj : Symbol(sobj, Decl(capturedLetConstInLoop1.ts, 115, 13))
(() => sobj[sx]);
>sobj : Symbol(sobj, Decl(capturedLetConstInLoop1.ts, 115, 13))
>sx : Symbol(sx, Decl(capturedLetConstInLoop1.ts, 116, 8))
}
declare const iobj: { [x: number]: any };
>iobj : Symbol(iobj, Decl(capturedLetConstInLoop1.ts, 119, 13))
>x : Symbol(x, Decl(capturedLetConstInLoop1.ts, 119, 23))
for (let ix in iobj) {
>ix : Symbol(ix, Decl(capturedLetConstInLoop1.ts, 120, 8))
>iobj : Symbol(iobj, Decl(capturedLetConstInLoop1.ts, 119, 13))
(() => iobj[ix]);
>iobj : Symbol(iobj, Decl(capturedLetConstInLoop1.ts, 119, 13))
>ix : Symbol(ix, Decl(capturedLetConstInLoop1.ts, 120, 8))
}
@@ -426,3 +426,35 @@ for (const y = 0; y < 1;) {
>x : 1
>y : 0
}
// https://github.com/Microsoft/TypeScript/issues/20594
declare const sobj: { [x: string]: any };
>sobj : { [x: string]: any; }
>x : string
for (let sx in sobj) {
>sx : string
>sobj : { [x: string]: any; }
(() => sobj[sx]);
>(() => sobj[sx]) : () => any
>() => sobj[sx] : () => any
>sobj[sx] : any
>sobj : { [x: string]: any; }
>sx : string
}
declare const iobj: { [x: number]: any };
>iobj : { [x: number]: any; }
>x : number
for (let ix in iobj) {
>ix : string
>iobj : { [x: number]: any; }
(() => iobj[ix]);
>(() => iobj[ix]) : () => any
>() => iobj[ix] : () => any
>iobj[ix] : any
>iobj : { [x: number]: any; }
>ix : string
}
@@ -0,0 +1,55 @@
//// [circularlySimplifyingConditionalTypesNoCrash.ts]
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
type Shared< // Circularly self constraining type, defered thanks to mapping
InjectedProps,
DecorationTargetProps extends Shared<InjectedProps, DecorationTargetProps>
> = {
[P in Extract<keyof InjectedProps, keyof DecorationTargetProps>]: InjectedProps[P] extends DecorationTargetProps[P] ? DecorationTargetProps[P] : never;
};
interface ComponentClass<P> {
defaultProps?: Partial<P>; // Inference target is also mapped _and_ optional
}
interface InferableComponentEnhancerWithProps<TInjectedProps, TNeedsProps> {
<P extends Shared<TInjectedProps, P>>(
component: ComponentClass<P>
): ComponentClass<Omit<P, keyof Shared<TInjectedProps, P>> & TNeedsProps> & { WrappedComponent: ComponentClass<P> }
} // Then intersected with and indexed via Omit and &
interface Connect { // Then strictly compared with another signature in its context
<TStateProps, TOwnProps>(
mapStateToProps: unknown,
): InferableComponentEnhancerWithProps<TStateProps, TOwnProps>;
<TDispatchProps, TOwnProps>(
mapStateToProps: null | undefined,
mapDispatchToProps: unknown,
mergeProps: null | undefined,
options: unknown
): InferableComponentEnhancerWithProps<TDispatchProps, TOwnProps>;
}
declare var connect: Connect;
const myStoreConnect: Connect = function(
mapStateToProps?: any,
mapDispatchToProps?: any,
mergeProps?: any,
options: unknown = {},
) {
return connect(
mapStateToProps,
mapDispatchToProps,
mergeProps,
options,
);
};
//// [circularlySimplifyingConditionalTypesNoCrash.js]
"use strict";
var myStoreConnect = function (mapStateToProps, mapDispatchToProps, mergeProps, options) {
if (options === void 0) { options = {}; }
return connect(mapStateToProps, mapDispatchToProps, mergeProps, options);
};
@@ -0,0 +1,154 @@
=== tests/cases/compiler/circularlySimplifyingConditionalTypesNoCrash.ts ===
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
>Omit : Symbol(Omit, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 0, 0))
>T : Symbol(T, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 0, 10))
>K : Symbol(K, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 0, 12))
>T : Symbol(T, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 0, 10))
>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 0, 10))
>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 0, 10))
>K : Symbol(K, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 0, 12))
type Shared< // Circularly self constraining type, defered thanks to mapping
>Shared : Symbol(Shared, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 0, 63))
InjectedProps,
>InjectedProps : Symbol(InjectedProps, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 2, 12))
DecorationTargetProps extends Shared<InjectedProps, DecorationTargetProps>
>DecorationTargetProps : Symbol(DecorationTargetProps, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 3, 18))
>Shared : Symbol(Shared, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 0, 63))
>InjectedProps : Symbol(InjectedProps, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 2, 12))
>DecorationTargetProps : Symbol(DecorationTargetProps, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 3, 18))
> = {
[P in Extract<keyof InjectedProps, keyof DecorationTargetProps>]: InjectedProps[P] extends DecorationTargetProps[P] ? DecorationTargetProps[P] : never;
>P : Symbol(P, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 6, 9))
>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --))
>InjectedProps : Symbol(InjectedProps, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 2, 12))
>DecorationTargetProps : Symbol(DecorationTargetProps, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 3, 18))
>InjectedProps : Symbol(InjectedProps, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 2, 12))
>P : Symbol(P, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 6, 9))
>DecorationTargetProps : Symbol(DecorationTargetProps, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 3, 18))
>P : Symbol(P, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 6, 9))
>DecorationTargetProps : Symbol(DecorationTargetProps, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 3, 18))
>P : Symbol(P, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 6, 9))
};
interface ComponentClass<P> {
>ComponentClass : Symbol(ComponentClass, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 7, 6))
>P : Symbol(P, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 9, 25))
defaultProps?: Partial<P>; // Inference target is also mapped _and_ optional
>defaultProps : Symbol(ComponentClass.defaultProps, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 9, 29))
>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --))
>P : Symbol(P, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 9, 25))
}
interface InferableComponentEnhancerWithProps<TInjectedProps, TNeedsProps> {
>InferableComponentEnhancerWithProps : Symbol(InferableComponentEnhancerWithProps, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 11, 1))
>TInjectedProps : Symbol(TInjectedProps, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 13, 46))
>TNeedsProps : Symbol(TNeedsProps, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 13, 61))
<P extends Shared<TInjectedProps, P>>(
>P : Symbol(P, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 14, 5))
>Shared : Symbol(Shared, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 0, 63))
>TInjectedProps : Symbol(TInjectedProps, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 13, 46))
>P : Symbol(P, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 14, 5))
component: ComponentClass<P>
>component : Symbol(component, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 14, 42))
>ComponentClass : Symbol(ComponentClass, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 7, 6))
>P : Symbol(P, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 14, 5))
): ComponentClass<Omit<P, keyof Shared<TInjectedProps, P>> & TNeedsProps> & { WrappedComponent: ComponentClass<P> }
>ComponentClass : Symbol(ComponentClass, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 7, 6))
>Omit : Symbol(Omit, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 0, 0))
>P : Symbol(P, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 14, 5))
>Shared : Symbol(Shared, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 0, 63))
>TInjectedProps : Symbol(TInjectedProps, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 13, 46))
>P : Symbol(P, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 14, 5))
>TNeedsProps : Symbol(TNeedsProps, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 13, 61))
>WrappedComponent : Symbol(WrappedComponent, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 16, 81))
>ComponentClass : Symbol(ComponentClass, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 7, 6))
>P : Symbol(P, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 14, 5))
} // Then intersected with and indexed via Omit and &
interface Connect { // Then strictly compared with another signature in its context
>Connect : Symbol(Connect, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 17, 1))
<TStateProps, TOwnProps>(
>TStateProps : Symbol(TStateProps, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 20, 5))
>TOwnProps : Symbol(TOwnProps, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 20, 17))
mapStateToProps: unknown,
>mapStateToProps : Symbol(mapStateToProps, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 20, 29))
): InferableComponentEnhancerWithProps<TStateProps, TOwnProps>;
>InferableComponentEnhancerWithProps : Symbol(InferableComponentEnhancerWithProps, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 11, 1))
>TStateProps : Symbol(TStateProps, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 20, 5))
>TOwnProps : Symbol(TOwnProps, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 20, 17))
<TDispatchProps, TOwnProps>(
>TDispatchProps : Symbol(TDispatchProps, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 24, 5))
>TOwnProps : Symbol(TOwnProps, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 24, 20))
mapStateToProps: null | undefined,
>mapStateToProps : Symbol(mapStateToProps, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 24, 32))
mapDispatchToProps: unknown,
>mapDispatchToProps : Symbol(mapDispatchToProps, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 25, 42))
mergeProps: null | undefined,
>mergeProps : Symbol(mergeProps, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 26, 36))
options: unknown
>options : Symbol(options, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 27, 37))
): InferableComponentEnhancerWithProps<TDispatchProps, TOwnProps>;
>InferableComponentEnhancerWithProps : Symbol(InferableComponentEnhancerWithProps, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 11, 1))
>TDispatchProps : Symbol(TDispatchProps, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 24, 5))
>TOwnProps : Symbol(TOwnProps, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 24, 20))
}
declare var connect: Connect;
>connect : Symbol(connect, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 32, 11))
>Connect : Symbol(Connect, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 17, 1))
const myStoreConnect: Connect = function(
>myStoreConnect : Symbol(myStoreConnect, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 34, 5))
>Connect : Symbol(Connect, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 17, 1))
mapStateToProps?: any,
>mapStateToProps : Symbol(mapStateToProps, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 34, 41))
mapDispatchToProps?: any,
>mapDispatchToProps : Symbol(mapDispatchToProps, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 35, 26))
mergeProps?: any,
>mergeProps : Symbol(mergeProps, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 36, 29))
options: unknown = {},
>options : Symbol(options, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 37, 21))
) {
return connect(
>connect : Symbol(connect, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 32, 11))
mapStateToProps,
>mapStateToProps : Symbol(mapStateToProps, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 34, 41))
mapDispatchToProps,
>mapDispatchToProps : Symbol(mapDispatchToProps, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 35, 26))
mergeProps,
>mergeProps : Symbol(mergeProps, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 36, 29))
options,
>options : Symbol(options, Decl(circularlySimplifyingConditionalTypesNoCrash.ts, 37, 21))
);
};
@@ -0,0 +1,92 @@
=== tests/cases/compiler/circularlySimplifyingConditionalTypesNoCrash.ts ===
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
>Omit : Pick<T, Exclude<keyof T, K>>
type Shared< // Circularly self constraining type, defered thanks to mapping
>Shared : Shared<InjectedProps, DecorationTargetProps>
InjectedProps,
DecorationTargetProps extends Shared<InjectedProps, DecorationTargetProps>
> = {
[P in Extract<keyof InjectedProps, keyof DecorationTargetProps>]: InjectedProps[P] extends DecorationTargetProps[P] ? DecorationTargetProps[P] : never;
};
interface ComponentClass<P> {
defaultProps?: Partial<P>; // Inference target is also mapped _and_ optional
>defaultProps : Partial<P> | undefined
}
interface InferableComponentEnhancerWithProps<TInjectedProps, TNeedsProps> {
<P extends Shared<TInjectedProps, P>>(
component: ComponentClass<P>
>component : ComponentClass<P>
): ComponentClass<Omit<P, keyof Shared<TInjectedProps, P>> & TNeedsProps> & { WrappedComponent: ComponentClass<P> }
>WrappedComponent : ComponentClass<P>
} // Then intersected with and indexed via Omit and &
interface Connect { // Then strictly compared with another signature in its context
<TStateProps, TOwnProps>(
mapStateToProps: unknown,
>mapStateToProps : unknown
): InferableComponentEnhancerWithProps<TStateProps, TOwnProps>;
<TDispatchProps, TOwnProps>(
mapStateToProps: null | undefined,
>mapStateToProps : null | undefined
>null : null
mapDispatchToProps: unknown,
>mapDispatchToProps : unknown
mergeProps: null | undefined,
>mergeProps : null | undefined
>null : null
options: unknown
>options : unknown
): InferableComponentEnhancerWithProps<TDispatchProps, TOwnProps>;
}
declare var connect: Connect;
>connect : Connect
const myStoreConnect: Connect = function(
>myStoreConnect : Connect
>function( mapStateToProps?: any, mapDispatchToProps?: any, mergeProps?: any, options: unknown = {},) { return connect( mapStateToProps, mapDispatchToProps, mergeProps, options, );} : (mapStateToProps?: any, mapDispatchToProps?: any, mergeProps?: any, options?: unknown) => InferableComponentEnhancerWithProps<{}, {}>
mapStateToProps?: any,
>mapStateToProps : any
mapDispatchToProps?: any,
>mapDispatchToProps : any
mergeProps?: any,
>mergeProps : any
options: unknown = {},
>options : unknown
>{} : {}
) {
return connect(
>connect( mapStateToProps, mapDispatchToProps, mergeProps, options, ) : InferableComponentEnhancerWithProps<{}, {}>
>connect : Connect
mapStateToProps,
>mapStateToProps : any
mapDispatchToProps,
>mapDispatchToProps : any
mergeProps,
>mergeProps : any
options,
>options : unknown
);
};
@@ -1,7 +1,7 @@
tests/cases/conformance/types/conditional/conditionalTypesExcessProperties.ts(8,5): error TS2322: Type '{ test: string; arg: A; }' is not assignable to type 'Something<A>'.
Type '{ test: string; arg: A; }' is not assignable to type 'A extends object ? { arg: A; } : { arg?: undefined; }'.
tests/cases/conformance/types/conditional/conditionalTypesExcessProperties.ts(9,33): error TS2322: Type 'A' is not assignable to type 'Something<A>["arr"]'.
Type 'object' is not assignable to type 'Something<A>["arr"]'.
tests/cases/conformance/types/conditional/conditionalTypesExcessProperties.ts(9,33): error TS2322: Type '{ test: string; arg: A; arr: A; }' is not assignable to type 'Something<A>'.
Object literal may only specify known properties, and 'arr' does not exist in type 'Something<A>'.
==== tests/cases/conformance/types/conditional/conditionalTypesExcessProperties.ts (2 errors) ====
@@ -17,8 +17,8 @@ tests/cases/conformance/types/conditional/conditionalTypesExcessProperties.ts(9,
!!! error TS2322: Type '{ test: string; arg: A; }' is not assignable to type 'Something<A>'.
!!! error TS2322: Type '{ test: string; arg: A; }' is not assignable to type 'A extends object ? { arg: A; } : { arg?: undefined; }'.
sa = { test: 'bye', arg: a, arr: a } // excess
~~~
!!! error TS2322: Type 'A' is not assignable to type 'Something<A>["arr"]'.
!!! error TS2322: Type 'object' is not assignable to type 'Something<A>["arr"]'.
~~~~~~
!!! error TS2322: Type '{ test: string; arg: A; arr: A; }' is not assignable to type 'Something<A>'.
!!! error TS2322: Object literal may only specify known properties, and 'arr' does not exist in type 'Something<A>'.
}
@@ -184,4 +184,24 @@ tests/cases/compiler/controlFlowSelfReferentialLoop.ts(17,29): error TS7006: Par
b=II(b,c,d,a,x[k+9], S44,0xEB86D391);
}
}
export default md5;
export default md5;
// Repro from #26655
interface DataShape {
message: { id: string }
}
function getObject(id: string | number) {
return {} as any
}
;(() => {
let id: string | number = 'a'
while (1) {
const data = getObject(id) as DataShape
const message = data.message
id = message.id
}
})()
@@ -98,7 +98,27 @@ function md5(string:string): void {
b=II(b,c,d,a,x[k+9], S44,0xEB86D391);
}
}
export default md5;
export default md5;
// Repro from #26655
interface DataShape {
message: { id: string }
}
function getObject(id: string | number) {
return {} as any
}
;(() => {
let id: string | number = 'a'
while (1) {
const data = getObject(id) as DataShape
const message = data.message
id = message.id
}
})()
//// [controlFlowSelfReferentialLoop.js]
"use strict";
@@ -204,3 +224,15 @@ function md5(string) {
}
}
exports["default"] = md5;
function getObject(id) {
return {};
}
;
(function () {
var id = 'a';
while (1) {
var data = getObject(id);
var message = data.message;
id = message.id;
}
})();
@@ -831,3 +831,45 @@ function md5(string:string): void {
export default md5;
>md5 : Symbol(md5, Decl(controlFlowSelfReferentialLoop.ts, 0, 0))
// Repro from #26655
interface DataShape {
>DataShape : Symbol(DataShape, Decl(controlFlowSelfReferentialLoop.ts, 99, 19))
message: { id: string }
>message : Symbol(DataShape.message, Decl(controlFlowSelfReferentialLoop.ts, 103, 21))
>id : Symbol(id, Decl(controlFlowSelfReferentialLoop.ts, 104, 12))
}
function getObject(id: string | number) {
>getObject : Symbol(getObject, Decl(controlFlowSelfReferentialLoop.ts, 105, 1))
>id : Symbol(id, Decl(controlFlowSelfReferentialLoop.ts, 107, 19))
return {} as any
}
;(() => {
let id: string | number = 'a'
>id : Symbol(id, Decl(controlFlowSelfReferentialLoop.ts, 112, 5))
while (1) {
const data = getObject(id) as DataShape
>data : Symbol(data, Decl(controlFlowSelfReferentialLoop.ts, 114, 9))
>getObject : Symbol(getObject, Decl(controlFlowSelfReferentialLoop.ts, 105, 1))
>id : Symbol(id, Decl(controlFlowSelfReferentialLoop.ts, 112, 5))
>DataShape : Symbol(DataShape, Decl(controlFlowSelfReferentialLoop.ts, 99, 19))
const message = data.message
>message : Symbol(message, Decl(controlFlowSelfReferentialLoop.ts, 115, 9))
>data.message : Symbol(DataShape.message, Decl(controlFlowSelfReferentialLoop.ts, 103, 21))
>data : Symbol(data, Decl(controlFlowSelfReferentialLoop.ts, 114, 9))
>message : Symbol(DataShape.message, Decl(controlFlowSelfReferentialLoop.ts, 103, 21))
id = message.id
>id : Symbol(id, Decl(controlFlowSelfReferentialLoop.ts, 112, 5))
>message.id : Symbol(id, Decl(controlFlowSelfReferentialLoop.ts, 104, 12))
>message : Symbol(message, Decl(controlFlowSelfReferentialLoop.ts, 115, 9))
>id : Symbol(id, Decl(controlFlowSelfReferentialLoop.ts, 104, 12))
}
})()
@@ -1260,3 +1260,54 @@ function md5(string:string): void {
export default md5;
>md5 : (string: string) => void
// Repro from #26655
interface DataShape {
message: { id: string }
>message : { id: string; }
>id : string
}
function getObject(id: string | number) {
>getObject : (id: string | number) => any
>id : string | number
return {} as any
>{} as any : any
>{} : {}
}
;(() => {
>(() => { let id: string | number = 'a' while (1) { const data = getObject(id) as DataShape const message = data.message id = message.id }})() : void
>(() => { let id: string | number = 'a' while (1) { const data = getObject(id) as DataShape const message = data.message id = message.id }}) : () => void
>() => { let id: string | number = 'a' while (1) { const data = getObject(id) as DataShape const message = data.message id = message.id }} : () => void
let id: string | number = 'a'
>id : string | number
>'a' : "a"
while (1) {
>1 : 1
const data = getObject(id) as DataShape
>data : DataShape
>getObject(id) as DataShape : DataShape
>getObject(id) : any
>getObject : (id: string | number) => any
>id : string
const message = data.message
>message : { id: string; }
>data.message : { id: string; }
>data : DataShape
>message : { id: string; }
id = message.id
>id = message.id : string
>id : string | number
>message.id : string
>message : { id: string; }
>id : string
}
})()
@@ -0,0 +1,158 @@
=== tests/cases/compiler/foo.js ===
// repro from #26031
function build() {
>build : Symbol(build, Decl(foo.js, 0, 0))
var arr = [];
>arr : Symbol(arr, Decl(foo.js, 2, 7))
arr[arr.length] = 'value';
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>arr.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
arr[arr.length] = 'value';
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>arr.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
arr[arr.length] = 'value';
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>arr.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
arr[arr.length] = 'value';
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>arr.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
arr[arr.length] = 'value';
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>arr.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
arr[arr.length] = 'value';
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>arr.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
arr[arr.length] = 'value';
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>arr.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
arr[arr.length] = 'value';
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>arr.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
arr[arr.length] = 'value';
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>arr.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
arr[arr.length] = 'value';
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>arr.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
arr[arr.length] = 'value';
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>arr.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
arr[arr.length] = 'value';
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>arr.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
arr[arr.length] = 'value';
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>arr.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
arr[arr.length] = 'value';
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>arr.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
arr[arr.length] = 'value';
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>arr.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
arr[arr.length] = 'value';
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>arr.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
arr[arr.length] = 'value';
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>arr.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
arr[arr.length] = 'value';
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>arr.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
arr[arr.length] = 'value';
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>arr.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
arr[arr.length] = 'value';
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>arr.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
arr[arr.length] = 'value';
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>arr.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
arr[arr.length] = 'value';
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>arr.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
arr[arr.length] = 'value';
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>arr.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
arr[arr.length] = 'value';
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>arr.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
arr[arr.length] = 'value';
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>arr.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
>arr : Symbol(arr, Decl(foo.js, 2, 7))
>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
}
@@ -0,0 +1,234 @@
=== tests/cases/compiler/foo.js ===
// repro from #26031
function build() {
>build : () => void
var arr = [];
>arr : any[]
>[] : undefined[]
arr[arr.length] = 'value';
>arr[arr.length] = 'value' : "value"
>arr[arr.length] : any
>arr : any[]
>arr.length : number
>arr : any[]
>length : number
>'value' : "value"
arr[arr.length] = 'value';
>arr[arr.length] = 'value' : "value"
>arr[arr.length] : any
>arr : any[]
>arr.length : number
>arr : any[]
>length : number
>'value' : "value"
arr[arr.length] = 'value';
>arr[arr.length] = 'value' : "value"
>arr[arr.length] : any
>arr : any[]
>arr.length : number
>arr : any[]
>length : number
>'value' : "value"
arr[arr.length] = 'value';
>arr[arr.length] = 'value' : "value"
>arr[arr.length] : any
>arr : any[]
>arr.length : number
>arr : any[]
>length : number
>'value' : "value"
arr[arr.length] = 'value';
>arr[arr.length] = 'value' : "value"
>arr[arr.length] : any
>arr : any[]
>arr.length : number
>arr : any[]
>length : number
>'value' : "value"
arr[arr.length] = 'value';
>arr[arr.length] = 'value' : "value"
>arr[arr.length] : any
>arr : any[]
>arr.length : number
>arr : any[]
>length : number
>'value' : "value"
arr[arr.length] = 'value';
>arr[arr.length] = 'value' : "value"
>arr[arr.length] : any
>arr : any[]
>arr.length : number
>arr : any[]
>length : number
>'value' : "value"
arr[arr.length] = 'value';
>arr[arr.length] = 'value' : "value"
>arr[arr.length] : any
>arr : any[]
>arr.length : number
>arr : any[]
>length : number
>'value' : "value"
arr[arr.length] = 'value';
>arr[arr.length] = 'value' : "value"
>arr[arr.length] : any
>arr : any[]
>arr.length : number
>arr : any[]
>length : number
>'value' : "value"
arr[arr.length] = 'value';
>arr[arr.length] = 'value' : "value"
>arr[arr.length] : any
>arr : any[]
>arr.length : number
>arr : any[]
>length : number
>'value' : "value"
arr[arr.length] = 'value';
>arr[arr.length] = 'value' : "value"
>arr[arr.length] : any
>arr : any[]
>arr.length : number
>arr : any[]
>length : number
>'value' : "value"
arr[arr.length] = 'value';
>arr[arr.length] = 'value' : "value"
>arr[arr.length] : any
>arr : any[]
>arr.length : number
>arr : any[]
>length : number
>'value' : "value"
arr[arr.length] = 'value';
>arr[arr.length] = 'value' : "value"
>arr[arr.length] : any
>arr : any[]
>arr.length : number
>arr : any[]
>length : number
>'value' : "value"
arr[arr.length] = 'value';
>arr[arr.length] = 'value' : "value"
>arr[arr.length] : any
>arr : any[]
>arr.length : number
>arr : any[]
>length : number
>'value' : "value"
arr[arr.length] = 'value';
>arr[arr.length] = 'value' : "value"
>arr[arr.length] : any
>arr : any[]
>arr.length : number
>arr : any[]
>length : number
>'value' : "value"
arr[arr.length] = 'value';
>arr[arr.length] = 'value' : "value"
>arr[arr.length] : any
>arr : any[]
>arr.length : number
>arr : any[]
>length : number
>'value' : "value"
arr[arr.length] = 'value';
>arr[arr.length] = 'value' : "value"
>arr[arr.length] : any
>arr : any[]
>arr.length : number
>arr : any[]
>length : number
>'value' : "value"
arr[arr.length] = 'value';
>arr[arr.length] = 'value' : "value"
>arr[arr.length] : any
>arr : any[]
>arr.length : number
>arr : any[]
>length : number
>'value' : "value"
arr[arr.length] = 'value';
>arr[arr.length] = 'value' : "value"
>arr[arr.length] : any
>arr : any[]
>arr.length : number
>arr : any[]
>length : number
>'value' : "value"
arr[arr.length] = 'value';
>arr[arr.length] = 'value' : "value"
>arr[arr.length] : any
>arr : any[]
>arr.length : number
>arr : any[]
>length : number
>'value' : "value"
arr[arr.length] = 'value';
>arr[arr.length] = 'value' : "value"
>arr[arr.length] : any
>arr : any[]
>arr.length : number
>arr : any[]
>length : number
>'value' : "value"
arr[arr.length] = 'value';
>arr[arr.length] = 'value' : "value"
>arr[arr.length] : any
>arr : any[]
>arr.length : number
>arr : any[]
>length : number
>'value' : "value"
arr[arr.length] = 'value';
>arr[arr.length] = 'value' : "value"
>arr[arr.length] : any
>arr : any[]
>arr.length : number
>arr : any[]
>length : number
>'value' : "value"
arr[arr.length] = 'value';
>arr[arr.length] = 'value' : "value"
>arr[arr.length] : any
>arr : any[]
>arr.length : number
>arr : any[]
>length : number
>'value' : "value"
arr[arr.length] = 'value';
>arr[arr.length] = 'value' : "value"
>arr[arr.length] : any
>arr : any[]
>arr.length : number
>arr : any[]
>length : number
>'value' : "value"
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -23,7 +23,7 @@ var o = {v:"Yo2"};
// WScript.Echo(o[0]);
1[0];
>1[0] : any
>1[0] : error
>1 : 1
>0 : 0
@@ -1,31 +0,0 @@
tests/cases/compiler/deferredLookupTypeResolution2.ts(14,13): error TS2536: Type '({ [K in Extract<keyof T, string>]: "true"; } & { [key: string]: "false"; })["1"]' cannot be used to index type '{ true: "true"; }'.
tests/cases/compiler/deferredLookupTypeResolution2.ts(19,21): error TS2536: Type '({ true: "otherwise"; } & { [k: string]: "true"; })[({ [K in Extract<keyof T, string>]: "true"; } & { [key: string]: "false"; })["1"]]' cannot be used to index type '{ true: "true"; }'.
==== tests/cases/compiler/deferredLookupTypeResolution2.ts (2 errors) ====
// Repro from #17456
type StringContains<S extends string, L extends string> = ({ [K in S]: 'true' } & { [key: string]: 'false'})[L];
type ObjectHasKey<O, L extends string> = StringContains<Extract<keyof O, string>, L>;
type A<T> = ObjectHasKey<T, '0'>;
type B = ObjectHasKey<[string, number], '1'>; // "true"
type C = ObjectHasKey<[string, number], '2'>; // "false"
type D = A<[string]>; // "true"
// Error, "false" not handled
type E<T> = { true: 'true' }[ObjectHasKey<T, '1'>];
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2536: Type '({ [K in Extract<keyof T, string>]: "true"; } & { [key: string]: "false"; })["1"]' cannot be used to index type '{ true: "true"; }'.
type Juxtapose<T> = ({ true: 'otherwise' } & { [k: string]: 'true' })[ObjectHasKey<T, '1'>];
// Error, "otherwise" is missing
type DeepError<T> = { true: 'true' }[Juxtapose<T>];
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2536: Type '({ true: "otherwise"; } & { [k: string]: "true"; })[({ [K in Extract<keyof T, string>]: "true"; } & { [key: string]: "false"; })["1"]]' cannot be used to index type '{ true: "true"; }'.
type DeepOK<T> = { true: 'true', otherwise: 'false' }[Juxtapose<T>];
@@ -1,8 +1,9 @@
tests/cases/compiler/destructureComputedProperty.ts(7,7): error TS2341: Property 'p' is private and only accessible within class 'C'.
tests/cases/compiler/destructureComputedProperty.ts(8,7): error TS2341: Property 'p' is private and only accessible within class 'C'.
tests/cases/compiler/destructureComputedProperty.ts(10,7): error TS2341: Property 'p' is private and only accessible within class 'C'.
==== tests/cases/compiler/destructureComputedProperty.ts (2 errors) ====
==== tests/cases/compiler/destructureComputedProperty.ts (3 errors) ====
declare const ab: { n: number } | { n: string };
const nameN = "n";
const { [nameN]: n } = ab;
@@ -13,6 +14,8 @@ tests/cases/compiler/destructureComputedProperty.ts(10,7): error TS2341: Propert
~~~~~~~~~~~
!!! error TS2341: Property 'p' is private and only accessible within class 'C'.
const { ["p"]: p1 } = new C();
~~~~~~~~~~~~~
!!! error TS2341: Property 'p' is private and only accessible within class 'C'.
const { [nameP]: p2 } = new C();
const { p: p3 } = new C();
~~~~~~~~~
+2 -2
View File
@@ -141,7 +141,7 @@ enum E7 {
A = 'foo'['foo']
>A : E7
>'foo'['foo'] : any
>'foo'['foo'] : error
>'foo' : "foo"
>'foo' : "foo"
}
@@ -152,7 +152,7 @@ enum E8 {
B = 'foo'['foo']
>B : E8
>'foo'['foo'] : any
>'foo'['foo'] : error
>'foo' : "foo"
>'foo' : "foo"
}
+1 -1
View File
@@ -31,7 +31,7 @@ var x = _arr.map(o => MyEnumType[o.key] === enumValue); // these are not same ty
>o => MyEnumType[o.key] === enumValue : (o: { key: string; }) => boolean
>o : { key: string; }
>MyEnumType[o.key] === enumValue : boolean
>MyEnumType[o.key] : any
>MyEnumType[o.key] : error
>MyEnumType : typeof MyEnumType
>o.key : string
>o : { key: string; }
@@ -0,0 +1,31 @@
//// [genericIndexedAccessMethodIntersectionCanBeAccessed.ts]
type ExtendedService<T> = {
[K in keyof T]: T[K] & {
__$daemonMode?: string;
__$action?: string;
};
};
type Service<T> = {
[K in keyof T]: T[K] & {id?: string};
};
export const createService = <T>(
ServiceCtr: ExtendedService<T> & Service<T>
) => {
Object.keys(ServiceCtr).forEach(key => {
const method = (ServiceCtr)[key as keyof T];
const {__$daemonMode, __$action, id} = method;
})
}
//// [genericIndexedAccessMethodIntersectionCanBeAccessed.js]
"use strict";
exports.__esModule = true;
exports.createService = function (ServiceCtr) {
Object.keys(ServiceCtr).forEach(function (key) {
var method = (ServiceCtr)[key];
var __$daemonMode = method.__$daemonMode, __$action = method.__$action, id = method.id;
});
};
@@ -0,0 +1,69 @@
=== tests/cases/compiler/genericIndexedAccessMethodIntersectionCanBeAccessed.ts ===
type ExtendedService<T> = {
>ExtendedService : Symbol(ExtendedService, Decl(genericIndexedAccessMethodIntersectionCanBeAccessed.ts, 0, 0))
>T : Symbol(T, Decl(genericIndexedAccessMethodIntersectionCanBeAccessed.ts, 0, 21))
[K in keyof T]: T[K] & {
>K : Symbol(K, Decl(genericIndexedAccessMethodIntersectionCanBeAccessed.ts, 1, 5))
>T : Symbol(T, Decl(genericIndexedAccessMethodIntersectionCanBeAccessed.ts, 0, 21))
>T : Symbol(T, Decl(genericIndexedAccessMethodIntersectionCanBeAccessed.ts, 0, 21))
>K : Symbol(K, Decl(genericIndexedAccessMethodIntersectionCanBeAccessed.ts, 1, 5))
__$daemonMode?: string;
>__$daemonMode : Symbol(__$daemonMode, Decl(genericIndexedAccessMethodIntersectionCanBeAccessed.ts, 1, 28))
__$action?: string;
>__$action : Symbol(__$action, Decl(genericIndexedAccessMethodIntersectionCanBeAccessed.ts, 2, 31))
};
};
type Service<T> = {
>Service : Symbol(Service, Decl(genericIndexedAccessMethodIntersectionCanBeAccessed.ts, 5, 2))
>T : Symbol(T, Decl(genericIndexedAccessMethodIntersectionCanBeAccessed.ts, 7, 13))
[K in keyof T]: T[K] & {id?: string};
>K : Symbol(K, Decl(genericIndexedAccessMethodIntersectionCanBeAccessed.ts, 8, 5))
>T : Symbol(T, Decl(genericIndexedAccessMethodIntersectionCanBeAccessed.ts, 7, 13))
>T : Symbol(T, Decl(genericIndexedAccessMethodIntersectionCanBeAccessed.ts, 7, 13))
>K : Symbol(K, Decl(genericIndexedAccessMethodIntersectionCanBeAccessed.ts, 8, 5))
>id : Symbol(id, Decl(genericIndexedAccessMethodIntersectionCanBeAccessed.ts, 8, 28))
};
export const createService = <T>(
>createService : Symbol(createService, Decl(genericIndexedAccessMethodIntersectionCanBeAccessed.ts, 11, 12))
>T : Symbol(T, Decl(genericIndexedAccessMethodIntersectionCanBeAccessed.ts, 11, 30))
ServiceCtr: ExtendedService<T> & Service<T>
>ServiceCtr : Symbol(ServiceCtr, Decl(genericIndexedAccessMethodIntersectionCanBeAccessed.ts, 11, 33))
>ExtendedService : Symbol(ExtendedService, Decl(genericIndexedAccessMethodIntersectionCanBeAccessed.ts, 0, 0))
>T : Symbol(T, Decl(genericIndexedAccessMethodIntersectionCanBeAccessed.ts, 11, 30))
>Service : Symbol(Service, Decl(genericIndexedAccessMethodIntersectionCanBeAccessed.ts, 5, 2))
>T : Symbol(T, Decl(genericIndexedAccessMethodIntersectionCanBeAccessed.ts, 11, 30))
) => {
Object.keys(ServiceCtr).forEach(key => {
>Object.keys(ServiceCtr).forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --))
>Object.keys : Symbol(ObjectConstructor.keys, Decl(lib.es5.d.ts, --, --))
>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>keys : Symbol(ObjectConstructor.keys, Decl(lib.es5.d.ts, --, --))
>ServiceCtr : Symbol(ServiceCtr, Decl(genericIndexedAccessMethodIntersectionCanBeAccessed.ts, 11, 33))
>forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --))
>key : Symbol(key, Decl(genericIndexedAccessMethodIntersectionCanBeAccessed.ts, 14, 36))
const method = (ServiceCtr)[key as keyof T];
>method : Symbol(method, Decl(genericIndexedAccessMethodIntersectionCanBeAccessed.ts, 15, 13))
>ServiceCtr : Symbol(ServiceCtr, Decl(genericIndexedAccessMethodIntersectionCanBeAccessed.ts, 11, 33))
>key : Symbol(key, Decl(genericIndexedAccessMethodIntersectionCanBeAccessed.ts, 14, 36))
>T : Symbol(T, Decl(genericIndexedAccessMethodIntersectionCanBeAccessed.ts, 11, 30))
const {__$daemonMode, __$action, id} = method;
>__$daemonMode : Symbol(__$daemonMode, Decl(genericIndexedAccessMethodIntersectionCanBeAccessed.ts, 16, 15))
>__$action : Symbol(__$action, Decl(genericIndexedAccessMethodIntersectionCanBeAccessed.ts, 16, 29))
>id : Symbol(id, Decl(genericIndexedAccessMethodIntersectionCanBeAccessed.ts, 16, 40))
>method : Symbol(method, Decl(genericIndexedAccessMethodIntersectionCanBeAccessed.ts, 15, 13))
})
}
@@ -0,0 +1,59 @@
=== tests/cases/compiler/genericIndexedAccessMethodIntersectionCanBeAccessed.ts ===
type ExtendedService<T> = {
>ExtendedService : ExtendedService<T>
[K in keyof T]: T[K] & {
__$daemonMode?: string;
>__$daemonMode : string | undefined
__$action?: string;
>__$action : string | undefined
};
};
type Service<T> = {
>Service : Service<T>
[K in keyof T]: T[K] & {id?: string};
>id : string | undefined
};
export const createService = <T>(
>createService : <T>(ServiceCtr: ExtendedService<T> & Service<T>) => void
><T>( ServiceCtr: ExtendedService<T> & Service<T>) => { Object.keys(ServiceCtr).forEach(key => { const method = (ServiceCtr)[key as keyof T]; const {__$daemonMode, __$action, id} = method; })} : <T>(ServiceCtr: ExtendedService<T> & Service<T>) => void
ServiceCtr: ExtendedService<T> & Service<T>
>ServiceCtr : ExtendedService<T> & Service<T>
) => {
Object.keys(ServiceCtr).forEach(key => {
>Object.keys(ServiceCtr).forEach(key => { const method = (ServiceCtr)[key as keyof T]; const {__$daemonMode, __$action, id} = method; }) : void
>Object.keys(ServiceCtr).forEach : (callbackfn: (value: string, index: number, array: string[]) => void, thisArg?: any) => void
>Object.keys(ServiceCtr) : string[]
>Object.keys : (o: {}) => string[]
>Object : ObjectConstructor
>keys : (o: {}) => string[]
>ServiceCtr : ExtendedService<T> & Service<T>
>forEach : (callbackfn: (value: string, index: number, array: string[]) => void, thisArg?: any) => void
>key => { const method = (ServiceCtr)[key as keyof T]; const {__$daemonMode, __$action, id} = method; } : (key: string) => void
>key : string
const method = (ServiceCtr)[key as keyof T];
>method : (ExtendedService<T> & Service<T>)[keyof T]
>(ServiceCtr)[key as keyof T] : (ExtendedService<T> & Service<T>)[keyof T]
>(ServiceCtr) : ExtendedService<T> & Service<T>
>ServiceCtr : ExtendedService<T> & Service<T>
>key as keyof T : keyof T
>key : string
const {__$daemonMode, __$action, id} = method;
>__$daemonMode : string | undefined
>__$action : string | undefined
>id : string | undefined
>method : (ExtendedService<T> & Service<T>)[keyof T]
})
}
@@ -11,7 +11,7 @@ var f = new foo();
f[0] = 4; // Shouldn't be allowed
>f[0] = 4 : 4
>f[0] : any
>f[0] : error
>f : foo
>0 : 0
>4 : 4
@@ -0,0 +1,37 @@
tests/cases/compiler/indexedAccessRelation.ts(16,23): error TS2345: Argument of type '{ a: T; }' is not assignable to parameter of type 'Pick<S & State<T>, "a">'.
Types of property 'a' are incompatible.
Type 'T' is not assignable to type 'S["a"] & T'.
Type 'Foo' is not assignable to type 'S["a"] & T'.
Type 'Foo' is not assignable to type 'S["a"]'.
Type 'T' is not assignable to type 'S["a"]'.
Type 'Foo' is not assignable to type 'S["a"]'.
==== tests/cases/compiler/indexedAccessRelation.ts (1 errors) ====
// Repro from #14723
class Component<S> {
setState<K extends keyof S>(state: Pick<S, K>) {}
}
export interface State<T> {
a?: T;
}
class Foo {}
class Comp<T extends Foo, S> extends Component<S & State<T>>
{
foo(a: T) {
this.setState({ a: a });
~~~~~~~~
!!! error TS2345: Argument of type '{ a: T; }' is not assignable to parameter of type 'Pick<S & State<T>, "a">'.
!!! error TS2345: Types of property 'a' are incompatible.
!!! error TS2345: Type 'T' is not assignable to type 'S["a"] & T'.
!!! error TS2345: Type 'Foo' is not assignable to type 'S["a"] & T'.
!!! error TS2345: Type 'Foo' is not assignable to type 'S["a"]'.
!!! error TS2345: Type 'T' is not assignable to type 'S["a"]'.
!!! error TS2345: Type 'Foo' is not assignable to type 'S["a"]'.
}
}
@@ -26,7 +26,7 @@ class Comp<T extends Foo, S> extends Component<S & State<T>>
>a : T
this.setState({ a: a });
>this.setState({ a: a }) : void
>this.setState({ a: a }) : any
>this.setState : <K extends keyof S | "a">(state: Pick<S & State<T>, K>) => void
>this : this
>setState : <K extends keyof S | "a">(state: Pick<S & State<T>, K>) => void
@@ -69,8 +69,8 @@ declare function genericFn3<
// Should be never
const result5 = genericFn3({ g: "gtest", h: "htest" }, "g", "h"); // 'g' & 'h' will reduce to never
>result5 : error
>genericFn3({ g: "gtest", h: "htest" }, "g", "h") : error
>result5 : unknown
>genericFn3({ g: "gtest", h: "htest" }, "g", "h") : unknown
>genericFn3 : <T extends { [K in keyof T]: T[K]; }, U extends keyof T, V extends keyof T>(obj: T, u: U, v: V) => T[U & V]
>{ g: "gtest", h: "htest" } : { g: string; h: string; }
>g : string
@@ -0,0 +1,53 @@
tests/cases/compiler/infiniteConstraints.ts(4,37): error TS2536: Type '"val"' cannot be used to index type 'B[Exclude<keyof B, K>]'.
tests/cases/compiler/infiniteConstraints.ts(31,42): error TS2345: Argument of type '{ main: Record<"val", "dup">; alternate: Record<"val", "dup">; }' is not assignable to parameter of type '{ main: never; alternate: never; }'.
Types of property 'main' are incompatible.
Type 'Record<"val", "dup">' is not assignable to type 'never'.
tests/cases/compiler/infiniteConstraints.ts(36,71): error TS2536: Type '"foo"' cannot be used to index type 'T[keyof T]'.
==== tests/cases/compiler/infiniteConstraints.ts (3 errors) ====
// Both of the following types trigger the recursion limiter in getImmediateBaseConstraint
type T1<B extends { [K in keyof B]: Extract<B[Exclude<keyof B, K>], { val: string }>["val"] }> = B;
type T2<B extends { [K in keyof B]: B[Exclude<keyof B, K>]["val"] }> = B;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2536: Type '"val"' cannot be used to index type 'B[Exclude<keyof B, K>]'.
// Repros from #22950
type AProp<T extends { a: string }> = T
declare function myBug<
T extends { [K in keyof T]: T[K] extends AProp<infer U> ? U : never }
>(arg: T): T
const out = myBug({obj1: {a: "test"}})
type Value<V extends string = string> = Record<"val", V>;
declare function value<V extends string>(val: V): Value<V>;
declare function ensureNoDuplicates<
T extends {
[K in keyof T]: Extract<T[K], Value>["val"] extends Extract<T[Exclude<keyof T, K>], Value>["val"]
? never
: any
}
>(vals: T): void;
const noError = ensureNoDuplicates({main: value("test"), alternate: value("test2")});
const shouldBeNoError = ensureNoDuplicates({main: value("test")});
const shouldBeError = ensureNoDuplicates({main: value("dup"), alternate: value("dup")});
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2345: Argument of type '{ main: Record<"val", "dup">; alternate: Record<"val", "dup">; }' is not assignable to parameter of type '{ main: never; alternate: never; }'.
!!! error TS2345: Types of property 'main' are incompatible.
!!! error TS2345: Type 'Record<"val", "dup">' is not assignable to type 'never'.
// Repro from #26448
type Cond<T> = T extends number ? number : never;
declare function function1<T extends {[K in keyof T]: Cond<T[K]>}>(): T[keyof T]["foo"];
~~~~~~~~~~~~~~~~~
!!! error TS2536: Type '"foo"' cannot be used to index type 'T[keyof T]'.
@@ -0,0 +1,46 @@
//// [infiniteConstraints.ts]
// Both of the following types trigger the recursion limiter in getImmediateBaseConstraint
type T1<B extends { [K in keyof B]: Extract<B[Exclude<keyof B, K>], { val: string }>["val"] }> = B;
type T2<B extends { [K in keyof B]: B[Exclude<keyof B, K>]["val"] }> = B;
// Repros from #22950
type AProp<T extends { a: string }> = T
declare function myBug<
T extends { [K in keyof T]: T[K] extends AProp<infer U> ? U : never }
>(arg: T): T
const out = myBug({obj1: {a: "test"}})
type Value<V extends string = string> = Record<"val", V>;
declare function value<V extends string>(val: V): Value<V>;
declare function ensureNoDuplicates<
T extends {
[K in keyof T]: Extract<T[K], Value>["val"] extends Extract<T[Exclude<keyof T, K>], Value>["val"]
? never
: any
}
>(vals: T): void;
const noError = ensureNoDuplicates({main: value("test"), alternate: value("test2")});
const shouldBeNoError = ensureNoDuplicates({main: value("test")});
const shouldBeError = ensureNoDuplicates({main: value("dup"), alternate: value("dup")});
// Repro from #26448
type Cond<T> = T extends number ? number : never;
declare function function1<T extends {[K in keyof T]: Cond<T[K]>}>(): T[keyof T]["foo"];
//// [infiniteConstraints.js]
"use strict";
// Both of the following types trigger the recursion limiter in getImmediateBaseConstraint
var out = myBug({ obj1: { a: "test" } });
var noError = ensureNoDuplicates({ main: value("test"), alternate: value("test2") });
var shouldBeNoError = ensureNoDuplicates({ main: value("test") });
var shouldBeError = ensureNoDuplicates({ main: value("dup"), alternate: value("dup") });
@@ -0,0 +1,140 @@
=== tests/cases/compiler/infiniteConstraints.ts ===
// Both of the following types trigger the recursion limiter in getImmediateBaseConstraint
type T1<B extends { [K in keyof B]: Extract<B[Exclude<keyof B, K>], { val: string }>["val"] }> = B;
>T1 : Symbol(T1, Decl(infiniteConstraints.ts, 0, 0))
>B : Symbol(B, Decl(infiniteConstraints.ts, 2, 8))
>K : Symbol(K, Decl(infiniteConstraints.ts, 2, 21))
>B : Symbol(B, Decl(infiniteConstraints.ts, 2, 8))
>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --))
>B : Symbol(B, Decl(infiniteConstraints.ts, 2, 8))
>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --))
>B : Symbol(B, Decl(infiniteConstraints.ts, 2, 8))
>K : Symbol(K, Decl(infiniteConstraints.ts, 2, 21))
>val : Symbol(val, Decl(infiniteConstraints.ts, 2, 69))
>B : Symbol(B, Decl(infiniteConstraints.ts, 2, 8))
type T2<B extends { [K in keyof B]: B[Exclude<keyof B, K>]["val"] }> = B;
>T2 : Symbol(T2, Decl(infiniteConstraints.ts, 2, 99))
>B : Symbol(B, Decl(infiniteConstraints.ts, 3, 8))
>K : Symbol(K, Decl(infiniteConstraints.ts, 3, 21))
>B : Symbol(B, Decl(infiniteConstraints.ts, 3, 8))
>B : Symbol(B, Decl(infiniteConstraints.ts, 3, 8))
>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --))
>B : Symbol(B, Decl(infiniteConstraints.ts, 3, 8))
>K : Symbol(K, Decl(infiniteConstraints.ts, 3, 21))
>B : Symbol(B, Decl(infiniteConstraints.ts, 3, 8))
// Repros from #22950
type AProp<T extends { a: string }> = T
>AProp : Symbol(AProp, Decl(infiniteConstraints.ts, 3, 73))
>T : Symbol(T, Decl(infiniteConstraints.ts, 7, 11))
>a : Symbol(a, Decl(infiniteConstraints.ts, 7, 22))
>T : Symbol(T, Decl(infiniteConstraints.ts, 7, 11))
declare function myBug<
>myBug : Symbol(myBug, Decl(infiniteConstraints.ts, 7, 39))
T extends { [K in keyof T]: T[K] extends AProp<infer U> ? U : never }
>T : Symbol(T, Decl(infiniteConstraints.ts, 9, 23))
>K : Symbol(K, Decl(infiniteConstraints.ts, 10, 15))
>T : Symbol(T, Decl(infiniteConstraints.ts, 9, 23))
>T : Symbol(T, Decl(infiniteConstraints.ts, 9, 23))
>K : Symbol(K, Decl(infiniteConstraints.ts, 10, 15))
>AProp : Symbol(AProp, Decl(infiniteConstraints.ts, 3, 73))
>U : Symbol(U, Decl(infiniteConstraints.ts, 10, 54))
>U : Symbol(U, Decl(infiniteConstraints.ts, 10, 54))
>(arg: T): T
>arg : Symbol(arg, Decl(infiniteConstraints.ts, 11, 2))
>T : Symbol(T, Decl(infiniteConstraints.ts, 9, 23))
>T : Symbol(T, Decl(infiniteConstraints.ts, 9, 23))
const out = myBug({obj1: {a: "test"}})
>out : Symbol(out, Decl(infiniteConstraints.ts, 13, 5))
>myBug : Symbol(myBug, Decl(infiniteConstraints.ts, 7, 39))
>obj1 : Symbol(obj1, Decl(infiniteConstraints.ts, 13, 19))
>a : Symbol(a, Decl(infiniteConstraints.ts, 13, 26))
type Value<V extends string = string> = Record<"val", V>;
>Value : Symbol(Value, Decl(infiniteConstraints.ts, 13, 38))
>V : Symbol(V, Decl(infiniteConstraints.ts, 15, 11))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>V : Symbol(V, Decl(infiniteConstraints.ts, 15, 11))
declare function value<V extends string>(val: V): Value<V>;
>value : Symbol(value, Decl(infiniteConstraints.ts, 15, 57))
>V : Symbol(V, Decl(infiniteConstraints.ts, 16, 23))
>val : Symbol(val, Decl(infiniteConstraints.ts, 16, 41))
>V : Symbol(V, Decl(infiniteConstraints.ts, 16, 23))
>Value : Symbol(Value, Decl(infiniteConstraints.ts, 13, 38))
>V : Symbol(V, Decl(infiniteConstraints.ts, 16, 23))
declare function ensureNoDuplicates<
>ensureNoDuplicates : Symbol(ensureNoDuplicates, Decl(infiniteConstraints.ts, 16, 59))
T extends {
>T : Symbol(T, Decl(infiniteConstraints.ts, 18, 36))
[K in keyof T]: Extract<T[K], Value>["val"] extends Extract<T[Exclude<keyof T, K>], Value>["val"]
>K : Symbol(K, Decl(infiniteConstraints.ts, 20, 5))
>T : Symbol(T, Decl(infiniteConstraints.ts, 18, 36))
>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(infiniteConstraints.ts, 18, 36))
>K : Symbol(K, Decl(infiniteConstraints.ts, 20, 5))
>Value : Symbol(Value, Decl(infiniteConstraints.ts, 13, 38))
>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(infiniteConstraints.ts, 18, 36))
>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(infiniteConstraints.ts, 18, 36))
>K : Symbol(K, Decl(infiniteConstraints.ts, 20, 5))
>Value : Symbol(Value, Decl(infiniteConstraints.ts, 13, 38))
? never
: any
}
>(vals: T): void;
>vals : Symbol(vals, Decl(infiniteConstraints.ts, 24, 2))
>T : Symbol(T, Decl(infiniteConstraints.ts, 18, 36))
const noError = ensureNoDuplicates({main: value("test"), alternate: value("test2")});
>noError : Symbol(noError, Decl(infiniteConstraints.ts, 26, 5))
>ensureNoDuplicates : Symbol(ensureNoDuplicates, Decl(infiniteConstraints.ts, 16, 59))
>main : Symbol(main, Decl(infiniteConstraints.ts, 26, 36))
>value : Symbol(value, Decl(infiniteConstraints.ts, 15, 57))
>alternate : Symbol(alternate, Decl(infiniteConstraints.ts, 26, 56))
>value : Symbol(value, Decl(infiniteConstraints.ts, 15, 57))
const shouldBeNoError = ensureNoDuplicates({main: value("test")});
>shouldBeNoError : Symbol(shouldBeNoError, Decl(infiniteConstraints.ts, 28, 5))
>ensureNoDuplicates : Symbol(ensureNoDuplicates, Decl(infiniteConstraints.ts, 16, 59))
>main : Symbol(main, Decl(infiniteConstraints.ts, 28, 44))
>value : Symbol(value, Decl(infiniteConstraints.ts, 15, 57))
const shouldBeError = ensureNoDuplicates({main: value("dup"), alternate: value("dup")});
>shouldBeError : Symbol(shouldBeError, Decl(infiniteConstraints.ts, 30, 5))
>ensureNoDuplicates : Symbol(ensureNoDuplicates, Decl(infiniteConstraints.ts, 16, 59))
>main : Symbol(main, Decl(infiniteConstraints.ts, 30, 42))
>value : Symbol(value, Decl(infiniteConstraints.ts, 15, 57))
>alternate : Symbol(alternate, Decl(infiniteConstraints.ts, 30, 61))
>value : Symbol(value, Decl(infiniteConstraints.ts, 15, 57))
// Repro from #26448
type Cond<T> = T extends number ? number : never;
>Cond : Symbol(Cond, Decl(infiniteConstraints.ts, 30, 88))
>T : Symbol(T, Decl(infiniteConstraints.ts, 34, 10))
>T : Symbol(T, Decl(infiniteConstraints.ts, 34, 10))
declare function function1<T extends {[K in keyof T]: Cond<T[K]>}>(): T[keyof T]["foo"];
>function1 : Symbol(function1, Decl(infiniteConstraints.ts, 34, 49))
>T : Symbol(T, Decl(infiniteConstraints.ts, 35, 27))
>K : Symbol(K, Decl(infiniteConstraints.ts, 35, 39))
>T : Symbol(T, Decl(infiniteConstraints.ts, 35, 27))
>Cond : Symbol(Cond, Decl(infiniteConstraints.ts, 30, 88))
>T : Symbol(T, Decl(infiniteConstraints.ts, 35, 27))
>K : Symbol(K, Decl(infiniteConstraints.ts, 35, 39))
>T : Symbol(T, Decl(infiniteConstraints.ts, 35, 27))
>T : Symbol(T, Decl(infiniteConstraints.ts, 35, 27))
@@ -0,0 +1,97 @@
=== tests/cases/compiler/infiniteConstraints.ts ===
// Both of the following types trigger the recursion limiter in getImmediateBaseConstraint
type T1<B extends { [K in keyof B]: Extract<B[Exclude<keyof B, K>], { val: string }>["val"] }> = B;
>T1 : B
>val : string
type T2<B extends { [K in keyof B]: B[Exclude<keyof B, K>]["val"] }> = B;
>T2 : B
// Repros from #22950
type AProp<T extends { a: string }> = T
>AProp : T
>a : string
declare function myBug<
>myBug : <T extends { [K in keyof T]: T[K]; }>(arg: T) => T
T extends { [K in keyof T]: T[K] extends AProp<infer U> ? U : never }
>(arg: T): T
>arg : T
const out = myBug({obj1: {a: "test"}})
>out : { obj1: { a: string; }; }
>myBug({obj1: {a: "test"}}) : { obj1: { a: string; }; }
>myBug : <T extends { [K in keyof T]: T[K]; }>(arg: T) => T
>{obj1: {a: "test"}} : { obj1: { a: string; }; }
>obj1 : { a: string; }
>{a: "test"} : { a: string; }
>a : string
>"test" : "test"
type Value<V extends string = string> = Record<"val", V>;
>Value : Record<"val", V>
declare function value<V extends string>(val: V): Value<V>;
>value : <V extends string>(val: V) => Record<"val", V>
>val : V
declare function ensureNoDuplicates<
>ensureNoDuplicates : <T extends { [K in keyof T]: Extract<T[K], Record<"val", string>>["val"] extends Extract<T[Exclude<keyof T, K>], Record<"val", string>>["val"] ? never : any; }>(vals: T) => void
T extends {
[K in keyof T]: Extract<T[K], Value>["val"] extends Extract<T[Exclude<keyof T, K>], Value>["val"]
? never
: any
}
>(vals: T): void;
>vals : T
const noError = ensureNoDuplicates({main: value("test"), alternate: value("test2")});
>noError : void
>ensureNoDuplicates({main: value("test"), alternate: value("test2")}) : void
>ensureNoDuplicates : <T extends { [K in keyof T]: Extract<T[K], Record<"val", string>>["val"] extends Extract<T[Exclude<keyof T, K>], Record<"val", string>>["val"] ? never : any; }>(vals: T) => void
>{main: value("test"), alternate: value("test2")} : { main: Record<"val", "test">; alternate: Record<"val", "test2">; }
>main : Record<"val", "test">
>value("test") : Record<"val", "test">
>value : <V extends string>(val: V) => Record<"val", V>
>"test" : "test"
>alternate : Record<"val", "test2">
>value("test2") : Record<"val", "test2">
>value : <V extends string>(val: V) => Record<"val", V>
>"test2" : "test2"
const shouldBeNoError = ensureNoDuplicates({main: value("test")});
>shouldBeNoError : void
>ensureNoDuplicates({main: value("test")}) : void
>ensureNoDuplicates : <T extends { [K in keyof T]: Extract<T[K], Record<"val", string>>["val"] extends Extract<T[Exclude<keyof T, K>], Record<"val", string>>["val"] ? never : any; }>(vals: T) => void
>{main: value("test")} : { main: Record<"val", "test">; }
>main : Record<"val", "test">
>value("test") : Record<"val", "test">
>value : <V extends string>(val: V) => Record<"val", V>
>"test" : "test"
const shouldBeError = ensureNoDuplicates({main: value("dup"), alternate: value("dup")});
>shouldBeError : any
>ensureNoDuplicates({main: value("dup"), alternate: value("dup")}) : any
>ensureNoDuplicates : <T extends { [K in keyof T]: Extract<T[K], Record<"val", string>>["val"] extends Extract<T[Exclude<keyof T, K>], Record<"val", string>>["val"] ? never : any; }>(vals: T) => void
>{main: value("dup"), alternate: value("dup")} : { main: Record<"val", "dup">; alternate: Record<"val", "dup">; }
>main : Record<"val", "dup">
>value("dup") : Record<"val", "dup">
>value : <V extends string>(val: V) => Record<"val", V>
>"dup" : "dup"
>alternate : Record<"val", "dup">
>value("dup") : Record<"val", "dup">
>value : <V extends string>(val: V) => Record<"val", V>
>"dup" : "dup"
// Repro from #26448
type Cond<T> = T extends number ? number : never;
>Cond : Cond<T>
declare function function1<T extends {[K in keyof T]: Cond<T[K]>}>(): T[keyof T]["foo"];
>function1 : <T extends { [K in keyof T]: Cond<T[K]>; }>() => T[keyof T]["foo"]
@@ -0,0 +1,30 @@
tests/cases/conformance/types/intersection/intersectionAsWeakTypeSource.ts(8,7): error TS2559: Type 'XY' has no properties in common with type 'Z'.
tests/cases/conformance/types/intersection/intersectionAsWeakTypeSource.ts(18,7): error TS2322: Type 'Brand<{ view: number; styleMedia: string; }>' is not assignable to type 'ViewStyle'.
Property 'view' is missing in type 'Number & { __brand: { view: number; styleMedia: string; }; }'.
==== tests/cases/conformance/types/intersection/intersectionAsWeakTypeSource.ts (2 errors) ====
interface X { x: string }
interface Y { y: number }
interface Z { z?: boolean }
type XY = X & Y;
const xy: XY = {x: 'x', y: 10};
const z1: Z = xy; // error, {xy} doesn't overlap with {z}
~~
!!! error TS2559: Type 'XY' has no properties in common with type 'Z'.
interface ViewStyle {
view: number
styleMedia: string
}
type Brand<T> = number & { __brand: T }
declare function create<T extends { [s: string]: ViewStyle }>(styles: T): { [P in keyof T]: Brand<T[P]> };
const wrapped = create({ first: { view: 0, styleMedia: "???" } });
const vs: ViewStyle = wrapped.first // error, first is a branded number
~~
!!! error TS2322: Type 'Brand<{ view: number; styleMedia: string; }>' is not assignable to type 'ViewStyle'.
!!! error TS2322: Property 'view' is missing in type 'Number & { __brand: { view: number; styleMedia: string; }; }'.
@@ -0,0 +1,26 @@
//// [intersectionAsWeakTypeSource.ts]
interface X { x: string }
interface Y { y: number }
interface Z { z?: boolean }
type XY = X & Y;
const xy: XY = {x: 'x', y: 10};
const z1: Z = xy; // error, {xy} doesn't overlap with {z}
interface ViewStyle {
view: number
styleMedia: string
}
type Brand<T> = number & { __brand: T }
declare function create<T extends { [s: string]: ViewStyle }>(styles: T): { [P in keyof T]: Brand<T[P]> };
const wrapped = create({ first: { view: 0, styleMedia: "???" } });
const vs: ViewStyle = wrapped.first // error, first is a branded number
//// [intersectionAsWeakTypeSource.js]
var xy = { x: 'x', y: 10 };
var z1 = xy; // error, {xy} doesn't overlap with {z}
var wrapped = create({ first: { view: 0, styleMedia: "???" } });
var vs = wrapped.first; // error, first is a branded number
@@ -0,0 +1,72 @@
=== tests/cases/conformance/types/intersection/intersectionAsWeakTypeSource.ts ===
interface X { x: string }
>X : Symbol(X, Decl(intersectionAsWeakTypeSource.ts, 0, 0))
>x : Symbol(X.x, Decl(intersectionAsWeakTypeSource.ts, 0, 13))
interface Y { y: number }
>Y : Symbol(Y, Decl(intersectionAsWeakTypeSource.ts, 0, 25))
>y : Symbol(Y.y, Decl(intersectionAsWeakTypeSource.ts, 1, 13))
interface Z { z?: boolean }
>Z : Symbol(Z, Decl(intersectionAsWeakTypeSource.ts, 1, 25))
>z : Symbol(Z.z, Decl(intersectionAsWeakTypeSource.ts, 2, 13))
type XY = X & Y;
>XY : Symbol(XY, Decl(intersectionAsWeakTypeSource.ts, 2, 27))
>X : Symbol(X, Decl(intersectionAsWeakTypeSource.ts, 0, 0))
>Y : Symbol(Y, Decl(intersectionAsWeakTypeSource.ts, 0, 25))
const xy: XY = {x: 'x', y: 10};
>xy : Symbol(xy, Decl(intersectionAsWeakTypeSource.ts, 5, 5))
>XY : Symbol(XY, Decl(intersectionAsWeakTypeSource.ts, 2, 27))
>x : Symbol(x, Decl(intersectionAsWeakTypeSource.ts, 5, 16))
>y : Symbol(y, Decl(intersectionAsWeakTypeSource.ts, 5, 23))
const z1: Z = xy; // error, {xy} doesn't overlap with {z}
>z1 : Symbol(z1, Decl(intersectionAsWeakTypeSource.ts, 7, 5))
>Z : Symbol(Z, Decl(intersectionAsWeakTypeSource.ts, 1, 25))
>xy : Symbol(xy, Decl(intersectionAsWeakTypeSource.ts, 5, 5))
interface ViewStyle {
>ViewStyle : Symbol(ViewStyle, Decl(intersectionAsWeakTypeSource.ts, 7, 17))
view: number
>view : Symbol(ViewStyle.view, Decl(intersectionAsWeakTypeSource.ts, 10, 21))
styleMedia: string
>styleMedia : Symbol(ViewStyle.styleMedia, Decl(intersectionAsWeakTypeSource.ts, 11, 16))
}
type Brand<T> = number & { __brand: T }
>Brand : Symbol(Brand, Decl(intersectionAsWeakTypeSource.ts, 13, 1))
>T : Symbol(T, Decl(intersectionAsWeakTypeSource.ts, 14, 11))
>__brand : Symbol(__brand, Decl(intersectionAsWeakTypeSource.ts, 14, 26))
>T : Symbol(T, Decl(intersectionAsWeakTypeSource.ts, 14, 11))
declare function create<T extends { [s: string]: ViewStyle }>(styles: T): { [P in keyof T]: Brand<T[P]> };
>create : Symbol(create, Decl(intersectionAsWeakTypeSource.ts, 14, 39))
>T : Symbol(T, Decl(intersectionAsWeakTypeSource.ts, 15, 24))
>s : Symbol(s, Decl(intersectionAsWeakTypeSource.ts, 15, 37))
>ViewStyle : Symbol(ViewStyle, Decl(intersectionAsWeakTypeSource.ts, 7, 17))
>styles : Symbol(styles, Decl(intersectionAsWeakTypeSource.ts, 15, 62))
>T : Symbol(T, Decl(intersectionAsWeakTypeSource.ts, 15, 24))
>P : Symbol(P, Decl(intersectionAsWeakTypeSource.ts, 15, 77))
>T : Symbol(T, Decl(intersectionAsWeakTypeSource.ts, 15, 24))
>Brand : Symbol(Brand, Decl(intersectionAsWeakTypeSource.ts, 13, 1))
>T : Symbol(T, Decl(intersectionAsWeakTypeSource.ts, 15, 24))
>P : Symbol(P, Decl(intersectionAsWeakTypeSource.ts, 15, 77))
const wrapped = create({ first: { view: 0, styleMedia: "???" } });
>wrapped : Symbol(wrapped, Decl(intersectionAsWeakTypeSource.ts, 16, 5))
>create : Symbol(create, Decl(intersectionAsWeakTypeSource.ts, 14, 39))
>first : Symbol(first, Decl(intersectionAsWeakTypeSource.ts, 16, 24))
>view : Symbol(view, Decl(intersectionAsWeakTypeSource.ts, 16, 33))
>styleMedia : Symbol(styleMedia, Decl(intersectionAsWeakTypeSource.ts, 16, 42))
const vs: ViewStyle = wrapped.first // error, first is a branded number
>vs : Symbol(vs, Decl(intersectionAsWeakTypeSource.ts, 17, 5))
>ViewStyle : Symbol(ViewStyle, Decl(intersectionAsWeakTypeSource.ts, 7, 17))
>wrapped.first : Symbol(first, Decl(intersectionAsWeakTypeSource.ts, 16, 24))
>wrapped : Symbol(wrapped, Decl(intersectionAsWeakTypeSource.ts, 16, 5))
>first : Symbol(first, Decl(intersectionAsWeakTypeSource.ts, 16, 24))
@@ -0,0 +1,60 @@
=== tests/cases/conformance/types/intersection/intersectionAsWeakTypeSource.ts ===
interface X { x: string }
>x : string
interface Y { y: number }
>y : number
interface Z { z?: boolean }
>z : boolean
type XY = X & Y;
>XY : XY
const xy: XY = {x: 'x', y: 10};
>xy : XY
>{x: 'x', y: 10} : { x: string; y: number; }
>x : string
>'x' : "x"
>y : number
>10 : 10
const z1: Z = xy; // error, {xy} doesn't overlap with {z}
>z1 : Z
>xy : XY
interface ViewStyle {
view: number
>view : number
styleMedia: string
>styleMedia : string
}
type Brand<T> = number & { __brand: T }
>Brand : Brand<T>
>__brand : T
declare function create<T extends { [s: string]: ViewStyle }>(styles: T): { [P in keyof T]: Brand<T[P]> };
>create : <T extends { [s: string]: ViewStyle; }>(styles: T) => { [P in keyof T]: Brand<T[P]>; }
>s : string
>styles : T
const wrapped = create({ first: { view: 0, styleMedia: "???" } });
>wrapped : { first: Brand<{ view: number; styleMedia: string; }>; }
>create({ first: { view: 0, styleMedia: "???" } }) : { first: Brand<{ view: number; styleMedia: string; }>; }
>create : <T extends { [s: string]: ViewStyle; }>(styles: T) => { [P in keyof T]: Brand<T[P]>; }
>{ first: { view: 0, styleMedia: "???" } } : { first: { view: number; styleMedia: string; }; }
>first : { view: number; styleMedia: string; }
>{ view: 0, styleMedia: "???" } : { view: number; styleMedia: string; }
>view : number
>0 : 0
>styleMedia : string
>"???" : "???"
const vs: ViewStyle = wrapped.first // error, first is a branded number
>vs : ViewStyle
>wrapped.first : Brand<{ view: number; styleMedia: string; }>
>wrapped : { first: Brand<{ view: number; styleMedia: string; }>; }
>first : Brand<{ view: number; styleMedia: string; }>
@@ -649,6 +649,20 @@ function ff2<V extends string, T extends string>(dd: DictDict<V, T>, k1: V, k2:
const d: Dict<T> = dd[k1];
return d[k2];
}
// Repro from #26409
const cf1 = <T extends { [P in K]: string; } & { cool: string; }, K extends keyof T>(t: T, k: K) =>
{
const s: string = t[k];
t.cool;
};
const cf2 = <T extends { [P in K | "cool"]: string; }, K extends keyof T>(t: T, k: K) =>
{
const s: string = t[k];
t.cool;
};
//// [keyofAndIndexedAccess.js]
@@ -1078,6 +1092,15 @@ function ff2(dd, k1, k2) {
var d = dd[k1];
return d[k2];
}
// Repro from #26409
var cf1 = function (t, k) {
var s = t[k];
t.cool;
};
var cf2 = function (t, k) {
var s = t[k];
t.cool;
};
//// [keyofAndIndexedAccess.d.ts]
@@ -1413,3 +1436,7 @@ declare type DictDict<V extends string, T extends string> = {
};
declare function ff1<V extends string, T extends string>(dd: DictDict<V, T>, k1: V, k2: T): number;
declare function ff2<V extends string, T extends string>(dd: DictDict<V, T>, k1: V, k2: T): number;
declare const cf1: <T extends { [P in K]: string; } & {
cool: string;
}, K extends keyof T>(t: T, k: K) => void;
declare const cf2: <T extends { [P in K | "cool"]: string; }, K extends keyof T>(t: T, k: K) => void;
@@ -2319,3 +2319,54 @@ function ff2<V extends string, T extends string>(dd: DictDict<V, T>, k1: V, k2:
>k2 : Symbol(k2, Decl(keyofAndIndexedAccess.ts, 646, 75))
}
// Repro from #26409
const cf1 = <T extends { [P in K]: string; } & { cool: string; }, K extends keyof T>(t: T, k: K) =>
>cf1 : Symbol(cf1, Decl(keyofAndIndexedAccess.ts, 653, 5))
>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 653, 13))
>P : Symbol(P, Decl(keyofAndIndexedAccess.ts, 653, 26))
>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 653, 65))
>cool : Symbol(cool, Decl(keyofAndIndexedAccess.ts, 653, 48))
>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 653, 65))
>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 653, 13))
>t : Symbol(t, Decl(keyofAndIndexedAccess.ts, 653, 85))
>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 653, 13))
>k : Symbol(k, Decl(keyofAndIndexedAccess.ts, 653, 90))
>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 653, 65))
{
const s: string = t[k];
>s : Symbol(s, Decl(keyofAndIndexedAccess.ts, 655, 9))
>t : Symbol(t, Decl(keyofAndIndexedAccess.ts, 653, 85))
>k : Symbol(k, Decl(keyofAndIndexedAccess.ts, 653, 90))
t.cool;
>t.cool : Symbol(cool, Decl(keyofAndIndexedAccess.ts, 653, 48))
>t : Symbol(t, Decl(keyofAndIndexedAccess.ts, 653, 85))
>cool : Symbol(cool, Decl(keyofAndIndexedAccess.ts, 653, 48))
};
const cf2 = <T extends { [P in K | "cool"]: string; }, K extends keyof T>(t: T, k: K) =>
>cf2 : Symbol(cf2, Decl(keyofAndIndexedAccess.ts, 659, 5))
>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 659, 13))
>P : Symbol(P, Decl(keyofAndIndexedAccess.ts, 659, 26))
>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 659, 54))
>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 659, 54))
>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 659, 13))
>t : Symbol(t, Decl(keyofAndIndexedAccess.ts, 659, 74))
>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 659, 13))
>k : Symbol(k, Decl(keyofAndIndexedAccess.ts, 659, 79))
>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 659, 54))
{
const s: string = t[k];
>s : Symbol(s, Decl(keyofAndIndexedAccess.ts, 661, 9))
>t : Symbol(t, Decl(keyofAndIndexedAccess.ts, 659, 74))
>k : Symbol(k, Decl(keyofAndIndexedAccess.ts, 659, 79))
t.cool;
>t.cool : Symbol(cool)
>t : Symbol(t, Decl(keyofAndIndexedAccess.ts, 659, 74))
>cool : Symbol(cool)
};
@@ -2184,3 +2184,44 @@ function ff2<V extends string, T extends string>(dd: DictDict<V, T>, k1: V, k2:
>k2 : T
}
// Repro from #26409
const cf1 = <T extends { [P in K]: string; } & { cool: string; }, K extends keyof T>(t: T, k: K) =>
>cf1 : <T extends { [P in K]: string; } & { cool: string; }, K extends keyof T>(t: T, k: K) => void
><T extends { [P in K]: string; } & { cool: string; }, K extends keyof T>(t: T, k: K) =>{ const s: string = t[k]; t.cool;} : <T extends { [P in K]: string; } & { cool: string; }, K extends keyof T>(t: T, k: K) => void
>cool : string
>t : T
>k : K
{
const s: string = t[k];
>s : string
>t[k] : T[K]
>t : T
>k : K
t.cool;
>t.cool : string
>t : T
>cool : string
};
const cf2 = <T extends { [P in K | "cool"]: string; }, K extends keyof T>(t: T, k: K) =>
>cf2 : <T extends { [P in K | "cool"]: string; }, K extends keyof T>(t: T, k: K) => void
><T extends { [P in K | "cool"]: string; }, K extends keyof T>(t: T, k: K) =>{ const s: string = t[k]; t.cool;} : <T extends { [P in K | "cool"]: string; }, K extends keyof T>(t: T, k: K) => void
>t : T
>k : K
{
const s: string = t[k];
>s : string
>t[k] : T[K]
>t : T
>k : K
t.cool;
>t.cool : string
>t : T
>cool : string
};
@@ -1,7 +1,8 @@
tests/cases/compiler/limitDeepInstantiations.ts(3,35): error TS2502: '"true"' is referenced directly or indirectly in its own type annotation.
tests/cases/compiler/limitDeepInstantiations.ts(5,13): error TS2344: Type '"false"' does not satisfy the constraint '"true"'.
==== tests/cases/compiler/limitDeepInstantiations.ts (1 errors) ====
==== tests/cases/compiler/limitDeepInstantiations.ts (2 errors) ====
// Repro from #14837
type Foo<T extends "true", B> = { "true": Foo<T, Foo<T, B>> }[T];
@@ -9,4 +10,6 @@ tests/cases/compiler/limitDeepInstantiations.ts(3,35): error TS2502: '"true"' is
!!! error TS2502: '"true"' is referenced directly or indirectly in its own type annotation.
let f1: Foo<"true", {}>;
let f2: Foo<"false", {}>;
~~~~~~~
!!! error TS2344: Type '"false"' does not satisfy the constraint '"true"'.
@@ -9,5 +9,5 @@ let f1: Foo<"true", {}>;
>f1 : any
let f2: Foo<"false", {}>;
>f2 : any
>f2 : unknown
@@ -1,4 +1,4 @@
=== tests/cases/compiler/mappedTypeNoTypeNoCrash.ts ===
type T0<T> = ({[K in keyof T]}) extends ({[key in K]: T[K]}) ? number : never;
>T0 : number
>T0 : T0<T>
@@ -83,6 +83,14 @@ declare function mapArray<T extends any[]>(arr: T): Mapped<T>;
function acceptMappedArray<T extends any[]>(arr: T) {
acceptArray(mapArray(arr));
}
// Repro from #26163
type Unconstrained<T> = ElementType<Mapped<T>>;
type T1 = Unconstrained<[string, number, boolean]>; // string | number | boolean
type Constrained<T extends any[]> = ElementType<Mapped<T>>;
type T2 = Constrained<[string, number, boolean]>; // string | number | boolean
//// [mappedTypesArraysTuples.js]
@@ -182,3 +190,7 @@ declare type R2 = ElementType<Mapped<[string, number, boolean]>>;
declare function acceptArray(arr: any[]): void;
declare function mapArray<T extends any[]>(arr: T): Mapped<T>;
declare function acceptMappedArray<T extends any[]>(arr: T): void;
declare type Unconstrained<T> = ElementType<Mapped<T>>;
declare type T1 = Unconstrained<[string, number, boolean]>;
declare type Constrained<T extends any[]> = ElementType<Mapped<T>>;
declare type T2 = Constrained<[string, number, boolean]>;
@@ -328,3 +328,27 @@ function acceptMappedArray<T extends any[]>(arr: T) {
>arr : Symbol(arr, Decl(mappedTypesArraysTuples.ts, 81, 44))
}
// Repro from #26163
type Unconstrained<T> = ElementType<Mapped<T>>;
>Unconstrained : Symbol(Unconstrained, Decl(mappedTypesArraysTuples.ts, 83, 1))
>T : Symbol(T, Decl(mappedTypesArraysTuples.ts, 87, 19))
>ElementType : Symbol(ElementType, Decl(mappedTypesArraysTuples.ts, 66, 1))
>Mapped : Symbol(Mapped, Decl(mappedTypesArraysTuples.ts, 70, 59))
>T : Symbol(T, Decl(mappedTypesArraysTuples.ts, 87, 19))
type T1 = Unconstrained<[string, number, boolean]>; // string | number | boolean
>T1 : Symbol(T1, Decl(mappedTypesArraysTuples.ts, 87, 47))
>Unconstrained : Symbol(Unconstrained, Decl(mappedTypesArraysTuples.ts, 83, 1))
type Constrained<T extends any[]> = ElementType<Mapped<T>>;
>Constrained : Symbol(Constrained, Decl(mappedTypesArraysTuples.ts, 88, 51))
>T : Symbol(T, Decl(mappedTypesArraysTuples.ts, 90, 17))
>ElementType : Symbol(ElementType, Decl(mappedTypesArraysTuples.ts, 66, 1))
>Mapped : Symbol(Mapped, Decl(mappedTypesArraysTuples.ts, 70, 59))
>T : Symbol(T, Decl(mappedTypesArraysTuples.ts, 90, 17))
type T2 = Constrained<[string, number, boolean]>; // string | number | boolean
>T2 : Symbol(T2, Decl(mappedTypesArraysTuples.ts, 90, 59))
>Constrained : Symbol(Constrained, Decl(mappedTypesArraysTuples.ts, 88, 51))
@@ -241,3 +241,17 @@ function acceptMappedArray<T extends any[]>(arr: T) {
>arr : T
}
// Repro from #26163
type Unconstrained<T> = ElementType<Mapped<T>>;
>Unconstrained : ElementType<Mapped<T>>
type T1 = Unconstrained<[string, number, boolean]>; // string | number | boolean
>T1 : string | number | boolean
type Constrained<T extends any[]> = ElementType<Mapped<T>>;
>Constrained : ElementType<Mapped<T>>
type T2 = Constrained<[string, number, boolean]>; // string | number | boolean
>T2 : string | number | boolean
@@ -28,7 +28,7 @@ let a = ['c', 'd'];
a[Symbol.isConcatSpreadable] = false;
>a[Symbol.isConcatSpreadable] = false : false
>a[Symbol.isConcatSpreadable] : any
>a[Symbol.isConcatSpreadable] : error
>a : string[]
>Symbol.isConcatSpreadable : symbol
>Symbol : SymbolConstructor
@@ -445,9 +445,9 @@ new h["a-b"]["a-b"](1, 2, ...a, "string");
// Element access expression with a number
new i["a-b"][1](1, 2, "string");
>new i["a-b"][1](1, 2, "string") : any
>i["a-b"][1] : any
>i["a-b"] : any
>new i["a-b"][1](1, 2, "string") : error
>i["a-b"][1] : error
>i["a-b"] : error
>i : C[][]
>"a-b" : "a-b"
>1 : 1
@@ -456,9 +456,9 @@ new i["a-b"][1](1, 2, "string");
>"string" : "string"
new i["a-b"][1](1, 2, ...a);
>new i["a-b"][1](1, 2, ...a) : any
>i["a-b"][1] : any
>i["a-b"] : any
>new i["a-b"][1](1, 2, ...a) : error
>i["a-b"][1] : error
>i["a-b"] : error
>i : C[][]
>"a-b" : "a-b"
>1 : 1
@@ -468,9 +468,9 @@ new i["a-b"][1](1, 2, ...a);
>a : string[]
new i["a-b"][1](1, 2, ...a, "string");
>new i["a-b"][1](1, 2, ...a, "string") : any
>i["a-b"][1] : any
>i["a-b"] : any
>new i["a-b"][1](1, 2, ...a, "string") : error
>i["a-b"][1] : error
>i["a-b"] : error
>i : C[][]
>"a-b" : "a-b"
>1 : 1
@@ -446,9 +446,9 @@ new h["a-b"]["a-b"](1, 2, ...a, "string");
// Element access expression with a number
new i["a-b"][1](1, 2, "string");
>new i["a-b"][1](1, 2, "string") : any
>i["a-b"][1] : any
>i["a-b"] : any
>new i["a-b"][1](1, 2, "string") : error
>i["a-b"][1] : error
>i["a-b"] : error
>i : C[][]
>"a-b" : "a-b"
>1 : 1
@@ -457,9 +457,9 @@ new i["a-b"][1](1, 2, "string");
>"string" : "string"
new i["a-b"][1](1, 2, ...a);
>new i["a-b"][1](1, 2, ...a) : any
>i["a-b"][1] : any
>i["a-b"] : any
>new i["a-b"][1](1, 2, ...a) : error
>i["a-b"][1] : error
>i["a-b"] : error
>i : C[][]
>"a-b" : "a-b"
>1 : 1
@@ -469,9 +469,9 @@ new i["a-b"][1](1, 2, ...a);
>a : string[]
new i["a-b"][1](1, 2, ...a, "string");
>new i["a-b"][1](1, 2, ...a, "string") : any
>i["a-b"][1] : any
>i["a-b"] : any
>new i["a-b"][1](1, 2, ...a, "string") : error
>i["a-b"][1] : error
>i["a-b"] : error
>i : C[][]
>"a-b" : "a-b"
>1 : 1
@@ -0,0 +1,15 @@
tests/cases/conformance/jsdoc/bug26693.js(1,15): error TS2304: Cannot find name 'module'.
tests/cases/conformance/jsdoc/bug26693.js(1,21): error TS1005: '}' expected.
tests/cases/conformance/jsdoc/bug26693.js(2,22): error TS2307: Cannot find module 'nope'.
==== tests/cases/conformance/jsdoc/bug26693.js (3 errors) ====
/** @typedef {module:locale} hi */
~~~~~~
!!! error TS2304: Cannot find name 'module'.
~
!!! error TS1005: '}' expected.
import { nope } from 'nope';
~~~~~~
!!! error TS2307: Cannot find module 'nope'.
@@ -0,0 +1,5 @@
=== tests/cases/conformance/jsdoc/bug26693.js ===
/** @typedef {module:locale} hi */
import { nope } from 'nope';
>nope : Symbol(nope, Decl(bug26693.js, 1, 8))
@@ -0,0 +1,5 @@
=== tests/cases/conformance/jsdoc/bug26693.js ===
/** @typedef {module:locale} hi */
import { nope } from 'nope';
>nope : any
@@ -24,8 +24,8 @@ var strRepresentation2 = MyEmusEnum[MyEmusEnum.emu]
// Should be okay, as we suppress implicit 'any' property access checks
var strRepresentation3 = MyEmusEnum["monehh"];
>strRepresentation3 : any
>MyEmusEnum["monehh"] : any
>strRepresentation3 : error
>MyEmusEnum["monehh"] : error
>MyEmusEnum : typeof MyEmusEnum
>"monehh" : "monehh"
@@ -39,15 +39,15 @@ var strRepresentation4 = MyEmusEnum["emu"];
// Should be okay, as we suppress implicit 'any' property access checks
var x = {}["hi"];
>x : any
>{}["hi"] : any
>x : error
>{}["hi"] : error
>{} : {}
>"hi" : "hi"
// Should be okay, as we suppress implicit 'any' property access checks
var y = {}[10];
>y : any
>{}[10] : any
>y : error
>{}[10] : error
>{} : {}
>10 : 10
@@ -61,8 +61,8 @@ var emptyObj = {};
// Should be okay, as we suppress implicit 'any' property access checks
var z1 = emptyObj[hi];
>z1 : any
>emptyObj[hi] : any
>z1 : error
>emptyObj[hi] : error
>emptyObj : {}
>hi : any
@@ -1,14 +1,22 @@
tests/cases/compiler/noUnusedLocals_writeOnly.ts(1,12): error TS6133: 'x' is declared but its value is never read.
tests/cases/compiler/noUnusedLocals_writeOnly.ts(10,9): error TS6133: 'z' is declared but its value is never read.
tests/cases/compiler/noUnusedLocals_writeOnly.ts(18,9): error TS6133: 'z' is declared but its value is never read.
==== tests/cases/compiler/noUnusedLocals_writeOnly.ts (2 errors) ====
function f(x = 0) {
function f(x = 0, b = false) {
~
!!! error TS6133: 'x' is declared but its value is never read.
// None of these statements read from 'x', so it will be marked unused.
x = 1;
x++;
x /= 2;
([x] = [1]);
({ x } = { x: 1 });
({ x: x } = { x: 1 });
({ a: [{ b: x }] } = { a: [{ b: 1 }] });
({ x = 2 } = { x: b ? 1 : undefined });
let used = 1;
({ x = used } = { x: b ? 1 : undefined });
let y = 0;
// This is a write access to y, but not a write-*only* access.
@@ -19,4 +27,5 @@ tests/cases/compiler/noUnusedLocals_writeOnly.ts(10,9): error TS6133: 'z' is dec
!!! error TS6133: 'z' is declared but its value is never read.
f(z = 1); // This effectively doesn't use `z`, values just pass through it.
}
function f2(_: ReadonlyArray<number>): void {}
@@ -1,8 +1,16 @@
//// [noUnusedLocals_writeOnly.ts]
function f(x = 0) {
function f(x = 0, b = false) {
// None of these statements read from 'x', so it will be marked unused.
x = 1;
x++;
x /= 2;
([x] = [1]);
({ x } = { x: 1 });
({ x: x } = { x: 1 });
({ a: [{ b: x }] } = { a: [{ b: 1 }] });
({ x = 2 } = { x: b ? 1 : undefined });
let used = 1;
({ x = used } = { x: b ? 1 : undefined });
let y = 0;
// This is a write access to y, but not a write-*only* access.
@@ -11,17 +19,30 @@ function f(x = 0) {
let z = 0;
f(z = 1); // This effectively doesn't use `z`, values just pass through it.
}
function f2(_: ReadonlyArray<number>): void {}
//// [noUnusedLocals_writeOnly.js]
function f(x) {
"use strict";
function f(x, b) {
if (x === void 0) { x = 0; }
if (b === void 0) { b = false; }
var _a, _b;
// None of these statements read from 'x', so it will be marked unused.
x = 1;
x++;
x /= 2;
(x = [1][0]);
(x = { x: 1 }.x);
(x = { x: 1 }.x);
(x = { a: [{ b: 1 }] }.a[0].b);
(_a = { x: b ? 1 : undefined }.x, x = _a === void 0 ? 2 : _a);
var used = 1;
(_b = { x: b ? 1 : undefined }.x, x = _b === void 0 ? used : _b);
var y = 0;
// This is a write access to y, but not a write-*only* access.
f(y++);
var z = 0;
f(z = 1); // This effectively doesn't use `z`, values just pass through it.
}
function f2(_) { }
@@ -1,8 +1,10 @@
=== tests/cases/compiler/noUnusedLocals_writeOnly.ts ===
function f(x = 0) {
function f(x = 0, b = false) {
>f : Symbol(f, Decl(noUnusedLocals_writeOnly.ts, 0, 0))
>x : Symbol(x, Decl(noUnusedLocals_writeOnly.ts, 0, 11))
>b : Symbol(b, Decl(noUnusedLocals_writeOnly.ts, 0, 17))
// None of these statements read from 'x', so it will be marked unused.
x = 1;
>x : Symbol(x, Decl(noUnusedLocals_writeOnly.ts, 0, 11))
@@ -12,19 +14,58 @@ function f(x = 0) {
x /= 2;
>x : Symbol(x, Decl(noUnusedLocals_writeOnly.ts, 0, 11))
([x] = [1]);
>x : Symbol(x, Decl(noUnusedLocals_writeOnly.ts, 0, 11))
({ x } = { x: 1 });
>x : Symbol(x, Decl(noUnusedLocals_writeOnly.ts, 6, 6))
>x : Symbol(x, Decl(noUnusedLocals_writeOnly.ts, 6, 14))
({ x: x } = { x: 1 });
>x : Symbol(x, Decl(noUnusedLocals_writeOnly.ts, 7, 6))
>x : Symbol(x, Decl(noUnusedLocals_writeOnly.ts, 0, 11))
>x : Symbol(x, Decl(noUnusedLocals_writeOnly.ts, 7, 17))
({ a: [{ b: x }] } = { a: [{ b: 1 }] });
>a : Symbol(a, Decl(noUnusedLocals_writeOnly.ts, 8, 6))
>b : Symbol(b, Decl(noUnusedLocals_writeOnly.ts, 8, 12))
>x : Symbol(x, Decl(noUnusedLocals_writeOnly.ts, 0, 11))
>a : Symbol(a, Decl(noUnusedLocals_writeOnly.ts, 8, 26))
>b : Symbol(b, Decl(noUnusedLocals_writeOnly.ts, 8, 32))
({ x = 2 } = { x: b ? 1 : undefined });
>x : Symbol(x, Decl(noUnusedLocals_writeOnly.ts, 9, 6))
>x : Symbol(x, Decl(noUnusedLocals_writeOnly.ts, 9, 18))
>b : Symbol(b, Decl(noUnusedLocals_writeOnly.ts, 0, 17))
>undefined : Symbol(undefined)
let used = 1;
>used : Symbol(used, Decl(noUnusedLocals_writeOnly.ts, 10, 7))
({ x = used } = { x: b ? 1 : undefined });
>x : Symbol(x, Decl(noUnusedLocals_writeOnly.ts, 11, 6))
>used : Symbol(used, Decl(noUnusedLocals_writeOnly.ts, 10, 7))
>x : Symbol(x, Decl(noUnusedLocals_writeOnly.ts, 11, 21))
>b : Symbol(b, Decl(noUnusedLocals_writeOnly.ts, 0, 17))
>undefined : Symbol(undefined)
let y = 0;
>y : Symbol(y, Decl(noUnusedLocals_writeOnly.ts, 5, 7))
>y : Symbol(y, Decl(noUnusedLocals_writeOnly.ts, 13, 7))
// This is a write access to y, but not a write-*only* access.
f(y++);
>f : Symbol(f, Decl(noUnusedLocals_writeOnly.ts, 0, 0))
>y : Symbol(y, Decl(noUnusedLocals_writeOnly.ts, 5, 7))
>y : Symbol(y, Decl(noUnusedLocals_writeOnly.ts, 13, 7))
let z = 0;
>z : Symbol(z, Decl(noUnusedLocals_writeOnly.ts, 9, 7))
>z : Symbol(z, Decl(noUnusedLocals_writeOnly.ts, 17, 7))
f(z = 1); // This effectively doesn't use `z`, values just pass through it.
>f : Symbol(f, Decl(noUnusedLocals_writeOnly.ts, 0, 0))
>z : Symbol(z, Decl(noUnusedLocals_writeOnly.ts, 9, 7))
>z : Symbol(z, Decl(noUnusedLocals_writeOnly.ts, 17, 7))
}
function f2(_: ReadonlyArray<number>): void {}
>f2 : Symbol(f2, Decl(noUnusedLocals_writeOnly.ts, 19, 1))
>_ : Symbol(_, Decl(noUnusedLocals_writeOnly.ts, 20, 12))
>ReadonlyArray : Symbol(ReadonlyArray, Decl(lib.es5.d.ts, --, --))

Some files were not shown because too many files have changed in this diff Show More