Merge branch 'master' into js-object-literal-assignments-as-declarations

This commit is contained in:
Nathan Shively-Sanders
2018-02-15 10:28:25 -08:00
1099 changed files with 12133 additions and 3350 deletions
+1 -1
View File
@@ -1308,7 +1308,7 @@ task("lint", ["build-rules"], () => {
: `Gulpfile.ts scripts/generateLocalizedDiagnosticMessages.ts "scripts/tslint/**/*.ts" "src/**/*.ts" --exclude "src/lib/*.d.ts"`;
const cmd = `node node_modules/tslint/bin/tslint ${files} --formatters-dir ./built/local/tslint/formatters --format autolinkableStylish`;
console.log("Linting: " + cmd);
jake.exec([cmd], { interactive: true }, () => {
jake.exec([cmd], { interactive: true, windowsVerbatimArguments: true }, () => {
if (fold.isTravis()) console.log(fold.end("lint"));
complete();
});
+1 -2
View File
@@ -792,8 +792,7 @@ interface Date {
interface DateConstructor {
new(): Date;
new(value: number): Date;
new(value: string): Date;
new(value: string | number): Date;
new(year: number, month: number, date?: number, hours?: number, minutes?: number, seconds?: number, ms?: number): Date;
(): string;
readonly prototype: Date;
+1 -1
View File
@@ -2,7 +2,7 @@
Thank you for submitting a pull request!
Here's a checklist you might find useful.
[ ] There is an associated issue that is labelled
[ ] There is an associated issue that is labeled
'Bug' or 'help wanted' or is in the Community milestone
[ ] Code is up-to-date with the `master` branch
[ ] You've successfully run `jake runtests` locally
+2 -2
View File
@@ -55,7 +55,7 @@ function updateTsFile(tsFilePath: string, tsFileContents: string, majorMinor: st
const parsedMajorMinor = majorMinorMatch[1];
ts.Debug.assert(parsedMajorMinor === majorMinor, "versionMajorMinor does not match.", () => `${tsFilePath}: '${parsedMajorMinor}'; package.json: '${majorMinor}'`);
const versionRgx = /export const version = `\$\{versionMajorMinor\}\.(\d)`;/;
const versionRgx = /export const version = `\$\{versionMajorMinor\}\.(\d)(-dev)?`;/;
const patchMatch = versionRgx.exec(tsFileContents);
ts.Debug.assert(patchMatch !== null, "The file seems to no longer have a string matching", () => versionRgx.toString());
const parsedPatch = patchMatch[1];
@@ -85,4 +85,4 @@ function getPrereleasePatch(tag: string, plainPatch: string): string {
return `${plainPatch}-${tag}.${timeStr}`;
}
main();
main();
+6 -2
View File
@@ -393,6 +393,10 @@ namespace ts {
? Diagnostics.Cannot_redeclare_block_scoped_variable_0
: Diagnostics.Duplicate_identifier_0;
if (symbol.flags & SymbolFlags.Enum || includes & SymbolFlags.Enum) {
message = Diagnostics.Enum_declarations_can_only_merge_with_namespace_or_other_enum_declarations;
}
if (symbol.declarations && symbol.declarations.length) {
// If the current node is a default export of some sort, then check if
// there are any other default exports that we need to error on.
@@ -756,11 +760,11 @@ namespace ts {
}
function isNarrowingTypeofOperands(expr1: Expression, expr2: Expression) {
return expr1.kind === SyntaxKind.TypeOfExpression && isNarrowableOperand((<TypeOfExpression>expr1).expression) && (expr2.kind === SyntaxKind.StringLiteral || expr2.kind === SyntaxKind.NoSubstitutionTemplateLiteral);
return isTypeOfExpression(expr1) && isNarrowableOperand(expr1.expression) && isStringLiteralLike(expr2);
}
function isNarrowableInOperands(left: Expression, right: Expression) {
return (left.kind === SyntaxKind.StringLiteral || left.kind === SyntaxKind.NoSubstitutionTemplateLiteral) && isNarrowingExpression(right);
return isStringLiteralLike(left) && isNarrowingExpression(right);
}
function isNarrowingBinaryExpression(expr: BinaryExpression) {
+2 -8
View File
@@ -41,15 +41,9 @@ namespace ts {
program: Program;
}
function hasSameKeys<T, U>(map1: ReadonlyMap<T> | undefined, map2: ReadonlyMap<U> | undefined) {
if (map1 === undefined) {
return map2 === undefined;
}
if (map2 === undefined) {
return map1 === undefined;
}
function hasSameKeys<T, U>(map1: ReadonlyMap<T> | undefined, map2: ReadonlyMap<U> | undefined): boolean {
// Has same size and every key is present in both maps
return map1.size === map2.size && !forEachKey(map1, key => !map2.has(key));
return map1 as ReadonlyMap<T | U> === map2 || map1 && map2 && map1.size === map2.size && !forEachKey(map1, key => !map2.has(key));
}
/**
+306 -200
View File
@@ -299,6 +299,10 @@ namespace ts {
node = getParseTreeNode(node);
return node && tryGetThisTypeAt(node);
},
getTypeArgumentConstraint: node => {
node = getParseTreeNode(node, isTypeNode);
return node && getTypeArgumentConstraint(node);
},
};
const tupleTypes: GenericType[] = [];
@@ -572,8 +576,10 @@ namespace ts {
}
const enum MappedTypeModifiers {
Readonly = 1 << 0,
Optional = 1 << 1,
IncludeReadonly = 1 << 0,
ExcludeReadonly = 1 << 1,
IncludeOptional = 1 << 2,
ExcludeOptional = 1 << 3,
}
const enum ExpandingFlags {
@@ -870,8 +876,11 @@ namespace ts {
error(getNameOfDeclaration(source.declarations[0]), Diagnostics.Cannot_augment_module_0_with_value_exports_because_it_resolves_to_a_non_module_entity, symbolToString(target));
}
else {
const message = target.flags & SymbolFlags.BlockScopedVariable || source.flags & SymbolFlags.BlockScopedVariable
? Diagnostics.Cannot_redeclare_block_scoped_variable_0 : Diagnostics.Duplicate_identifier_0;
const message = target.flags & SymbolFlags.Enum || source.flags & SymbolFlags.Enum
? Diagnostics.Enum_declarations_can_only_merge_with_namespace_or_other_enum_declarations
: target.flags & SymbolFlags.BlockScopedVariable || source.flags & SymbolFlags.BlockScopedVariable
? Diagnostics.Cannot_redeclare_block_scoped_variable_0
: Diagnostics.Duplicate_identifier_0;
forEach(source.declarations, node => {
error(getNameOfDeclaration(node) || node, message, symbolToString(source));
});
@@ -1157,7 +1166,7 @@ namespace ts {
const originalLocation = location; // needed for did-you-mean error reporting, which gathers candidates starting from the original location
let result: Symbol;
let lastLocation: Node;
let lastNonBlockLocation: Node;
let lastSelfReferenceLocation: Node;
let propertyWithInvalidInitializer: Node;
const errorLocation = location;
let grandparent: Node;
@@ -1381,17 +1390,17 @@ namespace ts {
}
break;
}
if (isNonBlockLocation(location)) {
lastNonBlockLocation = location;
if (isSelfReferenceLocation(location)) {
lastSelfReferenceLocation = location;
}
lastLocation = location;
location = location.parent;
}
// We just climbed up parents looking for the name, meaning that we started in a descendant node of `lastLocation`.
// If `result === lastNonBlockLocation.symbol`, that means that we are somewhere inside `lastNonBlockLocation` looking up a name, and resolving to `lastLocation` itself.
// If `result === lastSelfReferenceLocation.symbol`, that means that we are somewhere inside `lastSelfReferenceLocation` looking up a name, and resolving to `lastLocation` itself.
// That means that this is a self-reference of `lastLocation`, and shouldn't count this when considering whether `lastLocation` is used.
if (isUse && result && nameNotFoundMessage && noUnusedIdentifiers && result !== lastNonBlockLocation.symbol) {
if (isUse && result && nameNotFoundMessage && noUnusedIdentifiers && (!lastSelfReferenceLocation || result !== lastSelfReferenceLocation.symbol)) {
result.isReferenced = true;
}
@@ -1474,17 +1483,17 @@ namespace ts {
return result;
}
function isNonBlockLocation({ kind }: Node): boolean {
switch (kind) {
case SyntaxKind.Block:
case SyntaxKind.ModuleBlock:
case SyntaxKind.SwitchStatement:
case SyntaxKind.CaseBlock:
case SyntaxKind.CaseClause:
case SyntaxKind.DefaultClause:
return false;
default:
function isSelfReferenceLocation(node: Node): boolean {
switch (node.kind) {
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.TypeAliasDeclaration:
case SyntaxKind.ModuleDeclaration: // For `namespace N { N; }`
return true;
default:
return false;
}
}
@@ -1503,7 +1512,7 @@ namespace ts {
}
function checkAndReportErrorForMissingPrefix(errorLocation: Node, name: __String, nameArg: __String | Identifier): boolean {
if ((errorLocation.kind === SyntaxKind.Identifier && (isTypeReferenceIdentifier(<Identifier>errorLocation)) || isInTypeQuery(errorLocation))) {
if (!isIdentifier(errorLocation) || errorLocation.escapedText !== name || isTypeReferenceIdentifier(errorLocation) || isInTypeQuery(errorLocation)) {
return false;
}
@@ -2031,12 +2040,9 @@ namespace ts {
}
function resolveExternalModuleNameWorker(location: Node, moduleReferenceExpression: Expression, moduleNotFoundError: DiagnosticMessage, isForAugmentation = false): Symbol {
if (moduleReferenceExpression.kind !== SyntaxKind.StringLiteral && moduleReferenceExpression.kind !== SyntaxKind.NoSubstitutionTemplateLiteral) {
return;
}
const moduleReferenceLiteral = <LiteralExpression>moduleReferenceExpression;
return resolveExternalModule(location, moduleReferenceLiteral.text, moduleNotFoundError, moduleReferenceLiteral, isForAugmentation);
return isStringLiteralLike(moduleReferenceExpression)
? resolveExternalModule(location, moduleReferenceExpression.text, moduleNotFoundError, moduleReferenceExpression, isForAugmentation)
: undefined;
}
function resolveExternalModule(location: Node, moduleReference: string, moduleNotFoundError: DiagnosticMessage, errorNode: Node, isForAugmentation = false): Symbol {
@@ -2471,10 +2477,6 @@ namespace ts {
(ignoreQualification || canQualifySymbol(symbolFromSymbolTable, meaning));
}
function isUMDExportSymbol(symbol: Symbol) {
return symbol && symbol.declarations && symbol.declarations[0] && isNamespaceExportDeclaration(symbol.declarations[0]);
}
function trySymbolTable(symbols: SymbolTable, ignoreQualification: boolean | undefined) {
// If symbol is directly available by its name in the symbol table
if (isAccessible(symbols.get(symbol.escapedName), /*resolvedAliasSymbol*/ undefined, ignoreQualification)) {
@@ -2975,11 +2977,10 @@ namespace ts {
function createMappedTypeNodeFromType(type: MappedType) {
Debug.assert(!!(type.flags & TypeFlags.Object));
const readonlyToken = type.declaration && type.declaration.readonlyToken ? createToken(SyntaxKind.ReadonlyKeyword) : undefined;
const questionToken = type.declaration && type.declaration.questionToken ? createToken(SyntaxKind.QuestionToken) : undefined;
const readonlyToken = type.declaration.readonlyToken ? <ReadonlyToken | PlusToken | MinusToken>createToken(type.declaration.readonlyToken.kind) : undefined;
const questionToken = type.declaration.questionToken ? <QuestionToken | PlusToken | MinusToken>createToken(type.declaration.questionToken.kind) : undefined;
const typeParameterNode = typeParameterToDeclaration(getTypeParameterFromMappedType(type), context, getConstraintTypeFromMappedType(type));
const templateTypeNode = typeToTypeNodeHelper(getTemplateTypeFromMappedType(type), context);
const mappedTypeNode = createMappedTypeNode(readonlyToken, typeParameterNode, questionToken, templateTypeNode);
return setEmitFlags(mappedTypeNode, EmitFlags.SingleLine);
}
@@ -3226,7 +3227,8 @@ namespace ts {
context.tracker.reportPrivateInBaseOfClassExpression(unescapeLeadingUnderscores(propertySymbol.escapedName));
}
}
const propertyType = getCheckFlags(propertySymbol) & CheckFlags.ReverseMapped ? anyType : getTypeOfSymbol(propertySymbol);
const propertyType = getCheckFlags(propertySymbol) & CheckFlags.ReverseMapped && context.flags & NodeBuilderFlags.InReverseMappedType ?
anyType : getTypeOfSymbol(propertySymbol);
const saveEnclosingDeclaration = context.enclosingDeclaration;
context.enclosingDeclaration = undefined;
if (getCheckFlags(propertySymbol) & CheckFlags.Late) {
@@ -3249,7 +3251,10 @@ namespace ts {
}
}
else {
const savedFlags = context.flags;
context.flags |= !!(getCheckFlags(propertySymbol) & CheckFlags.ReverseMapped) ? NodeBuilderFlags.InReverseMappedType : 0;
const propertyTypeNode = propertyType ? typeToTypeNodeHelper(propertyType, context) : createKeywordTypeNode(SyntaxKind.AnyKeyword);
context.flags = savedFlags;
const modifiers = isReadonlySymbol(propertySymbol) ? [createToken(SyntaxKind.ReadonlyKeyword)] : undefined;
const propertySignature = createPropertySignature(
@@ -4418,7 +4423,7 @@ namespace ts {
type = getWidenedTypeForVariableLikeDeclaration(declaration, /*reportErrors*/ true);
}
else {
Debug.fail("Unhandled declaration kind! " + (ts as any).SyntaxKind[declaration.kind]);
Debug.fail("Unhandled declaration kind! " + Debug.showSyntaxKind(declaration));
}
if (!popTypeResolution()) {
@@ -5828,8 +5833,9 @@ namespace ts {
function resolveReverseMappedTypeMembers(type: ReverseMappedType) {
const indexInfo = getIndexInfoOfType(type.source, IndexKind.String);
const readonlyMask = type.mappedType.declaration.readonlyToken ? false : true;
const optionalMask = type.mappedType.declaration.questionToken ? 0 : SymbolFlags.Optional;
const modifiers = getMappedTypeModifiers(type.mappedType);
const readonlyMask = modifiers & MappedTypeModifiers.IncludeReadonly ? false : true;
const optionalMask = modifiers & MappedTypeModifiers.IncludeOptional ? 0 : SymbolFlags.Optional;
const stringIndexInfo = indexInfo && createIndexInfo(inferReverseMappedType(indexInfo.type, type.mappedType), readonlyMask && indexInfo.isReadonly);
const members = createSymbolTable();
for (const prop of getPropertiesOfType(type.source)) {
@@ -5855,8 +5861,7 @@ namespace ts {
const constraintType = getConstraintTypeFromMappedType(type);
const templateType = getTemplateTypeFromMappedType(<MappedType>type.target || type);
const modifiersType = getApparentType(getModifiersTypeFromMappedType(type)); // The 'T' in 'keyof T'
const templateReadonly = !!type.declaration.readonlyToken;
const templateOptional = !!type.declaration.questionToken;
const templateModifiers = getMappedTypeModifiers(type);
const constraintDeclaration = type.declaration.typeParameter.constraint;
if (constraintDeclaration.kind === SyntaxKind.TypeOperator &&
(<TypeOperatorNode>constraintDeclaration).operator === SyntaxKind.KeyOfKeyword) {
@@ -5897,10 +5902,17 @@ namespace ts {
if (t.flags & TypeFlags.StringLiteral) {
const propName = escapeLeadingUnderscores((<StringLiteralType>t).value);
const modifiersProp = getPropertyOfType(modifiersType, propName);
const isOptional = templateOptional || !!(modifiersProp && modifiersProp.flags & SymbolFlags.Optional);
const checkFlags = templateReadonly || modifiersProp && isReadonlySymbol(modifiersProp) ? CheckFlags.Readonly : 0;
const prop = createSymbol(SymbolFlags.Property | (isOptional ? SymbolFlags.Optional : 0), propName, checkFlags);
prop.type = propType;
const isOptional = !!(templateModifiers & MappedTypeModifiers.IncludeOptional ||
!(templateModifiers & MappedTypeModifiers.ExcludeOptional) && modifiersProp && modifiersProp.flags & SymbolFlags.Optional);
const isReadonly = !!(templateModifiers & MappedTypeModifiers.IncludeReadonly ||
!(templateModifiers & MappedTypeModifiers.ExcludeReadonly) && modifiersProp && isReadonlySymbol(modifiersProp));
const prop = createSymbol(SymbolFlags.Property | (isOptional ? SymbolFlags.Optional : 0), propName, isReadonly ? CheckFlags.Readonly : 0);
// When creating an optional property in strictNullChecks mode, if 'undefined' isn't assignable to the
// type, we include 'undefined' in the type. Similarly, when creating a non-optional property in strictNullChecks
// mode, if the underlying property is optional we remove 'undefined' from the type.
prop.type = strictNullChecks && isOptional && !isTypeAssignableTo(undefinedType, propType) ? getOptionalType(propType) :
strictNullChecks && !isOptional && modifiersProp && modifiersProp.flags & SymbolFlags.Optional ? getTypeWithFacts(propType, TypeFacts.NEUndefined) :
propType;
if (propertySymbol) {
prop.syntheticOrigin = propertySymbol;
prop.declarations = propertySymbol.declarations;
@@ -5909,7 +5921,7 @@ namespace ts {
members.set(propName, prop);
}
else if (t.flags & (TypeFlags.Any | TypeFlags.String)) {
stringIndexInfo = createIndexInfo(propType, templateReadonly);
stringIndexInfo = createIndexInfo(propType, !!(templateModifiers & MappedTypeModifiers.IncludeReadonly));
}
}
}
@@ -5927,7 +5939,7 @@ namespace ts {
function getTemplateTypeFromMappedType(type: MappedType) {
return type.templateType ||
(type.templateType = type.declaration.type ?
instantiateType(addOptionality(getTypeFromTypeNode(type.declaration.type), !!type.declaration.questionToken), type.mapper || identityMapper) :
instantiateType(addOptionality(getTypeFromTypeNode(type.declaration.type), !!(getMappedTypeModifiers(type) & MappedTypeModifiers.IncludeOptional)), type.mapper || identityMapper) :
unknownType);
}
@@ -5955,18 +5967,24 @@ namespace ts {
}
function getMappedTypeModifiers(type: MappedType): MappedTypeModifiers {
return (type.declaration.readonlyToken ? MappedTypeModifiers.Readonly : 0) |
(type.declaration.questionToken ? MappedTypeModifiers.Optional : 0);
const declaration = type.declaration;
return (declaration.readonlyToken ? declaration.readonlyToken.kind === SyntaxKind.MinusToken ? MappedTypeModifiers.ExcludeReadonly : MappedTypeModifiers.IncludeReadonly : 0) |
(declaration.questionToken ? declaration.questionToken.kind === SyntaxKind.MinusToken ? MappedTypeModifiers.ExcludeOptional : MappedTypeModifiers.IncludeOptional : 0);
}
function getCombinedMappedTypeModifiers(type: MappedType): MappedTypeModifiers {
function getMappedTypeOptionality(type: MappedType): number {
const modifiers = getMappedTypeModifiers(type);
return modifiers & MappedTypeModifiers.ExcludeOptional ? -1 : modifiers & MappedTypeModifiers.IncludeOptional ? 1 : 0;
}
function getCombinedMappedTypeOptionality(type: MappedType): number {
const optionality = getMappedTypeOptionality(type);
const modifiersType = getModifiersTypeFromMappedType(type);
return getMappedTypeModifiers(type) |
(isGenericMappedType(modifiersType) ? getMappedTypeModifiers(<MappedType>modifiersType) : 0);
return optionality || (isGenericMappedType(modifiersType) ? getMappedTypeOptionality(<MappedType>modifiersType) : 0);
}
function isPartialMappedType(type: Type) {
return getObjectFlags(type) & ObjectFlags.Mapped && !!(<MappedType>type).declaration.questionToken;
return !!(getObjectFlags(type) & ObjectFlags.Mapped && getMappedTypeModifiers(<MappedType>type) & MappedTypeModifiers.IncludeOptional);
}
function isGenericMappedType(type: Type): type is MappedType {
@@ -6108,11 +6126,13 @@ namespace ts {
// with its constraint. We do this because if the constraint is a union type it will be distributed
// over the conditional type and possibly reduced. For example, 'T extends undefined ? never : T'
// removes 'undefined' from T.
const checkType = type.checkType;
if (checkType.flags & TypeFlags.TypeParameter) {
const constraint = getConstraintOfTypeParameter(<TypeParameter>checkType);
if (isDistributiveConditionalType(type)) {
const constraint = getConstraintOfType(type.checkType);
if (constraint) {
return instantiateType(type, createTypeMapper([<TypeParameter>checkType], [constraint]));
const target = type.target || type;
const mapper = createTypeMapper([<TypeParameter>target.checkType], [constraint]);
const combinedMapper = type.mapper ? combineTypeMappers(mapper, type.mapper) : mapper;
return instantiateType(target, combinedMapper);
}
}
return undefined;
@@ -6975,6 +6995,42 @@ namespace ts {
return type.symbol && getDeclarationOfKind<TypeParameterDeclaration>(type.symbol, SyntaxKind.TypeParameter).constraint;
}
function getInferredTypeParameterConstraint(typeParameter: TypeParameter) {
let inferences: Type[];
if (typeParameter.symbol) {
for (const declaration of typeParameter.symbol.declarations) {
// When an 'infer T' declaration is immediately contained in a type reference node
// (such as 'Foo<infer T>'), T's constraint is inferred from the constraint of the
// corresponding type parameter in 'Foo'. When multiple 'infer T' declarations are
// present, we form an intersection of the inferred constraint types.
if (declaration.parent.kind === SyntaxKind.InferType && declaration.parent.parent.kind === SyntaxKind.TypeReference) {
const typeReference = <TypeReferenceNode>declaration.parent.parent;
const typeParameters = getTypeParametersForTypeReference(typeReference);
if (typeParameters) {
const index = typeReference.typeArguments.indexOf(<TypeNode>declaration.parent);
if (index < typeParameters.length) {
const declaredConstraint = getConstraintOfTypeParameter(typeParameters[index]);
if (declaredConstraint) {
// Type parameter constraints can reference other type parameters so
// constraints need to be instantiated. If instantiation produces the
// type parameter itself, we discard that inference. For example, in
// type Foo<T extends string, U extends T> = [T, U];
// type Bar<T> = T extends Foo<infer X, infer X> ? Foo<X, X> : T;
// the instantiated constraint for U is X, so we discard that inference.
const mapper = createTypeMapper(typeParameters, getEffectiveTypeArguments(typeReference, typeParameters));
const constraint = instantiateType(declaredConstraint, mapper);
if (constraint !== typeParameter) {
inferences = append(inferences, constraint);
}
}
}
}
}
}
}
return inferences && getIntersectionType(inferences);
}
function getConstraintFromTypeParameter(typeParameter: TypeParameter): Type {
if (!typeParameter.constraint) {
if (typeParameter.target) {
@@ -6983,7 +7039,8 @@ namespace ts {
}
else {
const constraintDeclaration = getConstraintDeclaration(typeParameter);
typeParameter.constraint = constraintDeclaration ? getTypeFromTypeNode(constraintDeclaration) : noConstraintType;
typeParameter.constraint = constraintDeclaration ? getTypeFromTypeNode(constraintDeclaration) :
getInferredTypeParameterConstraint(typeParameter) || noConstraintType;
}
}
return typeParameter.constraint === noConstraintType ? undefined : typeParameter.constraint;
@@ -7090,12 +7147,8 @@ namespace ts {
const typeArguments = concatenate(type.outerTypeParameters, fillMissingTypeArguments(typeArgs, typeParameters, minTypeArgumentCount, isJs));
return createTypeReference(<GenericType>type, typeArguments);
}
if (node.typeArguments) {
error(node, Diagnostics.Type_0_is_not_generic, typeToString(type));
return unknownType;
return checkNoTypeArguments(node, symbol) ? type : unknownType;
}
return type;
}
function getTypeAliasInstantiation(symbol: Symbol, typeArguments: Type[]): Type {
const type = getDeclaredTypeOfSymbol(symbol);
@@ -7132,12 +7185,8 @@ namespace ts {
}
return getTypeAliasInstantiation(symbol, typeArguments);
}
if (node.typeArguments) {
error(node, Diagnostics.Type_0_is_not_generic, symbolToString(symbol));
return unknownType;
return checkNoTypeArguments(node, symbol) ? type : unknownType;
}
return type;
}
function getTypeReferenceName(node: TypeReferenceType): EntityNameOrEntityNameExpression | undefined {
switch (node.kind) {
@@ -7177,12 +7226,10 @@ namespace ts {
// Get type from reference to named type that cannot be generic (enum or type parameter)
const res = tryGetDeclaredTypeOfSymbol(symbol);
if (res !== undefined) {
if (typeArguments) {
error(node, Diagnostics.Type_0_is_not_generic, symbolToString(symbol));
return unknownType;
}
return res.flags & TypeFlags.TypeParameter ? getConstrainedTypeParameter(<TypeParameter>res, node) : res;
if (res) {
return checkNoTypeArguments(node, symbol) ?
res.flags & TypeFlags.TypeParameter ? getConstrainedTypeParameter(<TypeParameter>res, node) : res :
unknownType;
}
if (!(symbol.flags & SymbolFlags.Value && isJSDocTypeReference(node))) {
@@ -7230,7 +7277,7 @@ namespace ts {
function getConstrainedTypeParameter(typeParameter: TypeParameter, node: Node) {
let constraints: Type[];
while (isTypeNode(node)) {
while (isPartOfTypeNode(node)) {
const parent = node.parent;
if (parent.kind === SyntaxKind.ConditionalType && node === (<ConditionalTypeNode>parent).trueType) {
if (getTypeFromTypeNode((<ConditionalTypeNode>parent).checkType) === typeParameter) {
@@ -7246,39 +7293,58 @@ namespace ts {
return node.flags & NodeFlags.JSDoc && node.kind === SyntaxKind.TypeReference;
}
function checkNoTypeArguments(node: TypeReferenceType, symbol?: Symbol) {
if (node.typeArguments) {
error(node, Diagnostics.Type_0_is_not_generic, symbol ? symbolToString(symbol) : declarationNameToString((<TypeReferenceNode>node).typeName));
return false;
}
return true;
}
function getIntendedTypeFromJSDocTypeReference(node: TypeReferenceNode): Type {
if (isIdentifier(node.typeName)) {
if (node.typeName.escapedText === "Object") {
if (isJSDocIndexSignature(node)) {
const indexed = getTypeFromTypeNode(node.typeArguments[0]);
const target = getTypeFromTypeNode(node.typeArguments[1]);
const index = createIndexInfo(target, /*isReadonly*/ false);
return createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, indexed === stringType && index, indexed === numberType && index);
}
return anyType;
}
const typeArgs = node.typeArguments;
switch (node.typeName.escapedText) {
case "String":
checkNoTypeArguments(node);
return stringType;
case "Number":
checkNoTypeArguments(node);
return numberType;
case "Boolean":
checkNoTypeArguments(node);
return booleanType;
case "Void":
checkNoTypeArguments(node);
return voidType;
case "Undefined":
checkNoTypeArguments(node);
return undefinedType;
case "Null":
checkNoTypeArguments(node);
return nullType;
case "Function":
case "function":
checkNoTypeArguments(node);
return globalFunctionType;
case "Array":
case "array":
return !node.typeArguments || !node.typeArguments.length ? anyArrayType : undefined;
return !typeArgs || !typeArgs.length ? anyArrayType : undefined;
case "Promise":
case "promise":
return !node.typeArguments || !node.typeArguments.length ? createPromiseType(anyType) : undefined;
return !typeArgs || !typeArgs.length ? createPromiseType(anyType) : undefined;
case "Object":
if (typeArgs && typeArgs.length === 2) {
if (isJSDocIndexSignature(node)) {
const indexed = getTypeFromTypeNode(typeArgs[0]);
const target = getTypeFromTypeNode(typeArgs[1]);
const index = createIndexInfo(target, /*isReadonly*/ false);
return createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, indexed === stringType && index, indexed === numberType && index);
}
return anyType;
}
checkNoTypeArguments(node);
return anyType;
}
}
}
@@ -7303,7 +7369,7 @@ namespace ts {
type = getTypeReferenceType(node, symbol);
}
// Cache both the resolved symbol and the resolved type. The resolved symbol is needed in when we check the
// type reference in checkTypeReferenceOrExpressionWithTypeArguments.
// type reference in checkTypeReferenceNode.
links.resolvedSymbol = symbol;
links.resolvedType = type;
}
@@ -7952,7 +8018,7 @@ namespace ts {
}
if (!(indexType.flags & TypeFlags.Nullable) && isTypeAssignableToKind(indexType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbolLike)) {
if (isTypeAny(objectType)) {
return anyType;
return objectType;
}
const indexInfo = isTypeAssignableToKind(indexType, TypeFlags.NumberLike) && getIndexInfoOfType(objectType, IndexKind.Number) ||
getIndexInfoOfType(objectType, IndexKind.String) ||
@@ -8156,7 +8222,7 @@ namespace ts {
// types with type parameters mapped to the wildcard type, the most permissive instantiations
// possible (the wildcard type is assignable to and from all types). If those are not related,
// then no instatiations will be and we can just return the false branch type.
if (!isTypeAssignableTo(getWildcardInstantiation(checkType), getWildcardInstantiation(extendsType))) {
if (!typeMaybeAssignableTo(getWildcardInstantiation(checkType), getWildcardInstantiation(extendsType))) {
return instantiateType(baseFalseType, mapper);
}
// The check could be true for some instantiation
@@ -8189,19 +8255,24 @@ namespace ts {
const erasedCheckType = getActualTypeParameter(checkType);
const trueType = instantiateType(baseTrueType, mapper);
const falseType = instantiateType(baseFalseType, mapper);
const id = target && (target.id + "," + erasedCheckType.id + "," + extendsType.id + "," + trueType.id + "," + falseType.id);
const cached = id && conditionalTypes.get(id);
// We compute the cache key from the ids of the four constituent types, plus an indicator of whether the
// type is distributive (i.e. whether the original declaration has a type parameter as the check type).
const isDistributive = (target ? target.checkType : erasedCheckType).flags & TypeFlags.TypeParameter ? 1 : 0;
const id = erasedCheckType.id + "," + extendsType.id + "," + trueType.id + "," + falseType.id + "," + isDistributive;
const cached = conditionalTypes.get(id);
if (cached) {
return cached;
}
const result = createConditionalType(erasedCheckType, extendsType, trueType, falseType,
inferTypeParameters, target, mapper, aliasSymbol, instantiateTypes(baseAliasTypeArguments, mapper));
if (id) {
conditionalTypes.set(id, result);
}
conditionalTypes.set(id, result);
return result;
}
function isDistributiveConditionalType(type: ConditionalType) {
return !!((type.target || type).checkType.flags & TypeFlags.TypeParameter);
}
function getInferTypeParameters(node: ConditionalTypeNode): TypeParameter[] {
let result: TypeParameter[];
if (node.locals) {
@@ -8814,9 +8885,9 @@ namespace ts {
// Check if we have a conditional type where the check type is a naked type parameter. If so,
// the conditional type is distributive over union types and when T is instantiated to a union
// type A | B, we produce (A extends U ? X : Y) | (B extends U ? X : Y).
const checkType = target.checkType;
if (checkType.flags & TypeFlags.TypeParameter) {
const instantiatedType = combinedMapper(<TypeParameter>checkType);
if (isDistributiveConditionalType(target)) {
const checkType = <TypeParameter>target.checkType;
const instantiatedType = combinedMapper(checkType);
if (checkType !== instantiatedType && instantiatedType.flags & TypeFlags.Union) {
return mapType(instantiatedType, t => instantiateConditionalType(target, createReplacementMapper(checkType, t, combinedMapper)));
}
@@ -9593,17 +9664,43 @@ namespace ts {
function isIdenticalTo(source: Type, target: Type): Ternary {
let result: Ternary;
if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Object) {
const flags = source.flags & target.flags;
if (flags & TypeFlags.Object) {
return recursiveTypeRelatedTo(source, target, /*reportErrors*/ false);
}
if (source.flags & TypeFlags.Union && target.flags & TypeFlags.Union ||
source.flags & TypeFlags.Intersection && target.flags & TypeFlags.Intersection) {
if (flags & (TypeFlags.Union | TypeFlags.Intersection)) {
if (result = eachTypeRelatedToSomeType(<UnionOrIntersectionType>source, <UnionOrIntersectionType>target)) {
if (result &= eachTypeRelatedToSomeType(<UnionOrIntersectionType>target, <UnionOrIntersectionType>source)) {
return result;
}
}
}
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 (result = isRelatedTo((<ConditionalType>source).checkType, (<ConditionalType>target).checkType, /*reportErrors*/ false)) {
if (result &= isRelatedTo((<ConditionalType>source).extendsType, (<ConditionalType>target).extendsType, /*reportErrors*/ false)) {
if (result &= isRelatedTo((<ConditionalType>source).trueType, (<ConditionalType>target).trueType, /*reportErrors*/ false)) {
if (result &= isRelatedTo((<ConditionalType>source).falseType, (<ConditionalType>target).falseType, /*reportErrors*/ false)) {
if (isDistributiveConditionalType(<ConditionalType>source) === isDistributiveConditionalType(<ConditionalType>target)) {
return result;
}
}
}
}
}
}
if (flags & TypeFlags.Substitution) {
return isRelatedTo((<SubstitutionType>source).substitute, (<SubstitutionType>target).substitute, /*reportErrors*/ false);
}
return Ternary.False;
}
@@ -9890,7 +9987,7 @@ namespace ts {
if (target.flags & TypeFlags.TypeParameter) {
// A source type { [P in keyof T]: X } is related to a target type T if X is related to T[P].
if (getObjectFlags(source) & ObjectFlags.Mapped && getConstraintTypeFromMappedType(<MappedType>source) === getIndexType(target)) {
if (!(<MappedType>source).declaration.questionToken) {
if (!(getMappedTypeModifiers(<MappedType>source) & MappedTypeModifiers.IncludeOptional)) {
const templateType = getTemplateTypeFromMappedType(<MappedType>source);
const indexedAccessType = getIndexedAccessType(target, getTypeParameterFromMappedType(<MappedType>source));
if (result = isRelatedTo(templateType, indexedAccessType, reportErrors)) {
@@ -9929,6 +10026,8 @@ namespace ts {
else if (isGenericMappedType(target)) {
// A source type T is related to a target type { [P in X]: T[P] }
const template = getTemplateTypeFromMappedType(<MappedType>target);
const modifiers = getMappedTypeModifiers(<MappedType>target);
if (!(modifiers & MappedTypeModifiers.ExcludeOptional)) {
if (template.flags & TypeFlags.IndexedAccess && (<IndexedAccessType>template).objectType === source &&
(<IndexedAccessType>template).indexType === getTypeParameterFromMappedType(<MappedType>target)) {
return Ternary.True;
@@ -9943,6 +10042,7 @@ namespace ts {
}
}
}
}
if (source.flags & TypeFlags.TypeParameter) {
let constraint = getConstraintForRelation(<TypeParameter>source);
@@ -9989,7 +10089,19 @@ namespace ts {
}
}
}
if (result = isRelatedTo(getDefaultConstraintOfConditionalType(<ConditionalType>source), target, reportErrors)) {
if (target.flags & TypeFlags.Conditional) {
if (isTypeIdenticalTo((<ConditionalType>source).checkType, (<ConditionalType>target).checkType) &&
isTypeIdenticalTo((<ConditionalType>source).extendsType, (<ConditionalType>target).extendsType)) {
if (result = isRelatedTo((<ConditionalType>source).trueType, (<ConditionalType>target).trueType, reportErrors)) {
result &= isRelatedTo((<ConditionalType>source).falseType, (<ConditionalType>target).falseType, reportErrors);
}
if (result) {
errorInfo = saveErrorInfo;
return result;
}
}
}
else if (result = isRelatedTo(getDefaultConstraintOfConditionalType(<ConditionalType>source), target, reportErrors)) {
errorInfo = saveErrorInfo;
return result;
}
@@ -10080,8 +10192,7 @@ namespace ts {
function mappedTypeRelatedTo(source: MappedType, target: MappedType, reportErrors: boolean): Ternary {
const modifiersRelated = relation === comparableRelation || (
relation === identityRelation ? getMappedTypeModifiers(source) === getMappedTypeModifiers(target) :
!(getCombinedMappedTypeModifiers(source) & MappedTypeModifiers.Optional) ||
getCombinedMappedTypeModifiers(target) & MappedTypeModifiers.Optional);
getCombinedMappedTypeOptionality(source) <= getCombinedMappedTypeOptionality(target));
if (modifiersRelated) {
let result: Ternary;
if (result = isRelatedTo(getConstraintTypeFromMappedType(<MappedType>target), getConstraintTypeFromMappedType(<MappedType>source), reportErrors)) {
@@ -11124,7 +11235,7 @@ namespace ts {
const t = getTypeOfSymbol(p);
if (t.flags & TypeFlags.ContainsWideningType) {
if (!reportWideningErrorsInType(t)) {
error(p.valueDeclaration, Diagnostics.Object_literal_s_property_0_implicitly_has_an_1_type, symbolName(p), typeToString(getWidenedType(t)));
error(p.valueDeclaration, Diagnostics.Object_literal_s_property_0_implicitly_has_an_1_type, symbolToString(p), typeToString(getWidenedType(t)));
}
errorReported = true;
}
@@ -12844,11 +12955,11 @@ namespace ts {
const operator = expr.operatorToken.kind;
const left = getReferenceCandidate(expr.left);
const right = getReferenceCandidate(expr.right);
if (left.kind === SyntaxKind.TypeOfExpression && (right.kind === SyntaxKind.StringLiteral || right.kind === SyntaxKind.NoSubstitutionTemplateLiteral)) {
return narrowTypeByTypeof(type, <TypeOfExpression>left, operator, <LiteralExpression>right, assumeTrue);
if (left.kind === SyntaxKind.TypeOfExpression && isStringLiteralLike(right)) {
return narrowTypeByTypeof(type, <TypeOfExpression>left, operator, right, assumeTrue);
}
if (right.kind === SyntaxKind.TypeOfExpression && (left.kind === SyntaxKind.StringLiteral || left.kind === SyntaxKind.NoSubstitutionTemplateLiteral)) {
return narrowTypeByTypeof(type, <TypeOfExpression>right, operator, <LiteralExpression>left, assumeTrue);
if (right.kind === SyntaxKind.TypeOfExpression && isStringLiteralLike(left)) {
return narrowTypeByTypeof(type, <TypeOfExpression>right, operator, left, assumeTrue);
}
if (isMatchingReference(reference, left)) {
return narrowTypeByEquality(type, operator, right, assumeTrue);
@@ -12870,8 +12981,8 @@ namespace ts {
return narrowTypeByInstanceof(type, expr, assumeTrue);
case SyntaxKind.InKeyword:
const target = getReferenceCandidate(expr.right);
if ((expr.left.kind === SyntaxKind.StringLiteral || expr.left.kind === SyntaxKind.NoSubstitutionTemplateLiteral) && isMatchingReference(reference, target)) {
return narrowByInKeyword(type, <LiteralExpression>expr.left, assumeTrue);
if (isStringLiteralLike(expr.left) && isMatchingReference(reference, target)) {
return narrowByInKeyword(type, expr.left, assumeTrue);
}
break;
case SyntaxKind.CommaToken:
@@ -16212,24 +16323,13 @@ namespace ts {
propertyName: __String,
type: Type): boolean {
if (type !== unknownType && !isTypeAny(type)) {
const prop = getPropertyOfType(type, propertyName);
if (prop) {
return checkPropertyAccessibility(node, left, type, prop);
}
// In js files properties of unions are allowed in completion
if (isInJavaScriptFile(left) && (type.flags & TypeFlags.Union)) {
for (const elementType of (<UnionType>type).types) {
if (isValidPropertyAccessWithType(node, left, propertyName, elementType)) {
return true;
}
}
}
return false;
if (type === unknownType || isTypeAny(type)) {
return true;
}
return true;
const prop = getPropertyOfType(type, propertyName);
return prop ? checkPropertyAccessibility(node, left, type, prop)
// In js files properties of unions are allowed in completion
: isInJavaScriptFile(node) && (type.flags & TypeFlags.Union) && (<UnionType>type).types.some(elementType => isValidPropertyAccessWithType(node, left, propertyName, elementType));
}
/**
@@ -18348,7 +18448,7 @@ namespace ts {
*
* @param returnType - return type of the function, can be undefined if return type is not explicitly specified
*/
function checkAllCodePathsInNonVoidFunctionReturnOrThrow(func: FunctionLikeDeclaration, returnType: Type): void {
function checkAllCodePathsInNonVoidFunctionReturnOrThrow(func: FunctionLikeDeclaration | MethodSignature, returnType: Type): void {
if (!produceDiagnostics) {
return;
}
@@ -18360,7 +18460,7 @@ namespace ts {
// If all we have is a function signature, or an arrow function with an expression body, then there is nothing to check.
// also if HasImplicitReturn flag is not set this means that all codepaths in function body end with return or throw
if (nodeIsMissing(func.body) || func.body.kind !== SyntaxKind.Block || !functionHasImplicitReturn(func)) {
if (func.kind === SyntaxKind.MethodSignature || nodeIsMissing(func.body) || func.body.kind !== SyntaxKind.Block || !functionHasImplicitReturn(func)) {
return;
}
@@ -19524,7 +19624,7 @@ namespace ts {
return nullWideningType;
case SyntaxKind.NoSubstitutionTemplateLiteral:
case SyntaxKind.StringLiteral:
return getFreshTypeOfLiteralType(getLiteralType((node as LiteralExpression).text));
return getFreshTypeOfLiteralType(getLiteralType((node as StringLiteralLike).text));
case SyntaxKind.NumericLiteral:
checkGrammarNumericLiteral(node as NumericLiteral);
return getFreshTypeOfLiteralType(getLiteralType(+(node as NumericLiteral).text));
@@ -20031,7 +20131,7 @@ namespace ts {
checkVariableLikeDeclaration(node);
}
function checkMethodDeclaration(node: MethodDeclaration) {
function checkMethodDeclaration(node: MethodDeclaration | MethodSignature) {
// Grammar checking
if (!checkGrammarMethod(node)) checkGrammarComputedPropertyName(node.name);
@@ -20040,7 +20140,7 @@ namespace ts {
// Abstract methods cannot have an implementation.
// Extra checks are to avoid reporting multiple errors relating to the "abstractness" of the node.
if (hasModifier(node, ModifierFlags.Abstract) && node.body) {
if (hasModifier(node, ModifierFlags.Abstract) && node.kind === SyntaxKind.MethodDeclaration && node.body) {
error(node, Diagnostics.Method_0_cannot_have_an_implementation_because_it_is_marked_abstract, declarationNameToString(node.name));
}
}
@@ -20187,8 +20287,12 @@ namespace ts {
checkDecorators(node);
}
function checkTypeArgumentConstraints(typeParameters: TypeParameter[], typeArgumentNodes: ReadonlyArray<TypeNode>): boolean {
const minTypeArgumentCount = getMinTypeArgumentCount(typeParameters);
function getEffectiveTypeArguments(node: TypeReferenceNode | ExpressionWithTypeArguments, typeParameters: TypeParameter[]) {
return fillMissingTypeArguments(map(node.typeArguments, getTypeFromTypeNode), typeParameters,
getMinTypeArgumentCount(typeParameters), isInJavaScriptFile(node));
}
function checkTypeArgumentConstraints(node: TypeReferenceNode | ExpressionWithTypeArguments, typeParameters: TypeParameter[]): boolean {
let typeArguments: Type[];
let mapper: TypeMapper;
let result = true;
@@ -20196,25 +20300,35 @@ namespace ts {
const constraint = getConstraintOfTypeParameter(typeParameters[i]);
if (constraint) {
if (!typeArguments) {
typeArguments = fillMissingTypeArguments(map(typeArgumentNodes, getTypeFromTypeNode), typeParameters, minTypeArgumentCount, isInJavaScriptFile(typeArgumentNodes[i]));
typeArguments = getEffectiveTypeArguments(node, typeParameters);
mapper = createTypeMapper(typeParameters, typeArguments);
}
const typeArgument = typeArguments[i];
result = result && checkTypeAssignableTo(
typeArgument,
typeArguments[i],
instantiateType(constraint, mapper),
typeArgumentNodes[i],
node.typeArguments[i],
Diagnostics.Type_0_does_not_satisfy_the_constraint_1);
}
}
return result;
}
function getTypeParametersForTypeReference(node: TypeReferenceNode | ExpressionWithTypeArguments) {
const type = getTypeFromTypeReference(node);
if (type !== unknownType) {
const symbol = getNodeLinks(node).resolvedSymbol;
if (symbol) {
return symbol.flags & SymbolFlags.TypeAlias && getSymbolLinks(symbol).typeParameters ||
(getObjectFlags(type) & ObjectFlags.Reference ? (<TypeReference>type).target.localTypeParameters : undefined);
}
}
return undefined;
}
function checkTypeReferenceNode(node: TypeReferenceNode | ExpressionWithTypeArguments) {
checkGrammarTypeArguments(node, node.typeArguments);
if (node.kind === SyntaxKind.TypeReference && node.typeName.jsdocDotPos !== undefined && !isInJavaScriptFile(node) && !isInJSDoc(node)) {
grammarErrorAtPos(node, node.typeName.jsdocDotPos, 1, Diagnostics.JSDoc_types_can_only_be_used_inside_documentation_comments);
}
const type = getTypeFromTypeReference(node);
if (type !== unknownType) {
@@ -20222,22 +20336,10 @@ namespace ts {
// Do type argument local checks only if referenced type is successfully resolved
forEach(node.typeArguments, checkSourceElement);
if (produceDiagnostics) {
const symbol = getNodeLinks(node).resolvedSymbol;
if (!symbol) {
// There is no resolved symbol cached if the type resolved to a builtin
// via JSDoc type reference resolution (eg, Boolean became boolean), none
// of which are generic when they have no associated symbol
// (additionally, JSDoc's index signature syntax, Object<string, T> actually uses generic syntax without being generic)
if (!isJSDocIndexSignature(node)) {
error(node, Diagnostics.Type_0_is_not_generic, typeToString(type));
}
return;
const typeParameters = getTypeParametersForTypeReference(node);
if (typeParameters) {
checkTypeArgumentConstraints(node, typeParameters);
}
let typeParameters = symbol.flags & SymbolFlags.TypeAlias && getSymbolLinks(symbol).typeParameters;
if (!typeParameters && getObjectFlags(type) & ObjectFlags.Reference) {
typeParameters = (<TypeReference>type).target.localTypeParameters;
}
checkTypeArgumentConstraints(typeParameters, node.typeArguments);
}
}
if (type.flags & TypeFlags.Enum && getNodeLinks(node).resolvedSymbol.flags & SymbolFlags.EnumMember) {
@@ -20246,6 +20348,14 @@ namespace ts {
}
}
function getTypeArgumentConstraint(node: TypeNode): Type | undefined {
const typeReferenceNode = tryCast(node.parent, isTypeReferenceType);
if (!typeReferenceNode) return undefined;
const typeParameters = getTypeParametersForTypeReference(typeReferenceNode);
const constraint = getConstraintOfTypeParameter(typeParameters[typeReferenceNode.typeArguments.indexOf(node)!]);
return constraint && instantiateType(constraint, createTypeMapper(typeParameters, getEffectiveTypeArguments(typeReferenceNode, typeParameters)));
}
function checkTypeQuery(node: TypeQueryNode) {
getTypeFromTypeQueryNode(node);
}
@@ -20287,7 +20397,7 @@ namespace ts {
const indexType = (<IndexedAccessType>type).indexType;
if (isTypeAssignableTo(indexType, getIndexType(objectType))) {
if (accessNode.kind === SyntaxKind.ElementAccessExpression && isAssignmentTarget(accessNode) &&
getObjectFlags(objectType) & ObjectFlags.Mapped && (<MappedType>objectType).declaration.readonlyToken) {
getObjectFlags(objectType) & ObjectFlags.Mapped && getMappedTypeModifiers(<MappedType>objectType) & MappedTypeModifiers.IncludeReadonly) {
error(accessNode, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(objectType));
}
return type;
@@ -20671,7 +20781,7 @@ namespace ts {
case SyntaxKind.ImportSpecifier: // https://github.com/Microsoft/TypeScript/pull/7591
return DeclarationSpaces.ExportValue;
default:
Debug.fail((ts as any).SyntaxKind[d.kind]);
Debug.fail(Debug.showSyntaxKind(d));
}
}
}
@@ -20867,7 +20977,7 @@ namespace ts {
*
* @param node The signature to check
*/
function checkAsyncFunctionReturnType(node: FunctionLikeDeclaration): Type {
function checkAsyncFunctionReturnType(node: FunctionLikeDeclaration | MethodSignature): Type {
// As part of our emit for an async function, we will need to emit the entity name of
// the return type annotation as an expression. To meet the necessary runtime semantics
// for __awaiter, we must also check that the type of the declaration (e.g. the static
@@ -21229,7 +21339,7 @@ namespace ts {
}
}
function checkFunctionOrMethodDeclaration(node: FunctionDeclaration | MethodDeclaration): void {
function checkFunctionOrMethodDeclaration(node: FunctionDeclaration | MethodDeclaration | MethodSignature): void {
checkDecorators(node);
checkSignatureDeclaration(node);
const functionFlags = getFunctionFlags(node);
@@ -21271,7 +21381,8 @@ namespace ts {
}
}
checkSourceElement(node.body);
const body = node.kind === SyntaxKind.MethodSignature ? undefined : node.body;
checkSourceElement(body);
const returnTypeNode = getEffectiveReturnTypeNode(node);
if ((functionFlags & FunctionFlags.Generator) === 0) { // Async function or normal function
@@ -21284,11 +21395,11 @@ namespace ts {
if (produceDiagnostics && !returnTypeNode) {
// Report an implicit any error if there is no body, no explicit return type, and node is not a private method
// in an ambient context
if (noImplicitAny && nodeIsMissing(node.body) && !isPrivateWithinAmbient(node)) {
if (noImplicitAny && nodeIsMissing(body) && !isPrivateWithinAmbient(node)) {
reportImplicitAnyError(node, anyType);
}
if (functionFlags & FunctionFlags.Generator && nodeIsPresent(node.body)) {
if (functionFlags & FunctionFlags.Generator && nodeIsPresent(body)) {
// A generator with a body and no type annotation can still cause errors. It can error if the
// yielded values have no common supertype, or it can give an implicit any error if it has no
// yielded values. The only way to trigger these errors is to try checking its return type.
@@ -21954,20 +22065,6 @@ namespace ts {
forEach(node.declarationList.declarations, checkSourceElement);
}
function checkGrammarDisallowedModifiersOnObjectLiteralExpressionMethod(node: MethodDeclaration) {
// We only disallow modifier on a method declaration if it is a property of object-literal-expression
if (node.modifiers && node.parent.kind === SyntaxKind.ObjectLiteralExpression) {
if (getFunctionFlags(node) & FunctionFlags.Async) {
if (node.modifiers.length > 1) {
return grammarErrorOnFirstToken(node, Diagnostics.Modifiers_cannot_appear_here);
}
}
else {
return grammarErrorOnFirstToken(node, Diagnostics.Modifiers_cannot_appear_here);
}
}
}
function checkExpressionStatement(node: ExpressionStatement) {
// Grammar checking
checkGrammarStatementInAmbientContext(node);
@@ -22935,7 +23032,7 @@ namespace ts {
if (some(baseTypeNode.typeArguments)) {
forEach(baseTypeNode.typeArguments, checkSourceElement);
for (const constructor of getConstructorsForTypeArguments(staticBaseType, baseTypeNode.typeArguments, baseTypeNode)) {
if (!checkTypeArgumentConstraints(constructor.typeParameters, baseTypeNode.typeArguments)) {
if (!checkTypeArgumentConstraints(baseTypeNode, constructor.typeParameters)) {
break;
}
}
@@ -24021,7 +24118,7 @@ namespace ts {
return checkSignatureDeclaration(<SignatureDeclaration>node);
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
return checkMethodDeclaration(<MethodDeclaration>node);
return checkMethodDeclaration(<MethodDeclaration | MethodSignature>node);
case SyntaxKind.Constructor:
return checkConstructorDeclaration(<ConstructorDeclaration>node);
case SyntaxKind.GetAccessor:
@@ -24619,7 +24716,8 @@ namespace ts {
if (entityName.kind === SyntaxKind.Identifier) {
if (isJSXTagName(entityName) && isJsxIntrinsicIdentifier(entityName)) {
return getIntrinsicTagSymbol(<JsxOpeningLikeElement>entityName.parent);
const symbol = getIntrinsicTagSymbol(<JsxOpeningLikeElement>entityName.parent);
return symbol === unknownSymbol ? undefined : symbol;
}
return resolveEntityName(entityName, SymbolFlags.Value, /*ignoreErrors*/ false, /*dontResolveAlias*/ true);
@@ -24829,8 +24927,10 @@ namespace ts {
if (isInRightSideOfImportOrExportAssignment(<Identifier>node)) {
const symbol = getSymbolAtLocation(node);
const declaredType = symbol && getDeclaredTypeOfSymbol(symbol);
return declaredType !== unknownType ? declaredType : getTypeOfSymbol(symbol);
if (symbol) {
const declaredType = getDeclaredTypeOfSymbol(symbol);
return declaredType !== unknownType ? declaredType : getTypeOfSymbol(symbol);
}
}
return unknownType;
@@ -25010,7 +25110,7 @@ namespace ts {
// we prefix depends on the kind of entity. SymbolFlags.ExportHasLocal encompasses all the
// kinds that we do NOT prefix.
const exportSymbol = getMergedSymbol(symbol.exportSymbol);
if (!prefixLocals && exportSymbol.flags & SymbolFlags.ExportHasLocal) {
if (!prefixLocals && exportSymbol.flags & SymbolFlags.ExportHasLocal && !(exportSymbol.flags & SymbolFlags.Variable)) {
return undefined;
}
symbol = exportSymbol;
@@ -26049,7 +26149,7 @@ namespace ts {
}
}
function checkGrammarFunctionLikeDeclaration(node: FunctionLikeDeclaration): boolean {
function checkGrammarFunctionLikeDeclaration(node: FunctionLikeDeclaration | MethodSignature): boolean {
// Prevent cascading error by short-circuit
const file = getSourceFileOfNode(node);
return checkGrammarDecoratorsAndModifiers(node) || checkGrammarTypeParameterList(node.typeParameters, file) ||
@@ -26061,16 +26161,15 @@ namespace ts {
return checkGrammarClassDeclarationHeritageClauses(node) || checkGrammarTypeParameterList(node.typeParameters, file);
}
function checkGrammarArrowFunction(node: FunctionLikeDeclaration, file: SourceFile): boolean {
if (node.kind === SyntaxKind.ArrowFunction) {
const arrowFunction = <ArrowFunction>node;
const startLine = getLineAndCharacterOfPosition(file, arrowFunction.equalsGreaterThanToken.pos).line;
const endLine = getLineAndCharacterOfPosition(file, arrowFunction.equalsGreaterThanToken.end).line;
if (startLine !== endLine) {
return grammarErrorOnNode(arrowFunction.equalsGreaterThanToken, Diagnostics.Line_terminator_not_permitted_before_arrow);
}
function checkGrammarArrowFunction(node: Node, file: SourceFile): boolean {
if (!isArrowFunction(node)) {
return false;
}
return false;
const { equalsGreaterThanToken } = node;
const startLine = getLineAndCharacterOfPosition(file, equalsGreaterThanToken.pos).line;
const endLine = getLineAndCharacterOfPosition(file, equalsGreaterThanToken.end).line;
return startLine !== endLine && grammarErrorOnNode(equalsGreaterThanToken, Diagnostics.Line_terminator_not_permitted_before_arrow);
}
function checkGrammarIndexSignatureParameters(node: SignatureDeclaration): boolean {
@@ -26535,19 +26634,26 @@ namespace ts {
}
}
function checkGrammarMethod(node: MethodDeclaration) {
if (checkGrammarDisallowedModifiersOnObjectLiteralExpressionMethod(node) ||
checkGrammarFunctionLikeDeclaration(node) ||
checkGrammarForGenerator(node)) {
function checkGrammarMethod(node: MethodDeclaration | MethodSignature) {
if (checkGrammarFunctionLikeDeclaration(node)) {
return true;
}
if (node.parent.kind === SyntaxKind.ObjectLiteralExpression) {
if (checkGrammarForInvalidQuestionMark(node.questionToken, Diagnostics.An_object_member_cannot_be_declared_optional)) {
return true;
if (node.kind === SyntaxKind.MethodDeclaration) {
if (node.parent.kind === SyntaxKind.ObjectLiteralExpression) {
// We only disallow modifier on a method declaration if it is a property of object-literal-expression
if (node.modifiers && !(node.modifiers.length === 1 && first(node.modifiers).kind === SyntaxKind.AsyncKeyword)) {
return grammarErrorOnFirstToken(node, Diagnostics.Modifiers_cannot_appear_here);
}
else if (checkGrammarForInvalidQuestionMark(node.questionToken, Diagnostics.An_object_member_cannot_be_declared_optional)) {
return true;
}
else if (node.body === undefined) {
return grammarErrorAtPos(node, node.end - 1, ";".length, Diagnostics._0_expected, "{");
}
}
else if (node.body === undefined) {
return grammarErrorAtPos(node, node.end - 1, ";".length, Diagnostics._0_expected, "{");
if (checkGrammarForGenerator(node)) {
return true;
}
}
@@ -26560,7 +26666,7 @@ namespace ts {
if (node.flags & NodeFlags.Ambient) {
return checkGrammarForInvalidDynamicName(node.name, Diagnostics.A_computed_property_name_in_an_ambient_context_must_refer_to_an_expression_whose_type_is_a_literal_type_or_a_unique_symbol_type);
}
else if (!node.body) {
else if (node.kind === SyntaxKind.MethodDeclaration && !node.body) {
return checkGrammarForInvalidDynamicName(node.name, Diagnostics.A_computed_property_name_in_a_method_overload_must_refer_to_an_expression_whose_type_is_a_literal_type_or_a_unique_symbol_type);
}
}
+43 -3
View File
@@ -6,7 +6,7 @@ namespace ts {
// If changing the text in this section, be sure to test `configureNightly` too.
export const versionMajorMinor = "2.8";
/** The version of the TypeScript compiler release */
export const version = `${versionMajorMinor}.0`;
export const version = `${versionMajorMinor}.0-dev`;
}
namespace ts {
@@ -794,6 +794,18 @@ namespace ts {
return deduplicated;
}
export function insertSorted<T>(array: SortedArray<T>, insert: T, compare: Comparer<T>): void {
if (array.length === 0) {
array.push(insert);
return;
}
const insertIndex = binarySearch(array, insert, identity, compare);
if (insertIndex < 0) {
array.splice(~insertIndex, 0, insert);
}
}
export function sortAndDeduplicate<T>(array: ReadonlyArray<T>, comparer: Comparer<T>, equalityComparer?: EqualityComparer<T>) {
return deduplicateSorted(sort(array, comparer), equalityComparer || comparer);
}
@@ -1454,7 +1466,7 @@ namespace ts {
if (value !== undefined && test(value)) return value;
if (value && typeof (value as any).kind === "number") {
Debug.fail(`Invalid cast. The supplied ${(ts as any).SyntaxKind[(value as any).kind]} did not pass the test '${Debug.getFunctionName(test)}'.`);
Debug.fail(`Invalid cast. The supplied ${Debug.showSyntaxKind(value as any as Node)} did not pass the test '${Debug.getFunctionName(test)}'.`);
}
else {
Debug.fail(`Invalid cast. The supplied value did not pass the test '${Debug.getFunctionName(test)}'.`);
@@ -2889,6 +2901,13 @@ namespace ts {
return value;
}
export function assertEachDefined<T, A extends ReadonlyArray<T>>(value: A, message: string): A {
for (const v of value) {
assertDefined(v, message);
}
return value;
}
export function assertNever(member: never, message?: string, stackCrawlMark?: AnyFunction): never {
return fail(message || `Illegal value: ${member}`, stackCrawlMark || assertNever);
}
@@ -2906,6 +2925,27 @@ namespace ts {
return match ? match[1] : "";
}
}
export function showSymbol(symbol: Symbol): string {
const symbolFlags = (ts as any).SymbolFlags;
return `{ flags: ${symbolFlags ? showFlags(symbol.flags, symbolFlags) : symbol.flags}; declarations: ${map(symbol.declarations, showSyntaxKind)} }`;
}
function showFlags(flags: number, flagsEnum: { [flag: number]: string }): string {
const out = [];
for (let pow = 0; pow <= 30; pow++) {
const n = 1 << pow;
if (flags & n) {
out.push(flagsEnum[n]);
}
}
return out.join("|");
}
export function showSyntaxKind(node: Node): string {
const syntaxKind = (ts as any).SyntaxKind;
return syntaxKind ? syntaxKind[node.kind] : node.kind.toString();
}
}
/** Remove an item from an array, moving everything to its right one space left. */
@@ -2985,7 +3025,7 @@ namespace ts {
*/
export function matchedText(pattern: Pattern, candidate: string): string {
Debug.assert(isPatternMatch(pattern, candidate));
return candidate.substr(pattern.prefix.length, candidate.length - pattern.suffix.length);
return candidate.substring(pattern.prefix.length, candidate.length - pattern.suffix.length);
}
/** Return the object corresponding to the best pattern to match `candidate`. */
+6 -2
View File
@@ -593,7 +593,9 @@ namespace ts {
writeLine();
increaseIndent();
if (node.readonlyToken) {
write("readonly ");
write(node.readonlyToken.kind === SyntaxKind.PlusToken ? "+readonly " :
node.readonlyToken.kind === SyntaxKind.MinusToken ? "-readonly " :
"readonly ");
}
write("[");
writeEntityName(node.typeParameter.name);
@@ -601,7 +603,9 @@ namespace ts {
emitType(node.typeParameter.constraint);
write("]");
if (node.questionToken) {
write("?");
write(node.questionToken.kind === SyntaxKind.PlusToken ? "+?" :
node.questionToken.kind === SyntaxKind.MinusToken ? "-?" :
"?");
}
write(": ");
emitType(node.type);
+5
View File
@@ -1984,6 +1984,11 @@
"category": "Error",
"code": 2566
},
"Enum declarations can only merge with namespace or other enum declarations.": {
"category": "Error",
"code": 2567
},
"JSX element attributes type '{0}' may not be a union type.": {
"category": "Error",
"code": 2600
+9 -3
View File
@@ -1251,14 +1251,20 @@ namespace ts {
}
if (node.readonlyToken) {
emit(node.readonlyToken);
if (node.readonlyToken.kind !== SyntaxKind.ReadonlyKeyword) {
writeKeyword("readonly");
}
writeSpace();
}
writePunctuation("[");
pipelineEmitWithNotification(EmitHint.MappedTypeParameter, node.typeParameter);
writePunctuation("]");
emitIfPresent(node.questionToken);
if (node.questionToken) {
emit(node.questionToken);
if (node.questionToken.kind !== SyntaxKind.QuestionToken) {
writePunctuation("?");
}
}
writePunctuation(":");
writeSpace();
emit(node.type);
+2 -2
View File
@@ -804,7 +804,7 @@ namespace ts {
: node;
}
export function createMappedTypeNode(readonlyToken: ReadonlyToken | undefined, typeParameter: TypeParameterDeclaration, questionToken: QuestionToken | undefined, type: TypeNode | undefined): MappedTypeNode {
export function createMappedTypeNode(readonlyToken: ReadonlyToken | PlusToken | MinusToken | undefined, typeParameter: TypeParameterDeclaration, questionToken: QuestionToken | PlusToken | MinusToken | undefined, type: TypeNode | undefined): MappedTypeNode {
const node = createSynthesizedNode(SyntaxKind.MappedType) as MappedTypeNode;
node.readonlyToken = readonlyToken;
node.typeParameter = typeParameter;
@@ -813,7 +813,7 @@ namespace ts {
return node;
}
export function updateMappedTypeNode(node: MappedTypeNode, readonlyToken: ReadonlyToken | undefined, typeParameter: TypeParameterDeclaration, questionToken: QuestionToken | undefined, type: TypeNode | undefined): MappedTypeNode {
export function updateMappedTypeNode(node: MappedTypeNode, readonlyToken: ReadonlyToken | PlusToken | MinusToken | undefined, typeParameter: TypeParameterDeclaration, questionToken: QuestionToken | PlusToken | MinusToken | undefined, type: TypeNode | undefined): MappedTypeNode {
return node.readonlyToken !== readonlyToken
|| node.typeParameter !== typeParameter
|| node.questionToken !== questionToken
+24 -12
View File
@@ -695,7 +695,7 @@ namespace ts {
else if (token() === SyntaxKind.OpenBraceToken ||
lookAhead(() => token() === SyntaxKind.StringLiteral)) {
result.jsonObject = parseObjectLiteralExpression();
sourceFile.endOfFileToken = parseExpectedToken(SyntaxKind.EndOfFileToken, /*reportAtCurrentPosition*/ false, Diagnostics.Unexpected_token);
sourceFile.endOfFileToken = parseExpectedToken(SyntaxKind.EndOfFileToken, Diagnostics.Unexpected_token);
}
else {
parseExpected(SyntaxKind.OpenBraceToken);
@@ -1135,10 +1135,10 @@ namespace ts {
return undefined;
}
function parseExpectedToken<TKind extends SyntaxKind>(t: TKind, reportAtCurrentPosition: boolean, diagnosticMessage: DiagnosticMessage, arg0?: any): Token<TKind>;
function parseExpectedToken(t: SyntaxKind, reportAtCurrentPosition: boolean, diagnosticMessage: DiagnosticMessage, arg0?: any): Node {
function parseExpectedToken<TKind extends SyntaxKind>(t: TKind, diagnosticMessage?: DiagnosticMessage, arg0?: any): Token<TKind>;
function parseExpectedToken(t: SyntaxKind, diagnosticMessage?: DiagnosticMessage, arg0?: any): Node {
return parseOptionalToken(t) ||
createMissingNode(t, reportAtCurrentPosition, diagnosticMessage, arg0);
createMissingNode(t, /*reportAtCurrentPosition*/ false, diagnosticMessage || Diagnostics._0_expected, arg0 || tokenToString(t));
}
function parseTokenNode<T extends Node>(): T {
@@ -2113,7 +2113,7 @@ namespace ts {
literal = parseTemplateMiddleOrTemplateTail();
}
else {
literal = <TemplateTail>parseExpectedToken(SyntaxKind.TemplateTail, /*reportAtCurrentPosition*/ false, Diagnostics._0_expected, tokenToString(SyntaxKind.CloseBraceToken));
literal = <TemplateTail>parseExpectedToken(SyntaxKind.TemplateTail, Diagnostics._0_expected, tokenToString(SyntaxKind.CloseBraceToken));
}
span.literal = literal;
@@ -2607,6 +2607,9 @@ namespace ts {
function isStartOfMappedType() {
nextToken();
if (token() === SyntaxKind.PlusToken || token() === SyntaxKind.MinusToken) {
return nextToken() === SyntaxKind.ReadonlyKeyword;
}
if (token() === SyntaxKind.ReadonlyKeyword) {
nextToken();
}
@@ -2624,11 +2627,21 @@ namespace ts {
function parseMappedType() {
const node = <MappedTypeNode>createNode(SyntaxKind.MappedType);
parseExpected(SyntaxKind.OpenBraceToken);
node.readonlyToken = parseOptionalToken(SyntaxKind.ReadonlyKeyword);
if (token() === SyntaxKind.ReadonlyKeyword || token() === SyntaxKind.PlusToken || token() === SyntaxKind.MinusToken) {
node.readonlyToken = parseTokenNode();
if (node.readonlyToken.kind !== SyntaxKind.ReadonlyKeyword) {
parseExpectedToken(SyntaxKind.ReadonlyKeyword);
}
}
parseExpected(SyntaxKind.OpenBracketToken);
node.typeParameter = parseMappedTypeParameter();
parseExpected(SyntaxKind.CloseBracketToken);
node.questionToken = parseOptionalToken(SyntaxKind.QuestionToken);
if (token() === SyntaxKind.QuestionToken || token() === SyntaxKind.PlusToken || token() === SyntaxKind.MinusToken) {
node.questionToken = parseTokenNode();
if (node.questionToken.kind !== SyntaxKind.QuestionToken) {
parseExpectedToken(SyntaxKind.QuestionToken);
}
}
node.type = parseTypeAnnotation();
parseSemicolon();
parseExpected(SyntaxKind.CloseBraceToken);
@@ -3242,7 +3255,7 @@ namespace ts {
node.parameters = createNodeArray<ParameterDeclaration>([parameter], parameter.pos, parameter.end);
node.equalsGreaterThanToken = parseExpectedToken(SyntaxKind.EqualsGreaterThanToken, /*reportAtCurrentPosition*/ false, Diagnostics._0_expected, "=>");
node.equalsGreaterThanToken = parseExpectedToken(SyntaxKind.EqualsGreaterThanToken);
node.body = parseArrowFunctionExpressionBody(/*isAsync*/ !!asyncModifier);
return addJSDocComment(finishNode(node));
@@ -3273,7 +3286,7 @@ namespace ts {
// If we have an arrow, then try to parse the body. Even if not, try to parse if we
// have an opening brace, just in case we're in an error state.
const lastToken = token();
arrowFunction.equalsGreaterThanToken = parseExpectedToken(SyntaxKind.EqualsGreaterThanToken, /*reportAtCurrentPosition*/ false, Diagnostics._0_expected, "=>");
arrowFunction.equalsGreaterThanToken = parseExpectedToken(SyntaxKind.EqualsGreaterThanToken);
arrowFunction.body = (lastToken === SyntaxKind.EqualsGreaterThanToken || lastToken === SyntaxKind.OpenBraceToken)
? parseArrowFunctionExpressionBody(isAsync)
: parseIdentifier();
@@ -3539,8 +3552,7 @@ namespace ts {
node.condition = leftOperand;
node.questionToken = questionToken;
node.whenTrue = doOutsideOfContext(disallowInAndDecoratorContext, parseAssignmentExpressionOrHigher);
node.colonToken = parseExpectedToken(SyntaxKind.ColonToken, /*reportAtCurrentPosition*/ false,
Diagnostics._0_expected, tokenToString(SyntaxKind.ColonToken));
node.colonToken = parseExpectedToken(SyntaxKind.ColonToken);
node.whenFalse = nodeIsPresent(node.colonToken)
? parseAssignmentExpressionOrHigher()
: createMissingNode(SyntaxKind.Identifier, /*reportAtCurrentPosition*/ false, Diagnostics._0_expected, tokenToString(SyntaxKind.ColonToken));
@@ -4014,7 +4026,7 @@ namespace ts {
// If it wasn't then just try to parse out a '.' and report an error.
const node = <PropertyAccessExpression>createNode(SyntaxKind.PropertyAccessExpression, expression.pos);
node.expression = expression;
parseExpectedToken(SyntaxKind.DotToken, /*reportAtCurrentPosition*/ false, Diagnostics.super_must_be_followed_by_an_argument_list_or_member_access);
parseExpectedToken(SyntaxKind.DotToken, Diagnostics.super_must_be_followed_by_an_argument_list_or_member_access);
node.name = parseRightSideOfDot(/*allowIdentifierNames*/ true);
return finishNode(node);
}
+6 -3
View File
@@ -120,7 +120,10 @@ namespace ts {
};
export let sys: System = (() => {
const utf8ByteOrderMark = "\u00EF\u00BB\u00BF";
// NodeJS detects "\uFEFF" at the start of the string and *replaces* it with the actual
// byte order mark from the specified encoding. Using any other byte order mark does
// not actually work.
const byteOrderMarkIndicator = "\uFEFF";
function getNodeSystem(): System {
const _fs = require("fs");
@@ -367,7 +370,7 @@ namespace ts {
function writeFile(fileName: string, data: string, writeByteOrderMark?: boolean): void {
// If a BOM is required, emit one
if (writeByteOrderMark) {
data = utf8ByteOrderMark + data;
data = byteOrderMarkIndicator + data;
}
let fd: number;
@@ -572,7 +575,7 @@ namespace ts {
writeFile(path: string, data: string, writeByteOrderMark?: boolean) {
// If a BOM is required, emit one
if (writeByteOrderMark) {
data = utf8ByteOrderMark + data;
data = byteOrderMarkIndicator + data;
}
ChakraHost.writeFile(path, data);
+1 -1
View File
@@ -153,7 +153,7 @@ namespace ts {
if (statement.kind === SyntaxKind.ForOfStatement && (<ForOfStatement>statement).awaitModifier) {
return visitForOfStatement(<ForOfStatement>statement, node);
}
return restoreEnclosingLabel(visitEachChild(node, visitor, context), node);
return restoreEnclosingLabel(visitEachChild(statement, visitor, context), node);
}
return visitEachChild(node, visitor, context);
}
+1 -2
View File
@@ -214,9 +214,8 @@ namespace ts {
* - this is mostly subjective beyond the requirement that the expression not be sideeffecting
*/
export function isSimpleCopiableExpression(expression: Expression) {
return expression.kind === SyntaxKind.StringLiteral ||
return isStringLiteralLike(expression) ||
expression.kind === SyntaxKind.NumericLiteral ||
expression.kind === SyntaxKind.NoSubstitutionTemplateLiteral ||
isKeyword(expression.kind) ||
isIdentifier(expression);
}
+13 -13
View File
@@ -661,6 +661,8 @@ namespace ts {
export type AtToken = Token<SyntaxKind.AtToken>;
export type ReadonlyToken = Token<SyntaxKind.ReadonlyKeyword>;
export type AwaitKeywordToken = Token<SyntaxKind.AwaitKeyword>;
export type PlusToken = Token<SyntaxKind.PlusToken>;
export type MinusToken = Token<SyntaxKind.MinusToken>;
export type Modifier
= Token<SyntaxKind.AbstractKeyword>
@@ -983,6 +985,7 @@ namespace ts {
export interface MethodSignature extends SignatureDeclarationBase, TypeElement {
kind: SyntaxKind.MethodSignature;
parent?: ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode;
name: PropertyName;
}
@@ -997,6 +1000,7 @@ namespace ts {
// of the method, or use helpers like isObjectLiteralMethodDeclaration
export interface MethodDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement, JSDocContainer {
kind: SyntaxKind.MethodDeclaration;
parent?: ClassLikeDeclaration | ObjectLiteralExpression;
name: PropertyName;
body?: FunctionBody;
}
@@ -1156,9 +1160,9 @@ namespace ts {
export interface MappedTypeNode extends TypeNode, Declaration {
kind: SyntaxKind.MappedType;
readonlyToken?: ReadonlyToken;
readonlyToken?: ReadonlyToken | PlusToken | MinusToken;
typeParameter: TypeParameterDeclaration;
questionToken?: QuestionToken;
questionToken?: QuestionToken | PlusToken | MinusToken;
type?: TypeNode;
}
@@ -1174,7 +1178,7 @@ namespace ts {
/* @internal */ singleQuote?: boolean;
}
/* @internal */ export type StringLiteralLike = StringLiteral | NoSubstitutionTemplateLiteral;
export type StringLiteralLike = StringLiteral | NoSubstitutionTemplateLiteral;
// Note: 'brands' in our syntax nodes serve to give us a small amount of nominal typing.
// Consider 'Expression'. Without the brand, 'Expression' is actually no different
@@ -2940,6 +2944,7 @@ namespace ts {
/* @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;
/* @internal */ getTypeArgumentConstraint(node: TypeNode): Type | undefined;
}
/* @internal */
@@ -2983,6 +2988,7 @@ namespace ts {
InObjectTypeLiteral = 1 << 22,
InTypeAlias = 1 << 23, // Writing type in type alias declaration
InInitialEntityName = 1 << 24, // Set when writing the LHS of an entity name or entity name expression
InReverseMappedType = 1 << 25,
}
// Ensure the shared flags between this and `NodeBuilderFlags` stay in alignment
@@ -4117,16 +4123,6 @@ namespace ts {
[option: string]: string[] | boolean | undefined;
}
export interface DiscoverTypingsInfo {
fileNames: string[]; // The file names that belong to the same project.
projectRootPath: string; // The path to the project root directory
safeListPath: string; // The path used to retrieve the safe list
packageNameToTypingLocation: Map<string>; // The map of package names to their cached typing locations
typeAcquisition: TypeAcquisition; // Used to customize the type acquisition process
compilerOptions: CompilerOptions; // Used as a source for typing inference
unresolvedImports: ReadonlyArray<string>; // List of unresolved module ids from imports
}
export enum ModuleKind {
None = 0,
CommonJS = 1,
@@ -5009,6 +5005,10 @@ namespace ts {
newLength: number;
}
export interface SortedArray<T> extends Array<T> {
" __sortedArrayBrand": any;
}
/* @internal */
export interface DiagnosticCollection {
// Adds a diagnostic to this diagnostic collection.
+43 -46
View File
@@ -771,6 +771,8 @@ namespace ts {
return node.parent.kind !== SyntaxKind.VoidExpression;
case SyntaxKind.ExpressionWithTypeArguments:
return !isExpressionWithTypeArgumentsInClassExtendsClause(node);
case SyntaxKind.TypeParameter:
return node.parent.kind === SyntaxKind.MappedType || node.parent.kind === SyntaxKind.InferType;
// Identifiers and qualified names may be type nodes, depending on their context. Climb
// above them to find the lowest container
@@ -1694,17 +1696,19 @@ namespace ts {
node.expression.right.right;
}
function getSingleInitializerOfVariableStatement(node: Node, child?: Node): Node {
return isVariableStatement(node) &&
node.declarationList.declarations.length > 0 &&
(!child || node.declarationList.declarations[0].initializer === child) &&
node.declarationList.declarations[0].initializer;
function getSingleInitializerOfVariableStatementOrPropertyDeclaration(node: Node): Expression | undefined {
switch (node.kind) {
case ts.SyntaxKind.VariableStatement:
const v = getSingleVariableOfVariableStatement(node);
return v && v.initializer;
case ts.SyntaxKind.PropertyDeclaration:
return (node as PropertyDeclaration).initializer;
}
}
function getSingleVariableOfVariableStatement(node: Node, child?: Node): Node {
function getSingleVariableOfVariableStatement(node: Node): VariableDeclaration | undefined {
return isVariableStatement(node) &&
node.declarationList.declarations.length > 0 &&
(!child || node.declarationList.declarations[0] === child) &&
node.declarationList.declarations[0];
}
@@ -1722,7 +1726,7 @@ namespace ts {
function getJSDocCommentsAndTagsWorker(node: Node): void {
const parent = node.parent;
if (parent && (parent.kind === SyntaxKind.PropertyAssignment || getNestedModuleDeclaration(parent))) {
if (parent && (parent.kind === SyntaxKind.PropertyAssignment || parent.kind === SyntaxKind.PropertyDeclaration || getNestedModuleDeclaration(parent))) {
getJSDocCommentsAndTagsWorker(parent);
}
// Try to recognize this pattern when node is initializer of variable declaration and JSDoc comments are on containing variable statement.
@@ -1732,11 +1736,11 @@ namespace ts {
// */
// var x = function(name) { return name.length; }
if (parent && parent.parent &&
(getSingleVariableOfVariableStatement(parent.parent, node) || getSourceOfAssignment(parent.parent))) {
(getSingleVariableOfVariableStatement(parent.parent) === node || getSourceOfAssignment(parent.parent))) {
getJSDocCommentsAndTagsWorker(parent.parent);
}
if (parent && parent.parent && parent.parent.parent &&
(getSingleInitializerOfVariableStatement(parent.parent.parent, node) || getSourceOfDefaultedAssignment(parent.parent.parent))) {
(getSingleInitializerOfVariableStatementOrPropertyDeclaration(parent.parent.parent) === node || getSourceOfDefaultedAssignment(parent.parent.parent))) {
getJSDocCommentsAndTagsWorker(parent.parent.parent);
}
if (isBinaryExpression(node) && getSpecialPropertyAssignmentKind(node) !== SpecialPropertyAssignmentKind.None ||
@@ -1780,7 +1784,7 @@ namespace ts {
const host = getJSDocHost(node);
const decl = getSourceOfDefaultedAssignment(host) ||
getSourceOfAssignment(host) ||
getSingleInitializerOfVariableStatement(host) ||
getSingleInitializerOfVariableStatementOrPropertyDeclaration(host) ||
getSingleVariableOfVariableStatement(host) ||
getNestedModuleDeclaration(host) ||
host;
@@ -1830,6 +1834,7 @@ namespace ts {
case SyntaxKind.ParenthesizedExpression:
case SyntaxKind.ArrayLiteralExpression:
case SyntaxKind.SpreadElement:
case SyntaxKind.NonNullExpression:
node = parent;
break;
case SyntaxKind.ShorthandPropertyAssignment:
@@ -2519,11 +2524,10 @@ namespace ts {
}
export function createDiagnosticCollection(): DiagnosticCollection {
let nonFileDiagnostics: Diagnostic[] = [];
const fileDiagnostics = createMap<Diagnostic[]>();
let nonFileDiagnostics = [] as SortedArray<Diagnostic>;
const filesWithDiagnostics = [] as SortedArray<string>;
const fileDiagnostics = createMap<SortedArray<Diagnostic>>();
let hasReadNonFileDiagnostics = false;
let diagnosticsModified = false;
let modificationCount = 0;
return {
@@ -2543,66 +2547,45 @@ namespace ts {
}
function add(diagnostic: Diagnostic): void {
let diagnostics: Diagnostic[];
let diagnostics: SortedArray<Diagnostic>;
if (diagnostic.file) {
diagnostics = fileDiagnostics.get(diagnostic.file.fileName);
if (!diagnostics) {
diagnostics = [];
diagnostics = [] as SortedArray<Diagnostic>;
fileDiagnostics.set(diagnostic.file.fileName, diagnostics);
insertSorted(filesWithDiagnostics, diagnostic.file.fileName, compareStringsCaseSensitive);
}
}
else {
// If we've already read the non-file diagnostics, do not modify the existing array.
if (hasReadNonFileDiagnostics) {
hasReadNonFileDiagnostics = false;
nonFileDiagnostics = nonFileDiagnostics.slice();
nonFileDiagnostics = nonFileDiagnostics.slice() as SortedArray<Diagnostic>;
}
diagnostics = nonFileDiagnostics;
}
diagnostics.push(diagnostic);
diagnosticsModified = true;
insertSorted(diagnostics, diagnostic, compareDiagnostics);
modificationCount++;
}
function getGlobalDiagnostics(): Diagnostic[] {
sortAndDeduplicate();
hasReadNonFileDiagnostics = true;
return nonFileDiagnostics;
}
function getDiagnostics(fileName?: string): Diagnostic[] {
sortAndDeduplicate();
if (fileName) {
return fileDiagnostics.get(fileName) || [];
}
const allDiagnostics: Diagnostic[] = [];
function pushDiagnostic(d: Diagnostic) {
allDiagnostics.push(d);
const fileDiags = flatMap(filesWithDiagnostics, f => fileDiagnostics.get(f));
if (!nonFileDiagnostics.length) {
return fileDiags;
}
forEach(nonFileDiagnostics, pushDiagnostic);
fileDiagnostics.forEach(diagnostics => {
forEach(diagnostics, pushDiagnostic);
});
return sortAndDeduplicateDiagnostics(allDiagnostics);
}
function sortAndDeduplicate() {
if (!diagnosticsModified) {
return;
}
diagnosticsModified = false;
nonFileDiagnostics = sortAndDeduplicateDiagnostics(nonFileDiagnostics);
fileDiagnostics.forEach((diagnostics, key) => {
fileDiagnostics.set(key, sortAndDeduplicateDiagnostics(diagnostics));
});
fileDiags.unshift(...nonFileDiagnostics);
return fileDiags;
}
}
@@ -3868,6 +3851,10 @@ namespace ts {
export function forSomeAncestorDirectory(directory: string, callback: (directory: string) => boolean): boolean {
return !!forEachAncestorDirectory(directory, d => callback(d) ? true : undefined);
}
export function isUMDExportSymbol(symbol: Symbol) {
return symbol && symbol.declarations && symbol.declarations[0] && isNamespaceExportDeclaration(symbol.declarations[0]);
}
}
namespace ts {
@@ -5246,6 +5233,7 @@ namespace ts {
/**
* True if node is of some token syntax kind.
* For example, this is true for an IfKeyword but not for an IfStatement.
* Literals are considered tokens, except TemplateLiteral, but does include TemplateHead/Middle/Tail.
*/
export function isToken(n: Node): boolean {
return n.kind >= SyntaxKind.FirstToken && n.kind <= SyntaxKind.LastToken;
@@ -6032,4 +6020,13 @@ namespace ts {
return false;
}
}
/* @internal */
export function isTypeReferenceType(node: Node): node is TypeReferenceType {
return node.kind === SyntaxKind.TypeReference || node.kind === SyntaxKind.ExpressionWithTypeArguments;
}
export function isStringLiteralLike(node: Node): node is StringLiteralLike {
return node.kind === SyntaxKind.StringLiteral || node.kind === SyntaxKind.NoSubstitutionTemplateLiteral;
}
}
+62 -60
View File
@@ -420,6 +420,8 @@ namespace ts {
}
}
const initialVersion = 1;
/**
* Creates the watch from the host for root files and compiler options
*/
@@ -429,11 +431,17 @@ namespace ts {
*/
export function createWatchProgram<T extends BuilderProgram>(host: WatchCompilerHostOfConfigFile<T>): WatchOfConfigFile<T>;
export function createWatchProgram<T extends BuilderProgram>(host: WatchCompilerHostOfFilesAndCompilerOptions<T> & WatchCompilerHostOfConfigFile<T>): WatchOfFilesAndCompilerOptions<T> | WatchOfConfigFile<T> {
interface HostFileInfo {
interface FilePresentOnHost {
version: number;
sourceFile: SourceFile;
fileWatcher: FileWatcher;
}
type FileMissingOnHost = number;
interface FilePresenceUnknownOnHost {
version: number;
}
type FileMayBePresentOnHost = FilePresentOnHost | FilePresenceUnknownOnHost;
type HostFileInfo = FilePresentOnHost | FileMissingOnHost | FilePresenceUnknownOnHost;
let builderProgram: T;
let reloadLevel: ConfigFileProgramReloadLevel; // level to indicate if the program needs to be reloaded from config file/just filenames etc
@@ -441,7 +449,7 @@ namespace ts {
let watchedWildcardDirectories: Map<WildcardDirectoryWatcher>; // map of watchers for the wild card directories in the config file
let timerToUpdateProgram: any; // timer callback to recompile the program
const sourceFilesCache = createMap<HostFileInfo | string>(); // Cache that stores the source file and version info
const sourceFilesCache = createMap<HostFileInfo>(); // Cache that stores the source file and version info
let missingFilePathsRequestedForRelease: Path[]; // These paths are held temparirly so that we can remove the entry from source file cache if the file is not tracked by missing files
let hasChangedCompilerOptions = false; // True if the compiler options have changed between compilations
let hasChangedAutomaticTypeDirectiveNames = false; // True if the automatic type directives have changed
@@ -480,14 +488,14 @@ namespace ts {
const watchFilePath = compilerOptions.extendedDiagnostics ? ts.addFilePathWatcherWithLogging : ts.addFilePathWatcher;
const watchDirectoryWorker = compilerOptions.extendedDiagnostics ? ts.addDirectoryWatcherWithLogging : ts.addDirectoryWatcher;
const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
let newLine = updateNewLine();
writeLog(`Current directory: ${currentDirectory} CaseSensitiveFileNames: ${useCaseSensitiveFileNames}`);
if (configFileName) {
watchFile(host, configFileName, scheduleProgramReload, writeLog);
}
const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
let newLine = updateNewLine();
const compilerHost: CompilerHost & ResolutionCacheHost = {
// Members for CompilerHost
getSourceFile: (fileName, languageVersion, onError?, shouldCreateNewSourceFile?) => getVersionedSourceFileByPath(fileName, toPath(fileName), languageVersion, onError, shouldCreateNewSourceFile),
@@ -575,7 +583,9 @@ namespace ts {
// Compile the program
if (loggingEnabled) {
writeLog(`CreatingProgramWith::\n roots: ${JSON.stringify(rootFileNames)}\n options: ${JSON.stringify(compilerOptions)}`);
writeLog(`CreatingProgramWith::`);
writeLog(` roots: ${JSON.stringify(rootFileNames)}`);
writeLog(` options: ${JSON.stringify(compilerOptions)}`);
}
const needsUpdateInTypeRootWatch = hasChangedCompilerOptions || !program;
@@ -627,11 +637,20 @@ namespace ts {
return ts.toPath(fileName, currentDirectory, getCanonicalFileName);
}
function isFileMissingOnHost(hostSourceFile: HostFileInfo): hostSourceFile is FileMissingOnHost {
return typeof hostSourceFile === "number";
}
function isFilePresentOnHost(hostSourceFile: FileMayBePresentOnHost): hostSourceFile is FilePresentOnHost {
return !!(hostSourceFile as FilePresentOnHost).sourceFile;
}
function fileExists(fileName: string) {
const path = toPath(fileName);
const hostSourceFileInfo = sourceFilesCache.get(path);
if (hostSourceFileInfo !== undefined) {
return !isString(hostSourceFileInfo);
// If file is missing on host from cache, we can definitely say file doesnt exist
// otherwise we need to ensure from the disk
if (isFileMissingOnHost(sourceFilesCache.get(path))) {
return true;
}
return directoryStructureHost.fileExists(fileName);
@@ -640,39 +659,42 @@ namespace ts {
function getVersionedSourceFileByPath(fileName: string, path: Path, languageVersion: ScriptTarget, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile {
const hostSourceFile = sourceFilesCache.get(path);
// No source file on the host
if (isString(hostSourceFile)) {
if (isFileMissingOnHost(hostSourceFile)) {
return undefined;
}
// Create new source file if requested or the versions dont match
if (!hostSourceFile || shouldCreateNewSourceFile || hostSourceFile.version.toString() !== hostSourceFile.sourceFile.version) {
if (!hostSourceFile || shouldCreateNewSourceFile || !isFilePresentOnHost(hostSourceFile) || hostSourceFile.version.toString() !== hostSourceFile.sourceFile.version) {
const sourceFile = getNewSourceFile();
if (hostSourceFile) {
if (shouldCreateNewSourceFile) {
hostSourceFile.version++;
}
if (sourceFile) {
hostSourceFile.sourceFile = sourceFile;
// Set the source file and create file watcher now that file was present on the disk
(hostSourceFile as FilePresentOnHost).sourceFile = sourceFile;
sourceFile.version = hostSourceFile.version.toString();
if (!hostSourceFile.fileWatcher) {
hostSourceFile.fileWatcher = watchFilePath(host, fileName, onSourceFileChange, path, writeLog);
if (!(hostSourceFile as FilePresentOnHost).fileWatcher) {
(hostSourceFile as FilePresentOnHost).fileWatcher = watchFilePath(host, fileName, onSourceFileChange, path, writeLog);
}
}
else {
// There is no source file on host any more, close the watch, missing file paths will track it
hostSourceFile.fileWatcher.close();
sourceFilesCache.set(path, hostSourceFile.version.toString());
if (isFilePresentOnHost(hostSourceFile)) {
hostSourceFile.fileWatcher.close();
}
sourceFilesCache.set(path, hostSourceFile.version);
}
}
else {
let fileWatcher: FileWatcher;
if (sourceFile) {
sourceFile.version = "1";
fileWatcher = watchFilePath(host, fileName, onSourceFileChange, path, writeLog);
sourceFilesCache.set(path, { sourceFile, version: 1, fileWatcher });
sourceFile.version = initialVersion.toString();
const fileWatcher = watchFilePath(host, fileName, onSourceFileChange, path, writeLog);
sourceFilesCache.set(path, { sourceFile, version: initialVersion, fileWatcher });
}
else {
sourceFilesCache.set(path, "0");
sourceFilesCache.set(path, initialVersion);
}
}
return sourceFile;
@@ -697,20 +719,22 @@ namespace ts {
}
}
function removeSourceFile(path: Path) {
function nextSourceFileVersion(path: Path) {
const hostSourceFile = sourceFilesCache.get(path);
if (hostSourceFile !== undefined) {
if (!isString(hostSourceFile)) {
hostSourceFile.fileWatcher.close();
resolutionCache.invalidateResolutionOfFile(path);
if (isFileMissingOnHost(hostSourceFile)) {
// The next version, lets set it as presence unknown file
sourceFilesCache.set(path, { version: Number(hostSourceFile) + 1 });
}
else {
hostSourceFile.version++;
}
sourceFilesCache.delete(path);
}
}
function getSourceVersion(path: Path): string {
const hostSourceFile = sourceFilesCache.get(path);
return !hostSourceFile || isString(hostSourceFile) ? undefined : hostSourceFile.version.toString();
return !hostSourceFile || isFileMissingOnHost(hostSourceFile) ? undefined : hostSourceFile.version.toString();
}
function onReleaseOldSourceFile(oldSourceFile: SourceFile, _oldOptions: CompilerOptions) {
@@ -721,10 +745,10 @@ namespace ts {
// there was version update and new source file was created.
if (hostSourceFileInfo) {
// record the missing file paths so they can be removed later if watchers arent tracking them
if (isString(hostSourceFileInfo)) {
if (isFileMissingOnHost(hostSourceFileInfo)) {
(missingFilePathsRequestedForRelease || (missingFilePathsRequestedForRelease = [])).push(oldSourceFile.path);
}
else if (hostSourceFileInfo.sourceFile === oldSourceFile) {
else if ((hostSourceFileInfo as FilePresentOnHost).sourceFile === oldSourceFile) {
sourceFilesCache.delete(oldSourceFile.path);
resolutionCache.removeResolutionsOfFile(oldSourceFile.path);
}
@@ -808,27 +832,12 @@ namespace ts {
function onSourceFileChange(fileName: string, eventKind: FileWatcherEventKind, path: Path) {
updateCachedSystemWithFile(fileName, path, eventKind);
const hostSourceFile = sourceFilesCache.get(path);
if (hostSourceFile) {
// Update the cache
if (eventKind === FileWatcherEventKind.Deleted) {
resolutionCache.invalidateResolutionOfFile(path);
if (!isString(hostSourceFile)) {
hostSourceFile.fileWatcher.close();
sourceFilesCache.set(path, (++hostSourceFile.version).toString());
}
}
else {
// Deleted file created
if (isString(hostSourceFile)) {
sourceFilesCache.delete(path);
}
else {
// file changed - just update the version
hostSourceFile.version++;
}
}
// Update the source file cache
if (eventKind === FileWatcherEventKind.Deleted && sourceFilesCache.get(path)) {
resolutionCache.invalidateResolutionOfFile(path);
}
nextSourceFileVersion(path);
// Update the program
scheduleProgramUpdate();
@@ -856,7 +865,7 @@ namespace ts {
missingFilesMap.delete(missingFilePath);
// Delete the entry in the source files cache so that new source file is created
removeSourceFile(missingFilePath);
nextSourceFileVersion(missingFilePath);
// When a missing file is created, we should update the graph.
scheduleProgramUpdate();
@@ -885,17 +894,10 @@ namespace ts {
const fileOrDirectoryPath = toPath(fileOrDirectory);
// Since the file existance changed, update the sourceFiles cache
const result = cachedDirectoryStructureHost && cachedDirectoryStructureHost.addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath);
// Instead of deleting the file, mark it as changed instead
// Many times node calls add/remove/file when watching directories recursively
const hostSourceFile = sourceFilesCache.get(fileOrDirectoryPath);
if (hostSourceFile && !isString(hostSourceFile) && (result ? result.fileExists : directoryStructureHost.fileExists(fileOrDirectory))) {
hostSourceFile.version++;
}
else {
removeSourceFile(fileOrDirectoryPath);
if (cachedDirectoryStructureHost) {
cachedDirectoryStructureHost.addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath);
}
nextSourceFileVersion(fileOrDirectoryPath);
// If the the added or created file or directory is not supported file name, ignore the file
// But when watched directory is added/removed, we need to reload the file list
+67 -63
View File
@@ -64,7 +64,7 @@ namespace FourSlash {
export interface Range {
fileName: string;
start: number;
pos: number;
end: number;
marker?: Marker;
}
@@ -710,9 +710,9 @@ namespace FourSlash {
if (!range) {
this.raiseError(`goToDefinitionsAndBoundSpan failed - found a TextSpan ${JSON.stringify(defs.textSpan)} when it wasn't expected.`);
}
else if (defs.textSpan.start !== range.start || defs.textSpan.length !== range.end - range.start) {
else if (defs.textSpan.start !== range.pos || defs.textSpan.length !== range.end - range.pos) {
const expected: ts.TextSpan = {
start: range.start, length: range.end - range.start
start: range.pos, length: range.end - range.pos
};
this.raiseError(`goToDefinitionsAndBoundSpan failed - expected to find TextSpan ${JSON.stringify(expected)} but got ${JSON.stringify(defs.textSpan)}`);
}
@@ -855,7 +855,7 @@ namespace FourSlash {
if (completion.insertText !== insertText) {
this.raiseError(`Expected completion insert text at index ${index} to be ${insertText}, got ${completion.insertText}`);
}
const convertedReplacementSpan = replacementSpan && textSpanFromRange(replacementSpan);
const convertedReplacementSpan = replacementSpan && ts.createTextSpanFromRange(replacementSpan);
try {
assert.deepEqual(completion.replacementSpan, convertedReplacementSpan);
}
@@ -1002,8 +1002,8 @@ namespace FourSlash {
private verifyRange(desc: string, expected: Range, actual: ts.Node) {
const actualStart = actual.getStart();
const actualEnd = actual.getEnd();
if (actualStart !== expected.start || actualEnd !== expected.end) {
this.raiseError(`${desc} should be ${expected.start}-${expected.end}, got ${actualStart}-${actualEnd}`);
if (actualStart !== expected.pos || actualEnd !== expected.end) {
this.raiseError(`${desc} should be ${expected.pos}-${expected.end}, got ${actualStart}-${actualEnd}`);
}
}
@@ -1053,7 +1053,7 @@ namespace FourSlash {
if (actualReferences.length > expectedReferences.length) {
// Find the unaccounted-for reference.
for (const actual of actualReferences) {
if (!ts.forEach(expectedReferences, r => r.start === actual.textSpan.start)) {
if (!ts.forEach(expectedReferences, r => r.pos === actual.textSpan.start)) {
this.raiseError(`A reference ${stringify(actual)} is unaccounted for.`);
}
}
@@ -1062,13 +1062,13 @@ namespace FourSlash {
}
for (const reference of expectedReferences) {
const { fileName, start, end } = reference;
const { fileName, pos, end } = reference;
if (reference.marker && reference.marker.data) {
const { isWriteAccess, isDefinition } = reference.marker.data as { isWriteAccess?: boolean, isDefinition?: boolean };
this.verifyReferencesWorker(actualReferences, fileName, start, end, isWriteAccess, isDefinition);
this.verifyReferencesWorker(actualReferences, fileName, pos, end, isWriteAccess, isDefinition);
}
else {
this.verifyReferencesWorker(actualReferences, fileName, start, end);
this.verifyReferencesWorker(actualReferences, fileName, pos, end);
}
}
}
@@ -1092,14 +1092,14 @@ namespace FourSlash {
references: ts.ReferenceEntry[];
}
const fullExpected = ts.map<FourSlashInterface.ReferenceGroup, ReferenceGroupJson>(parts, ({ definition, ranges }) => ({
definition: typeof definition === "string" ? definition : { ...definition, range: textSpanFromRange(definition.range) },
definition: typeof definition === "string" ? definition : { ...definition, range: ts.createTextSpanFromRange(definition.range) },
references: ranges.map<ts.ReferenceEntry>(r => {
const { isWriteAccess = false, isDefinition = false, isInString } = (r.marker && r.marker.data || {}) as { isWriteAccess?: boolean, isDefinition?: boolean, isInString?: true };
return {
isWriteAccess,
isDefinition,
fileName: r.fileName,
textSpan: textSpanFromRange(r),
textSpan: ts.createTextSpanFromRange(r),
...(isInString ? { isInString: true } : undefined),
};
}),
@@ -1280,7 +1280,7 @@ Actual: ${stringify(fullActual)}`);
assert.equal(actualQuickInfoDocumentation, expectedDocumentation || "", this.assertionMessageAtLastKnownMarker("quick info doc"));
}
public verifyQuickInfoDisplayParts(kind: string, kindModifiers: string, textSpan: { start: number; length: number; },
public verifyQuickInfoDisplayParts(kind: string, kindModifiers: string, textSpan: TextSpan,
displayParts: ts.SymbolDisplayPart[],
documentation: ts.SymbolDisplayPart[],
tags: ts.JSDocTagInfo[]
@@ -1346,11 +1346,11 @@ Actual: ${stringify(fullActual)}`);
this.raiseError("Rename location count does not match result.\n\nExpected: " + stringify(ranges) + "\n\nActual:" + stringify(references));
}
ranges = ranges.sort((r1, r2) => r1.start - r2.start);
ranges = ranges.sort((r1, r2) => r1.pos - r2.pos);
references = references.sort((r1, r2) => r1.textSpan.start - r2.textSpan.start);
ts.zipWith(references, ranges, (reference, range) => {
if (reference.textSpan.start !== range.start || ts.textSpanEnd(reference.textSpan) !== range.end) {
if (reference.textSpan.start !== range.pos || ts.textSpanEnd(reference.textSpan) !== range.end) {
this.raiseError("Rename location results do not match.\n\nExpected: " + stringify(ranges) + "\n\nActual:" + stringify(references));
}
});
@@ -1473,9 +1473,9 @@ Actual: ${stringify(fullActual)}`);
}
const expectedRange = this.getRanges()[0];
if (renameInfo.triggerSpan.start !== expectedRange.start ||
if (renameInfo.triggerSpan.start !== expectedRange.pos ||
ts.textSpanEnd(renameInfo.triggerSpan) !== expectedRange.end) {
this.raiseError("Expected triggerSpan [" + expectedRange.start + "," + expectedRange.end + "). Got [" +
this.raiseError("Expected triggerSpan [" + expectedRange.pos + "," + expectedRange.end + "). Got [" +
renameInfo.triggerSpan.start + "," + ts.textSpanEnd(renameInfo.triggerSpan) + ") instead.");
}
}
@@ -1964,7 +1964,7 @@ Actual: ${stringify(fullActual)}`);
for (const range of this.testData.ranges) {
if (range.fileName === fileName) {
range.start = updatePosition(range.start);
range.pos = updatePosition(range.pos);
range.end = updatePosition(range.end);
}
}
@@ -1999,9 +1999,9 @@ Actual: ${stringify(fullActual)}`);
this.goToPosition(len);
}
public goToRangeStart({ fileName, start }: Range) {
public goToRangeStart({ fileName, pos }: Range) {
this.openFile(fileName);
this.goToPosition(start);
this.goToPosition(pos);
}
public goToTypeDefinition(definitionIndex: number) {
@@ -2088,9 +2088,9 @@ Actual: ${stringify(fullActual)}`);
const delayedErrors: string[] = [];
for (const range of ranges) {
const length = range.end - range.start;
const length = range.end - range.pos;
const matchingImpl = ts.find(implementations, impl =>
range.fileName === impl.fileName && range.start === impl.textSpan.start && length === impl.textSpan.length);
range.fileName === impl.fileName && range.pos === impl.textSpan.start && length === impl.textSpan.length);
if (matchingImpl) {
if (range.marker && range.marker.data) {
const expected = <{ displayParts?: ts.SymbolDisplayPart[], parts: string[], kind?: string }>range.marker.data;
@@ -2128,7 +2128,7 @@ Actual: ${stringify(fullActual)}`);
if (unsatisfiedRanges.length) {
error += "\nUnsatisfied ranges:";
for (const range of unsatisfiedRanges) {
error += `\n (${range.start}, ${range.end}) in ${range.fileName}: ${this.rangeText(range)}`;
error += `\n (${range.pos}, ${range.end}) in ${range.fileName}: ${this.rangeText(range)}`;
}
}
@@ -2173,8 +2173,8 @@ Actual: ${stringify(fullActual)}`);
return result;
}
private rangeText({ fileName, start, end }: Range): string {
return this.getFileContent(fileName).slice(start, end);
private rangeText({ fileName, pos, end }: Range): string {
return this.getFileContent(fileName).slice(pos, end);
}
public verifyCaretAtMarker(markerName = "") {
@@ -2345,7 +2345,7 @@ Actual: ${stringify(fullActual)}`);
this.verifyClassifications(expected, actual, this.activeFile.content);
}
public verifyOutliningSpans(spans: TextSpan[]) {
public verifyOutliningSpans(spans: FourSlash.Range[]) {
const actual = this.languageService.getOutliningSpans(this.activeFile.fileName);
if (actual.length !== spans.length) {
@@ -2353,13 +2353,13 @@ Actual: ${stringify(fullActual)}`);
}
ts.zipWith(spans, actual, (expectedSpan, actualSpan, i) => {
if (expectedSpan.start !== actualSpan.textSpan.start || expectedSpan.end !== ts.textSpanEnd(actualSpan.textSpan)) {
this.raiseError(`verifyOutliningSpans failed - span ${(i + 1)} expected: (${expectedSpan.start},${expectedSpan.end}), actual: (${actualSpan.textSpan.start},${ts.textSpanEnd(actualSpan.textSpan)})`);
if (expectedSpan.pos !== actualSpan.textSpan.start || expectedSpan.end !== ts.textSpanEnd(actualSpan.textSpan)) {
this.raiseError(`verifyOutliningSpans failed - span ${(i + 1)} expected: (${expectedSpan.pos},${expectedSpan.end}), actual: (${actualSpan.textSpan.start},${ts.textSpanEnd(actualSpan.textSpan)})`);
}
});
}
public verifyTodoComments(descriptors: string[], spans: TextSpan[]) {
public verifyTodoComments(descriptors: string[], spans: Range[]) {
const actual = this.languageService.getTodoComments(this.activeFile.fileName,
descriptors.map(d => { return { text: d, priority: 0 }; }));
@@ -2370,8 +2370,8 @@ Actual: ${stringify(fullActual)}`);
ts.zipWith(spans, actual, (expectedSpan, actualComment, i) => {
const actualCommentSpan = ts.createTextSpan(actualComment.position, actualComment.message.length);
if (expectedSpan.start !== actualCommentSpan.start || expectedSpan.end !== ts.textSpanEnd(actualCommentSpan)) {
this.raiseError(`verifyOutliningSpans failed - span ${(i + 1)} expected: (${expectedSpan.start},${expectedSpan.end}), actual: (${actualCommentSpan.start},${ts.textSpanEnd(actualCommentSpan)})`);
if (expectedSpan.pos !== actualCommentSpan.start || expectedSpan.end !== ts.textSpanEnd(actualCommentSpan)) {
this.raiseError(`verifyOutliningSpans failed - span ${(i + 1)} expected: (${expectedSpan.pos},${expectedSpan.end}), actual: (${actualCommentSpan.start},${ts.textSpanEnd(actualCommentSpan)})`);
}
});
}
@@ -2549,12 +2549,14 @@ Actual: ${stringify(fullActual)}`);
}
public verifyImportFixAtPosition(expectedTextArray: string[], errorCode?: number) {
const ranges = this.getRanges().filter(r => r.fileName === this.activeFile.fileName);
const { fileName } = this.activeFile;
const ranges = this.getRanges().filter(r => r.fileName === fileName);
if (ranges.length !== 1) {
this.raiseError("Exactly one range should be specified in the testfile.");
}
const range = ts.first(ranges);
const codeFixes = this.getCodeFixes(this.activeFile.fileName, errorCode);
const codeFixes = this.getCodeFixes(fileName, errorCode);
if (codeFixes.length === 0) {
if (expectedTextArray.length !== 0) {
@@ -2564,11 +2566,14 @@ Actual: ${stringify(fullActual)}`);
}
const actualTextArray: string[] = [];
const scriptInfo = this.languageServiceAdapterHost.getScriptInfo(codeFixes[0].changes[0].fileName);
const scriptInfo = this.languageServiceAdapterHost.getScriptInfo(fileName);
const originalContent = scriptInfo.content;
for (const codeFix of codeFixes) {
this.applyEdits(codeFix.changes[0].fileName, codeFix.changes[0].textChanges, /*isFormattingEdit*/ false);
const text = this.rangeText(ranges[0]);
ts.Debug.assert(codeFix.changes.length === 1);
const change = ts.first(codeFix.changes);
ts.Debug.assert(change.fileName === fileName);
this.applyEdits(change.fileName, change.textChanges, /*isFormattingEdit*/ false);
const text = this.rangeText(range);
actualTextArray.push(text);
scriptInfo.updateContent(originalContent);
}
@@ -2830,7 +2835,7 @@ Actual: ${stringify(fullActual)}`);
this.goToRangeStart(r);
this.verifyOccurrencesAtPositionListCount(ranges.length);
for (const range of ranges) {
this.verifyOccurrencesAtPositionListContains(range.fileName, range.start, range.end, isWriteAccess);
this.verifyOccurrencesAtPositionListContains(range.fileName, range.pos, range.end, isWriteAccess);
}
}
}
@@ -2886,8 +2891,8 @@ Actual: ${stringify(fullActual)}`);
}
ts.zipWith(expectedRangesInFile, spansInFile, (expectedRange, span) => {
if (span.textSpan.start !== expectedRange.start || ts.textSpanEnd(span.textSpan) !== expectedRange.end) {
this.raiseError(`verifyDocumentHighlights failed - span does not match, actual: ${stringify(span.textSpan)}, expected: ${expectedRange.start}--${expectedRange.end}`);
if (span.textSpan.start !== expectedRange.pos || ts.textSpanEnd(span.textSpan) !== expectedRange.end) {
this.raiseError(`verifyDocumentHighlights failed - span does not match, actual: ${stringify(span.textSpan)}, expected: ${expectedRange.pos}--${expectedRange.end}`);
}
});
}
@@ -2970,7 +2975,7 @@ Actual: ${stringify(fullActual)}`);
throw new Error("Exactly one refactor range is allowed per test.");
}
const applicableRefactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, { pos: ranges[0].start, end: ranges[0].end });
const applicableRefactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, { pos: ranges[0].pos, end: ranges[0].end });
const isAvailable = applicableRefactors && applicableRefactors.length > 0;
if (negative && isAvailable) {
this.raiseError(`verifyApplicableRefactorAvailableForRange failed - expected no refactor but found some.`);
@@ -3104,6 +3109,9 @@ Actual: ${stringify(fullActual)}`);
hasAction: boolean | undefined,
options: FourSlashInterface.VerifyCompletionListContainsOptions | undefined,
) {
const eq = <T>(a: T, b: T, msg: string) => {
assert.deepEqual(a, b, this.assertionMessageAtLastKnownMarker(msg + " for " + stringify(entryId)));
};
const matchingItems = items.filter(item => item.name === entryId.name && item.source === entryId.source);
if (matchingItems.length === 0) {
const itemsString = items.map(item => stringify({ name: item.name, source: item.source, kind: item.kind })).join(",\n");
@@ -3118,30 +3126,30 @@ Actual: ${stringify(fullActual)}`);
const details = this.getCompletionEntryDetails(item.name, item.source);
if (documentation !== undefined) {
assert.equal(ts.displayPartsToString(details.documentation), documentation, this.assertionMessageAtLastKnownMarker("completion item documentation for " + entryId));
eq(ts.displayPartsToString(details.documentation), documentation, "completion item documentation");
}
if (text !== undefined) {
assert.equal(ts.displayPartsToString(details.displayParts), text, this.assertionMessageAtLastKnownMarker("completion item detail text for " + entryId));
eq(ts.displayPartsToString(details.displayParts), text, "completion item detail text");
}
if (entryId.source === undefined) {
assert.equal(options && options.sourceDisplay, undefined);
eq(options && options.sourceDisplay, /*b*/ undefined, "source display");
}
else {
assert.deepEqual(details.source, [ts.textPart(options!.sourceDisplay)]);
eq(details.source, [ts.textPart(options!.sourceDisplay)], "source display");
}
}
if (kind !== undefined) {
if (typeof kind === "string") {
assert.equal(item.kind, kind, this.assertionMessageAtLastKnownMarker("completion item kind for " + entryId));
eq(item.kind, kind, "completion item kind");
}
else {
if (kind.kind) {
assert.equal(item.kind, kind.kind, this.assertionMessageAtLastKnownMarker("completion item kind for " + entryId));
eq(item.kind, kind.kind, "completion item kind");
}
if (kind.kindModifiers !== undefined) {
assert.equal(item.kindModifiers, kind.kindModifiers, this.assertionMessageAtLastKnownMarker("completion item kindModifiers for " + entryId));
eq(item.kindModifiers, kind.kindModifiers, "completion item kindModifiers");
}
}
}
@@ -3150,14 +3158,14 @@ Actual: ${stringify(fullActual)}`);
if (spanIndex !== undefined) {
const span = this.getTextSpanForRangeAtIndex(spanIndex);
assert.isTrue(TestState.textSpansEqual(span, item.replacementSpan), this.assertionMessageAtLastKnownMarker(stringify(span) + " does not equal " + stringify(item.replacementSpan) + " replacement span for " + entryId));
assert.isTrue(TestState.textSpansEqual(span, item.replacementSpan), this.assertionMessageAtLastKnownMarker(stringify(span) + " does not equal " + stringify(item.replacementSpan) + " replacement span for " + stringify(entryId)));
}
assert.equal(item.hasAction, hasAction, "hasAction");
assert.equal(item.isRecommended, options && options.isRecommended, "isRecommended");
assert.equal(item.insertText, options && options.insertText, "insertText");
eq(item.hasAction, hasAction, "hasAction");
eq(item.isRecommended, options && options.isRecommended, "isRecommended");
eq(item.insertText, options && options.insertText, "insertText");
if (options && options.replacementSpan) { // TODO: GH#21679
assert.deepEqual(item.replacementSpan, options && options.replacementSpan && textSpanFromRange(options.replacementSpan), "replacementSpan");
eq(item.replacementSpan, options && options.replacementSpan && ts.createTextSpanFromRange(options.replacementSpan), "replacementSpan");
}
}
@@ -3208,7 +3216,7 @@ Actual: ${stringify(fullActual)}`);
private getTextSpanForRangeAtIndex(index: number): ts.TextSpan {
const ranges = this.getRanges();
if (ranges && ranges.length > index) {
return textSpanFromRange(ranges[index]);
return ts.createTextSpanFromRange(ranges[index]);
}
else {
this.raiseError("Supplied span index: " + index + " does not exist in range list of size: " + (ranges ? 0 : ranges.length));
@@ -3238,10 +3246,6 @@ Actual: ${stringify(fullActual)}`);
}
}
function textSpanFromRange(range: FourSlash.Range): ts.TextSpan {
return ts.createTextSpanFromBounds(range.start, range.end);
}
export function runFourSlashTest(basePath: string, testType: FourSlashTestType, fileName: string) {
const content = Harness.IO.readFile(fileName);
runFourSlashTestContent(basePath, testType, content, fileName);
@@ -3562,7 +3566,7 @@ ${code}
const range: Range = {
fileName,
start: rangeStart.position,
pos: rangeStart.position,
end: (i - 1) - difference,
marker: rangeStart.marker
};
@@ -3682,7 +3686,7 @@ ${code}
}
// put ranges in the correct order
localRanges = localRanges.sort((a, b) => a.start < b.start ? -1 : 1);
localRanges = localRanges.sort((a, b) => a.pos < b.pos ? -1 : 1);
localRanges.forEach((r) => { ranges.push(r); });
return {
@@ -3755,7 +3759,7 @@ namespace FourSlashInterface {
}
public spans(): ts.TextSpan[] {
return this.ranges().map(r => ts.createTextSpan(r.start, r.end - r.start));
return this.ranges().map(r => ts.createTextSpan(r.pos, r.end - r.pos));
}
public rangesByText(): ts.Map<FourSlash.Range[]> {
@@ -4161,7 +4165,7 @@ namespace FourSlashInterface {
this.state.verifyCurrentNameOrDottedNameSpanText(text);
}
public outliningSpansInCurrentFile(spans: FourSlash.TextSpan[]) {
public outliningSpansInCurrentFile(spans: FourSlash.Range[]) {
this.state.verifyOutliningSpans(spans);
}
@@ -4244,7 +4248,7 @@ namespace FourSlashInterface {
}
public occurrencesAtPositionContains(range: FourSlash.Range, isWriteAccess?: boolean) {
this.state.verifyOccurrencesAtPositionListContains(range.fileName, range.start, range.end, isWriteAccess);
this.state.verifyOccurrencesAtPositionListContains(range.fileName, range.pos, range.end, isWriteAccess);
}
public occurrencesAtPositionCount(expectedCount: number) {
@@ -4309,7 +4313,7 @@ namespace FourSlashInterface {
this.state.verifyRenameLocations(startRanges, options);
}
public verifyQuickInfoDisplayParts(kind: string, kindModifiers: string, textSpan: { start: number; length: number; },
public verifyQuickInfoDisplayParts(kind: string, kindModifiers: string, textSpan: FourSlash.TextSpan,
displayParts: ts.SymbolDisplayPart[], documentation: ts.SymbolDisplayPart[], tags: ts.JSDocTagInfo[]) {
this.state.verifyQuickInfoDisplayParts(kind, kindModifiers, textSpan, displayParts, documentation, tags);
}
+2 -1
View File
@@ -170,7 +170,8 @@ namespace RWC {
});
it("has the expected emitted code", () => {
it("has the expected emitted code", function(this: Mocha.ITestCallbackContext) {
this.timeout(10000); // Allow long timeouts for RWC js verification
Harness.Baseline.runMultifileBaseline(baseName, "", () => {
return Harness.Compiler.iterateOutputs(compilerResult.files);
}, baselineOpts, [".js", ".jsx"]);
+2 -2
View File
@@ -52,7 +52,7 @@ class TypeWriterWalker {
}
private *visitNode(node: ts.Node, isSymbolWalk: boolean): IterableIterator<TypeWriterResult> {
if (ts.isExpressionNode(node) || node.kind === ts.SyntaxKind.Identifier) {
if (ts.isExpressionNode(node) || node.kind === ts.SyntaxKind.Identifier || ts.isDeclarationName(node)) {
const result = this.writeTypeOrSymbol(node, isSymbolWalk);
if (result) {
yield result;
@@ -122,4 +122,4 @@ class TypeWriterWalker {
symbol: symbolString
};
}
}
}
+12
View File
@@ -546,6 +546,18 @@ var q = /*b*/ //c
/*g*/ + /*h*/ //i
/*j*/ 2|] /*k*/ //l
/*m*/; /*n*/ //o`);
testExtractFunction("extractFunction_NamelessClass", `
export default class {
M() {
[#|1 + 1|];
}
}`);
testExtractFunction("extractFunction_NoDeclarations", `
function F() {
[#|arguments.length|]; // arguments has no declaration
}`);
});
function testExtractFunction(caption: string, text: string, includeLib?: boolean) {
+6 -6
View File
@@ -9,7 +9,7 @@ namespace ts {
if (!selectionRange) {
throw new Error(`Test ${s} does not specify selection range`);
}
const result = refactor.extractSymbol.getRangeToExtract(file, createTextSpanFromBounds(selectionRange.start, selectionRange.end));
const result = refactor.extractSymbol.getRangeToExtract(file, createTextSpanFromRange(selectionRange));
assert(result.targetRange === undefined, "failure expected");
const sortedErrors = result.errors.map(e => <string>e.messageText).sort();
assert.deepEqual(sortedErrors, expectedErrors.sort(), "unexpected errors");
@@ -23,19 +23,19 @@ namespace ts {
if (!selectionRange) {
throw new Error(`Test ${s} does not specify selection range`);
}
const result = refactor.extractSymbol.getRangeToExtract(f, createTextSpanFromBounds(selectionRange.start, selectionRange.end));
const result = refactor.extractSymbol.getRangeToExtract(f, createTextSpanFromRange(selectionRange));
const expectedRange = t.ranges.get("extracted");
if (expectedRange) {
let start: number, end: number;
let pos: number, end: number;
if (ts.isArray(result.targetRange.range)) {
start = result.targetRange.range[0].getStart(f);
pos = result.targetRange.range[0].getStart(f);
end = ts.lastOrUndefined(result.targetRange.range).getEnd();
}
else {
start = result.targetRange.range.getStart(f);
pos = result.targetRange.range.getStart(f);
end = result.targetRange.range.getEnd();
}
assert.equal(start, expectedRange.start, "incorrect start of range");
assert.equal(pos, expectedRange.pos, "incorrect pos of range");
assert.equal(end, expectedRange.end, "incorrect end of range");
}
else {
+8 -8
View File
@@ -2,13 +2,13 @@
/// <reference path="tsserverProjectSystem.ts" />
namespace ts {
export interface Range {
start: number;
interface Range {
pos: number;
end: number;
name: string;
}
export interface Test {
interface Test {
source: string;
ranges: Map<Range>;
}
@@ -34,7 +34,7 @@ namespace ts {
const name = s === e
? source.charCodeAt(saved + 1) === CharacterCodes.hash ? "selection" : "extracted"
: source.substring(s, e);
activeRanges.push({ name, start: text.length, end: undefined });
activeRanges.push({ name, pos: text.length, end: undefined });
lastPos = pos;
continue;
}
@@ -123,12 +123,12 @@ namespace ts {
cancellationToken: { throwIfCancellationRequested: noop, isCancellationRequested: returnFalse },
program,
file: sourceFile,
startPosition: selectionRange.start,
startPosition: selectionRange.pos,
endPosition: selectionRange.end,
host: notImplementedHost,
formatContext: formatting.getFormatContext(testFormatOptions),
};
const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromBounds(selectionRange.start, selectionRange.end));
const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromRange(selectionRange));
assert.equal(rangeToExtract.errors, undefined, rangeToExtract.errors && "Range error: " + rangeToExtract.errors[0].messageText);
const infos = refactor.extractSymbol.getAvailableActions(context);
const actions = find(infos, info => info.description === description.message).actions;
@@ -186,12 +186,12 @@ namespace ts {
cancellationToken: { throwIfCancellationRequested: noop, isCancellationRequested: returnFalse },
program,
file: sourceFile,
startPosition: selectionRange.start,
startPosition: selectionRange.pos,
endPosition: selectionRange.end,
host: notImplementedHost,
formatContext: formatting.getFormatContext(testFormatOptions),
};
const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromBounds(selectionRange.start, selectionRange.end));
const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromRange(selectionRange));
assert.isUndefined(rangeToExtract.errors, rangeToExtract.errors && "Range error: " + rangeToExtract.errors[0].messageText);
const infos = refactor.extractSymbol.getAvailableActions(context);
assert.isUndefined(find(infos, info => info.description === description.message));
+32
View File
@@ -1719,6 +1719,38 @@ namespace ts.tscWatch {
return [files[0]];
}
});
it("file is deleted and created as part of change", () => {
const projectLocation = "/home/username/project";
const file: FileOrFolder = {
path: `${projectLocation}/app/file.ts`,
content: "var a = 10;"
};
const fileJs = `${projectLocation}/app/file.js`;
const configFile: FileOrFolder = {
path: `${projectLocation}/tsconfig.json`,
content: JSON.stringify({
include: [
"app/**/*.ts"
]
})
};
const files = [file, configFile, libFile];
const host = createWatchedSystem(files, { currentDirectory: projectLocation, useCaseSensitiveFileNames: true });
createWatchOfConfigFile("tsconfig.json", host);
verifyProgram();
file.content += "\nvar b = 10;";
host.reloadFS(files, { invokeFileDeleteCreateAsPartInsteadOfChange: true });
host.runQueuedTimeoutCallbacks();
verifyProgram();
function verifyProgram() {
assert.isTrue(host.fileExists(fileJs));
assert.equal(host.readFile(fileJs), file.content + "\n");
}
});
});
describe("tsc-watch module resolution caching", () => {
+158 -26
View File
@@ -63,7 +63,7 @@ namespace ts.projectSystem {
readonly globalTypingsCacheLocation: string,
throttleLimit: number,
installTypingHost: server.ServerHost,
readonly typesRegistry = createMap<void>(),
readonly typesRegistry = createMap<MapLike<string>>(),
log?: TI.Log) {
super(installTypingHost, globalTypingsCacheLocation, safeList.path, customTypesMap.path, throttleLimit, log);
}
@@ -126,6 +126,25 @@ namespace ts.projectSystem {
return JSON.stringify({ dependencies });
}
export function createTypesRegistry(...list: string[]): Map<MapLike<string>> {
const versionMap = {
"latest": "1.3.0",
"ts2.0": "1.0.0",
"ts2.1": "1.0.0",
"ts2.2": "1.2.0",
"ts2.3": "1.3.0",
"ts2.4": "1.3.0",
"ts2.5": "1.3.0",
"ts2.6": "1.3.0",
"ts2.7": "1.3.0"
};
const map = createMap<MapLike<string>>();
for (const l of list) {
map.set(l, versionMap);
}
return map;
}
export function toExternalFile(fileName: string): protocol.ExternalFile {
return { fileName };
}
@@ -2092,9 +2111,6 @@ namespace ts.projectSystem {
/*closedFiles*/ undefined);
checkNumberOfProjects(projectService, { inferredProjects: 1 });
const changedFiles = projectService.getChangedFiles_TestOnly();
assert(changedFiles && changedFiles.length === 1, `expected 1 changed file, got ${JSON.stringify(changedFiles && changedFiles.length || 0)}`);
projectService.ensureInferredProjectsUpToDate_TestOnly();
checkNumberOfProjects(projectService, { inferredProjects: 2 });
});
@@ -2885,9 +2901,109 @@ namespace ts.projectSystem {
tags: []
});
});
it("files opened, closed affecting multiple projects", () => {
const file: FileOrFolder = {
path: "/a/b/projects/config/file.ts",
content: `import {a} from "../files/file1"; export let b = a;`
};
const config: FileOrFolder = {
path: "/a/b/projects/config/tsconfig.json",
content: ""
};
const filesFile1: FileOrFolder = {
path: "/a/b/projects/files/file1.ts",
content: "export let a = 10;"
};
const filesFile2: FileOrFolder = {
path: "/a/b/projects/files/file2.ts",
content: "export let aa = 10;"
};
const files = [config, file, filesFile1, filesFile2, libFile];
const host = createServerHost(files);
const session = createSession(host);
// Create configured project
session.executeCommandSeq<protocol.OpenRequest>({
command: protocol.CommandTypes.Open,
arguments: {
file: file.path
}
});
const projectService = session.getProjectService();
const configuredProject = projectService.configuredProjects.get(config.path);
verifyConfiguredProject();
// open files/file1 = should not create another project
session.executeCommandSeq<protocol.OpenRequest>({
command: protocol.CommandTypes.Open,
arguments: {
file: filesFile1.path
}
});
verifyConfiguredProject();
// Close the file = should still have project
session.executeCommandSeq<protocol.CloseRequest>({
command: protocol.CommandTypes.Close,
arguments: {
file: file.path
}
});
verifyConfiguredProject();
// Open files/file2 - should create inferred project and close configured project
session.executeCommandSeq<protocol.OpenRequest>({
command: protocol.CommandTypes.Open,
arguments: {
file: filesFile2.path
}
});
checkNumberOfProjects(projectService, { inferredProjects: 1 });
checkProjectActualFiles(projectService.inferredProjects[0], [libFile.path, filesFile2.path]);
// Actions on file1 would result in assert
session.executeCommandSeq<protocol.OccurrencesRequest>({
command: protocol.CommandTypes.Occurrences,
arguments: {
file: filesFile1.path,
line: 1,
offset: filesFile1.content.indexOf("a")
}
});
function verifyConfiguredProject() {
checkNumberOfProjects(projectService, { configuredProjects: 1 });
checkProjectActualFiles(configuredProject, [file.path, filesFile1.path, libFile.path, config.path]);
}
});
});
describe("tsserverProjectSystem Proper errors", () => {
function createErrorLogger() {
let hasError = false;
const errorLogger: server.Logger = {
close: noop,
hasLevel: () => true,
loggingEnabled: () => true,
perftrc: noop,
info: noop,
msg: (_s, type) => {
if (type === server.Msg.Err) {
hasError = true;
}
},
startGroup: noop,
endGroup: noop,
getLogFileName: (): string => undefined
};
return {
errorLogger,
hasError: () => hasError
};
}
it("document is not contained in project", () => {
const file1 = {
path: "/a/b/app.ts",
@@ -2910,23 +3026,8 @@ namespace ts.projectSystem {
describe("when opening new file that doesnt exist on disk yet", () => {
function verifyNonExistentFile(useProjectRoot: boolean) {
const host = createServerHost([libFile]);
let hasError = false;
const errLogger: server.Logger = {
close: noop,
hasLevel: () => true,
loggingEnabled: () => true,
perftrc: noop,
info: noop,
msg: (_s, type) => {
if (type === server.Msg.Err) {
hasError = true;
}
},
startGroup: noop,
endGroup: noop,
getLogFileName: (): string => undefined
};
const session = createSession(host, { canUseEvents: true, logger: errLogger, useInferredProjectPerProjectRoot: true });
const { hasError, errorLogger } = createErrorLogger();
const session = createSession(host, { canUseEvents: true, logger: errorLogger, useInferredProjectPerProjectRoot: true });
const folderPath = "/user/someuser/projects/someFolder";
const projectService = session.getProjectService();
@@ -2967,13 +3068,13 @@ namespace ts.projectSystem {
// Run the last one = get error request
host.runQueuedTimeoutCallbacks(newTimeoutId);
assert.isFalse(hasError);
assert.isFalse(hasError());
host.checkTimeoutQueueLength(2);
checkErrorMessage(session, "syntaxDiag", { file: untitledFile, diagnostics: [] });
session.clearMessages();
host.runQueuedImmediateCallbacks();
assert.isFalse(hasError);
assert.isFalse(hasError());
checkErrorMessage(session, "semanticDiag", { file: untitledFile, diagnostics: [] });
checkCompleteEvent(session, 2, expectedSequenceId);
@@ -3039,6 +3140,31 @@ namespace ts.projectSystem {
session.clearMessages();
}
});
it("Getting errors before opening file", () => {
const file: FileOrFolder = {
path: "/a/b/project/file.ts",
content: "let x: number = false;"
};
const host = createServerHost([file, libFile]);
const { hasError, errorLogger } = createErrorLogger();
const session = createSession(host, { canUseEvents: true, logger: errorLogger });
session.clearMessages();
const expectedSequenceId = session.getNextSeq();
session.executeCommandSeq<protocol.GeterrRequest>({
command: server.CommandNames.Geterr,
arguments: {
delay: 0,
files: [file.path]
}
});
host.runQueuedImmediateCallbacks();
assert.isFalse(hasError());
checkCompleteEvent(session, 1, expectedSequenceId);
session.clearMessages();
});
});
describe("tsserverProjectSystem autoDiscovery", () => {
@@ -6649,12 +6775,18 @@ namespace ts.projectSystem {
},
})
};
const typingsCachePackageLockJson: FileOrFolder = {
path: `${typingsCache}/package-lock.json`,
content: JSON.stringify({
dependencies: {
},
})
};
const files = [file, packageJsonInCurrentDirectory, packageJsonOfPkgcurrentdirectory, indexOfPkgcurrentdirectory, typingsCachePackageJson];
const files = [file, packageJsonInCurrentDirectory, packageJsonOfPkgcurrentdirectory, indexOfPkgcurrentdirectory, typingsCachePackageJson, typingsCachePackageLockJson];
const host = createServerHost(files, { currentDirectory });
const typesRegistry = createMap<void>();
typesRegistry.set("pkgcurrentdirectory", void 0);
const typesRegistry = createTypesRegistry("pkgcurrentdirectory");
const typingsInstaller = new TestTypingsInstaller(typingsCache, /*throttleLimit*/ 5, host, typesRegistry);
const projectService = createProjectService(host, { typingsInstaller });
+246 -18
View File
@@ -1,6 +1,7 @@
/// <reference path="../harness.ts" />
/// <reference path="./tsserverProjectSystem.ts" />
/// <reference path="../../server/typingsInstaller/typingsInstaller.ts" />
/// <reference path="../../services/semver.ts" />
namespace ts.projectSystem {
import TI = server.typingsInstaller;
@@ -10,15 +11,7 @@ namespace ts.projectSystem {
interface InstallerParams {
globalTypingsCacheLocation?: string;
throttleLimit?: number;
typesRegistry?: Map<void>;
}
function createTypesRegistry(...list: string[]): Map<void> {
const map = createMap<void>();
for (const l of list) {
map.set(l, undefined);
}
return map;
typesRegistry?: Map<MapLike<string>>;
}
class Installer extends TestTypingsInstaller {
@@ -50,7 +43,7 @@ namespace ts.projectSystem {
const logs: string[] = [];
return {
log(message) {
logs.push(message);
logs.push(message);
},
finish() {
return logs;
@@ -1053,6 +1046,142 @@ namespace ts.projectSystem {
const version2 = proj.getCachedUnresolvedImportsPerFile_TestOnly().getVersion();
assert.notEqual(version1, version2, "set of unresolved imports should change");
});
it("expired cache entry (inferred project, should install typings)", () => {
const file1 = {
path: "/a/b/app.js",
content: ""
};
const packageJson = {
path: "/a/b/package.json",
content: JSON.stringify({
name: "test",
dependencies: {
jquery: "^3.1.0"
}
})
};
const jquery = {
path: "/a/data/node_modules/@types/jquery/index.d.ts",
content: "declare const $: { x: number }"
};
const cacheConfig = {
path: "/a/data/package.json",
content: JSON.stringify({
dependencies: {
"types-registry": "^0.1.317"
},
devDependencies: {
"@types/jquery": "^1.0.0"
}
})
};
const cacheLockConfig = {
path: "/a/data/package-lock.json",
content: JSON.stringify({
dependencies: {
"@types/jquery": {
version: "1.0.0"
}
}
})
};
const host = createServerHost([file1, packageJson, jquery, cacheConfig, cacheLockConfig]);
const installer = new (class extends Installer {
constructor() {
super(host, { typesRegistry: createTypesRegistry("jquery") });
}
installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) {
const installedTypings = ["@types/jquery"];
const typingFiles = [jquery];
executeCommand(this, host, installedTypings, typingFiles, cb);
}
})();
const projectService = createProjectService(host, { useSingleInferredProject: true, typingsInstaller: installer });
projectService.openClientFile(file1.path);
checkNumberOfProjects(projectService, { inferredProjects: 1 });
const p = projectService.inferredProjects[0];
checkProjectActualFiles(p, [file1.path]);
installer.installAll(/*expectedCount*/ 1);
checkNumberOfProjects(projectService, { inferredProjects: 1 });
checkProjectActualFiles(p, [file1.path, jquery.path]);
});
it("non-expired cache entry (inferred project, should not install typings)", () => {
const file1 = {
path: "/a/b/app.js",
content: ""
};
const packageJson = {
path: "/a/b/package.json",
content: JSON.stringify({
name: "test",
dependencies: {
jquery: "^3.1.0"
}
})
};
const timestamps = {
path: "/a/data/timestamps.json",
content: JSON.stringify({
entries: {
"@types/jquery": Date.now()
}
})
};
const cacheConfig = {
path: "/a/data/package.json",
content: JSON.stringify({
dependencies: {
"types-registry": "^0.1.317"
},
devDependencies: {
"@types/jquery": "^1.3.0"
}
})
};
const cacheLockConfig = {
path: "/a/data/package-lock.json",
content: JSON.stringify({
dependencies: {
"@types/jquery": {
version: "1.3.0"
}
}
})
};
const jquery = {
path: "/a/data/node_modules/@types/jquery/index.d.ts",
content: "declare const $: { x: number }"
};
const host = createServerHost([file1, packageJson, timestamps, cacheConfig, cacheLockConfig, jquery]);
const installer = new (class extends Installer {
constructor() {
super(host, { typesRegistry: createTypesRegistry("jquery") });
}
installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) {
const installedTypings: string[] = [];
const typingFiles: FileOrFolder[] = [];
executeCommand(this, host, installedTypings, typingFiles, cb);
}
})();
const projectService = createProjectService(host, { useSingleInferredProject: true, typingsInstaller: installer });
projectService.openClientFile(file1.path);
checkNumberOfProjects(projectService, { inferredProjects: 1 });
const p = projectService.inferredProjects[0];
checkProjectActualFiles(p, [file1.path]);
installer.installAll(/*expectedCount*/ 0);
checkNumberOfProjects(projectService, { inferredProjects: 1 });
checkProjectActualFiles(p, [file1.path]);
});
});
describe("Validate package name:", () => {
@@ -1132,7 +1261,7 @@ namespace ts.projectSystem {
const host = createServerHost([app, jquery, chroma]);
const logger = trackingLogger();
const result = JsTyping.discoverTypings(host, logger.log, [app.path, jquery.path, chroma.path], getDirectoryPath(<Path>app.path), safeList, emptyMap, { enable: true }, emptyArray);
const result = JsTyping.discoverTypings(host, logger.log, [app.path, jquery.path, chroma.path], getDirectoryPath(<Path>app.path), safeList, emptyMap, { enable: true }, emptyArray, emptyMap);
const finish = logger.finish();
assert.deepEqual(finish, [
'Inferred typings from file names: ["jquery","chroma-js"]',
@@ -1148,11 +1277,11 @@ namespace ts.projectSystem {
content: ""
};
const host = createServerHost([f]);
const cache = createMap<string>();
const cache = createMap<JsTyping.CachedTyping>();
for (const name of JsTyping.nodeCoreModuleList) {
const logger = trackingLogger();
const result = JsTyping.discoverTypings(host, logger.log, [f.path], getDirectoryPath(<Path>f.path), emptySafeList, cache, { enable: true }, [name, "somename"]);
const result = JsTyping.discoverTypings(host, logger.log, [f.path], getDirectoryPath(<Path>f.path), emptySafeList, cache, { enable: true }, [name, "somename"], emptyMap);
assert.deepEqual(logger.finish(), [
'Inferred typings from unresolved imports: ["node","somename"]',
'Result: {"cachedTypingPaths":[],"newTypingNames":["node","somename"],"filesToWatch":["/a/b/bower_components","/a/b/node_modules"]}',
@@ -1171,9 +1300,10 @@ namespace ts.projectSystem {
content: ""
};
const host = createServerHost([f, node]);
const cache = createMapFromTemplate<string>({ node: node.path });
const cache = createMapFromTemplate<JsTyping.CachedTyping>({ node: { typingLocation: node.path, version: Semver.parse("1.3.0") } });
const registry = createTypesRegistry("node");
const logger = trackingLogger();
const result = JsTyping.discoverTypings(host, logger.log, [f.path], getDirectoryPath(<Path>f.path), emptySafeList, cache, { enable: true }, ["fs", "bar"]);
const result = JsTyping.discoverTypings(host, logger.log, [f.path], getDirectoryPath(<Path>f.path), emptySafeList, cache, { enable: true }, ["fs", "bar"], registry);
assert.deepEqual(logger.finish(), [
'Inferred typings from unresolved imports: ["node","bar"]',
'Result: {"cachedTypingPaths":["/a/b/node.d.ts"],"newTypingNames":["bar"],"filesToWatch":["/a/b/bower_components","/a/b/node_modules"]}',
@@ -1196,9 +1326,9 @@ namespace ts.projectSystem {
content: JSON.stringify({ name: "b" }),
};
const host = createServerHost([app, a, b]);
const cache = createMap<string>();
const cache = createMap<JsTyping.CachedTyping>();
const logger = trackingLogger();
const result = JsTyping.discoverTypings(host, logger.log, [app.path], getDirectoryPath(<Path>app.path), emptySafeList, cache, { enable: true }, /*unresolvedImports*/ []);
const result = JsTyping.discoverTypings(host, logger.log, [app.path], getDirectoryPath(<Path>app.path), emptySafeList, cache, { enable: true }, /*unresolvedImports*/ [], emptyMap);
assert.deepEqual(logger.finish(), [
'Searching for typing names in /node_modules; all files: ["/node_modules/a/package.json"]',
' Found package names: ["a"]',
@@ -1211,6 +1341,94 @@ namespace ts.projectSystem {
filesToWatch: ["/bower_components", "/node_modules"],
});
});
it("should install expired typings", () => {
const app = {
path: "/a/app.js",
content: ""
};
const cachePath = "/a/cache/";
const commander = {
path: cachePath + "node_modules/@types/commander/index.d.ts",
content: "export let x: number"
};
const node = {
path: cachePath + "node_modules/@types/node/index.d.ts",
content: "export let y: number"
};
const host = createServerHost([app]);
const cache = createMapFromTemplate<JsTyping.CachedTyping>({
node: { typingLocation: node.path, version: Semver.parse("1.3.0") },
commander: { typingLocation: commander.path, version: Semver.parse("1.0.0") }
});
const registry = createTypesRegistry("node", "commander");
const logger = trackingLogger();
const result = JsTyping.discoverTypings(host, logger.log, [app.path], getDirectoryPath(<Path>app.path), emptySafeList, cache, { enable: true }, ["http", "commander"], registry);
assert.deepEqual(logger.finish(), [
'Inferred typings from unresolved imports: ["node","commander"]',
'Result: {"cachedTypingPaths":["/a/cache/node_modules/@types/node/index.d.ts"],"newTypingNames":["commander"],"filesToWatch":["/a/bower_components","/a/node_modules"]}',
]);
assert.deepEqual(result.cachedTypingPaths, [node.path]);
assert.deepEqual(result.newTypingNames, ["commander"]);
});
it("should install expired typings with prerelease version of tsserver", () => {
const app = {
path: "/a/app.js",
content: ""
};
const cachePath = "/a/cache/";
const node = {
path: cachePath + "node_modules/@types/node/index.d.ts",
content: "export let y: number"
};
const host = createServerHost([app]);
const cache = createMapFromTemplate<JsTyping.CachedTyping>({
node: { typingLocation: node.path, version: Semver.parse("1.0.0") }
});
const registry = createTypesRegistry("node");
registry.delete(`ts${ts.versionMajorMinor}`);
const logger = trackingLogger();
const result = JsTyping.discoverTypings(host, logger.log, [app.path], getDirectoryPath(<Path>app.path), emptySafeList, cache, { enable: true }, ["http"], registry);
assert.deepEqual(logger.finish(), [
'Inferred typings from unresolved imports: ["node"]',
'Result: {"cachedTypingPaths":[],"newTypingNames":["node"],"filesToWatch":["/a/bower_components","/a/node_modules"]}',
]);
assert.deepEqual(result.cachedTypingPaths, []);
assert.deepEqual(result.newTypingNames, ["node"]);
});
it("prerelease typings are properly handled", () => {
const app = {
path: "/a/app.js",
content: ""
};
const cachePath = "/a/cache/";
const commander = {
path: cachePath + "node_modules/@types/commander/index.d.ts",
content: "export let x: number"
};
const node = {
path: cachePath + "node_modules/@types/node/index.d.ts",
content: "export let y: number"
};
const host = createServerHost([app]);
const cache = createMapFromTemplate<JsTyping.CachedTyping>({
node: { typingLocation: node.path, version: Semver.parse("1.3.0-next.0") },
commander: { typingLocation: commander.path, version: Semver.parse("1.3.0-next.0") }
});
const registry = createTypesRegistry("node", "commander");
registry.get("node")[`ts${ts.versionMajorMinor}`] = "1.3.0-next.1";
const logger = trackingLogger();
const result = JsTyping.discoverTypings(host, logger.log, [app.path], getDirectoryPath(<Path>app.path), emptySafeList, cache, { enable: true }, ["http", "commander"], registry);
assert.deepEqual(logger.finish(), [
'Inferred typings from unresolved imports: ["node","commander"]',
'Result: {"cachedTypingPaths":[],"newTypingNames":["node","commander"],"filesToWatch":["/a/bower_components","/a/node_modules"]}',
]);
assert.deepEqual(result.cachedTypingPaths, []);
assert.deepEqual(result.newTypingNames, ["node", "commander"]);
});
});
describe("telemetry events", () => {
@@ -1273,12 +1491,22 @@ namespace ts.projectSystem {
path: "/a/package.json",
content: JSON.stringify({ dependencies: { commander: "1.0.0" } })
};
const packageLockFile = {
path: "/a/cache/package-lock.json",
content: JSON.stringify({
dependencies: {
"@types/commander": {
version: "1.0.0"
}
}
})
};
const cachePath = "/a/cache/";
const commander = {
path: cachePath + "node_modules/@types/commander/index.d.ts",
content: "export let x: number"
};
const host = createServerHost([f1, packageFile]);
const host = createServerHost([f1, packageFile, packageLockFile]);
let beginEvent: server.BeginInstallTypes;
let endEvent: server.EndInstallTypes;
const installer = new (class extends Installer {
+19 -7
View File
@@ -246,8 +246,12 @@ interface Array<T> {}`
}
export interface ReloadWatchInvokeOptions {
/** Invokes the directory watcher for the parent instead of the file changed */
invokeDirectoryWatcherInsteadOfFileChanged: boolean;
/** When new file is created, do not invoke watches for it */
ignoreWatchInvokedWithTriggerAsFileCreate: boolean;
/** Invoke the file delete, followed by create instead of file changed */
invokeFileDeleteCreateAsPartInsteadOfChange: boolean;
}
export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost, ModuleResolutionHost {
@@ -315,12 +319,18 @@ interface Array<T> {}`
if (isString(fileOrDirectory.content)) {
// Update file
if (currentEntry.content !== fileOrDirectory.content) {
currentEntry.content = fileOrDirectory.content;
if (options && options.invokeDirectoryWatcherInsteadOfFileChanged) {
this.invokeDirectoryWatcher(getDirectoryPath(currentEntry.fullPath), currentEntry.fullPath);
if (options && options.invokeFileDeleteCreateAsPartInsteadOfChange) {
this.removeFileOrFolder(currentEntry, returnFalse);
this.ensureFileOrFolder(fileOrDirectory);
}
else {
this.invokeFileWatcher(currentEntry.fullPath, FileWatcherEventKind.Changed);
currentEntry.content = fileOrDirectory.content;
if (options && options.invokeDirectoryWatcherInsteadOfFileChanged) {
this.invokeDirectoryWatcher(getDirectoryPath(currentEntry.fullPath), currentEntry.fullPath);
}
else {
this.invokeFileWatcher(currentEntry.fullPath, FileWatcherEventKind.Changed);
}
}
}
}
@@ -395,9 +405,11 @@ interface Array<T> {}`
ensureFileOrFolder(fileOrDirectory: FileOrFolder, ignoreWatchInvokedWithTriggerAsFileCreate?: boolean) {
if (isString(fileOrDirectory.content)) {
const file = this.toFile(fileOrDirectory);
Debug.assert(!this.fs.get(file.path));
const baseFolder = this.ensureFolder(getDirectoryPath(file.fullPath));
this.addFileOrFolderInFolder(baseFolder, file, ignoreWatchInvokedWithTriggerAsFileCreate);
// file may already exist when updating existing type declaration file
if (!this.fs.get(file.path)) {
const baseFolder = this.ensureFolder(getDirectoryPath(file.fullPath));
this.addFileOrFolderInFolder(baseFolder, file, ignoreWatchInvokedWithTriggerAsFileCreate);
}
}
else if (isString(fileOrDirectory.symLink)) {
const symLink = this.toSymLink(fileOrDirectory);
+485 -426
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -69,7 +69,7 @@ interface ArrayConstructor {
}
interface DateConstructor {
new (value: Date): Date;
new (value: number | string | Date): Date;
}
interface Function {
+34 -2
View File
@@ -985,7 +985,7 @@ interface ReadonlyArray<T> {
*/
toString(): string;
/**
* Returns a string representation of an array. The elements are converted to string using thier toLocalString methods.
* Returns a string representation of an array. The elements are converted to string using their toLocalString methods.
*/
toLocaleString(): string;
/**
@@ -1104,7 +1104,7 @@ interface Array<T> {
*/
toString(): string;
/**
* Returns a string representation of an array. The elements are converted to string using thier toLocalString methods.
* Returns a string representation of an array. The elements are converted to string using their toLocalString methods.
*/
toLocaleString(): string;
/**
@@ -1317,6 +1317,13 @@ type Partial<T> = {
[P in keyof T]?: T[P];
};
/**
* Make all properties in T required
*/
type Required<T> = {
[P in keyof T]-?: T[P];
};
/**
* Make all properties in T readonly
*/
@@ -1338,6 +1345,31 @@ type Record<K extends string, T> = {
[P in K]: T;
};
/**
* Exclude from T those types that are assignable to U
*/
type Exclude<T, U> = T extends U ? never : T;
/**
* Extract from T those types that are assignable to U
*/
type Extract<T, U> = T extends U ? T : never;
/**
* Exclude null and undefined from T
*/
type NonNullable<T> = T extends null | undefined ? never : T;
/**
* Obtain the return type of a function type
*/
type ReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : any;
/**
* Obtain the return type of a constructor function type
*/
type InstanceType<T extends new (...args: any[]) => any> = T extends new (...args: any[]) => infer R ? R : any;
/**
* Marker for contextual 'this' type
*/
+48 -68
View File
@@ -128,21 +128,7 @@ interface SyncEventInit extends ExtendableEventInit {
lastChance?: boolean;
}
interface EventListener {
(evt: Event): void;
}
interface WebKitEntriesCallback {
(evt: Event): void;
}
interface WebKitErrorCallback {
(evt: Event): void;
}
interface WebKitFileCallback {
(evt: Event): void;
}
type EventListener = (evt: Event) => void | { handleEvent(evt: Event): void; };
interface AudioBuffer {
readonly duration: number;
@@ -404,9 +390,9 @@ declare var Event: {
};
interface EventTarget {
addEventListener(type: string, listener?: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListener, options?: boolean | AddEventListenerOptions): void;
dispatchEvent(evt: Event): boolean;
removeEventListener(type: string, listener?: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListener, options?: boolean | EventListenerOptions): void;
}
declare var EventTarget: {
@@ -444,9 +430,9 @@ interface FileReader extends EventTarget, MSBaseReader {
readAsDataURL(blob: Blob): void;
readAsText(blob: Blob, encoding?: string): void;
addEventListener<K extends keyof MSBaseReaderEventMap>(type: K, listener: (this: FileReader, ev: MSBaseReaderEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListener, options?: boolean | AddEventListenerOptions): void;
removeEventListener<K extends keyof MSBaseReaderEventMap>(type: K, listener: (this: FileReader, ev: MSBaseReaderEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListener, options?: boolean | EventListenerOptions): void;
}
declare var FileReader: {
@@ -529,9 +515,9 @@ interface IDBDatabase extends EventTarget {
addEventListener(type: "versionchange", listener: (this: IDBDatabase, ev: IDBVersionChangeEvent) => any, options?: boolean | AddEventListenerOptions): void;
removeEventListener(type: "versionchange", listener: (this: IDBDatabase, ev: IDBVersionChangeEvent) => any, options?: boolean | EventListenerOptions): void;
addEventListener<K extends keyof IDBDatabaseEventMap>(type: K, listener: (this: IDBDatabase, ev: IDBDatabaseEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListener, options?: boolean | AddEventListenerOptions): void;
removeEventListener<K extends keyof IDBDatabaseEventMap>(type: K, listener: (this: IDBDatabase, ev: IDBDatabaseEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListener, options?: boolean | EventListenerOptions): void;
}
declare var IDBDatabase: {
@@ -616,9 +602,9 @@ interface IDBOpenDBRequest extends IDBRequest {
onblocked: (this: IDBOpenDBRequest, ev: Event) => any;
onupgradeneeded: (this: IDBOpenDBRequest, ev: IDBVersionChangeEvent) => any;
addEventListener<K extends keyof IDBOpenDBRequestEventMap>(type: K, listener: (this: IDBOpenDBRequest, ev: IDBOpenDBRequestEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListener, options?: boolean | AddEventListenerOptions): void;
removeEventListener<K extends keyof IDBOpenDBRequestEventMap>(type: K, listener: (this: IDBOpenDBRequest, ev: IDBOpenDBRequestEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListener, options?: boolean | EventListenerOptions): void;
}
declare var IDBOpenDBRequest: {
@@ -640,9 +626,9 @@ interface IDBRequest extends EventTarget {
source: IDBObjectStore | IDBIndex | IDBCursor;
readonly transaction: IDBTransaction;
addEventListener<K extends keyof IDBRequestEventMap>(type: K, listener: (this: IDBRequest, ev: IDBRequestEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListener, options?: boolean | AddEventListenerOptions): void;
removeEventListener<K extends keyof IDBRequestEventMap>(type: K, listener: (this: IDBRequest, ev: IDBRequestEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListener, options?: boolean | EventListenerOptions): void;
}
declare var IDBRequest: {
@@ -669,9 +655,9 @@ interface IDBTransaction extends EventTarget {
readonly READ_WRITE: string;
readonly VERSION_CHANGE: string;
addEventListener<K extends keyof IDBTransactionEventMap>(type: K, listener: (this: IDBTransaction, ev: IDBTransactionEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListener, options?: boolean | AddEventListenerOptions): void;
removeEventListener<K extends keyof IDBTransactionEventMap>(type: K, listener: (this: IDBTransaction, ev: IDBTransactionEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListener, options?: boolean | EventListenerOptions): void;
}
declare var IDBTransaction: {
@@ -737,9 +723,9 @@ interface MessagePort extends EventTarget {
postMessage(message?: any, transfer?: any[]): void;
start(): void;
addEventListener<K extends keyof MessagePortEventMap>(type: K, listener: (this: MessagePort, ev: MessagePortEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListener, options?: boolean | AddEventListenerOptions): void;
removeEventListener<K extends keyof MessagePortEventMap>(type: K, listener: (this: MessagePort, ev: MessagePortEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListener, options?: boolean | EventListenerOptions): void;
}
declare var MessagePort: {
@@ -768,9 +754,9 @@ interface Notification extends EventTarget {
readonly title: string;
close(): void;
addEventListener<K extends keyof NotificationEventMap>(type: K, listener: (this: Notification, ev: NotificationEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListener, options?: boolean | AddEventListenerOptions): void;
removeEventListener<K extends keyof NotificationEventMap>(type: K, listener: (this: Notification, ev: NotificationEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListener, options?: boolean | EventListenerOptions): void;
}
declare var Notification: {
@@ -892,7 +878,7 @@ declare var ProgressEvent: {
};
interface PushManager {
getSubscription(): Promise<PushSubscription>;
getSubscription(): Promise<PushSubscription | null>;
permissionState(options?: PushSubscriptionOptionsInit): Promise<PushPermissionState>;
subscribe(options?: PushSubscriptionOptionsInit): Promise<PushSubscription>;
}
@@ -999,9 +985,9 @@ interface ServiceWorker extends EventTarget, AbstractWorker {
readonly state: ServiceWorkerState;
postMessage(message: any, transfer?: any[]): void;
addEventListener<K extends keyof ServiceWorkerEventMap>(type: K, listener: (this: ServiceWorker, ev: ServiceWorkerEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListener, options?: boolean | AddEventListenerOptions): void;
removeEventListener<K extends keyof ServiceWorkerEventMap>(type: K, listener: (this: ServiceWorker, ev: ServiceWorkerEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListener, options?: boolean | EventListenerOptions): void;
}
declare var ServiceWorker: {
@@ -1026,9 +1012,9 @@ interface ServiceWorkerRegistration extends EventTarget {
unregister(): Promise<boolean>;
update(): Promise<void>;
addEventListener<K extends keyof ServiceWorkerRegistrationEventMap>(type: K, listener: (this: ServiceWorkerRegistration, ev: ServiceWorkerRegistrationEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListener, options?: boolean | AddEventListenerOptions): void;
removeEventListener<K extends keyof ServiceWorkerRegistrationEventMap>(type: K, listener: (this: ServiceWorkerRegistration, ev: ServiceWorkerRegistrationEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListener, options?: boolean | EventListenerOptions): void;
}
declare var ServiceWorkerRegistration: {
@@ -1088,15 +1074,15 @@ interface WebSocket extends EventTarget {
readonly readyState: number;
readonly url: string;
close(code?: number, reason?: string): void;
send(data: any): void;
send(data: USVString | ArrayBuffer | Blob | ArrayBufferView): void;
readonly CLOSED: number;
readonly CLOSING: number;
readonly CONNECTING: number;
readonly OPEN: number;
addEventListener<K extends keyof WebSocketEventMap>(type: K, listener: (this: WebSocket, ev: WebSocketEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListener, options?: boolean | AddEventListenerOptions): void;
removeEventListener<K extends keyof WebSocketEventMap>(type: K, listener: (this: WebSocket, ev: WebSocketEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListener, options?: boolean | EventListenerOptions): void;
}
declare var WebSocket: {
@@ -1117,9 +1103,9 @@ interface Worker extends EventTarget, AbstractWorker {
postMessage(message: any, transfer?: any[]): void;
terminate(): void;
addEventListener<K extends keyof WorkerEventMap>(type: K, listener: (this: Worker, ev: WorkerEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListener, options?: boolean | AddEventListenerOptions): void;
removeEventListener<K extends keyof WorkerEventMap>(type: K, listener: (this: Worker, ev: WorkerEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListener, options?: boolean | EventListenerOptions): void;
}
declare var Worker: {
@@ -1160,9 +1146,9 @@ interface XMLHttpRequest extends EventTarget, XMLHttpRequestEventTarget {
readonly OPENED: number;
readonly UNSENT: number;
addEventListener<K extends keyof XMLHttpRequestEventMap>(type: K, listener: (this: XMLHttpRequest, ev: XMLHttpRequestEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListener, options?: boolean | AddEventListenerOptions): void;
removeEventListener<K extends keyof XMLHttpRequestEventMap>(type: K, listener: (this: XMLHttpRequest, ev: XMLHttpRequestEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListener, options?: boolean | EventListenerOptions): void;
}
declare var XMLHttpRequest: {
@@ -1177,9 +1163,9 @@ declare var XMLHttpRequest: {
interface XMLHttpRequestUpload extends EventTarget, XMLHttpRequestEventTarget {
addEventListener<K extends keyof XMLHttpRequestEventTargetEventMap>(type: K, listener: (this: XMLHttpRequestUpload, ev: XMLHttpRequestEventTargetEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListener, options?: boolean | AddEventListenerOptions): void;
removeEventListener<K extends keyof XMLHttpRequestEventTargetEventMap>(type: K, listener: (this: XMLHttpRequestUpload, ev: XMLHttpRequestEventTargetEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListener, options?: boolean | EventListenerOptions): void;
}
declare var XMLHttpRequestUpload: {
@@ -1194,9 +1180,9 @@ interface AbstractWorkerEventMap {
interface AbstractWorker {
onerror: (this: AbstractWorker, ev: ErrorEvent) => any;
addEventListener<K extends keyof AbstractWorkerEventMap>(type: K, listener: (this: AbstractWorker, ev: AbstractWorkerEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListener, options?: boolean | AddEventListenerOptions): void;
removeEventListener<K extends keyof AbstractWorkerEventMap>(type: K, listener: (this: AbstractWorker, ev: AbstractWorkerEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListener, options?: boolean | EventListenerOptions): void;
}
interface Body {
@@ -1234,9 +1220,9 @@ interface MSBaseReader {
readonly EMPTY: number;
readonly LOADING: number;
addEventListener<K extends keyof MSBaseReaderEventMap>(type: K, listener: (this: MSBaseReader, ev: MSBaseReaderEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListener, options?: boolean | AddEventListenerOptions): void;
removeEventListener<K extends keyof MSBaseReaderEventMap>(type: K, listener: (this: MSBaseReader, ev: MSBaseReaderEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListener, options?: boolean | EventListenerOptions): void;
}
interface NavigatorBeacon {
@@ -1291,9 +1277,9 @@ interface XMLHttpRequestEventTarget {
onprogress: (this: XMLHttpRequest, ev: ProgressEvent) => any;
ontimeout: (this: XMLHttpRequest, ev: ProgressEvent) => any;
addEventListener<K extends keyof XMLHttpRequestEventTargetEventMap>(type: K, listener: (this: XMLHttpRequestEventTarget, ev: XMLHttpRequestEventTargetEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListener, options?: boolean | AddEventListenerOptions): void;
removeEventListener<K extends keyof XMLHttpRequestEventTargetEventMap>(type: K, listener: (this: XMLHttpRequestEventTarget, ev: XMLHttpRequestEventTargetEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListener, options?: boolean | EventListenerOptions): void;
}
interface Client {
@@ -1329,9 +1315,9 @@ interface DedicatedWorkerGlobalScope extends WorkerGlobalScope {
close(): void;
postMessage(message: any, transfer?: any[]): void;
addEventListener<K extends keyof DedicatedWorkerGlobalScopeEventMap>(type: K, listener: (this: DedicatedWorkerGlobalScope, ev: DedicatedWorkerGlobalScopeEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListener, options?: boolean | AddEventListenerOptions): void;
removeEventListener<K extends keyof DedicatedWorkerGlobalScopeEventMap>(type: K, listener: (this: DedicatedWorkerGlobalScope, ev: DedicatedWorkerGlobalScopeEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListener, options?: boolean | EventListenerOptions): void;
}
declare var DedicatedWorkerGlobalScope: {
@@ -1442,9 +1428,9 @@ interface ServiceWorkerGlobalScope extends WorkerGlobalScope {
readonly registration: ServiceWorkerRegistration;
skipWaiting(): Promise<void>;
addEventListener<K extends keyof ServiceWorkerGlobalScopeEventMap>(type: K, listener: (this: ServiceWorkerGlobalScope, ev: ServiceWorkerGlobalScopeEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListener, options?: boolean | AddEventListenerOptions): void;
removeEventListener<K extends keyof ServiceWorkerGlobalScopeEventMap>(type: K, listener: (this: ServiceWorkerGlobalScope, ev: ServiceWorkerGlobalScopeEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListener, options?: boolean | EventListenerOptions): void;
}
declare var ServiceWorkerGlobalScope: {
@@ -1489,9 +1475,9 @@ interface WorkerGlobalScope extends EventTarget, WorkerUtils, WindowConsole, Glo
createImageBitmap(image: ImageBitmap | ImageData | Blob, options?: ImageBitmapOptions): Promise<ImageBitmap>;
createImageBitmap(image: ImageBitmap | ImageData | Blob, sx: number, sy: number, sw: number, sh: number, options?: ImageBitmapOptions): Promise<ImageBitmap>;
addEventListener<K extends keyof WorkerGlobalScopeEventMap>(type: K, listener: (this: WorkerGlobalScope, ev: WorkerGlobalScopeEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListener, options?: boolean | AddEventListenerOptions): void;
removeEventListener<K extends keyof WorkerGlobalScopeEventMap>(type: K, listener: (this: WorkerGlobalScope, ev: WorkerGlobalScopeEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListener, options?: boolean | EventListenerOptions): void;
}
declare var WorkerGlobalScope: {
@@ -1549,9 +1535,9 @@ interface BroadcastChannel extends EventTarget {
close(): void;
postMessage(message: any): void;
addEventListener<K extends keyof BroadcastChannelEventMap>(type: K, listener: (this: BroadcastChannel, ev: BroadcastChannelEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListener, options?: boolean | AddEventListenerOptions): void;
removeEventListener<K extends keyof BroadcastChannelEventMap>(type: K, listener: (this: BroadcastChannel, ev: BroadcastChannelEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListener, options?: boolean | EventListenerOptions): void;
}
declare var BroadcastChannel: {
@@ -1631,10 +1617,6 @@ interface FilePropertyBag extends BlobPropertyBag {
lastModified?: number;
}
interface EventListenerObject {
handleEvent(evt: Event): void;
}
interface ProgressEventInit extends EventInit {
lengthComputable?: boolean;
loaded?: number;
@@ -1861,8 +1843,6 @@ interface EventSourceInit {
readonly withCredentials: boolean;
}
declare type EventListenerOrEventListenerObject = EventListener | EventListenerObject;
interface DecodeErrorCallback {
(error: DOMException): void;
}
@@ -1873,7 +1853,7 @@ interface ErrorEventHandler {
(message: string, filename?: string, lineno?: number, colno?: number, error?: Error): void;
}
interface ForEachCallback {
(keyId: BufferSource, status: MediaKeyStatus): void;
(keyId: any, status: MediaKeyStatus): void;
}
interface FunctionStringCallback {
(data: string): void;
@@ -1919,9 +1899,9 @@ declare var console: Console;
declare function fetch(input: RequestInfo, init?: RequestInit): Promise<Response>;
declare function dispatchEvent(evt: Event): boolean;
declare function addEventListener<K extends keyof DedicatedWorkerGlobalScopeEventMap>(type: K, listener: (this: DedicatedWorkerGlobalScope, ev: DedicatedWorkerGlobalScopeEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
declare function addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
declare function addEventListener(type: string, listener: EventListener, options?: boolean | AddEventListenerOptions): void;
declare function removeEventListener<K extends keyof DedicatedWorkerGlobalScopeEventMap>(type: K, listener: (this: DedicatedWorkerGlobalScope, ev: DedicatedWorkerGlobalScopeEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
declare function removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
declare function removeEventListener(type: string, listener: EventListener, options?: boolean | EventListenerOptions): void;
type AlgorithmIdentifier = string | Algorithm;
type BodyInit = Blob | BufferSource | FormData | string;
type IDBKeyPath = string;
@@ -8778,6 +8778,15 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";infer_declarations_are_only_permitted_in_the_extends_clause_of_a_conditional_type_1338" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA['infer' declarations are only permitted in the 'extends' clause of a conditional type.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Deklarace infer jsou povolené jenom v klauzuli extends podmíněného typu.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";interface_declarations_can_only_be_used_in_a_ts_file_8006" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA['interface declarations' can only be used in a .ts file.]]></Val>
@@ -8760,6 +8760,15 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";infer_declarations_are_only_permitted_in_the_extends_clause_of_a_conditional_type_1338" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA['infer' declarations are only permitted in the 'extends' clause of a conditional type.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[infer-Deklarationen sind nur in der extends-Klausel eines bedingten Typs zulässig.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";interface_declarations_can_only_be_used_in_a_ts_file_8006" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA['interface declarations' can only be used in a .ts file.]]></Val>
@@ -8778,6 +8778,15 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";infer_declarations_are_only_permitted_in_the_extends_clause_of_a_conditional_type_1338" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA['infer' declarations are only permitted in the 'extends' clause of a conditional type.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Les déclarations 'infer' sont uniquement autorisées dans la clause 'extends' dun type conditionnel.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";interface_declarations_can_only_be_used_in_a_ts_file_8006" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA['interface declarations' can only be used in a .ts file.]]></Val>
@@ -8768,6 +8768,15 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";infer_declarations_are_only_permitted_in_the_extends_clause_of_a_conditional_type_1338" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA['infer' declarations are only permitted in the 'extends' clause of a conditional type.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA[Объявления "infer" допустимы только в предложении "extends" условного типа.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";interface_declarations_can_only_be_used_in_a_ts_file_8006" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA['interface declarations' can only be used in a .ts file.]]></Val>
@@ -8762,6 +8762,15 @@
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";infer_declarations_are_only_permitted_in_the_extends_clause_of_a_conditional_type_1338" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA['infer' declarations are only permitted in the 'extends' clause of a conditional type.]]></Val>
<Tgt Cat="Text" Stat="Loc" Orig="New">
<Val><![CDATA['infer' bildirimlerine yalnızca bir koşullu türün 'extends' yan tümcesinde izin verilir.]]></Val>
</Tgt>
</Str>
<Disp Icon="Str" />
</Item>
<Item ItemId=";interface_declarations_can_only_be_used_in_a_ts_file_8006" ItemType="0" PsrId="306" Leaf="true">
<Str Cat="Text">
<Val><![CDATA['interface declarations' can only be used in a .ts file.]]></Val>
+87 -147
View File
@@ -376,9 +376,9 @@ namespace ts.server {
private safelist: SafeList = defaultTypeSafeList;
private legacySafelist: { [key: string]: string } = {};
private changedFiles: ScriptInfo[];
private pendingProjectUpdates = createMap<Project>();
private pendingInferredProjectUpdate: boolean;
/* @internal */
pendingEnsureProjectForOpenFiles: boolean;
readonly currentDirectory: string;
readonly toCanonicalFileName: (f: string) => string;
@@ -483,11 +483,6 @@ namespace ts.server {
return getNormalizedAbsolutePath(fileName, this.host.getCurrentDirectory());
}
/* @internal */
getChangedFiles_TestOnly() {
return this.changedFiles;
}
/* @internal */
ensureInferredProjectsUpToDate_TestOnly() {
this.ensureProjectStructuresUptoDate();
@@ -552,19 +547,18 @@ namespace ts.server {
this.typingsCache.deleteTypingsForProject(response.projectName);
break;
}
this.delayUpdateProjectGraphAndInferredProjectsRefresh(project);
this.delayUpdateProjectGraphAndEnsureProjectStructureForOpenFiles(project);
}
private delayInferredProjectsRefresh() {
this.pendingInferredProjectUpdate = true;
this.throttledOperations.schedule("*refreshInferredProjects*", /*delay*/ 250, () => {
private delayEnsureProjectForOpenFiles() {
this.pendingEnsureProjectForOpenFiles = true;
this.throttledOperations.schedule("*ensureProjectForOpenFiles*", /*delay*/ 250, () => {
if (this.pendingProjectUpdates.size !== 0) {
this.delayInferredProjectsRefresh();
this.delayEnsureProjectForOpenFiles();
}
else {
if (this.pendingInferredProjectUpdate) {
this.pendingInferredProjectUpdate = false;
this.refreshInferredProjects();
if (this.pendingEnsureProjectForOpenFiles) {
this.ensureProjectForOpenFiles();
}
// Send the event to notify that there were background project updates
// send current list of open files
@@ -574,6 +568,7 @@ namespace ts.server {
}
private delayUpdateProjectGraph(project: Project) {
project.markAsDirty();
const projectName = project.getProjectName();
this.pendingProjectUpdates.set(projectName, project);
this.throttledOperations.schedule(projectName, /*delay*/ 250, () => {
@@ -603,17 +598,16 @@ namespace ts.server {
}
/* @internal */
delayUpdateProjectGraphAndInferredProjectsRefresh(project: Project) {
project.markAsDirty();
delayUpdateProjectGraphAndEnsureProjectStructureForOpenFiles(project: Project) {
this.delayUpdateProjectGraph(project);
this.delayInferredProjectsRefresh();
this.delayEnsureProjectForOpenFiles();
}
private delayUpdateProjectGraphs(projects: Project[]) {
private delayUpdateProjectGraphs(projects: ReadonlyArray<Project>) {
for (const project of projects) {
this.delayUpdateProjectGraph(project);
}
this.delayInferredProjectsRefresh();
this.delayEnsureProjectForOpenFiles();
}
setCompilerOptionsForInferredProjects(projectCompilerOptions: protocol.ExternalProjectCompilerOptions, projectRootPath?: string): void {
@@ -632,7 +626,6 @@ namespace ts.server {
this.compilerOptionsForInferredProjects = compilerOptions;
}
const projectsToUpdate: Project[] = [];
for (const project of this.inferredProjects) {
// Only update compiler options in the following cases:
// - Inferred projects without a projectRootPath, if the new options do not apply to
@@ -648,11 +641,11 @@ namespace ts.server {
project.setCompilerOptions(compilerOptions);
project.compileOnSaveEnabled = compilerOptions.compileOnSave;
project.markAsDirty();
projectsToUpdate.push(project);
this.delayUpdateProjectGraph(project);
}
}
this.delayUpdateProjectGraphs(projectsToUpdate);
this.delayEnsureProjectForOpenFiles();
}
findProject(projectName: string): Project | undefined {
@@ -668,7 +661,7 @@ namespace ts.server {
getDefaultProjectForFile(fileName: NormalizedPath, ensureProject: boolean) {
let scriptInfo = this.getScriptInfoForNormalizedPath(fileName);
if (ensureProject && !scriptInfo || scriptInfo.isOrphan()) {
if (ensureProject && (!scriptInfo || scriptInfo.isOrphan())) {
this.ensureProjectStructuresUptoDate();
scriptInfo = this.getScriptInfoForNormalizedPath(fileName);
if (!scriptInfo) {
@@ -687,41 +680,27 @@ namespace ts.server {
/**
* Ensures the project structures are upto date
* This means,
* - if there are changedFiles (the files were updated but their containing project graph was not upto date),
* their project graph is updated
* - If there are pendingProjectUpdates (scheduled to be updated with delay so they can batch update the graph if there are several changes in short time span)
* their project graph is updated
* - If there were project graph updates and/or there was pending inferred project update and/or called forced the inferred project structure refresh
* Inferred projects are created/updated/deleted based on open files states
* @param forceInferredProjectsRefresh when true updates the inferred projects even if there is no pending work to update the files/project structures
* - we go through all the projects and update them if they are dirty
* - if updates reflect some change in structure or there was pending request to ensure projects for open files
* ensure that each open script info has project
*/
private ensureProjectStructuresUptoDate(forceInferredProjectsRefresh?: boolean) {
if (this.changedFiles) {
let projectsToUpdate: Project[];
if (this.changedFiles.length === 1) {
// simpliest case - no allocations
projectsToUpdate = this.changedFiles[0].containingProjects;
}
else {
projectsToUpdate = [];
for (const f of this.changedFiles) {
addRange(projectsToUpdate, f.containingProjects);
}
}
this.changedFiles = undefined;
this.updateProjectGraphs(projectsToUpdate);
}
private ensureProjectStructuresUptoDate() {
let hasChanges = this.pendingEnsureProjectForOpenFiles;
this.pendingProjectUpdates.clear();
const updateGraph = (project: Project) => {
hasChanges = this.updateProjectIfDirty(project) || hasChanges;
};
if (this.pendingProjectUpdates.size !== 0) {
const projectsToUpdate = arrayFrom(this.pendingProjectUpdates.values());
this.pendingProjectUpdates.clear();
this.updateProjectGraphs(projectsToUpdate);
this.externalProjects.forEach(updateGraph);
this.configuredProjects.forEach(updateGraph);
this.inferredProjects.forEach(updateGraph);
if (hasChanges) {
this.ensureProjectForOpenFiles();
}
}
if (this.pendingInferredProjectUpdate || forceInferredProjectsRefresh) {
this.pendingInferredProjectUpdate = false;
this.refreshInferredProjects();
}
private updateProjectIfDirty(project: Project) {
return project.dirty && project.updateGraph();
}
getFormatCodeOptions(file?: NormalizedPath) {
@@ -735,14 +714,6 @@ namespace ts.server {
return formatCodeSettings || this.hostConfiguration.formatCodeOptions;
}
private updateProjectGraphs(projects: Project[]) {
for (const p of projects) {
if (!p.updateGraph()) {
this.pendingInferredProjectUpdate = true;
}
}
}
private onSourceFileChanged(fileName: NormalizedPath, eventKind: FileWatcherEventKind) {
const info = this.getScriptInfoForNormalizedPath(fileName);
if (!info) {
@@ -770,8 +741,6 @@ namespace ts.server {
private handleDeletedFile(info: ScriptInfo) {
this.stopWatchingScriptInfo(info);
// TODO: handle isOpen = true case
if (!info.isScriptOpen()) {
this.deleteScriptInfo(info);
@@ -808,7 +777,7 @@ namespace ts.server {
// Reload is pending, do the reload
if (project.pendingReload !== ConfigFileProgramReloadLevel.Full) {
project.pendingReload = ConfigFileProgramReloadLevel.Partial;
this.delayUpdateProjectGraphAndInferredProjectsRefresh(project);
this.delayUpdateProjectGraphAndEnsureProjectStructureForOpenFiles(project);
}
},
flags,
@@ -1317,7 +1286,11 @@ namespace ts.server {
this.logger.info("Open files: ");
this.openFiles.forEach((projectRootPath, path) => {
this.logger.info(`\tFileName: ${this.getScriptInfoForPath(path as Path).fileName} ProjectRootPath: ${projectRootPath}`);
const info = this.getScriptInfoForPath(path as Path);
this.logger.info(`\tFileName: ${info.fileName} ProjectRootPath: ${projectRootPath}`);
if (writeProjectFileNames) {
this.logger.info(`\t\tProjects: ${info.containingProjects.map(p => p.getProjectName())}`);
}
});
this.logger.endGroup();
@@ -1377,9 +1350,10 @@ namespace ts.server {
return { projectOptions, configFileErrors: errors, configFileSpecs: parsedCommandLine.configFileSpecs };
}
private exceededTotalSizeLimitForNonTsFiles<T>(name: string, options: CompilerOptions, fileNames: T[], propertyReader: FilePropertyReader<T>) {
/** Get a filename if the language service exceeds the maximum allowed program size; otherwise returns undefined. */
private getFilenameForExceededTotalSizeLimitForNonTsFiles<T>(name: string, options: CompilerOptions, fileNames: T[], propertyReader: FilePropertyReader<T>): string | undefined {
if (options && options.disableSizeLimit || !this.host.getFileSize) {
return false;
return;
}
let availableSpace = maxProgramSizeForNonTsFiles;
@@ -1396,20 +1370,16 @@ namespace ts.server {
totalNonTsFileSize += this.host.getFileSize(fileName);
if (totalNonTsFileSize > maxProgramSizeForNonTsFiles) {
if (totalNonTsFileSize > maxProgramSizeForNonTsFiles || totalNonTsFileSize > availableSpace) {
this.logger.info(getExceedLimitMessage({ propertyReader, hasTypeScriptFileExtension, host: this.host }, totalNonTsFileSize));
// Keep the size as zero since it's disabled
return true;
return fileName;
}
}
if (totalNonTsFileSize > availableSpace) {
this.logger.info(getExceedLimitMessage({ propertyReader, hasTypeScriptFileExtension, host: this.host }, totalNonTsFileSize));
return true;
}
this.projectToSizeMap.set(name, totalNonTsFileSize);
return false;
return;
function getExceedLimitMessage(context: { propertyReader: FilePropertyReader<any>, hasTypeScriptFileExtension: (filename: string) => boolean, host: ServerHost }, totalNonTsFileSize: number) {
const files = getTop5LargestFiles(context);
@@ -1432,7 +1402,7 @@ namespace ts.server {
this,
this.documentRegistry,
compilerOptions,
/*languageServiceEnabled*/ !this.exceededTotalSizeLimitForNonTsFiles(projectFileName, compilerOptions, files, externalFilePropertyReader),
/*lastFileExceededProgramSize*/ this.getFilenameForExceededTotalSizeLimitForNonTsFiles(projectFileName, compilerOptions, files, externalFilePropertyReader),
options.compileOnSave === undefined ? true : options.compileOnSave);
project.excludedFiles = excludedFiles;
@@ -1498,14 +1468,14 @@ namespace ts.server {
const cachedDirectoryStructureHost = createCachedDirectoryStructureHost(this.host, this.host.getCurrentDirectory(), this.host.useCaseSensitiveFileNames);
const { projectOptions, configFileErrors, configFileSpecs } = this.convertConfigFileContentToProjectOptions(configFileName, cachedDirectoryStructureHost);
this.logger.info(`Opened configuration file ${configFileName}`);
const languageServiceEnabled = !this.exceededTotalSizeLimitForNonTsFiles(configFileName, projectOptions.compilerOptions, projectOptions.files, fileNamePropertyReader);
const lastFileExceededProgramSize = this.getFilenameForExceededTotalSizeLimitForNonTsFiles(configFileName, projectOptions.compilerOptions, projectOptions.files, fileNamePropertyReader);
const project = new ConfiguredProject(
configFileName,
this,
this.documentRegistry,
projectOptions.configHasFilesProperty,
projectOptions.compilerOptions,
languageServiceEnabled,
lastFileExceededProgramSize,
projectOptions.compileOnSave === undefined ? false : projectOptions.compileOnSave,
cachedDirectoryStructureHost);
@@ -1518,7 +1488,7 @@ namespace ts.server {
WatchType.ConfigFilePath,
project
);
if (languageServiceEnabled) {
if (!lastFileExceededProgramSize) {
project.watchWildcards(projectOptions.wildcardDirectories);
}
@@ -1631,8 +1601,9 @@ namespace ts.server {
// Update the project
project.configFileSpecs = configFileSpecs;
project.setProjectErrors(configFileErrors);
if (this.exceededTotalSizeLimitForNonTsFiles(project.canonicalConfigFilePath, projectOptions.compilerOptions, projectOptions.files, fileNamePropertyReader)) {
project.disableLanguageService();
const lastFileExceededProgramSize = this.getFilenameForExceededTotalSizeLimitForNonTsFiles(project.canonicalConfigFilePath, projectOptions.compilerOptions, projectOptions.files, fileNamePropertyReader);
if (lastFileExceededProgramSize) {
project.disableLanguageService(lastFileExceededProgramSize);
project.stopWatchingWildCards();
}
else {
@@ -1898,7 +1869,7 @@ namespace ts.server {
// Reload Projects
this.reloadConfiguredProjectForFiles(this.openFiles, /*delayReload*/ false, returnTrue);
this.refreshInferredProjects();
this.ensureProjectForOpenFiles();
}
private delayReloadConfiguredProjectForFiles(configFileExistenceInfo: ConfigFileExistenceInfo, ignoreIfNotRootOfInferredProject: boolean) {
@@ -1910,7 +1881,7 @@ namespace ts.server {
isRootOfInferredProject => isRootOfInferredProject : // Reload open files if they are root of inferred project
returnTrue // Reload all the open files impacted by config file
);
this.delayInferredProjectsRefresh();
this.delayEnsureProjectForOpenFiles();
}
/**
@@ -1994,8 +1965,8 @@ namespace ts.server {
* This will go through open files and assign them to inferred project if open file is not part of any other project
* After that all the inferred project graphs are updated
*/
private refreshInferredProjects() {
this.logger.info("refreshInferredProjects: updating project structure from ...");
private ensureProjectForOpenFiles() {
this.logger.info("Structure before ensureProjectForOpenFiles:");
this.printProjects();
this.openFiles.forEach((projectRootPath, path) => {
@@ -2009,12 +1980,10 @@ namespace ts.server {
this.removeRootOfInferredProjectIfNowPartOfOtherProject(info);
}
});
this.pendingEnsureProjectForOpenFiles = false;
this.inferredProjects.forEach(p => this.updateProjectIfDirty(p));
for (const p of this.inferredProjects) {
p.updateGraph();
}
this.logger.info("refreshInferredProjects: updated project structure ...");
this.logger.info("Structure after ensureProjectForOpenFiles:");
this.printProjects();
}
@@ -2040,7 +2009,6 @@ namespace ts.server {
openClientFileWithNormalizedPath(fileName: NormalizedPath, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, projectRootPath?: NormalizedPath): OpenConfiguredProjectResult {
let configFileName: NormalizedPath;
let sendConfigFileDiagEvent = false;
let configFileErrors: ReadonlyArray<Diagnostic>;
const info = this.getOrCreateScriptInfoOpenedByClientForNormalizedPath(fileName, projectRootPath ? this.getNormalizedAbsolutePath(projectRootPath) : this.currentDirectory, fileContent, scriptKind, hasMixedContent);
@@ -2051,8 +2019,15 @@ namespace ts.server {
project = this.findConfiguredProjectByProjectName(configFileName);
if (!project) {
project = this.createConfiguredProject(configFileName);
// Send the event only if the project got created as part of this open request
sendConfigFileDiagEvent = true;
// 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
configFileName = undefined;
}
else {
configFileErrors = project.getAllProjectErrors();
this.sendConfigFileDiagEvent(project as ConfiguredProject, fileName);
}
}
else {
// Ensure project is ready to check if it contains opened script info
@@ -2060,30 +2035,20 @@ namespace ts.server {
}
}
}
if (project && !project.languageServiceEnabled) {
// if project language service is disabled then we create a program only for open files.
// this means that project should be marked as dirty to force rebuilding of the program
// on the next request
project.markAsDirty();
}
// Project we have at this point is going to be updated since its either found through
// - external project search, which updates the project before checking if info is present in it
// - configured project - either created or updated to ensure we know correct status of info
// At this point if file is part of any any configured or external project, then it would be present in the containing projects
// So if it still doesnt have any containing projects, it needs to be part of inferred project
if (info.isOrphan()) {
// Since the file isnt part of configured project, do not send config file event
configFileName = undefined;
sendConfigFileDiagEvent = false;
this.assignOrphanScriptInfoToInferredProject(info, projectRootPath);
}
Debug.assert(!info.isOrphan());
this.openFiles.set(info.path, projectRootPath);
if (sendConfigFileDiagEvent) {
configFileErrors = project.getAllProjectErrors();
this.sendConfigFileDiagEvent(project as ConfiguredProject, fileName);
}
// Remove the configured projects that have zero references from open files.
// This was postponed from closeOpenFile to after opening next file,
// so that we can reuse the project if we need to right away
@@ -2155,11 +2120,6 @@ namespace ts.server {
this.closeClientFile(file);
}
}
// if files were open or closed then explicitly refresh list of inferred projects
// otherwise if there were only changes in files - record changed files in `changedFiles` and defer the update
if (openFiles || closedFiles) {
this.ensureProjectStructuresUptoDate(/*refreshInferredProjects*/ true);
}
}
/* @internal */
@@ -2169,49 +2129,33 @@ namespace ts.server {
const change = changes[i];
scriptInfo.editContent(change.span.start, change.span.start + change.span.length, change.newText);
}
if (!this.changedFiles) {
this.changedFiles = [scriptInfo];
}
else if (!contains(this.changedFiles, scriptInfo)) {
this.changedFiles.push(scriptInfo);
}
}
private closeConfiguredProjectReferencedFromExternalProject(configFile: NormalizedPath): boolean {
private closeConfiguredProjectReferencedFromExternalProject(configFile: NormalizedPath) {
const configuredProject = this.findConfiguredProjectByProjectName(configFile);
if (configuredProject) {
configuredProject.deleteExternalProjectReference();
if (!configuredProject.hasOpenRef()) {
this.removeProject(configuredProject);
return true;
return;
}
}
return false;
}
closeExternalProject(uncheckedFileName: string, suppressRefresh = false): void {
closeExternalProject(uncheckedFileName: string): void {
const fileName = toNormalizedPath(uncheckedFileName);
const configFiles = this.externalProjectToConfiguredProjectMap.get(fileName);
if (configFiles) {
let shouldRefreshInferredProjects = false;
for (const configFile of configFiles) {
if (this.closeConfiguredProjectReferencedFromExternalProject(configFile)) {
shouldRefreshInferredProjects = true;
}
this.closeConfiguredProjectReferencedFromExternalProject(configFile);
}
this.externalProjectToConfiguredProjectMap.delete(fileName);
if (shouldRefreshInferredProjects && !suppressRefresh) {
this.ensureProjectStructuresUptoDate(/*refreshInferredProjects*/ true);
}
}
else {
// close external project
const externalProject = this.findExternalProjectByProjectName(uncheckedFileName);
if (externalProject) {
this.removeProject(externalProject);
if (!suppressRefresh) {
this.ensureProjectStructuresUptoDate(/*refreshInferredProjects*/ true);
}
}
}
}
@@ -2224,17 +2168,15 @@ namespace ts.server {
});
for (const externalProject of projects) {
this.openExternalProject(externalProject, /*suppressRefreshOfInferredProjects*/ true);
this.openExternalProject(externalProject);
// delete project that is present in input list
projectsToClose.delete(externalProject.projectFileName);
}
// close projects that were missing in the input list
forEachKey(projectsToClose, externalProjectName => {
this.closeExternalProject(externalProjectName, /*suppressRefresh*/ true);
this.closeExternalProject(externalProjectName);
});
this.ensureProjectStructuresUptoDate(/*refreshInferredProjects*/ true);
}
/** Makes a filename safe to insert in a RegExp */
@@ -2355,7 +2297,7 @@ namespace ts.server {
return excludedFiles;
}
openExternalProject(proj: protocol.ExternalProject, suppressRefreshOfInferredProjects = false): void {
openExternalProject(proj: protocol.ExternalProject): void {
// typingOptions has been deprecated and is only supported for backward compatibility
// purposes. It should be removed in future releases - use typeAcquisition instead.
if (proj.typingOptions && !proj.typeAcquisition) {
@@ -2396,8 +2338,9 @@ namespace ts.server {
externalProject.excludedFiles = excludedFiles;
if (!tsConfigFiles) {
const compilerOptions = convertCompilerOptions(proj.options);
if (this.exceededTotalSizeLimitForNonTsFiles(proj.projectFileName, compilerOptions, proj.rootFiles, externalFilePropertyReader)) {
externalProject.disableLanguageService();
const lastFileExceededProgramSize = this.getFilenameForExceededTotalSizeLimitForNonTsFiles(proj.projectFileName, compilerOptions, proj.rootFiles, externalFilePropertyReader);
if (lastFileExceededProgramSize) {
externalProject.disableLanguageService(lastFileExceededProgramSize);
}
else {
externalProject.enableLanguageService();
@@ -2408,13 +2351,13 @@ namespace ts.server {
}
// some config files were added to external project (that previously were not there)
// close existing project and later we'll open a set of configured projects for these files
this.closeExternalProject(proj.projectFileName, /*suppressRefresh*/ true);
this.closeExternalProject(proj.projectFileName);
}
else if (this.externalProjectToConfiguredProjectMap.get(proj.projectFileName)) {
// this project used to include config files
if (!tsConfigFiles) {
// config files were removed from the project - close existing external project which in turn will close configured projects
this.closeExternalProject(proj.projectFileName, /*suppressRefresh*/ true);
this.closeExternalProject(proj.projectFileName);
}
else {
// project previously had some config files - compare them with new set of files and close all configured projects that correspond to unused files
@@ -2464,9 +2407,6 @@ namespace ts.server {
this.externalProjectToConfiguredProjectMap.delete(proj.projectFileName);
this.createExternalProject(proj.projectFileName, rootFiles, proj.options, proj.typeAcquisition, excludedFiles);
}
if (!suppressRefreshOfInferredProjects) {
this.ensureProjectStructuresUptoDate(/*refreshInferredProjects*/ true);
}
}
}
}
+33 -26
View File
@@ -126,6 +126,8 @@ namespace ts.server {
private cachedUnresolvedImportsPerFile = new UnresolvedImportsMap();
private lastCachedUnresolvedImportsList: SortedReadonlyArray<string>;
private lastFileExceededProgramSize: string | undefined;
// wrapper over the real language service that will suppress all semantic operations
protected languageService: LanguageService;
@@ -166,6 +168,9 @@ namespace ts.server {
*/
private projectStateVersion = 0;
/*@internal*/
dirty = false;
/*@internal*/
hasChangedAutomaticTypeDirectiveNames = false;
@@ -212,7 +217,7 @@ namespace ts.server {
readonly projectService: ProjectService,
private documentRegistry: DocumentRegistry,
hasExplicitListOfFiles: boolean,
languageServiceEnabled: boolean,
lastFileExceededProgramSize: string | undefined,
private compilerOptions: CompilerOptions,
public compileOnSaveEnabled: boolean,
directoryStructureHost: DirectoryStructureHost,
@@ -244,10 +249,11 @@ namespace ts.server {
// Use the current directory as resolution root only if the project created using current directory string
this.resolutionCache = createResolutionCache(this, currentDirectory && this.currentDirectory, /*logChangesWhenResolvingModule*/ true);
this.languageService = createLanguageService(this, this.documentRegistry);
if (!languageServiceEnabled) {
this.disableLanguageService();
if (lastFileExceededProgramSize) {
this.disableLanguageService(lastFileExceededProgramSize);
}
this.markAsDirty();
this.projectService.pendingEnsureProjectForOpenFiles = true;
}
isKnownTypesPackageName(name: string): boolean {
@@ -397,7 +403,7 @@ namespace ts.server {
/*@internal*/
onInvalidatedResolution() {
this.projectService.delayUpdateProjectGraphAndInferredProjectsRefresh(this);
this.projectService.delayUpdateProjectGraphAndEnsureProjectStructureForOpenFiles(this);
}
/*@internal*/
@@ -415,7 +421,7 @@ namespace ts.server {
/*@internal*/
onChangedAutomaticTypeDirectiveNames() {
this.hasChangedAutomaticTypeDirectiveNames = true;
this.projectService.delayUpdateProjectGraphAndInferredProjectsRefresh(this);
this.projectService.delayUpdateProjectGraphAndEnsureProjectStructureForOpenFiles(this);
}
/*@internal*/
@@ -497,15 +503,17 @@ namespace ts.server {
return;
}
this.languageServiceEnabled = true;
this.lastFileExceededProgramSize = undefined;
this.projectService.onUpdateLanguageServiceStateForProject(this, /*languageServiceEnabled*/ true);
}
disableLanguageService() {
disableLanguageService(lastFileExceededProgramSize?: string) {
if (!this.languageServiceEnabled) {
return;
}
this.languageService.cleanupSemanticCache();
this.languageServiceEnabled = false;
this.lastFileExceededProgramSize = lastFileExceededProgramSize;
this.builderState = undefined;
this.resolutionCache.closeTypeRootsWatch();
this.projectService.onUpdateLanguageServiceStateForProject(this, /*languageServiceEnabled*/ false);
@@ -561,6 +569,7 @@ namespace ts.server {
for (const root of this.rootFiles) {
root.detachFromProject(this);
}
this.projectService.pendingEnsureProjectForOpenFiles = true;
this.rootFiles = undefined;
this.rootFilesMap = undefined;
@@ -744,7 +753,10 @@ namespace ts.server {
}
markAsDirty() {
this.projectStateVersion++;
if (!this.dirty) {
this.projectStateVersion++;
this.dirty = true;
}
}
/* @internal */
@@ -819,7 +831,9 @@ namespace ts.server {
}
const cachedTypings = this.projectService.typingsCache.getTypingsForProject(this, this.lastCachedUnresolvedImportsList, hasChanges);
if (this.setTypings(cachedTypings)) {
if (!arrayIsEqualTo(this.typingFiles, cachedTypings)) {
this.typingFiles = cachedTypings;
this.markAsDirty();
hasChanges = this.updateGraphWorker() || hasChanges;
}
}
@@ -843,15 +857,6 @@ namespace ts.server {
return include.filter(i => existing.indexOf(i) < 0);
}
private setTypings(typings: SortedReadonlyArray<string>): boolean {
if (arrayIsEqualTo(this.typingFiles, typings)) {
return false;
}
this.typingFiles = typings;
this.markAsDirty();
return true;
}
private updateGraphWorker() {
const oldProgram = this.program;
Debug.assert(!this.isClosed(), "Called update graph worker of closed project");
@@ -860,6 +865,7 @@ namespace ts.server {
this.hasInvalidatedResolution = this.resolutionCache.createHasInvalidatedResolution();
this.resolutionCache.startCachingPerDirectoryResolution();
this.program = this.languageService.getProgram();
this.dirty = false;
this.resolutionCache.finishCachingPerDirectoryResolution();
// bump up the version if
@@ -906,7 +912,7 @@ namespace ts.server {
compareStringsCaseSensitive
);
const elapsed = timestamp() - start;
this.writeLog(`Finishing updateGraphWorker: Project: ${this.getProjectName()} structureChanged: ${hasChanges} Elapsed: ${elapsed}ms`);
this.writeLog(`Finishing updateGraphWorker: Project: ${this.getProjectName()} Version: ${this.getProjectVersion()} structureChanged: ${hasChanges} Elapsed: ${elapsed}ms`);
return hasChanges;
}
@@ -932,7 +938,7 @@ namespace ts.server {
fileWatcher.close();
// When a missing file is created, we should update the graph.
this.projectService.delayUpdateProjectGraphAndInferredProjectsRefresh(this);
this.projectService.delayUpdateProjectGraphAndEnsureProjectStructureForOpenFiles(this);
}
},
WatchType.MissingFilePath,
@@ -993,12 +999,13 @@ namespace ts.server {
getChangesSinceVersion(lastKnownVersion?: number): ProjectFilesWithTSDiagnostics {
this.updateGraph();
const info = {
const info: protocol.ProjectVersionInfo = {
projectName: this.getProjectName(),
version: this.projectStructureVersion,
isInferred: this.projectKind === ProjectKind.Inferred,
options: this.getCompilationSettings(),
languageServiceDisabled: !this.languageServiceEnabled
languageServiceDisabled: !this.languageServiceEnabled,
lastFileExceededProgramSize: this.lastFileExceededProgramSize
};
const updatedFileNames = this.updatedFileNames;
this.updatedFileNames = undefined;
@@ -1182,7 +1189,7 @@ namespace ts.server {
projectService,
documentRegistry,
/*files*/ undefined,
/*languageServiceEnabled*/ true,
/*lastFileExceededProgramSize*/ undefined,
compilerOptions,
/*compileOnSaveEnabled*/ false,
projectService.host,
@@ -1261,7 +1268,7 @@ namespace ts.server {
documentRegistry: DocumentRegistry,
hasExplicitListOfFiles: boolean,
compilerOptions: CompilerOptions,
languageServiceEnabled: boolean,
lastFileExceededProgramSize: string | undefined,
public compileOnSaveEnabled: boolean,
cachedDirectoryStructureHost: CachedDirectoryStructureHost) {
super(configFileName,
@@ -1269,7 +1276,7 @@ namespace ts.server {
projectService,
documentRegistry,
hasExplicitListOfFiles,
languageServiceEnabled,
lastFileExceededProgramSize,
compilerOptions,
compileOnSaveEnabled,
cachedDirectoryStructureHost,
@@ -1456,7 +1463,7 @@ namespace ts.server {
projectService: ProjectService,
documentRegistry: DocumentRegistry,
compilerOptions: CompilerOptions,
languageServiceEnabled: boolean,
lastFileExceededProgramSize: string | undefined,
public compileOnSaveEnabled: boolean,
projectFilePath?: string) {
super(externalProjectName,
@@ -1464,7 +1471,7 @@ namespace ts.server {
projectService,
documentRegistry,
/*hasExplicitListOfFiles*/ true,
languageServiceEnabled,
lastFileExceededProgramSize,
compilerOptions,
compileOnSaveEnabled,
projectService.host,
+4 -1
View File
@@ -1104,11 +1104,14 @@ namespace ts.server.protocol {
* Current set of compiler options for project
*/
options: ts.CompilerOptions;
/**
* true if project language service is disabled
*/
languageServiceDisabled: boolean;
/**
* Filename of the last file analyzed before disabling the language service. undefined, if the language service is enabled.
*/
lastFileExceededProgramSize: string | undefined;
}
/**
+1 -1
View File
@@ -253,7 +253,7 @@ namespace ts.server {
private requestMap = createMap<QueuedOperation>(); // Maps operation ID to newest requestQueue entry with that ID
/** We will lazily request the types registry on the first call to `isKnownTypesPackageName` and store it in `typesRegistryCache`. */
private requestedRegistry: boolean;
private typesRegistryCache: Map<void> | undefined;
private typesRegistryCache: Map<MapLike<string>> | undefined;
// This number is essentially arbitrary. Processing more than one typings request
// at a time makes sense, but having too many in the pipe results in a hang
+1 -1
View File
@@ -1769,7 +1769,7 @@ namespace ts.server {
return this.requiredResponse(response);
},
[CommandNames.OpenExternalProject]: (request: protocol.OpenExternalProjectRequest) => {
this.projectService.openExternalProject(request.arguments, /*suppressRefreshOfInferredProjects*/ false);
this.projectService.openExternalProject(request.arguments);
// TODO: GH#20447 report errors
return this.requiredResponse(/*response*/ true);
},
+1 -5
View File
@@ -22,10 +22,6 @@ declare namespace ts.server {
require?(initialPath: string, moduleName: string): RequireResult;
}
export interface SortedArray<T> extends Array<T> {
" __sortedArrayBrand": any;
}
export interface SortedReadonlyArray<T> extends ReadonlyArray<T> {
" __sortedArrayBrand": any;
}
@@ -79,7 +75,7 @@ declare namespace ts.server {
/* @internal */
export interface TypesRegistryResponse extends TypingInstallerResponse {
readonly kind: EventTypesRegistry;
readonly typesRegistry: MapLike<void>;
readonly typesRegistry: MapLike<MapLike<string>>;
}
export interface PackageInstalledResponse extends ProjectResponse {
@@ -41,15 +41,15 @@ namespace ts.server.typingsInstaller {
}
interface TypesRegistryFile {
entries: MapLike<void>;
entries: MapLike<MapLike<string>>;
}
function loadTypesRegistryFile(typesRegistryFilePath: string, host: InstallTypingHost, log: Log): Map<void> {
function loadTypesRegistryFile(typesRegistryFilePath: string, host: InstallTypingHost, log: Log): Map<MapLike<string>> {
if (!host.fileExists(typesRegistryFilePath)) {
if (log.isEnabled()) {
log.writeLine(`Types registry file '${typesRegistryFilePath}' does not exist`);
}
return createMap<void>();
return createMap<MapLike<string>>();
}
try {
const content = <TypesRegistryFile>JSON.parse(host.readFile(typesRegistryFilePath));
@@ -59,7 +59,7 @@ namespace ts.server.typingsInstaller {
if (log.isEnabled()) {
log.writeLine(`Error when loading types registry file '${typesRegistryFilePath}': ${(<Error>e).message}, ${(<Error>e).stack}`);
}
return createMap<void>();
return createMap<MapLike<string>>();
}
}
@@ -77,7 +77,7 @@ namespace ts.server.typingsInstaller {
export class NodeTypingsInstaller extends TypingsInstaller {
private readonly nodeExecSync: ExecSync;
private readonly npmPath: string;
readonly typesRegistry: Map<void>;
readonly typesRegistry: Map<MapLike<string>>;
private delayedInitializationError: InitializationFailedResponse | undefined;
@@ -141,7 +141,7 @@ namespace ts.server.typingsInstaller {
this.closeProject(req);
break;
case "typesRegistry": {
const typesRegistry: { [key: string]: void } = {};
const typesRegistry: { [key: string]: MapLike<string> } = {};
this.typesRegistry.forEach((value, key) => {
typesRegistry[key] = value;
});
+40 -17
View File
@@ -1,6 +1,7 @@
/// <reference path="../../compiler/core.ts" />
/// <reference path="../../compiler/moduleNameResolver.ts" />
/// <reference path="../../services/jsTyping.ts"/>
/// <reference path="../../services/semver.ts"/>
/// <reference path="../types.ts"/>
/// <reference path="../shared.ts"/>
@@ -9,6 +10,10 @@ namespace ts.server.typingsInstaller {
devDependencies: MapLike<any>;
}
interface NpmLock {
dependencies: { [packageName: string]: { version: string } };
}
export interface Log {
isEnabled(): boolean;
writeLine(text: string): void;
@@ -42,7 +47,7 @@ namespace ts.server.typingsInstaller {
}
export abstract class TypingsInstaller {
private readonly packageNameToTypingLocation: Map<string> = createMap<string>();
private readonly packageNameToTypingLocation: Map<JsTyping.CachedTyping> = createMap<JsTyping.CachedTyping>();
private readonly missingTypingsSet: Map<true> = createMap<true>();
private readonly knownCachesSet: Map<true> = createMap<true>();
private readonly projectWatchers: Map<FileWatcher[]> = createMap<FileWatcher[]>();
@@ -52,7 +57,7 @@ namespace ts.server.typingsInstaller {
private installRunCount = 1;
private inFlightRequestCount = 0;
abstract readonly typesRegistry: Map<void>;
abstract readonly typesRegistry: Map<MapLike<string>>;
constructor(
protected readonly installTypingHost: InstallTypingHost,
@@ -117,7 +122,8 @@ namespace ts.server.typingsInstaller {
this.safeList,
this.packageNameToTypingLocation,
req.typeAcquisition,
req.unresolvedImports);
req.unresolvedImports,
this.typesRegistry);
if (this.log.isEnabled()) {
this.log.writeLine(`Finished typings discovery: ${JSON.stringify(discoverTypingsResult)}`);
@@ -156,23 +162,30 @@ namespace ts.server.typingsInstaller {
if (this.log.isEnabled()) {
this.log.writeLine(`Processing cache location '${cacheLocation}'`);
}
if (this.knownCachesSet.get(cacheLocation)) {
if (this.knownCachesSet.has(cacheLocation)) {
if (this.log.isEnabled()) {
this.log.writeLine(`Cache location was already processed...`);
}
return;
}
const packageJson = combinePaths(cacheLocation, "package.json");
const packageLockJson = combinePaths(cacheLocation, "package-lock.json");
if (this.log.isEnabled()) {
this.log.writeLine(`Trying to find '${packageJson}'...`);
}
if (this.installTypingHost.fileExists(packageJson)) {
if (this.installTypingHost.fileExists(packageJson) && this.installTypingHost.fileExists(packageLockJson)) {
const npmConfig = <NpmConfig>JSON.parse(this.installTypingHost.readFile(packageJson));
const npmLock = <NpmLock>JSON.parse(this.installTypingHost.readFile(packageLockJson));
if (this.log.isEnabled()) {
this.log.writeLine(`Loaded content of '${packageJson}': ${JSON.stringify(npmConfig)}`);
this.log.writeLine(`Loaded content of '${packageLockJson}'`);
}
if (npmConfig.devDependencies) {
if (npmConfig.devDependencies && npmLock.dependencies) {
for (const key in npmConfig.devDependencies) {
if (!hasProperty(npmLock.dependencies, key)) {
// if package in package.json but not package-lock.json, skip adding to cache so it is reinstalled on next use
continue;
}
// key is @types/<package name>
const packageName = getBaseFileName(key);
if (!packageName) {
@@ -184,10 +197,11 @@ namespace ts.server.typingsInstaller {
continue;
}
const existingTypingFile = this.packageNameToTypingLocation.get(packageName);
if (existingTypingFile === typingFile) {
continue;
}
if (existingTypingFile) {
if (existingTypingFile.typingLocation === typingFile) {
continue;
}
if (this.log.isEnabled()) {
this.log.writeLine(`New typing for package ${packageName} from '${typingFile}' conflicts with existing typing file '${existingTypingFile}'`);
}
@@ -195,7 +209,11 @@ namespace ts.server.typingsInstaller {
if (this.log.isEnabled()) {
this.log.writeLine(`Adding entry into typings cache: '${packageName}' => '${typingFile}'`);
}
this.packageNameToTypingLocation.set(packageName, typingFile);
const info = getProperty(npmLock.dependencies, key);
const version = info && info.version;
const semver = Semver.parse(version);
const newTyping: JsTyping.CachedTyping = { typingLocation: typingFile, version: semver };
this.packageNameToTypingLocation.set(packageName, newTyping);
}
}
}
@@ -211,10 +229,6 @@ namespace ts.server.typingsInstaller {
if (this.log.isEnabled()) this.log.writeLine(`'${typing}' is in missingTypingsSet - skipping...`);
return false;
}
if (this.packageNameToTypingLocation.get(typing)) {
if (this.log.isEnabled()) this.log.writeLine(`'${typing}' already has a typing - skipping...`);
return false;
}
const validationResult = JsTyping.validatePackageName(typing);
if (validationResult !== JsTyping.PackageNameValidationResult.Ok) {
// add typing name to missing set so we won't process it again
@@ -226,6 +240,10 @@ namespace ts.server.typingsInstaller {
if (this.log.isEnabled()) this.log.writeLine(`Entry for package '${typing}' does not exist in local types registry - skipping...`);
return false;
}
if (this.packageNameToTypingLocation.get(typing) && JsTyping.isTypingUpToDate(this.packageNameToTypingLocation.get(typing), this.typesRegistry.get(typing))) {
if (this.log.isEnabled()) this.log.writeLine(`'${typing}' already has an up-to-date typing - skipping...`);
return false;
}
return true;
});
}
@@ -294,9 +312,12 @@ namespace ts.server.typingsInstaller {
this.missingTypingsSet.set(packageName, true);
continue;
}
if (!this.packageNameToTypingLocation.has(packageName)) {
this.packageNameToTypingLocation.set(packageName, typingFile);
}
// packageName is guaranteed to exist in typesRegistry by filterTypings
const distTags = this.typesRegistry.get(packageName);
const newVersion = Semver.parse(distTags[`ts${ts.versionMajorMinor}`] || distTags[latestDistTag]);
const newTyping: JsTyping.CachedTyping = { typingLocation: typingFile, version: newVersion };
this.packageNameToTypingLocation.set(packageName, newTyping);
installedTypingFiles.push(typingFile);
}
if (this.log.isEnabled()) {
@@ -390,4 +411,6 @@ namespace ts.server.typingsInstaller {
export function typingsName(packageName: string): string {
return `@types/${packageName}@ts${versionMajorMinor}`;
}
const latestDistTag = "latest";
}
-12
View File
@@ -232,18 +232,6 @@ namespace ts.server {
return base === "tsconfig.json" || base === "jsconfig.json" ? base : undefined;
}
export function insertSorted<T>(array: SortedArray<T>, insert: T, compare: Comparer<T>): void {
if (array.length === 0) {
array.push(insert);
return;
}
const insertIndex = binarySearch(array, insert, identity, compare);
if (insertIndex < 0) {
array.splice(~insertIndex, 0, insert);
}
}
export function removeSorted<T>(array: SortedArray<T>, remove: T, compare: Comparer<T>): void {
if (!array || array.length === 0) {
return;
@@ -86,7 +86,7 @@ namespace ts.codefix {
else {
const leftExpressionType = checker.getTypeAtLocation(parent.expression);
const { symbol } = leftExpressionType;
if (!(leftExpressionType.flags & TypeFlags.Object && symbol.flags & SymbolFlags.Class)) {
if (!(symbol && leftExpressionType.flags & TypeFlags.Object && symbol.flags & SymbolFlags.Class)) {
return undefined;
}
const classDeclaration = cast(first(symbol.declarations), isClassLike);
@@ -34,7 +34,7 @@ namespace ts.codefix {
function getNodes(sourceFile: SourceFile, pos: number): { readonly constructor: ConstructorDeclaration, readonly superCall: ExpressionStatement } {
const token = getTokenAtPosition(sourceFile, pos, /*includeJsDocComment*/ false);
Debug.assert(token.kind === SyntaxKind.ThisKeyword);
if (token.kind !== SyntaxKind.ThisKeyword) return undefined;
const constructor = getContainingFunction(token) as ConstructorDeclaration;
const superCall = findSuperCall(constructor.body);
// figure out if the `this` access is actually inside the supercall
@@ -27,7 +27,7 @@ namespace ts.codefix {
}
function doChanges(changes: textChanges.ChangeTracker, sourceFile: SourceFile, extendsToken: Node, heritageClauses: ReadonlyArray<HeritageClause>): void {
changes.replaceRange(sourceFile, { pos: extendsToken.getStart(), end: extendsToken.end }, createToken(SyntaxKind.ImplementsKeyword));
changes.replaceNode(sourceFile, extendsToken, createToken(SyntaxKind.ImplementsKeyword), textChanges.useNonAdjustedPositions);
// If there is already an implements clause, replace the implements keyword with a comma.
if (heritageClauses.length === 2 &&
@@ -7,6 +7,9 @@ namespace ts.codefix {
getCodeActions(context) {
const { sourceFile } = context;
const token = getNode(sourceFile, context.span.start);
if (!token) {
return undefined;
}
const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, token));
return [{ description: getLocaleSpecificMessage(Diagnostics.Add_this_to_unresolved_variable), changes, fixId }];
},
@@ -16,13 +19,17 @@ namespace ts.codefix {
}),
});
function getNode(sourceFile: SourceFile, pos: number): Identifier {
return cast(getTokenAtPosition(sourceFile, pos, /*includeJsDocComment*/ false), isIdentifier);
function getNode(sourceFile: SourceFile, pos: number): Identifier | undefined {
const node = getTokenAtPosition(sourceFile, pos, /*includeJsDocComment*/ false);
return isIdentifier(node) ? node : undefined;
}
function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, token: Identifier): void {
function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, token: Identifier | undefined): void {
if (!token) {
return;
}
// TODO (https://github.com/Microsoft/TypeScript/issues/21246): use shared helper
suppressLeadingAndTrailingTrivia(token);
changes.replaceRange(sourceFile, { pos: token.getStart(), end: token.end }, createPropertyAccess(createThis(), token));
changes.replaceNode(sourceFile, token, createPropertyAccess(createThis(), token), textChanges.useNonAdjustedPositions);
}
}
}
@@ -140,7 +140,7 @@ namespace ts.codefix {
// and trailing trivia will remain.
suppressLeadingAndTrailingTrivia(newFunction);
changes.replaceRange(sourceFile, { pos: oldFunction.getStart(), end: oldFunction.end }, newFunction);
changes.replaceNode(sourceFile, oldFunction, newFunction, textChanges.useNonAdjustedPositions);
}
else {
changes.deleteNodeInList(sourceFile, parent);
+53 -32
View File
@@ -24,7 +24,7 @@ namespace ts.codefix {
}
interface ImportCodeFixContext extends SymbolContext {
symbolToken: Identifier | undefined;
symbolToken: Node;
program: Program;
checker: TypeChecker;
compilerOptions: CompilerOptions;
@@ -38,12 +38,11 @@ namespace ts.codefix {
return { description, changes, fixId: undefined };
}
function convertToImportCodeFixContext(context: CodeFixContext): ImportCodeFixContext {
function convertToImportCodeFixContext(context: CodeFixContext, symbolToken: Node, symbolName: string): ImportCodeFixContext {
const useCaseSensitiveFileNames = context.host.useCaseSensitiveFileNames ? context.host.useCaseSensitiveFileNames() : false;
const { program } = context;
const checker = program.getTypeChecker();
// This will always be an Identifier, since the diagnostics we fix only fail on identifiers.
const symbolToken = cast(getTokenAtPosition(context.sourceFile, context.span.start, /*includeJsDocComment*/ false), isIdentifier);
return {
host: context.host,
formatContext: context.formatContext,
@@ -53,8 +52,8 @@ namespace ts.codefix {
compilerOptions: program.getCompilerOptions(),
cachedImportDeclarations: [],
getCanonicalFileName: createGetCanonicalFileName(useCaseSensitiveFileNames),
symbolName: symbolToken.getText(),
symbolToken,
symbolName,
symbolToken
};
}
@@ -95,7 +94,7 @@ namespace ts.codefix {
allSourceFiles: ReadonlyArray<ts.SourceFile>,
formatContext: ts.formatting.FormatContext,
getCanonicalFileName: GetCanonicalFileName,
symbolToken: Identifier | undefined,
symbolToken: Node | undefined,
): { readonly moduleSpecifier: string, readonly codeAction: CodeAction } {
const exportInfos = getAllReExportingModules(exportedSymbol, checker, allSourceFiles);
Debug.assert(exportInfos.some(info => info.moduleSymbol === moduleSymbol));
@@ -132,12 +131,12 @@ namespace ts.codefix {
// 1. change "member3" to "ns.member3"
// 2. add "member3" to the second import statement's import list
// and it is up to the user to decide which one fits best.
const useExistingImportActions = !context.symbolToken ? emptyArray : mapDefined(existingImports, ({ declaration }) => {
const useExistingImportActions = !context.symbolToken || !isIdentifier(context.symbolToken) ? emptyArray : mapDefined(existingImports, ({ declaration }) => {
const namespace = getNamespaceImportName(declaration);
if (namespace) {
const moduleSymbol = context.checker.getAliasedSymbol(context.checker.getSymbolAtLocation(namespace));
if (moduleSymbol && moduleSymbol.exports.has(escapeLeadingUnderscores(context.symbolName))) {
return getCodeActionForUseExistingNamespaceImport(namespace.text, context, context.symbolToken);
return getCodeActionForUseExistingNamespaceImport(namespace.text, context, context.symbolToken as Identifier);
}
}
});
@@ -638,37 +637,48 @@ namespace ts.codefix {
* become "ns.foo"
*/
const changes = ChangeTracker.with(context, tracker =>
tracker.changeIdentifierToPropertyAccess(sourceFile, namespacePrefix, symbolToken));
tracker.replaceNode(sourceFile, symbolToken, createPropertyAccess(createIdentifier(namespacePrefix), symbolToken)));
return createCodeAction(Diagnostics.Change_0_to_1, [symbolName, `${namespacePrefix}.${symbolName}`], changes);
}
function getImportCodeActions(context: CodeFixContext): CodeAction[] {
const importFixContext = convertToImportCodeFixContext(context);
return context.errorCode === Diagnostics._0_refers_to_a_UMD_global_but_the_current_file_is_a_module_Consider_adding_an_import_instead.code
? getActionsForUMDImport(importFixContext)
: getActionsForNonUMDImport(importFixContext, context.program.getSourceFiles(), context.cancellationToken);
? getActionsForUMDImport(context)
: getActionsForNonUMDImport(context);
}
function getActionsForUMDImport(context: ImportCodeFixContext): CodeAction[] {
const { checker, symbolToken, compilerOptions } = context;
const umdSymbol = checker.getSymbolAtLocation(symbolToken);
let symbol: ts.Symbol;
let symbolName: string;
if (umdSymbol.flags & ts.SymbolFlags.Alias) {
symbol = checker.getAliasedSymbol(umdSymbol);
symbolName = context.symbolName;
function getActionsForUMDImport(context: CodeFixContext): CodeAction[] {
const token = getTokenAtPosition(context.sourceFile, context.span.start, /*includeJsDocComment*/ false);
const checker = context.program.getTypeChecker();
let umdSymbol: Symbol | undefined;
if (isIdentifier(token)) {
// try the identifier to see if it is the umd symbol
umdSymbol = checker.getSymbolAtLocation(token);
}
else if (isJsxOpeningLikeElement(symbolToken.parent) && symbolToken.parent.tagName === symbolToken) {
if (!isUMDExportSymbol(umdSymbol)) {
// The error wasn't for the symbolAtLocation, it was for the JSX tag itself, which needs access to e.g. `React`.
symbol = checker.getAliasedSymbol(checker.resolveName(checker.getJsxNamespace(), symbolToken.parent.tagName, SymbolFlags.Value, /*excludeGlobals*/ false));
symbolName = symbol.name;
}
else {
throw Debug.fail("Either the symbol or the JSX namespace should be a UMD global if we got here");
const parent = token.parent;
const isNodeOpeningLikeElement = isJsxOpeningLikeElement(parent);
if ((isJsxOpeningLikeElement && (<JsxOpeningLikeElement>parent).tagName === token) || parent.kind === SyntaxKind.JsxOpeningFragment) {
umdSymbol = checker.resolveName(checker.getJsxNamespace(),
isNodeOpeningLikeElement ? (<JsxOpeningLikeElement>parent).tagName : parent, SymbolFlags.Value, /*excludeGlobals*/ false);
}
}
return getCodeActionsForImport([{ moduleSymbol: symbol, importKind: getUmdImportKind(compilerOptions) }], { ...context, symbolName });
if (isUMDExportSymbol(umdSymbol)) {
const symbol = checker.getAliasedSymbol(umdSymbol);
if (symbol) {
return getCodeActionsForImport([{ moduleSymbol: symbol, importKind: getUmdImportKind(context.program.getCompilerOptions()) }],
convertToImportCodeFixContext(context, token, umdSymbol.name));
}
}
return undefined;
}
function getUmdImportKind(compilerOptions: CompilerOptions) {
// Import a synthetic `default` if enabled.
if (getAllowSyntheticDefaultImports(compilerOptions)) {
@@ -693,8 +703,19 @@ namespace ts.codefix {
}
}
function getActionsForNonUMDImport(context: ImportCodeFixContext, allSourceFiles: ReadonlyArray<SourceFile>, cancellationToken: CancellationToken): CodeAction[] {
const { sourceFile, checker, symbolName, symbolToken } = context;
function getActionsForNonUMDImport(context: CodeFixContext): CodeAction[] {
// This will always be an Identifier, since the diagnostics we fix only fail on identifiers.
const { sourceFile, span, program, cancellationToken } = context;
const checker = program.getTypeChecker();
const symbolToken = getTokenAtPosition(sourceFile, span.start, /*includeJsDocComment*/ false);
const isJsxNamespace = isJsxOpeningLikeElement(symbolToken.parent) && symbolToken.parent.tagName === symbolToken;
if (!isJsxNamespace && !isIdentifier(symbolToken)) {
return undefined;
}
const symbolName = isJsxNamespace ? checker.getJsxNamespace() : (<Identifier>symbolToken).text;
const allSourceFiles = program.getSourceFiles();
const compilerOptions = program.getCompilerOptions();
// "default" is a keyword and not a legal identifier for the import, so we don't expect it here
Debug.assert(symbolName !== "default");
const currentTokenMeaning = getMeaningFromLocation(symbolToken);
@@ -715,7 +736,7 @@ namespace ts.codefix {
if ((
localSymbol && localSymbol.escapedName === symbolName ||
getEscapedNameForExportDefault(defaultExport) === symbolName ||
moduleSymbolToValidIdentifier(moduleSymbol, context.compilerOptions.target) === symbolName
moduleSymbolToValidIdentifier(moduleSymbol, compilerOptions.target) === symbolName
) && checkSymbolHasMeaning(localSymbol || defaultExport, currentTokenMeaning)) {
addSymbol(moduleSymbol, localSymbol || defaultExport, ImportKind.Default);
}
@@ -744,7 +765,7 @@ namespace ts.codefix {
}
});
return arrayFrom(flatMapIterator(originalSymbolToExportInfos.values(), exportInfos => getCodeActionsForImport(exportInfos, context)));
return arrayFrom(flatMapIterator(originalSymbolToExportInfos.values(), exportInfos => getCodeActionsForImport(exportInfos, convertToImportCodeFixContext(context, symbolToken, symbolName))));
}
function checkSymbolHasMeaning({ declarations }: Symbol, meaning: SemanticMeaning): boolean {
+53 -60
View File
@@ -74,11 +74,11 @@ namespace ts.codefix {
// Variable and Property declarations
case Diagnostics.Member_0_implicitly_has_an_1_type.code:
case Diagnostics.Variable_0_implicitly_has_type_1_in_some_locations_where_its_type_cannot_be_determined.code:
return getCodeActionForVariableDeclaration(<PropertyDeclaration | PropertySignature | VariableDeclaration>token.parent, sourceFile, program, cancellationToken);
return getCodeActionForVariableDeclaration(<PropertyDeclaration | PropertySignature | VariableDeclaration>token.parent, program, cancellationToken);
case Diagnostics.Variable_0_implicitly_has_an_1_type.code: {
const symbol = program.getTypeChecker().getSymbolAtLocation(token);
return symbol && symbol.valueDeclaration && getCodeActionForVariableDeclaration(<VariableDeclaration>symbol.valueDeclaration, sourceFile, program, cancellationToken);
return symbol && symbol.valueDeclaration && getCodeActionForVariableDeclaration(<VariableDeclaration>symbol.valueDeclaration, program, cancellationToken);
}
}
@@ -86,17 +86,17 @@ namespace ts.codefix {
if (containingFunction === undefined) {
return undefined;
}
switch (errorCode) {
switch (errorCode) {
// Parameter declarations
case Diagnostics.Parameter_0_implicitly_has_an_1_type.code:
if (isSetAccessor(containingFunction)) {
return getCodeActionForSetAccessor(containingFunction, sourceFile, program, cancellationToken);
return getCodeActionForSetAccessor(containingFunction, program, cancellationToken);
}
// falls through
case Diagnostics.Rest_parameter_0_implicitly_has_an_any_type.code:
return !seenFunctions || addToSeen(seenFunctions, getNodeId(containingFunction))
? getCodeActionForParameters(<ParameterDeclaration>token.parent, containingFunction, sourceFile, program, cancellationToken)
? getCodeActionForParameters(cast(token.parent, isParameter), containingFunction, sourceFile, program, cancellationToken)
: undefined;
// Get Accessor declarations
@@ -106,7 +106,7 @@ namespace ts.codefix {
// Set Accessor declarations
case Diagnostics.Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_parameter_type_annotation.code:
return isSetAccessor(containingFunction) ? getCodeActionForSetAccessor(containingFunction, sourceFile, program, cancellationToken) : undefined;
return isSetAccessor(containingFunction) ? getCodeActionForSetAccessor(containingFunction, program, cancellationToken) : undefined;
default:
throw Debug.fail(String(errorCode));
@@ -127,9 +127,9 @@ namespace ts.codefix {
}
}
function getCodeActionForVariableDeclaration(declaration: VariableDeclaration | PropertyDeclaration | PropertySignature, sourceFile: SourceFile, program: Program, cancellationToken: CancellationToken): Fix | undefined {
function getCodeActionForVariableDeclaration(declaration: VariableDeclaration | PropertyDeclaration | PropertySignature, program: Program, cancellationToken: CancellationToken): Fix | undefined {
if (!isIdentifier(declaration.name)) return undefined;
const type = inferTypeForVariableFromUsage(declaration.name, sourceFile, program, cancellationToken);
const type = inferTypeForVariableFromUsage(declaration.name, program, cancellationToken);
return makeFix(declaration, declaration.name.getEnd(), type, program);
}
@@ -151,7 +151,7 @@ namespace ts.codefix {
}
const types = inferTypeForParametersFromUsage(containingFunction, sourceFile, program, cancellationToken) ||
containingFunction.parameters.map(p => isIdentifier(p.name) ? inferTypeForVariableFromUsage(p.name, sourceFile, program, cancellationToken) : undefined);
containingFunction.parameters.map(p => isIdentifier(p.name) ? inferTypeForVariableFromUsage(p.name, program, cancellationToken) : undefined);
if (!types) return undefined;
// We didn't actually find a set of type inference positions matching each parameter position
@@ -164,14 +164,14 @@ namespace ts.codefix {
return textChanges.length ? { declaration: parameterDeclaration, textChanges } : undefined;
}
function getCodeActionForSetAccessor(setAccessorDeclaration: SetAccessorDeclaration, sourceFile: SourceFile, program: Program, cancellationToken: CancellationToken): Fix | undefined {
function getCodeActionForSetAccessor(setAccessorDeclaration: SetAccessorDeclaration, program: Program, cancellationToken: CancellationToken): Fix | undefined {
const setAccessorParameter = setAccessorDeclaration.parameters[0];
if (!setAccessorParameter || !isIdentifier(setAccessorDeclaration.name) || !isIdentifier(setAccessorParameter.name)) {
return undefined;
}
const type = inferTypeForVariableFromUsage(setAccessorDeclaration.name, sourceFile, program, cancellationToken) ||
inferTypeForVariableFromUsage(setAccessorParameter.name, sourceFile, program, cancellationToken);
const type = inferTypeForVariableFromUsage(setAccessorDeclaration.name, program, cancellationToken) ||
inferTypeForVariableFromUsage(setAccessorParameter.name, program, cancellationToken);
return makeFix(setAccessorParameter, setAccessorParameter.name.getEnd(), type, program);
}
@@ -180,7 +180,7 @@ namespace ts.codefix {
return undefined;
}
const type = inferTypeForVariableFromUsage(getAccessorDeclaration.name, sourceFile, program, cancellationToken);
const type = inferTypeForVariableFromUsage(getAccessorDeclaration.name, program, cancellationToken);
const closeParenToken = findChildOfKind(getAccessorDeclaration, SyntaxKind.CloseParenToken, sourceFile);
return makeFix(getAccessorDeclaration, closeParenToken.getEnd(), type, program);
}
@@ -194,23 +194,14 @@ namespace ts.codefix {
return typeString === undefined ? undefined : createTextChangeFromStartLength(start, 0, `: ${typeString}`);
}
function getReferences(token: PropertyName | Token<SyntaxKind.ConstructorKeyword>, sourceFile: SourceFile, program: Program, cancellationToken: CancellationToken): Identifier[] {
const references = FindAllReferences.findReferencedSymbols(
program,
cancellationToken,
program.getSourceFiles(),
sourceFile,
token.getStart(sourceFile));
if (!references || references.length !== 1) {
return [];
}
return references[0].references.map(r => <Identifier>getTokenAtPosition(program.getSourceFile(r.fileName), r.textSpan.start, /*includeJsDocComment*/ false));
function getReferences(token: PropertyName | Token<SyntaxKind.ConstructorKeyword>, program: Program, cancellationToken: CancellationToken): ReadonlyArray<Identifier> {
// Position shouldn't matter since token is not a SourceFile.
return mapDefined(FindAllReferences.getReferenceEntriesForNode(-1, token, program, program.getSourceFiles(), cancellationToken), entry =>
entry.type === "node" ? tryCast(entry.node, isIdentifier) : undefined);
}
function inferTypeForVariableFromUsage(token: Identifier, sourceFile: SourceFile, program: Program, cancellationToken: CancellationToken): Type | undefined {
return InferFromReference.inferTypeFromReferences(getReferences(token, sourceFile, program, cancellationToken), program.getTypeChecker(), cancellationToken);
function inferTypeForVariableFromUsage(token: Identifier, program: Program, cancellationToken: CancellationToken): Type | undefined {
return InferFromReference.inferTypeFromReferences(getReferences(token, program, cancellationToken), program.getTypeChecker(), cancellationToken);
}
function inferTypeForParametersFromUsage(containingFunction: FunctionLikeDeclaration, sourceFile: SourceFile, program: Program, cancellationToken: CancellationToken): (Type | undefined)[] | undefined {
@@ -224,7 +215,7 @@ namespace ts.codefix {
findChildOfKind<Token<SyntaxKind.ConstructorKeyword>>(containingFunction, SyntaxKind.ConstructorKeyword, sourceFile) :
containingFunction.name;
if (searchToken) {
return InferFromReference.inferTypeForParametersFromReferences(getReferences(searchToken, sourceFile, program, cancellationToken), containingFunction, program.getTypeChecker(), cancellationToken);
return InferFromReference.inferTypeForParametersFromReferences(getReferences(searchToken, program, cancellationToken), containingFunction, program.getTypeChecker(), cancellationToken);
}
}
}
@@ -292,7 +283,7 @@ namespace ts.codefix {
stringIndexContext?: UsageContext;
}
export function inferTypeFromReferences(references: Identifier[], checker: TypeChecker, cancellationToken: CancellationToken): Type | undefined {
export function inferTypeFromReferences(references: ReadonlyArray<Identifier>, checker: TypeChecker, cancellationToken: CancellationToken): Type | undefined {
const usageContext: UsageContext = {};
for (const reference of references) {
cancellationToken.throwIfCancellationRequested();
@@ -301,43 +292,45 @@ namespace ts.codefix {
return getTypeFromUsageContext(usageContext, checker);
}
export function inferTypeForParametersFromReferences(references: Identifier[], declaration: FunctionLikeDeclaration, checker: TypeChecker, cancellationToken: CancellationToken): (Type | undefined)[] | undefined {
export function inferTypeForParametersFromReferences(references: ReadonlyArray<Identifier>, declaration: FunctionLikeDeclaration, checker: TypeChecker, cancellationToken: CancellationToken): (Type | undefined)[] | undefined {
if (references.length === 0) {
return undefined;
}
if (declaration.parameters) {
const usageContext: UsageContext = {};
for (const reference of references) {
cancellationToken.throwIfCancellationRequested();
inferTypeFromContext(reference, checker, usageContext);
}
const isConstructor = declaration.kind === SyntaxKind.Constructor;
const callContexts = isConstructor ? usageContext.constructContexts : usageContext.callContexts;
if (callContexts) {
const paramTypes: Type[] = [];
for (let parameterIndex = 0; parameterIndex < declaration.parameters.length; parameterIndex++) {
let types: Type[] = [];
const isRestParameter = ts.isRestParameter(declaration.parameters[parameterIndex]);
for (const callContext of callContexts) {
if (callContext.argumentTypes.length > parameterIndex) {
if (isRestParameter) {
types = concatenate(types, map(callContext.argumentTypes.slice(parameterIndex), a => checker.getBaseTypeOfLiteralType(a)));
}
else {
types.push(checker.getBaseTypeOfLiteralType(callContext.argumentTypes[parameterIndex]));
}
}
}
if (types.length) {
const type = checker.getWidenedType(checker.getUnionType(types, UnionReduction.Subtype));
paramTypes[parameterIndex] = isRestParameter ? checker.createArrayType(type) : type;
if (!declaration.parameters) {
return undefined;
}
const usageContext: UsageContext = {};
for (const reference of references) {
cancellationToken.throwIfCancellationRequested();
inferTypeFromContext(reference, checker, usageContext);
}
const isConstructor = declaration.kind === SyntaxKind.Constructor;
const callContexts = isConstructor ? usageContext.constructContexts : usageContext.callContexts;
return callContexts && declaration.parameters.map((parameter, parameterIndex) => {
const types: Type[] = [];
const isRestParameter = ts.isRestParameter(parameter);
for (const callContext of callContexts) {
if (callContext.argumentTypes.length <= parameterIndex) {
continue;
}
if (isRestParameter) {
for (let i = parameterIndex; i < callContext.argumentTypes.length; i++) {
types.push(checker.getBaseTypeOfLiteralType(callContext.argumentTypes[i]));
}
}
return paramTypes;
else {
types.push(checker.getBaseTypeOfLiteralType(callContext.argumentTypes[parameterIndex]));
}
}
}
return undefined;
if (!types.length) {
return undefined;
}
const type = checker.getWidenedType(checker.getUnionType(types, UnionReduction.Subtype));
return isRestParameter ? checker.createArrayType(type) : type;
});
}
function inferTypeFromContext(node: Expression, checker: TypeChecker, usageContext: UsageContext): void {
+36 -25
View File
@@ -42,7 +42,7 @@ namespace ts.Completions {
const contextToken = findPrecedingToken(position, sourceFile);
if (isInString(sourceFile, position, contextToken)) {
return !contextToken || !isStringLiteral(contextToken) && !isNoSubstitutionTemplateLiteral(contextToken)
return !contextToken || !isStringLiteralLike(contextToken)
? undefined
: convertStringLiteralCompletions(getStringLiteralCompletionEntries(sourceFile, contextToken, position, typeChecker, compilerOptions, host), sourceFile, typeChecker, log);
}
@@ -358,8 +358,7 @@ namespace ts.Completions {
case SyntaxKind.LiteralType:
switch (node.parent.parent.kind) {
case SyntaxKind.TypeReference:
// TODO: GH#21168
return undefined;
return { kind: StringLiteralCompletionKind.Types, types: getStringLiteralTypes(typeChecker.getTypeArgumentConstraint(node.parent as LiteralTypeNode), typeChecker) };
case SyntaxKind.IndexedAccessType:
// Get all apparent property names
// i.e. interface Foo {
@@ -612,7 +611,7 @@ namespace ts.Completions {
allSourceFiles,
formatContext,
getCanonicalFileName,
tryCast(previousToken, isIdentifier));
previousToken);
return { sourceDisplay: [textPart(moduleSpecifier)], codeActions: [codeAction] };
}
@@ -712,7 +711,7 @@ namespace ts.Completions {
function getFirstSymbolInChain(symbol: Symbol, enclosingDeclaration: Node, checker: TypeChecker): Symbol | undefined {
const chain = checker.getAccessibleSymbolChain(symbol, enclosingDeclaration, /*meaning*/ SymbolFlags.All, /*useOnlyExternalAliasing*/ false);
if (chain) return first(chain);
return isModuleSymbol(symbol.parent) ? symbol : symbol.parent && getFirstSymbolInChain(symbol.parent, enclosingDeclaration, checker);
return symbol.parent && (isModuleSymbol(symbol.parent) ? symbol : getFirstSymbolInChain(symbol.parent, enclosingDeclaration, checker));
}
function isModuleSymbol(symbol: Symbol): boolean {
@@ -862,6 +861,23 @@ namespace ts.Completions {
parent = parent.parent;
}
// Fix location
if (currentToken.parent === location) {
switch (currentToken.kind) {
case SyntaxKind.GreaterThanToken:
if (currentToken.parent.kind === SyntaxKind.JsxElement || currentToken.parent.kind === SyntaxKind.JsxOpeningElement) {
location = currentToken;
}
break;
case SyntaxKind.SlashToken:
if (currentToken.parent.kind === SyntaxKind.JsxSelfClosingElement) {
location = currentToken;
}
break;
}
}
switch (parent.kind) {
case SyntaxKind.JsxClosingElement:
if (contextToken.kind === SyntaxKind.SlashToken) {
@@ -912,7 +928,7 @@ namespace ts.Completions {
getTypeScriptMemberSymbols();
}
else if (isRightOfOpenTag) {
const tagSymbols = typeChecker.getJsxIntrinsicTagNames();
const tagSymbols = Debug.assertEachDefined(typeChecker.getJsxIntrinsicTagNames(), "getJsxIntrinsicTagNames() should all be defined");
if (tryGetGlobalSymbols()) {
symbols = tagSymbols.concat(symbols.filter(s => !!(s.flags & (SymbolFlags.Value | SymbolFlags.Alias))));
}
@@ -924,8 +940,7 @@ namespace ts.Completions {
else if (isStartingCloseTag) {
const tagName = (<JsxElement>contextToken.parent.parent).openingElement.tagName;
const tagSymbol = typeChecker.getSymbolAtLocation(tagName);
if (!typeChecker.isUnknownSymbol(tagSymbol)) {
if (tagSymbol) {
symbols = [tagSymbol];
}
completionKind = CompletionKind.MemberLike;
@@ -971,7 +986,7 @@ namespace ts.Completions {
if (symbol.flags & (SymbolFlags.Module | SymbolFlags.Enum)) {
// Extract module or enum members
const exportedSymbols = typeChecker.getExportsOfModule(symbol);
const exportedSymbols = Debug.assertEachDefined(typeChecker.getExportsOfModule(symbol), "getExportsOfModule() should all be defined");
const isValidValueAccess = (symbol: Symbol) => typeChecker.isValidPropertyAccess(<PropertyAccessExpression>(node.parent), symbol.name);
const isValidTypeAccess = (symbol: Symbol) => symbolCanBeReferencedAtTypeLocation(symbol);
const isValidAccess = isRhsOfImportDeclaration ?
@@ -1043,10 +1058,6 @@ namespace ts.Completions {
return true;
}
if (tryGetFunctionLikeBodyCompletionContainer(contextToken)) {
keywordFilters = KeywordCompletionFilters.FunctionLikeBodyKeywords;
}
if (classLikeContainer = tryGetClassLikeCompletionContainer(contextToken)) {
// cursor inside class declaration
getGetClassLikeCompletionSymbols(classLikeContainer);
@@ -1068,6 +1079,10 @@ namespace ts.Completions {
}
}
if (tryGetFunctionLikeBodyCompletionContainer(contextToken)) {
keywordFilters = KeywordCompletionFilters.FunctionLikeBodyKeywords;
}
// Get all entities in the current scope.
completionKind = CompletionKind.None;
isNewIdentifierLocation = isNewIdentifierDefinitionLocation(contextToken);
@@ -1111,7 +1126,7 @@ namespace ts.Completions {
const symbolMeanings = SymbolFlags.Type | SymbolFlags.Value | SymbolFlags.Namespace | SymbolFlags.Alias;
symbols = typeChecker.getSymbolsInScope(scopeNode, symbolMeanings);
symbols = Debug.assertEachDefined(typeChecker.getSymbolsInScope(scopeNode, symbolMeanings), "getSymbolsInScope() should all be defined");
// Need to insert 'this.' before properties of `this` type, so only do that if `includeInsertTextCompletions`
if (options.includeInsertTextCompletions && scopeNode.kind !== SyntaxKind.SourceFile) {
@@ -1452,7 +1467,7 @@ namespace ts.Completions {
if (typeMembers && typeMembers.length > 0) {
// Add filtered items to the completion list
symbols = filterObjectMembersList(typeMembers, existingMembers);
symbols = filterObjectMembersList(typeMembers, Debug.assertDefined(existingMembers));
}
return true;
}
@@ -1926,11 +1941,7 @@ namespace ts.Completions {
existingImportsOrExports.set(name.escapedText, true);
}
if (existingImportsOrExports.size === 0) {
return filter(exportsOfModule, e => e.escapedName !== InternalSymbolName.Default);
}
return filter(exportsOfModule, e => e.escapedName !== InternalSymbolName.Default && !existingImportsOrExports.get(e.escapedName));
return exportsOfModule.filter(e => e.escapedName !== InternalSymbolName.Default && !existingImportsOrExports.get(e.escapedName));
}
/**
@@ -1940,7 +1951,7 @@ namespace ts.Completions {
* do not occur at the current position and have not otherwise been typed.
*/
function filterObjectMembersList(contextualMemberSymbols: Symbol[], existingMembers: ReadonlyArray<Declaration>): Symbol[] {
if (!existingMembers || existingMembers.length === 0) {
if (existingMembers.length === 0) {
return contextualMemberSymbols;
}
@@ -1980,7 +1991,7 @@ namespace ts.Completions {
existingMemberNames.set(existingName, true);
}
return filter(contextualMemberSymbols, m => !existingMemberNames.get(m.escapedName));
return contextualMemberSymbols.filter(m => !existingMemberNames.get(m.escapedName));
}
/**
@@ -2066,7 +2077,7 @@ namespace ts.Completions {
}
}
return filter(symbols, a => !seenNames.get(a.escapedName));
return symbols.filter(a => !seenNames.get(a.escapedName));
}
function isCurrentlyEditingNode(node: Node): boolean {
@@ -2248,13 +2259,13 @@ namespace ts.Completions {
*/
function getPropertiesForCompletion(type: Type, checker: TypeChecker, isForAccess: boolean): Symbol[] {
if (!(type.flags & TypeFlags.Union)) {
return type.getApparentProperties();
return Debug.assertEachDefined(type.getApparentProperties(), "getApparentProperties() should all be defined");
}
const { types } = type as UnionType;
// If we're providing completions for an object literal, skip primitive, array-like, or callable types since those shouldn't be implemented by object literals.
const filteredTypes = isForAccess ? types : types.filter(memberType =>
!(memberType.flags & TypeFlags.Primitive || checker.isArrayLikeType(memberType) || typeHasCallOrConstructSignatures(memberType, checker)));
return checker.getAllPossiblePropertiesOfTypes(filteredTypes);
return Debug.assertEachDefined(checker.getAllPossiblePropertiesOfTypes(filteredTypes), "getAllPossiblePropertiesOfTypes() should all be defined");
}
}
+15 -43
View File
@@ -1,6 +1,6 @@
/* @internal */
namespace ts.DocumentHighlights {
export function getDocumentHighlights(program: Program, cancellationToken: CancellationToken, sourceFile: SourceFile, position: number, sourceFilesToSearch: SourceFile[]): DocumentHighlights[] | undefined {
export function getDocumentHighlights(program: Program, cancellationToken: CancellationToken, sourceFile: SourceFile, position: number, sourceFilesToSearch: ReadonlyArray<SourceFile>): DocumentHighlights[] | undefined {
const node = getTouchingWord(sourceFile, position, /*includeJsDocComment*/ true);
if (node.parent && (isJsxOpeningElement(node.parent) && node.parent.tagName === node || isJsxClosingElement(node.parent))) {
@@ -21,12 +21,12 @@ namespace ts.DocumentHighlights {
};
}
function getSemanticDocumentHighlights(position: number, node: Node, program: Program, cancellationToken: CancellationToken, sourceFilesToSearch: SourceFile[]): DocumentHighlights[] {
function getSemanticDocumentHighlights(position: number, node: Node, program: Program, cancellationToken: CancellationToken, sourceFilesToSearch: ReadonlyArray<SourceFile>): DocumentHighlights[] {
const referenceEntries = FindAllReferences.getReferenceEntriesForNode(position, node, program, sourceFilesToSearch, cancellationToken);
return referenceEntries && convertReferencedSymbols(referenceEntries);
}
function convertReferencedSymbols(referenceEntries: FindAllReferences.Entry[]): DocumentHighlights[] {
function convertReferencedSymbols(referenceEntries: ReadonlyArray<FindAllReferences.Entry>): DocumentHighlights[] {
const fileNameToDocumentHighlights = createMap<HighlightSpan[]>();
for (const entry of referenceEntries) {
const { fileName, span } = FindAllReferences.toHighlightSpan(entry);
@@ -189,11 +189,6 @@ namespace ts.DocumentHighlights {
}
function getModifierOccurrences(modifier: SyntaxKind, declaration: Node): Node[] {
// Make sure we only highlight the keyword when it makes sense to do so.
if (!isLegalModifier(modifier, declaration)) {
return undefined;
}
const modifierFlag = modifierToFlag(modifier);
return mapDefined(getNodesToSearchForModifier(declaration, modifierFlag), node => {
if (getModifierFlags(node) & modifierFlag) {
@@ -205,7 +200,8 @@ namespace ts.DocumentHighlights {
}
function getNodesToSearchForModifier(declaration: Node, modifierFlag: ModifierFlags): ReadonlyArray<Node> {
const container = declaration.parent;
// Types of node whose children might have modifiers.
const container = declaration.parent as ModuleBlock | SourceFile | Block | CaseClause | DefaultClause | ConstructorDeclaration | MethodDeclaration | FunctionDeclaration | ClassLikeDeclaration;
switch (container.kind) {
case SyntaxKind.ModuleBlock:
case SyntaxKind.SourceFile:
@@ -213,22 +209,25 @@ namespace ts.DocumentHighlights {
case SyntaxKind.CaseClause:
case SyntaxKind.DefaultClause:
// Container is either a class declaration or the declaration is a classDeclaration
if (modifierFlag & ModifierFlags.Abstract) {
return [...(<ClassDeclaration>declaration).members, declaration];
if (modifierFlag & ModifierFlags.Abstract && isClassDeclaration(declaration)) {
return [...declaration.members, declaration];
}
else {
return (<ModuleBlock | SourceFile | Block | CaseClause | DefaultClause>container).statements;
return container.statements;
}
case SyntaxKind.Constructor:
return [...(<ConstructorDeclaration>container).parameters, ...(<ClassDeclaration>container.parent).members];
case SyntaxKind.MethodDeclaration:
case SyntaxKind.FunctionDeclaration: {
return [...container.parameters, ...(isClassLike(container.parent) ? container.parent.members : [])];
}
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression:
const nodes = (<ClassLikeDeclaration>container).members;
const nodes = container.members;
// If we're an accessibility modifier, we're in an instance member and should search
// the constructor's parameter list for instance members as well.
if (modifierFlag & ModifierFlags.AccessibilityModifier) {
const constructor = find((<ClassLikeDeclaration>container).members, isConstructorDeclaration);
const constructor = find(container.members, isConstructorDeclaration);
if (constructor) {
return [...nodes, ...constructor.parameters];
}
@@ -238,34 +237,7 @@ namespace ts.DocumentHighlights {
}
return nodes;
default:
Debug.fail("Invalid container kind.");
}
}
function isLegalModifier(modifier: SyntaxKind, declaration: Node): boolean {
const container = declaration.parent;
switch (modifier) {
case SyntaxKind.PrivateKeyword:
case SyntaxKind.ProtectedKeyword:
case SyntaxKind.PublicKeyword:
switch (container.kind) {
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression:
return true;
case SyntaxKind.Constructor:
return declaration.kind === SyntaxKind.Parameter;
default:
return false;
}
case SyntaxKind.StaticKeyword:
return container.kind === SyntaxKind.ClassDeclaration || container.kind === SyntaxKind.ClassExpression;
case SyntaxKind.ExportKeyword:
case SyntaxKind.DeclareKeyword:
return container.kind === SyntaxKind.ModuleBlock || container.kind === SyntaxKind.SourceFile;
case SyntaxKind.AbstractKeyword:
return container.kind === SyntaxKind.ClassDeclaration || declaration.kind === SyntaxKind.ClassDeclaration;
default:
return false;
Debug.assertNever(container, "Invalid container kind.");
}
}
+54 -47
View File
@@ -272,7 +272,7 @@ namespace ts.FindAllReferences.Core {
}
function isModuleReferenceLocation(node: ts.Node): boolean {
if (node.kind !== SyntaxKind.StringLiteral && node.kind !== SyntaxKind.NoSubstitutionTemplateLiteral) {
if (!isStringLiteralLike(node)) {
return false;
}
switch (node.parent.kind) {
@@ -416,10 +416,16 @@ namespace ts.FindAllReferences.Core {
}
// If the symbol is declared as part of a declaration like `{ type: "a" } | { type: "b" }`, use the property on the union type to get more references.
return firstDefined(symbol.declarations, decl =>
isTypeLiteralNode(decl.parent) && isUnionTypeNode(decl.parent.parent)
return firstDefined(symbol.declarations, decl => {
if (!decl.parent) {
// Assertions for GH#21814. We should be handling SourceFile symbols in `getReferencedSymbolsForModule` instead of getting here.
Debug.assert(decl.kind === SyntaxKind.SourceFile);
Debug.fail(`Unexpected symbol at ${Debug.showSyntaxKind(node)}: ${Debug.showSymbol(symbol)}`);
}
return isTypeLiteralNode(decl.parent) && isUnionTypeNode(decl.parent.parent)
? checker.getPropertyOfType(checker.getTypeFromTypeNode(decl.parent.parent), symbol.name)
: undefined) || symbol;
: undefined;
}) || symbol;
}
/**
@@ -703,7 +709,7 @@ namespace ts.FindAllReferences.Core {
return exposedByParent ? scope.getSourceFile() : scope;
}
function getPossibleSymbolReferencePositions(sourceFile: SourceFile, symbolName: string, container: Node = sourceFile): number[] {
function getPossibleSymbolReferencePositions(sourceFile: SourceFile, symbolName: string, container: Node = sourceFile): ReadonlyArray<number> {
const positions: number[] = [];
/// TODO: Cache symbol existence for files to save text search
@@ -914,7 +920,8 @@ namespace ts.FindAllReferences.Core {
// At `export { x } from "foo"`, also search for the imported symbol `"foo".x`.
if (search.comingFrom !== ImportExport.Export && exportDeclaration.moduleSpecifier && !propertyName) {
searchForImportedSymbol(state.checker.getExportSpecifierLocalTargetSymbol(exportSpecifier), state);
const imported = state.checker.getExportSpecifierLocalTargetSymbol(exportSpecifier);
if (imported) searchForImportedSymbol(imported, state);
}
function addRef() {
@@ -923,7 +930,7 @@ namespace ts.FindAllReferences.Core {
}
function getLocalSymbolForExportSpecifier(referenceLocation: Identifier, referenceSymbol: Symbol, exportSpecifier: ExportSpecifier, checker: TypeChecker): Symbol {
return isExportSpecifierAlias(referenceLocation, exportSpecifier) ? checker.getExportSpecifierLocalTargetSymbol(exportSpecifier) : referenceSymbol;
return isExportSpecifierAlias(referenceLocation, exportSpecifier) && checker.getExportSpecifierLocalTargetSymbol(exportSpecifier) || referenceSymbol;
}
function isExportSpecifierAlias(referenceLocation: Identifier, exportSpecifier: ExportSpecifier): boolean {
@@ -1344,63 +1351,63 @@ namespace ts.FindAllReferences.Core {
const references: Entry[] = [];
let possiblePositions: number[];
let possiblePositions: ReadonlyArray<number>;
if (searchSpaceNode.kind === SyntaxKind.SourceFile) {
forEach(sourceFiles, sourceFile => {
cancellationToken.throwIfCancellationRequested();
possiblePositions = getPossibleSymbolReferencePositions(sourceFile, "this");
getThisReferencesInFile(sourceFile, sourceFile, possiblePositions, references);
getThisReferencesInFile(sourceFile, sourceFile, possiblePositions, staticFlag, references);
});
}
else {
const sourceFile = searchSpaceNode.getSourceFile();
possiblePositions = getPossibleSymbolReferencePositions(sourceFile, "this", searchSpaceNode);
getThisReferencesInFile(sourceFile, searchSpaceNode, possiblePositions, references);
getThisReferencesInFile(sourceFile, searchSpaceNode, possiblePositions, staticFlag, references);
}
return [{
definition: { type: "this", node: thisOrSuperKeyword },
references
}];
}
function getThisReferencesInFile(sourceFile: SourceFile, searchSpaceNode: Node, possiblePositions: number[], result: Entry[]): void {
forEach(possiblePositions, position => {
const node = getTouchingWord(sourceFile, position, /*includeJsDocComment*/ false);
if (!node || !isThis(node)) {
return;
}
function getThisReferencesInFile(sourceFile: SourceFile, searchSpaceNode: Node, possiblePositions: ReadonlyArray<number>, staticFlag: ModifierFlags, result: Push<Entry>): void {
forEach(possiblePositions, position => {
const node = getTouchingWord(sourceFile, position, /*includeJsDocComment*/ false);
if (!node || !isThis(node)) {
return;
}
const container = getThisContainer(node, /* includeArrowFunctions */ false);
const container = getThisContainer(node, /* includeArrowFunctions */ false);
switch (searchSpaceNode.kind) {
case SyntaxKind.FunctionExpression:
case SyntaxKind.FunctionDeclaration:
if (searchSpaceNode.symbol === container.symbol) {
result.push(nodeEntry(node));
}
break;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
if (isObjectLiteralMethod(searchSpaceNode) && searchSpaceNode.symbol === container.symbol) {
result.push(nodeEntry(node));
}
break;
case SyntaxKind.ClassExpression:
case SyntaxKind.ClassDeclaration:
// Make sure the container belongs to the same class
// and has the appropriate static modifier from the original container.
if (container.parent && searchSpaceNode.symbol === container.parent.symbol && (getModifierFlags(container) & ModifierFlags.Static) === staticFlag) {
result.push(nodeEntry(node));
}
break;
case SyntaxKind.SourceFile:
if (container.kind === SyntaxKind.SourceFile && !isExternalModule(<SourceFile>container)) {
result.push(nodeEntry(node));
}
break;
}
});
}
switch (searchSpaceNode.kind) {
case SyntaxKind.FunctionExpression:
case SyntaxKind.FunctionDeclaration:
if (searchSpaceNode.symbol === container.symbol) {
result.push(nodeEntry(node));
}
break;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
if (isObjectLiteralMethod(searchSpaceNode) && searchSpaceNode.symbol === container.symbol) {
result.push(nodeEntry(node));
}
break;
case SyntaxKind.ClassExpression:
case SyntaxKind.ClassDeclaration:
// Make sure the container belongs to the same class
// and has the appropriate static modifier from the original container.
if (container.parent && searchSpaceNode.symbol === container.parent.symbol && (getModifierFlags(container) & ModifierFlags.Static) === staticFlag) {
result.push(nodeEntry(node));
}
break;
case SyntaxKind.SourceFile:
if (container.kind === SyntaxKind.SourceFile && !isExternalModule(<SourceFile>container)) {
result.push(nodeEntry(node));
}
break;
}
});
}
function getReferencesForStringLiteral(node: StringLiteral, sourceFiles: ReadonlyArray<SourceFile>, cancellationToken: CancellationToken): SymbolAndEntries[] {
@@ -1417,7 +1424,7 @@ namespace ts.FindAllReferences.Core {
references
}];
function getReferencesForStringLiteralInFile(sourceFile: SourceFile, searchText: string, possiblePositions: number[], references: Push<NodeEntry>): void {
function getReferencesForStringLiteralInFile(sourceFile: SourceFile, searchText: string, possiblePositions: ReadonlyArray<number>, references: Push<NodeEntry>): void {
for (const position of possiblePositions) {
const node = getTouchingWord(sourceFile, position, /*includeJsDocComment*/ false);
if (node && node.kind === SyntaxKind.StringLiteral && (node as StringLiteral).text === searchText) {
+10 -2
View File
@@ -92,6 +92,9 @@ namespace ts.formatting {
rule("SpaceBetweenCloseBraceAndWhile", SyntaxKind.CloseBraceToken, SyntaxKind.WhileKeyword, [isNonJsxSameLineTokenContext], RuleAction.Space),
rule("NoSpaceBetweenEmptyBraceBrackets", SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken, [isNonJsxSameLineTokenContext, isObjectContext], RuleAction.Delete),
// Add a space after control dec context if the next character is an open bracket ex: 'if (false)[a, b] = [1, 2];' -> 'if (false) [a, b] = [1, 2];'
rule("SpaceAfterConditionalClosingParen", SyntaxKind.CloseParenToken, SyntaxKind.OpenBracketToken, [isControlDeclContext], RuleAction.Space),
rule("NoSpaceBetweenFunctionKeywordAndStar", SyntaxKind.FunctionKeyword, SyntaxKind.AsteriskToken, [isFunctionDeclarationOrFunctionExpressionContext], RuleAction.Delete),
rule("SpaceAfterStarInGeneratorDeclaration", SyntaxKind.AsteriskToken, [SyntaxKind.Identifier, SyntaxKind.OpenParenToken], [isFunctionDeclarationOrFunctionExpressionContext], RuleAction.Space),
@@ -162,6 +165,7 @@ namespace ts.formatting {
SyntaxKind.TypeKeyword,
SyntaxKind.FromKeyword,
SyntaxKind.KeyOfKeyword,
SyntaxKind.InferKeyword,
],
anyToken,
[isNonJsxSameLineTokenContext],
@@ -409,6 +413,7 @@ namespace ts.formatting {
switch (context.contextNode.kind) {
case SyntaxKind.BinaryExpression:
case SyntaxKind.ConditionalExpression:
case SyntaxKind.ConditionalType:
case SyntaxKind.AsExpression:
case SyntaxKind.ExportSpecifier:
case SyntaxKind.ImportSpecifier:
@@ -461,7 +466,8 @@ namespace ts.formatting {
}
function isConditionalOperatorContext(context: FormattingContext): boolean {
return context.contextNode.kind === SyntaxKind.ConditionalExpression;
return context.contextNode.kind === SyntaxKind.ConditionalExpression ||
context.contextNode.kind === SyntaxKind.ConditionalType;
}
function isSameLineTokenOrBeforeBlockContext(context: FormattingContext): boolean {
@@ -469,7 +475,9 @@ namespace ts.formatting {
}
function isBraceWrappedContext(context: FormattingContext): boolean {
return context.contextNode.kind === SyntaxKind.ObjectBindingPattern || isSingleLineBlockContext(context);
return context.contextNode.kind === SyntaxKind.ObjectBindingPattern ||
context.contextNode.kind === SyntaxKind.MappedType ||
isSingleLineBlockContext(context);
}
// This check is done before an open brace in a control construct, a function, or a typescript block declaration
+4 -6
View File
@@ -149,10 +149,7 @@ namespace ts.GoToDefinition {
// Check if position is on triple slash reference.
const comment = findReferenceInPosition(sourceFile.referencedFiles, position) || findReferenceInPosition(sourceFile.typeReferenceDirectives, position);
if (comment) {
return {
definitions,
textSpan: createTextSpanFromBounds(comment.pos, comment.end)
};
return { definitions, textSpan: createTextSpanFromRange(comment) };
}
const node = getTouchingPropertyName(sourceFile, position, /*includeJsDocComment*/ true);
@@ -191,7 +188,7 @@ namespace ts.GoToDefinition {
function getConstructSignatureDefinition(): DefinitionInfo[] | undefined {
// Applicable only if we are in a new expression, or we are on a constructor declaration
// and in either case the symbol has a construct signature definition, i.e. class
if (isNewExpressionTarget(node) || node.kind === SyntaxKind.ConstructorKeyword && symbol.flags & SymbolFlags.Class) {
if (symbol.flags & SymbolFlags.Class && (isNewExpressionTarget(node) || node.kind === SyntaxKind.ConstructorKeyword)) {
const cls = find(symbol.declarations, isClassLike) || Debug.fail("Expected declaration to have at least one class-like declaration");
return getSignatureDefinition(cls.members, /*selectConstructors*/ true);
}
@@ -217,6 +214,7 @@ namespace ts.GoToDefinition {
function isSignatureDeclaration(node: Node): boolean {
switch (node.kind) {
case ts.SyntaxKind.Constructor:
case ts.SyntaxKind.ConstructSignature:
case ts.SyntaxKind.FunctionDeclaration:
case ts.SyntaxKind.MethodDeclaration:
case ts.SyntaxKind.MethodSignature:
@@ -257,7 +255,7 @@ namespace ts.GoToDefinition {
return createDefinitionInfo(decl, symbolKind, symbolName, containerName);
}
export function findReferenceInPosition(refs: ReadonlyArray<FileReference>, pos: number): FileReference {
export function findReferenceInPosition(refs: ReadonlyArray<FileReference>, pos: number): FileReference | undefined {
for (const ref of refs) {
if (ref.pos <= pos && pos <= ref.end) {
return ref;
+13 -23
View File
@@ -107,6 +107,11 @@ namespace ts.FindAllReferences {
if (namedBindings && namedBindings.kind === SyntaxKind.NamespaceImport) {
handleNamespaceImport(direct, namedBindings.name);
}
else if (isDefaultImport(direct)) {
const sourceFileLike = getSourceFileLikeForImportDeclaration(direct);
addIndirectUser(sourceFileLike); // Add a check for indirect uses to handle synthetic default imports
directImports.push(direct);
}
else {
directImports.push(direct);
}
@@ -512,29 +517,12 @@ namespace ts.FindAllReferences {
const sym = useLhsSymbol ? checker.getSymbolAtLocation(cast(node.left, isPropertyAccessExpression).name) : symbol;
// Better detection for GH#20803
if (sym && !(checker.getMergedSymbol(sym.parent).flags & SymbolFlags.Module)) {
Debug.fail(`Special property assignment kind does not have a module as its parent. Assignment is ${showSymbol(sym)}, parent is ${showSymbol(sym.parent)}`);
Debug.fail(`Special property assignment kind does not have a module as its parent. Assignment is ${Debug.showSymbol(sym)}, parent is ${Debug.showSymbol(sym.parent)}`);
}
return sym && exportInfo(sym, kind);
}
}
function showSymbol(s: Symbol): string {
const decls = s.declarations.map(d => (ts as any).SyntaxKind[d.kind]).join(",");
const flags = showFlags(s.flags, (ts as any).SymbolFlags);
return `{ declarations: ${decls}, flags: ${flags} }`;
}
function showFlags(f: number, flags: any) {
const out = [];
for (let pow = 0; pow <= 30; pow++) {
const n = 1 << pow;
if (f & n) {
out.push(flags[n]);
}
}
return out.join("|");
}
function getImport(): ImportedSymbol | undefined {
const isImport = isNodeImport(node);
if (!isImport) return undefined;
@@ -572,17 +560,17 @@ namespace ts.FindAllReferences {
function getExportEqualsLocalSymbol(importedSymbol: Symbol, checker: TypeChecker): Symbol {
if (importedSymbol.flags & SymbolFlags.Alias) {
return checker.getImmediateAliasedSymbol(importedSymbol);
return Debug.assertDefined(checker.getImmediateAliasedSymbol(importedSymbol));
}
const decl = importedSymbol.valueDeclaration;
if (isExportAssignment(decl)) { // `export = class {}`
return decl.expression.symbol;
return Debug.assertDefined(decl.expression.symbol);
}
else if (isBinaryExpression(decl)) { // `module.exports = class {}`
return decl.right.symbol;
return Debug.assertDefined(decl.right.symbol);
}
Debug.fail();
return Debug.fail();
}
// If a reference is a class expression, the exported node would be its parent.
@@ -618,7 +606,9 @@ namespace ts.FindAllReferences {
}
export function getExportInfo(exportSymbol: Symbol, exportKind: ExportKind, checker: TypeChecker): ExportInfo | undefined {
const exportingModuleSymbol = checker.getMergedSymbol(exportSymbol.parent); // Need to get merged symbol in case there's an augmentation.
const moduleSymbol = exportSymbol.parent;
if (!moduleSymbol) return undefined; // This can happen if an `export` is not at the top-level (which is a compile error).
const exportingModuleSymbol = checker.getMergedSymbol(moduleSymbol); // Need to get merged symbol in case there's an augmentation.
// `export` may appear in a namespace. In that case, just rely on global search.
return isExternalModuleSymbol(exportingModuleSymbol) ? { exportingModuleSymbol, exportKind } : undefined;
}
+19 -6
View File
@@ -4,6 +4,7 @@
/// <reference path='../compiler/types.ts' />
/// <reference path='../compiler/core.ts' />
/// <reference path='../compiler/commandLineParser.ts' />
/// <reference path='../services/semver.ts' />
/* @internal */
namespace ts.JsTyping {
@@ -26,6 +27,17 @@ namespace ts.JsTyping {
typings?: string;
}
export interface CachedTyping {
typingLocation: string;
version: Semver;
}
/* @internal */
export function isTypingUpToDate(cachedTyping: JsTyping.CachedTyping, availableTypingVersions: MapLike<string>) {
const availableVersion = Semver.parse(getProperty(availableTypingVersions, `ts${ts.versionMajorMinor}`) || getProperty(availableTypingVersions, "latest"));
return !availableVersion.greaterThan(cachedTyping.version);
}
/* @internal */
export const nodeCoreModuleList: ReadonlyArray<string> = [
"buffer", "querystring", "events", "http", "cluster",
@@ -60,7 +72,7 @@ namespace ts.JsTyping {
* @param fileNames are the file names that belong to the same project
* @param projectRootPath is the path to the project root directory
* @param safeListPath is the path used to retrieve the safe list
* @param packageNameToTypingLocation is the map of package names to their cached typing locations
* @param packageNameToTypingLocation is the map of package names to their cached typing locations and installed versions
* @param typeAcquisition is used to customize the typing acquisition process
* @param compilerOptions are used as a source for typing inference
*/
@@ -70,9 +82,10 @@ namespace ts.JsTyping {
fileNames: string[],
projectRootPath: Path,
safeList: SafeList,
packageNameToTypingLocation: ReadonlyMap<string>,
packageNameToTypingLocation: ReadonlyMap<CachedTyping>,
typeAcquisition: TypeAcquisition,
unresolvedImports: ReadonlyArray<string>):
unresolvedImports: ReadonlyArray<string>,
typesRegistry: ReadonlyMap<MapLike<string>>):
{ cachedTypingPaths: string[], newTypingNames: string[], filesToWatch: string[] } {
if (!typeAcquisition || !typeAcquisition.enable) {
@@ -122,9 +135,9 @@ namespace ts.JsTyping {
addInferredTypings(module, "Inferred typings from unresolved imports");
}
// Add the cached typing locations for inferred typings that are already installed
packageNameToTypingLocation.forEach((typingLocation, name) => {
if (inferredTypings.has(name) && inferredTypings.get(name) === undefined) {
inferredTypings.set(name, typingLocation);
packageNameToTypingLocation.forEach((typing, name) => {
if (inferredTypings.has(name) && inferredTypings.get(name) === undefined && isTypingUpToDate(typing, typesRegistry.get(name))) {
inferredTypings.set(name, typing.typingLocation);
}
});
+1 -3
View File
@@ -632,9 +632,7 @@ namespace ts.NavigationBar {
}
function getNodeSpan(node: Node): TextSpan {
return node.kind === SyntaxKind.SourceFile
? createTextSpanFromBounds(node.getFullStart(), node.getEnd())
: createTextSpanFromNode(node, curSourceFile);
return node.kind === SyntaxKind.SourceFile ? createTextSpanFromRange(node) : createTextSpanFromNode(node, curSourceFile);
}
function getModifiers(node: ts.Node): string {
+1 -1
View File
@@ -462,7 +462,7 @@ namespace ts.Completions.PathCompletions {
}
function normalizeAndPreserveTrailingSlash(path: string) {
if (path === "./") {
if (normalizeSlashes(path) === "./") {
// normalizePath turns "./" into "". "" + "/" would then be a rooted path instead of a relative one, so avoid this particular case.
// There is no problem for adding "/" to a non-empty string -- it's only a problem at the beginning.
return "";
@@ -76,7 +76,7 @@ namespace ts.refactor.annotateWithTypeFromJSDoc {
const changeTracker = textChanges.ChangeTracker.fromContext(context);
const declarationWithType = addType(decl, transformJSDocType(jsdocType) as TypeNode);
suppressLeadingAndTrailingTrivia(declarationWithType);
changeTracker.replaceRange(sourceFile, { pos: decl.getStart(), end: decl.end }, declarationWithType);
changeTracker.replaceNode(sourceFile, decl, declarationWithType, textChanges.useNonAdjustedPositions);
return {
edits: changeTracker.getChanges(),
renameFilename: undefined,
@@ -91,7 +91,7 @@ namespace ts.refactor.annotateWithTypeFromJSDoc {
const changeTracker = textChanges.ChangeTracker.fromContext(context);
const functionWithType = addTypesToFunctionLike(decl);
suppressLeadingAndTrailingTrivia(functionWithType);
changeTracker.replaceRange(sourceFile, { pos: decl.getStart(), end: decl.end }, functionWithType);
changeTracker.replaceNode(sourceFile, decl, functionWithType, textChanges.useNonAdjustedPositions);
return {
edits: changeTracker.getChanges(),
renameFilename: undefined,
+20 -6
View File
@@ -33,14 +33,17 @@ namespace ts.refactor {
return isExportsOrModuleExportsOrAlias(sourceFile, node as PropertyAccessExpression)
|| isExportsOrModuleExportsOrAlias(sourceFile, (node as PropertyAccessExpression).expression);
case SyntaxKind.VariableDeclarationList:
const decl = (node as VariableDeclarationList).declarations[0];
return isExportsOrModuleExportsOrAlias(sourceFile, decl.initializer);
return isVariableDeclarationTriggerLocation(firstOrUndefined((node as VariableDeclarationList).declarations));
case SyntaxKind.VariableDeclaration:
return isExportsOrModuleExportsOrAlias(sourceFile, (node as VariableDeclaration).initializer);
return isVariableDeclarationTriggerLocation(node as VariableDeclaration);
default:
return isExpression(node) && isExportsOrModuleExportsOrAlias(sourceFile, node)
|| !onSecondTry && isAtTriggerLocation(sourceFile, node.parent, /*onSecondTry*/ true);
}
function isVariableDeclarationTriggerLocation(decl: VariableDeclaration | undefined) {
return !!decl && !!decl.initializer && isExportsOrModuleExportsOrAlias(sourceFile, decl.initializer);
}
}
function isAtTopLevelRequire(call: CallExpression): boolean {
@@ -375,7 +378,14 @@ namespace ts.refactor {
function convertExportsDotXEquals(name: string | undefined, exported: Expression): Statement {
const modifiers = [createToken(SyntaxKind.ExportKeyword)];
switch (exported.kind) {
case SyntaxKind.FunctionExpression:
case SyntaxKind.FunctionExpression: {
const { name: expressionName } = exported as FunctionExpression;
if (expressionName && expressionName.text !== name) {
// `exports.f = function g() {}` -> `export const f = function g() {}`
return exportConst();
}
}
// falls through
case SyntaxKind.ArrowFunction:
// `exports.f = function() {}` --> `export function f() {}`
return functionExpressionToDeclaration(name, modifiers, exported as FunctionExpression | ArrowFunction);
@@ -383,8 +393,12 @@ namespace ts.refactor {
// `exports.C = class {}` --> `export class C {}`
return classExpressionToDeclaration(name, modifiers, exported as ClassExpression);
default:
// `exports.x = 0;` --> `export const x = 0;`
return makeConst(modifiers, createIdentifier(name), exported);
return exportConst();
}
function exportConst() {
// `exports.x = 0;` --> `export const x = 0;`
return makeConst(modifiers, createIdentifier(name), exported);
}
}
+11 -9
View File
@@ -687,7 +687,7 @@ namespace ts.refactor.extractSymbol {
}
function getDescriptionForClassLikeDeclaration(scope: ClassLikeDeclaration): string {
return scope.kind === SyntaxKind.ClassDeclaration
? `class '${scope.name.text}'`
? scope.name ? `class '${scope.name.text}'` : "anonymous class declaration"
: scope.name ? `class expression '${scope.name.text}'` : "anonymous class expression";
}
function getDescriptionForModuleLikeDeclaration(scope: SourceFile | ModuleBlock): string | SpecialScope {
@@ -968,7 +968,7 @@ namespace ts.refactor.extractSymbol {
}
if (isReadonlyArray(range.range)) {
changeTracker.replaceNodesWithNodes(context.file, range.range, newNodes);
changeTracker.replaceNodeRangeWithNodes(context.file, first(range.range), last(range.range), newNodes);
}
else {
changeTracker.replaceNodeWithNodes(context.file, range.range, newNodes);
@@ -1053,7 +1053,7 @@ namespace ts.refactor.extractSymbol {
changeTracker.insertNodeBefore(context.file, nodeToInsertBefore, newVariable, /*blankLineBetween*/ true);
// Consume
changeTracker.replaceRange(context.file, { pos: node.getStart(), end: node.end }, localReference);
changeTracker.replaceNode(context.file, node, localReference, textChanges.useNonAdjustedPositions);
}
else {
const newVariableDeclaration = createVariableDeclaration(localNameText, variableType, initializer);
@@ -1070,7 +1070,7 @@ namespace ts.refactor.extractSymbol {
// Consume
const localReference = createIdentifier(localNameText);
changeTracker.replaceRange(context.file, { pos: node.getStart(), end: node.end }, localReference);
changeTracker.replaceNode(context.file, node, localReference, textChanges.useNonAdjustedPositions);
}
else if (node.parent.kind === SyntaxKind.ExpressionStatement && scope === findAncestor(node, isScope)) {
// If the parent is an expression statement and the target scope is the immediately enclosing one,
@@ -1078,7 +1078,7 @@ namespace ts.refactor.extractSymbol {
const newVariableStatement = createVariableStatement(
/*modifiers*/ undefined,
createVariableDeclarationList([newVariableDeclaration], NodeFlags.Const));
changeTracker.replaceRange(context.file, { pos: node.parent.getStart(), end: node.parent.end }, newVariableStatement);
changeTracker.replaceNode(context.file, node.parent, newVariableStatement, textChanges.useNonAdjustedPositions);
}
else {
const newVariableStatement = createVariableStatement(
@@ -1097,11 +1097,11 @@ namespace ts.refactor.extractSymbol {
// Consume
if (node.parent.kind === SyntaxKind.ExpressionStatement) {
// If the parent is an expression statement, delete it.
changeTracker.deleteRange(context.file, { pos: node.parent.getStart(), end: node.parent.end });
changeTracker.deleteNode(context.file, node.parent, textChanges.useNonAdjustedPositions);
}
else {
const localReference = createIdentifier(localNameText);
changeTracker.replaceRange(context.file, { pos: node.getStart(), end: node.end }, localReference);
changeTracker.replaceNode(context.file, node, localReference, textChanges.useNonAdjustedPositions);
}
}
}
@@ -1689,7 +1689,8 @@ namespace ts.refactor.extractSymbol {
return symbolId;
}
// find first declaration in this file
const declInFile = find(symbol.getDeclarations(), d => d.getSourceFile() === sourceFile);
const decls = symbol.getDeclarations();
const declInFile = decls && find(decls, d => d.getSourceFile() === sourceFile);
if (!declInFile) {
return undefined;
}
@@ -1782,7 +1783,8 @@ namespace ts.refactor.extractSymbol {
if (!symbol) {
return undefined;
}
if (symbol.getDeclarations().some(d => d.parent === scopeDecl)) {
const decls = symbol.getDeclarations();
if (decls && decls.some(d => d.parent === scopeDecl)) {
return createIdentifier(symbol.name);
}
const prefix = tryReplaceWithQualifiedNameOrPropertyAccess(symbol.parent, scopeDecl, isTypeNode);
+4 -4
View File
@@ -7,7 +7,7 @@ namespace ts.refactor.installTypesForPackage {
function getAvailableActions(context: RefactorContext): ApplicableRefactorInfo[] | undefined {
const { file, startPosition, program } = context;
if (!program.getCompilerOptions().allowSyntheticDefaultImports) {
if (!getAllowSyntheticDefaultImports(program.getCompilerOptions())) {
return undefined;
}
@@ -17,8 +17,8 @@ namespace ts.refactor.installTypesForPackage {
}
const module = getResolvedModule(file, importInfo.moduleSpecifier.text);
const resolvedFile = program.getSourceFile(module.resolvedFileName);
if (!(resolvedFile.externalModuleIndicator && isExportAssignment(resolvedFile.externalModuleIndicator) && resolvedFile.externalModuleIndicator.isExportEquals)) {
const resolvedFile = module && program.getSourceFile(module.resolvedFileName);
if (!(resolvedFile && resolvedFile.externalModuleIndicator && isExportAssignment(resolvedFile.externalModuleIndicator) && resolvedFile.externalModuleIndicator.isExportEquals)) {
return undefined;
}
@@ -69,7 +69,7 @@ namespace ts.refactor.installTypesForPackage {
case SyntaxKind.ImportDeclaration:
const d = node as ImportDeclaration;
const { importClause } = d;
return !importClause.name && importClause.namedBindings.kind === SyntaxKind.NamespaceImport && isStringLiteral(d.moduleSpecifier)
return importClause && !importClause.name && importClause.namedBindings.kind === SyntaxKind.NamespaceImport && isStringLiteral(d.moduleSpecifier)
? { importStatement: d, name: importClause.namedBindings.name, moduleSpecifier: d.moduleSpecifier }
: undefined;
// For known child node kinds of convertible imports, try again with parent node.
+61
View File
@@ -0,0 +1,61 @@
/* @internal */
namespace ts {
function stringToInt(str: string): number {
const n = parseInt(str, 10);
if (isNaN(n)) {
throw new Error(`Error in parseInt(${JSON.stringify(str)})`);
}
return n;
}
const isPrereleaseRegex = /^(.*)-next.\d+/;
const prereleaseSemverRegex = /^(\d+)\.(\d+)\.0-next.(\d+)$/;
const semverRegex = /^(\d+)\.(\d+)\.(\d+)$/;
export class Semver {
static parse(semver: string): Semver {
const isPrerelease = isPrereleaseRegex.test(semver);
const result = Semver.tryParse(semver, isPrerelease);
if (!result) {
throw new Error(`Unexpected semver: ${semver} (isPrerelease: ${isPrerelease})`);
}
return result;
}
static fromRaw({ major, minor, patch, isPrerelease }: Semver): Semver {
return new Semver(major, minor, patch, isPrerelease);
}
// This must parse the output of `versionString`.
private static tryParse(semver: string, isPrerelease: boolean): Semver | undefined {
// Per the semver spec <http://semver.org/#spec-item-2>:
// "A normal version number MUST take the form X.Y.Z where X, Y, and Z are non-negative integers, and MUST NOT contain leading zeroes."
const rgx = isPrerelease ? prereleaseSemverRegex : semverRegex;
const match = rgx.exec(semver);
return match ? new Semver(stringToInt(match[1]), stringToInt(match[2]), stringToInt(match[3]), isPrerelease) : undefined;
}
private constructor(
readonly major: number, readonly minor: number, readonly patch: number,
/**
* If true, this is `major.minor.0-next.patch`.
* If false, this is `major.minor.patch`.
*/
readonly isPrerelease: boolean) { }
get versionString(): string {
return this.isPrerelease ? `${this.major}.${this.minor}.0-next.${this.patch}` : `${this.major}.${this.minor}.${this.patch}`;
}
equals(sem: Semver): boolean {
return this.major === sem.major && this.minor === sem.minor && this.patch === sem.patch && this.isPrerelease === sem.isPrerelease;
}
greaterThan(sem: Semver): boolean {
return this.major > sem.major || this.major === sem.major
&& (this.minor > sem.minor || this.minor === sem.minor
&& (!this.isPrerelease && sem.isPrerelease || this.isPrerelease === sem.isPrerelease
&& this.patch > sem.patch));
}
}
}
+2 -2
View File
@@ -121,7 +121,7 @@ namespace ts {
const textPos = scanner.getTextPos();
if (textPos <= end) {
if (token === SyntaxKind.Identifier) {
Debug.fail(`Did not expect ${(ts as any).SyntaxKind[this.kind]} to have an Identifier in its trivia`);
Debug.fail(`Did not expect ${Debug.showSyntaxKind(this)} to have an Identifier in its trivia`);
}
nodes.push(createNode(token, pos, textPos, this));
}
@@ -1580,7 +1580,7 @@ namespace ts {
return results;
}
function getDocumentHighlights(fileName: string, position: number, filesToSearch: string[]): DocumentHighlights[] {
function getDocumentHighlights(fileName: string, position: number, filesToSearch: ReadonlyArray<string>): DocumentHighlights[] {
synchronizeHostData();
const sourceFilesToSearch = map(filesToSearch, f => Debug.assertDefined(program.getSourceFile(f)));
const sourceFile = getValidSourceFile(fileName);
+13 -1
View File
@@ -24,6 +24,17 @@ let debugObjectHost: { CollectGarbage(): void } = (function (this: any) { return
/* @internal */
namespace ts {
interface DiscoverTypingsInfo {
fileNames: string[]; // The file names that belong to the same project.
projectRootPath: string; // The path to the project root directory
safeListPath: string; // The path used to retrieve the safe list
packageNameToTypingLocation: Map<JsTyping.CachedTyping>; // The map of package names to their cached typing locations and installed versions
typeAcquisition: TypeAcquisition; // Used to customize the type acquisition process
compilerOptions: CompilerOptions; // Used as a source for typing inference
unresolvedImports: ReadonlyArray<string>; // List of unresolved module ids from imports
typesRegistry: ReadonlyMap<MapLike<string>>; // The map of available typings in npm to maps of TS versions to their latest supported versions
}
export interface ScriptSnapshotShim {
/** Gets a portion of the script snapshot specified by [start, end). */
getText(start: number, end: number): string;
@@ -1159,7 +1170,8 @@ namespace ts {
this.safeList,
info.packageNameToTypingLocation,
info.typeAcquisition,
info.unresolvedImports);
info.unresolvedImports,
info.typesRegistry);
});
}
}
+2
View File
@@ -79,6 +79,8 @@ namespace ts.SymbolDisplay {
switch (location.parent && location.parent.kind) {
// If we've typed a character of the attribute name, will be 'JsxAttribute', else will be 'JsxOpeningElement'.
case SyntaxKind.JsxOpeningElement:
case SyntaxKind.JsxElement:
case SyntaxKind.JsxSelfClosingElement:
return location.kind === SyntaxKind.Identifier ? ScriptElementKind.memberVariableElement : ScriptElementKind.jsxAttribute;
case SyntaxKind.JsxAttribute:
return ScriptElementKind.jsxAttribute;
+44 -49
View File
@@ -28,9 +28,11 @@ namespace ts.textChanges {
}
export interface ConfigurableStart {
/** True to use getStart() (NB, not getFullStart()) without adjustment. */
useNonAdjustedStartPosition?: boolean;
}
export interface ConfigurableEnd {
/** True to use getEnd() without adjustment. */
useNonAdjustedEndPosition?: boolean;
}
@@ -70,6 +72,11 @@ namespace ts.textChanges {
*/
export type ConfigurableStartEnd = ConfigurableStart & ConfigurableEnd;
export const useNonAdjustedPositions: ConfigurableStartEnd = {
useNonAdjustedStartPosition: true,
useNonAdjustedEndPosition: true,
};
export interface InsertNodeOptions {
/**
* Text to be inserted before the new node
@@ -117,13 +124,10 @@ namespace ts.textChanges {
readonly options?: never;
}
interface ChangeMultipleNodesOptions extends ChangeNodeOptions {
nodeSeparator: string;
}
interface ReplaceWithMultipleNodes extends BaseChange {
readonly kind: ChangeKind.ReplaceWithMultipleNodes;
readonly nodes: ReadonlyArray<Node>;
readonly options?: ChangeMultipleNodesOptions;
readonly options?: ChangeNodeOptions;
}
export function getSeparatorCharacter(separator: Token<SyntaxKind.CommaToken | SyntaxKind.SemicolonToken>) {
@@ -132,7 +136,7 @@ namespace ts.textChanges {
export function getAdjustedStartPosition(sourceFile: SourceFile, node: Node, options: ConfigurableStart, position: Position) {
if (options.useNonAdjustedStartPosition) {
return node.getFullStart();
return node.getStart();
}
const fullStart = node.getFullStart();
const start = node.getStart(sourceFile);
@@ -280,51 +284,41 @@ namespace ts.textChanges {
return this;
}
public replaceRange(sourceFile: SourceFile, range: TextRange, newNode: Node, options: InsertNodeOptions = {}) {
// TODO (https://github.com/Microsoft/TypeScript/issues/21246): default should probably be useNonAdjustedPositions
public replaceRange(sourceFile: SourceFile, range: TextRange, newNode: Node, options: ChangeNodeOptions = {}) {
this.changes.push({ kind: ChangeKind.ReplaceWithSingleNode, sourceFile, range, options, node: newNode });
return this;
}
// TODO (https://github.com/Microsoft/TypeScript/issues/21246): default should probably be useNonAdjustedPositions
public replaceNode(sourceFile: SourceFile, oldNode: Node, newNode: Node, options: ChangeNodeOptions = {}) {
const startPosition = getAdjustedStartPosition(sourceFile, oldNode, options, Position.Start);
const endPosition = getAdjustedEndPosition(sourceFile, oldNode, options);
return this.replaceWithSingle(sourceFile, startPosition, endPosition, newNode, options);
const pos = getAdjustedStartPosition(sourceFile, oldNode, options, Position.Start);
const end = getAdjustedEndPosition(sourceFile, oldNode, options);
return this.replaceRange(sourceFile, { pos, end }, newNode, options);
}
// TODO (https://github.com/Microsoft/TypeScript/issues/21246): default should probably be useNonAdjustedPositions
public replaceNodeRange(sourceFile: SourceFile, startNode: Node, endNode: Node, newNode: Node, options: ChangeNodeOptions = {}) {
const startPosition = getAdjustedStartPosition(sourceFile, startNode, options, Position.Start);
const endPosition = getAdjustedEndPosition(sourceFile, endNode, options);
return this.replaceWithSingle(sourceFile, startPosition, endPosition, newNode, options);
const pos = getAdjustedStartPosition(sourceFile, startNode, options, Position.Start);
const end = getAdjustedEndPosition(sourceFile, endNode, options);
return this.replaceRange(sourceFile, { pos, end }, newNode, options);
}
private replaceWithSingle(sourceFile: SourceFile, startPosition: number, endPosition: number, newNode: Node, options: ChangeNodeOptions): this {
this.changes.push({
kind: ChangeKind.ReplaceWithSingleNode,
sourceFile,
options,
node: newNode,
range: { pos: startPosition, end: endPosition }
});
public replaceRangeWithNodes(sourceFile: SourceFile, range: TextRange, newNodes: ReadonlyArray<Node>, options: ChangeNodeOptions = useNonAdjustedPositions) {
this.changes.push({ kind: ChangeKind.ReplaceWithMultipleNodes, sourceFile, range, options, nodes: newNodes });
return this;
}
private replaceWithMultiple(sourceFile: SourceFile, startPosition: number, endPosition: number, newNodes: ReadonlyArray<Node>, options: ChangeMultipleNodesOptions): this {
this.changes.push({
kind: ChangeKind.ReplaceWithMultipleNodes,
sourceFile,
options,
nodes: newNodes,
range: { pos: startPosition, end: endPosition }
});
return this;
public replaceNodeWithNodes(sourceFile: SourceFile, oldNode: Node, newNodes: ReadonlyArray<Node>, options: ChangeNodeOptions = useNonAdjustedPositions) {
const pos = getAdjustedStartPosition(sourceFile, oldNode, options, Position.Start);
const end = getAdjustedEndPosition(sourceFile, oldNode, options);
return this.replaceRangeWithNodes(sourceFile, { pos, end }, newNodes, options);
}
public replaceNodeWithNodes(sourceFile: SourceFile, oldNode: Node, newNodes: ReadonlyArray<Node>): void {
this.replaceWithMultiple(sourceFile, oldNode.getStart(sourceFile), oldNode.getEnd(), newNodes, { nodeSeparator: this.newLineCharacter });
}
public replaceNodesWithNodes(sourceFile: SourceFile, oldNodes: ReadonlyArray<Node>, newNodes: ReadonlyArray<Node>): void {
this.replaceWithMultiple(sourceFile, first(oldNodes).getStart(sourceFile), last(oldNodes).getEnd(), newNodes, { nodeSeparator: this.newLineCharacter });
public replaceNodeRangeWithNodes(sourceFile: SourceFile, startNode: Node, endNode: Node, newNodes: ReadonlyArray<Node>, options: ChangeNodeOptions = useNonAdjustedPositions) {
const pos = getAdjustedStartPosition(sourceFile, startNode, options, Position.Start);
const end = getAdjustedEndPosition(sourceFile, endNode, options);
return this.replaceRangeWithNodes(sourceFile, { pos, end }, newNodes, options);
}
private insertNodeAt(sourceFile: SourceFile, pos: number, newNode: Node, options: InsertNodeOptions = {}) {
@@ -341,18 +335,13 @@ namespace ts.textChanges {
}
public insertNodeBefore(sourceFile: SourceFile, before: Node, newNode: Node, blankLineBetween = false) {
const startPosition = getAdjustedStartPosition(sourceFile, before, {}, Position.Start);
return this.replaceWithSingle(sourceFile, startPosition, startPosition, newNode, this.getOptionsForInsertNodeBefore(before, blankLineBetween));
const pos = getAdjustedStartPosition(sourceFile, before, {}, Position.Start);
return this.replaceRange(sourceFile, { pos, end: pos }, newNode, this.getOptionsForInsertNodeBefore(before, blankLineBetween));
}
public insertModifierBefore(sourceFile: SourceFile, modifier: SyntaxKind, before: Node): void {
const pos = before.getStart(sourceFile);
this.replaceWithSingle(sourceFile, pos, pos, createToken(modifier), { suffix: " " });
}
public changeIdentifierToPropertyAccess(sourceFile: SourceFile, prefix: string, node: Identifier): void {
const startPosition = getAdjustedStartPosition(sourceFile, node, {}, Position.Start);
this.replaceWithSingle(sourceFile, startPosition, startPosition, createPropertyAccess(createIdentifier(prefix), ""), {});
this.replaceRange(sourceFile, { pos, end: pos }, createToken(modifier), { suffix: " " });
}
private getOptionsForInsertNodeBefore(before: Node, doubleNewlines: boolean): ChangeNodeOptions {
@@ -390,8 +379,8 @@ namespace ts.textChanges {
}
public insertNodeAtEndOfScope(sourceFile: SourceFile, scope: Node, newNode: Node): void {
const startPosition = getAdjustedStartPosition(sourceFile, scope.getLastToken(), {}, Position.Start);
this.replaceWithSingle(sourceFile, startPosition, startPosition, newNode, {
const pos = getAdjustedStartPosition(sourceFile, scope.getLastToken(), {}, Position.Start);
this.replaceRange(sourceFile, { pos, end: pos }, newNode, {
prefix: isLineBreak(sourceFile.text.charCodeAt(scope.getLastToken().pos)) ? this.newLineCharacter : this.newLineCharacter + this.newLineCharacter,
suffix: this.newLineCharacter
});
@@ -433,7 +422,7 @@ namespace ts.textChanges {
}
}
const endPosition = getAdjustedEndPosition(sourceFile, after, {});
return this.replaceWithSingle(sourceFile, endPosition, endPosition, newNode, this.getInsertNodeAfterOptions(after));
return this.replaceRange(sourceFile, { pos: endPosition, end: endPosition }, newNode, this.getInsertNodeAfterOptions(after));
}
private getInsertNodeAfterOptions(node: Node): InsertNodeOptions {
@@ -629,7 +618,7 @@ namespace ts.textChanges {
}
private computeSpan(change: Change, _sourceFile: SourceFile): TextSpan {
return createTextSpanFromBounds(change.range.pos, change.range.end);
return createTextSpanFromRange(change.range);
}
private computeNewText(change: Change, sourceFile: SourceFile): string {
@@ -643,8 +632,14 @@ namespace ts.textChanges {
const pos = change.range.pos;
const posStartsLine = getLineStartPositionForPosition(pos, sourceFile) === pos;
if (change.kind === ChangeKind.ReplaceWithMultipleNodes) {
const parts = change.nodes.map(n => this.getFormattedTextOfNode(n, sourceFile, pos, options));
text = parts.join(change.options.nodeSeparator);
const lastIndex = change.nodes.length - 1;
const parts = change.nodes.map((n, index) => {
const formatted = this.getFormattedTextOfNode(n, sourceFile, pos, options);
return index === lastIndex || endsWith(formatted, this.newLineCharacter)
? formatted
: (formatted + this.newLineCharacter);
});
text = parts.join("");
}
else {
Debug.assert(change.kind === ChangeKind.ReplaceWithSingleNode, "change.kind === ReplaceWithSingleNode");
+1
View File
@@ -66,6 +66,7 @@
"services.ts",
"transform.ts",
"transpile.ts",
"semver.ts",
"shims.ts",
"signatureHelp.ts",
"symbolDisplay.ts",
+32 -29
View File
@@ -761,23 +761,12 @@ namespace ts {
Debug.assert(!(result && isWhiteSpaceOnlyJsxText(result)));
return result;
function findRightmostToken(n: Node): Node {
if (isToken(n)) {
function find(n: Node): Node | undefined {
if (isNonWhitespaceToken(n)) {
return n;
}
const children = n.getChildren();
const candidate = findRightmostChildNodeWithTokens(children, /*exclusiveStartPosition*/ children.length);
return candidate && findRightmostToken(candidate);
}
function find(n: Node): Node {
if (isToken(n)) {
return n;
}
const children = n.getChildren();
const children = n.getChildren(sourceFile);
for (let i = 0; i < children.length; i++) {
const child = children[i];
// Note that the span of a node's tokens is [node.getStart(...), node.end).
@@ -795,7 +784,7 @@ namespace ts {
if (lookInPreviousChild) {
// actual start of the node is past the position - previous token should be at the end of previous child
const candidate = findRightmostChildNodeWithTokens(children, /*exclusiveStartPosition*/ i);
return candidate && findRightmostToken(candidate);
return candidate && findRightmostToken(candidate, sourceFile);
}
else {
// candidate should be in this node
@@ -812,23 +801,37 @@ namespace ts {
// Namely we are skipping the check: 'position < node.end'
if (children.length) {
const candidate = findRightmostChildNodeWithTokens(children, /*exclusiveStartPosition*/ children.length);
return candidate && findRightmostToken(candidate);
return candidate && findRightmostToken(candidate, sourceFile);
}
}
}
/**
* Finds the rightmost child to the left of `children[exclusiveStartPosition]` which is a non-all-whitespace token or has constituent tokens.
*/
function findRightmostChildNodeWithTokens(children: Node[], exclusiveStartPosition: number): Node {
for (let i = exclusiveStartPosition - 1; i >= 0; i--) {
const child = children[i];
function isNonWhitespaceToken(n: Node): boolean {
return isToken(n) && !isWhiteSpaceOnlyJsxText(n);
}
if (isWhiteSpaceOnlyJsxText(child)) {
Debug.assert(i > 0, "`JsxText` tokens should not be the first child of `JsxElement | JsxSelfClosingElement`");
}
else if (nodeHasTokens(children[i])) {
return children[i];
}
function findRightmostToken(n: Node, sourceFile: SourceFile): Node | undefined {
if (isNonWhitespaceToken(n)) {
return n;
}
const children = n.getChildren(sourceFile);
const candidate = findRightmostChildNodeWithTokens(children, /*exclusiveStartPosition*/ children.length);
return candidate && findRightmostToken(candidate, sourceFile);
}
/**
* Finds the rightmost child to the left of `children[exclusiveStartPosition]` which is a non-all-whitespace token or has constituent tokens.
*/
function findRightmostChildNodeWithTokens(children: Node[], exclusiveStartPosition: number): Node | undefined {
for (let i = exclusiveStartPosition - 1; i >= 0; i--) {
const child = children[i];
if (isWhiteSpaceOnlyJsxText(child)) {
Debug.assert(i > 0, "`JsxText` tokens should not be the first child of `JsxElement | JsxSelfClosingElement`");
}
else if (nodeHasTokens(children[i])) {
return children[i];
}
}
}
@@ -893,7 +896,7 @@ namespace ts {
return false;
}
export function isWhiteSpaceOnlyJsxText(node: Node): node is JsxText {
function isWhiteSpaceOnlyJsxText(node: Node): boolean {
return isJsxText(node) && node.containsOnlyWhiteSpaces;
}
@@ -3,5 +3,8 @@ class C {
>C : Symbol(C, Decl(ClassDeclaration21.ts, 0, 0))
0();
>0 : Symbol(C[0], Decl(ClassDeclaration21.ts, 0, 9))
1() { }
>1 : Symbol(C[1], Decl(ClassDeclaration21.ts, 1, 8))
}
@@ -3,5 +3,8 @@ class C {
>C : C
0();
>0 : () => any
1() { }
>1 : () => void
}
@@ -3,5 +3,8 @@ class C {
>C : Symbol(C, Decl(ClassDeclaration22.ts, 0, 0))
"foo"();
>"foo" : Symbol(C["foo"], Decl(ClassDeclaration22.ts, 0, 9))
"bar"() { }
>"bar" : Symbol(C["bar"], Decl(ClassDeclaration22.ts, 1, 12))
}
@@ -3,5 +3,8 @@ class C {
>C : C
"foo"();
>"foo" : () => any
"bar"() { }
>"bar" : () => void
}
@@ -4,5 +4,5 @@ export class C {
}
export = B;
>B : No type information available!
>B : any
@@ -1,6 +1,6 @@
=== tests/cases/compiler/ExportAssignment8.ts ===
export = B;
>B : No type information available!
>B : any
export class C {
>C : C
+7 -7
View File
@@ -28,31 +28,31 @@ import beez = foo.bar;
import m = no;
>m : any
>no : No type information available!
>no : any
import m2 = no.mod;
>m2 : any
>no : No type information available!
>mod : No type information available!
>no : any
>mod : any
import n = 5;
>n : any
> : No type information available!
> : any
>5 : 5
import o = "s";
>o : any
> : No type information available!
> : any
>"s" : "s"
import q = null;
>q : any
> : No type information available!
> : any
>null : null
import r = undefined;
>r : any
>undefined : No type information available!
>undefined : any
var p = new provide.Provide();
@@ -19,6 +19,7 @@ var x: foo.A = foo.bar("hello"); // foo.A should be ok but foo.bar should be err
=== tests/cases/compiler/aliasOnMergedModuleInterface_0.ts ===
declare module "foo"
>"foo" : Symbol("foo", Decl(aliasOnMergedModuleInterface_0.ts, 0, 0))
{
module B {
>B : Symbol(B, Decl(aliasOnMergedModuleInterface_0.ts, 1, 1), Decl(aliasOnMergedModuleInterface_0.ts, 5, 5))
@@ -26,6 +26,7 @@ var x: foo.A = foo.bar("hello"); // foo.A should be ok but foo.bar should be err
=== tests/cases/compiler/aliasOnMergedModuleInterface_0.ts ===
declare module "foo"
>"foo" : typeof "foo"
{
module B {
>B : any
@@ -160,6 +160,8 @@ var q = M1.fn();
// Ambient external module in the global module
// Ambient external module with a string literal name that is a top level external module name
declare module 'external1' {
>'external1' : Symbol('external1', Decl(ambientDeclarations.ts, 67, 16))
var q;
>q : Symbol(q, Decl(ambientDeclarations.ts, 72, 7))
}
@@ -163,6 +163,8 @@ var q = M1.fn();
// Ambient external module in the global module
// Ambient external module with a string literal name that is a top level external module name
declare module 'external1' {
>'external1' : typeof 'external1'
var q;
>q : any
}
@@ -20,6 +20,8 @@ var n: number;
=== tests/cases/conformance/ambient/decls.ts ===
// Ambient external module with export assignment
declare module 'equ' {
>'equ' : Symbol('equ', Decl(decls.ts, 0, 0))
var x;
>x : Symbol(x, Decl(decls.ts, 2, 7))
@@ -28,6 +30,8 @@ declare module 'equ' {
}
declare module 'equ2' {
>'equ2' : Symbol('equ2', Decl(decls.ts, 4, 1))
var x: number;
>x : Symbol(x, Decl(decls.ts, 7, 7))
}
@@ -20,6 +20,8 @@ var n: number;
=== tests/cases/conformance/ambient/decls.ts ===
// Ambient external module with export assignment
declare module 'equ' {
>'equ' : typeof 'equ'
var x;
>x : any
@@ -28,6 +30,8 @@ declare module 'equ' {
}
declare module 'equ2' {
>'equ2' : typeof 'equ2'
var x: number;
>x : number
}
@@ -25,23 +25,31 @@ foo(fileText);
=== tests/cases/conformance/ambient/declarations.d.ts ===
declare module "foo*baz" {
>"foo*baz" : Symbol("foo*baz", Decl(declarations.d.ts, 0, 0), Decl(declarations.d.ts, 2, 1))
export function foo(s: string): void;
>foo : Symbol(foo, Decl(declarations.d.ts, 0, 26))
>s : Symbol(s, Decl(declarations.d.ts, 1, 24))
}
// Augmentations still work
declare module "foo*baz" {
>"foo*baz" : Symbol("foo*baz", Decl(declarations.d.ts, 0, 0), Decl(declarations.d.ts, 2, 1))
export const baz: string;
>baz : Symbol(baz, Decl(declarations.d.ts, 5, 16))
}
// Longest prefix wins
declare module "foos*" {
>"foos*" : Symbol("foos*", Decl(declarations.d.ts, 6, 1))
export const foos: string;
>foos : Symbol(foos, Decl(declarations.d.ts, 10, 16))
}
declare module "*!text" {
>"*!text" : Symbol("*!text", Decl(declarations.d.ts, 11, 1))
const x: string;
>x : Symbol(x, Decl(declarations.d.ts, 14, 9))
@@ -28,23 +28,31 @@ foo(fileText);
=== tests/cases/conformance/ambient/declarations.d.ts ===
declare module "foo*baz" {
>"foo*baz" : typeof "foo*baz"
export function foo(s: string): void;
>foo : (s: string) => void
>s : string
}
// Augmentations still work
declare module "foo*baz" {
>"foo*baz" : typeof "foo*baz"
export const baz: string;
>baz : string
}
// Longest prefix wins
declare module "foos*" {
>"foos*" : typeof "foos*"
export const foos: string;
>foos : string
}
declare module "*!text" {
>"*!text" : typeof "*!text"
const x: string;
>x : string
@@ -1,4 +1,4 @@
=== tests/cases/conformance/ambient/ambientDeclarationsPatterns_tooManyAsterisks.ts ===
declare module "too*many*asterisks" { }
No type information for this code.
No type information for this code.
>"too*many*asterisks" : Symbol("too*many*asterisks", Decl(ambientDeclarationsPatterns_tooManyAsterisks.ts, 0, 0))
@@ -1,4 +1,4 @@
=== tests/cases/conformance/ambient/ambientDeclarationsPatterns_tooManyAsterisks.ts ===
declare module "too*many*asterisks" { }
No type information for this code.
No type information for this code.
>"too*many*asterisks" : typeof "too*many*asterisks"
@@ -90,13 +90,17 @@ module M2 {
>M2 : Symbol(M2, Decl(ambientErrors.ts, 42, 1))
declare module 'nope' { }
>'nope' : Symbol('nope', Decl(ambientErrors.ts, 45, 11))
}
// Ambient external module with a string literal name that isn't a top level external module name
declare module '../foo' { }
>'../foo' : Symbol('../foo', Decl(ambientErrors.ts, 47, 1))
// Ambient external module with export assignment and other exported members
declare module 'bar' {
>'bar' : Symbol('bar', Decl(ambientErrors.ts, 50, 27))
var n;
>n : Symbol(n, Decl(ambientErrors.ts, 54, 7))
@@ -97,13 +97,17 @@ module M2 {
>M2 : any
declare module 'nope' { }
>'nope' : typeof 'nope'
}
// Ambient external module with a string literal name that isn't a top level external module name
declare module '../foo' { }
>'../foo' : typeof '../foo'
// Ambient external module with export assignment and other exported members
declare module 'bar' {
>'bar' : typeof 'bar'
var n;
>n : any
@@ -18,6 +18,8 @@ export as namespace Foo2;
=== tests/cases/compiler/indirection.d.ts ===
/// <reference path="./foo.d.ts" />
declare module "indirect" {
>"indirect" : Symbol("indirect", Decl(indirection.d.ts, 0, 0))
export default typeof Foo.default;
>Foo.default : Symbol(Foo.default, Decl(foo.d.ts, 0, 0))
>Foo : Symbol(Foo, Decl(foo.d.ts, 0, 21))
@@ -27,6 +29,8 @@ declare module "indirect" {
=== tests/cases/compiler/indirection2.d.ts ===
/// <reference path="./foo2.d.ts" />
declare module "indirect2" {
>"indirect2" : Symbol("indirect2", Decl(indirection2.d.ts, 0, 0))
export = typeof Foo2;
>Foo2 : Symbol(Foo2, Decl(foo2.d.ts, 0, 15))
}
@@ -26,6 +26,8 @@ export as namespace Foo2;
=== tests/cases/compiler/indirection.d.ts ===
/// <reference path="./foo.d.ts" />
declare module "indirect" {
>"indirect" : typeof "indirect"
export default typeof Foo.default;
>typeof Foo.default : "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function"
>Foo.default : number
@@ -36,6 +38,8 @@ declare module "indirect" {
=== tests/cases/compiler/indirection2.d.ts ===
/// <reference path="./foo2.d.ts" />
declare module "indirect2" {
>"indirect2" : typeof "indirect2"
export = typeof Foo2;
>typeof Foo2 : "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function"
>Foo2 : number
@@ -6,6 +6,8 @@ export = D;
>D : Symbol(D, Decl(ambientExternalModuleInAnotherExternalModule.ts, 0, 0))
declare module "ext" {
>"ext" : Symbol("ext", Decl(ambientExternalModuleInAnotherExternalModule.ts, 1, 11))
export class C { }
>C : Symbol(C, Decl(ambientExternalModuleInAnotherExternalModule.ts, 3, 22))
}
@@ -6,6 +6,8 @@ export = D;
>D : D
declare module "ext" {
>"ext" : typeof "ext"
export class C { }
>C : C
}
@@ -3,4 +3,5 @@ module M {
>M : Symbol(M, Decl(ambientExternalModuleInsideNonAmbient.ts, 0, 0))
export declare module "M" { }
>"M" : Symbol("M", Decl(ambientExternalModuleInsideNonAmbient.ts, 0, 10))
}
@@ -3,4 +3,5 @@ module M {
>M : any
export declare module "M" { }
>"M" : typeof "M"
}
@@ -1,3 +1,4 @@
=== tests/cases/conformance/ambient/ambientExternalModuleInsideNonAmbientExternalModule.ts ===
export declare module "M" { }
No type information for this code.
>"M" : Symbol("M", Decl(ambientExternalModuleInsideNonAmbientExternalModule.ts, 0, 0))

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