Merge branch 'master' into release-2.6

This commit is contained in:
Mohamed Hegazy
2017-10-12 17:19:16 -07:00
571 changed files with 18591 additions and 4197 deletions
-1
View File
@@ -58,5 +58,4 @@ internal/
!tests/baselines/reference/project/nodeModules*/**/*
.idea
yarn.lock
package-lock.json
.parallelperf.*
+12 -1
View File
@@ -276,4 +276,15 @@ Francois Wouts <f@codonut.com>
Jan Melcher <jan.melcher@aeb.com> Jan Melcher <mail@jan-melcher.de>
Matt Mitchell <mmitche@microsoft.com>
Maxwell Paul Brickner <mbrickn@users.noreply.github.com>
Tycho Grouwstra <tychogrouwstra@gmail.com>
Tycho Grouwstra <tychogrouwstra@gmail.com>
Adrian Leonhard <adrianleonhard@gmail.com>
Alex Chugaev <achugaev93@gmail.com>
Henry Mercer <henry.mercer@me.com>
Ivan Enderlin <ivan.enderlin@hoa-project.net>
Joe Calzaretta <jcalz@mit.edu>
Magnus Kulke <mkulke@gmail.com>
Stas Vilchik <stas.vilchik@sonarsource.com>
Taras Mankovski <tarasm@gmail.com>
Thomas den Hollander <ThomasdenH@users.noreply.github.com>
Vakhurin Sergey <igelbox@gmail.com>
Zeeshan Ahmed <ziishaned@gmail.com>
+11
View File
@@ -3,8 +3,10 @@ TypeScript is authored by:
* Abubaker Bashir
* Adam Freidin
* Adi Dahiya
* Adrian Leonhard
* Ahmad Farid
* Akshar Patel
* Alex Chugaev
* Alex Eagle
* Alexander Kuvaev
* Alexander Rusakov
@@ -105,6 +107,7 @@ TypeScript is authored by:
* Halasi Tamás
* Harald Niesche
* Hendrik Liebau
* Henry Mercer
* Herrington Darkholme
* Homa Wong
* Iain Monro
@@ -112,6 +115,7 @@ TypeScript is authored by:
* Ika
* Ingvar Stepanyan
* Isiah Meadows
* Ivan Enderlin
* Ivo Gabe de Wolff
* Iwata Hidetaka
* Jakub Młokosiewicz
@@ -127,6 +131,7 @@ TypeScript is authored by:
* Jeffrey Morlan
* Jesse Schalken
* Jiri Tobisek
* Joe Calzaretta
* Joe Chung
* Joel Day
* Joey Wilson
@@ -161,6 +166,7 @@ TypeScript is authored by:
* Lucien Greathouse
* Lukas Elmer
* Magnus Hiie
* Magnus Kulke
* Manish Giri
* Marin Marinov
* Marius Schulz
@@ -232,13 +238,16 @@ TypeScript is authored by:
* Soo Jae Hwang
* Stan Thomas
* Stanislav Sysoev
* Stas Vilchik
* Steve Lucco
* Sudheesh Singanamalla
* Sébastien Arod
* @T18970237136
* @t_
* Taras Mankovski
* Tarik Ozket
* Tetsuharu Ohzeki
* Thomas den Hollander
* Thomas Loubiou
* Tien Hoanhtien
* Tim Lancina
@@ -253,6 +262,7 @@ TypeScript is authored by:
* TruongSinh Tran-Nguyen
* Tycho Grouwstra
* Vadi Taslim
* Vakhurin Sergey
* Vidar Tonaas Fauske
* Viktor Zozulyak
* Vilic Vane
@@ -263,5 +273,6 @@ TypeScript is authored by:
* York Yao
* @yortus
* Yuichi Nukiyama
* Zeeshan Ahmed
* Zev Spitz
* Zhengbo Li
+2 -1
View File
@@ -2,7 +2,8 @@
<!-- QUESTIONS: This is not a general support forum! Ask Qs at http://stackoverflow.com/questions/tagged/typescript -->
<!-- SUGGESTIONS: See https://github.com/Microsoft/TypeScript-wiki/blob/master/Writing-Good-Design-Proposals.md -->
**TypeScript Version:** 2.4.0 / nightly (2.5.0-dev.201xxxxx)
<!-- Please try to reproduce the issue with `typescript@next`. It may have already been fixed. -->
**TypeScript Version:** 2.6.0-dev.201xxxxx
**Code**
+5302
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -3,7 +3,7 @@ Thank you for submitting a pull request!
Here's a checklist you might find useful.
[ ] There is an associated issue that is labelled
'Bug' or 'Accepting PRs' or is in the Community milestone
'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
[ ] You've signed the CLA
+39 -23
View File
@@ -143,6 +143,15 @@ namespace ts {
let subtreeTransformFlags: TransformFlags = TransformFlags.None;
let skipTransformFlagAggregation: boolean;
/**
* Inside the binder, we may create a diagnostic for an as-yet unbound node (with potentially no parent pointers, implying no accessible source file)
* If so, the node _must_ be in the current file (as that's the only way anything could have traversed to it to yield it as the error node)
* This version of `createDiagnosticForNode` uses the binder's context to account for this, and always yields correct diagnostics even in these situations.
*/
function createDiagnosticForNode(node: Node, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): Diagnostic {
return createDiagnosticForNodeInSourceFile(getSourceFileOfNode(node) || file, node, message, arg0, arg1, arg2);
}
function bindSourceFile(f: SourceFile, opts: CompilerOptions) {
file = f;
options = opts;
@@ -230,6 +239,10 @@ namespace ts {
// Should not be called on a declaration with a computed property name,
// unless it is a well known Symbol.
function getDeclarationName(node: Declaration): __String {
if (node.kind === SyntaxKind.ExportAssignment) {
return (<ExportAssignment>node).isExportEquals ? InternalSymbolName.ExportEquals : InternalSymbolName.Default;
}
const name = getNameOfDeclaration(node);
if (name) {
if (isAmbientModule(node)) {
@@ -261,8 +274,6 @@ namespace ts {
return InternalSymbolName.Index;
case SyntaxKind.ExportDeclaration:
return InternalSymbolName.ExportStar;
case SyntaxKind.ExportAssignment:
return (<ExportAssignment>node).isExportEquals ? InternalSymbolName.ExportEquals : InternalSymbolName.Default;
case SyntaxKind.BinaryExpression:
if (getSpecialPropertyAssignmentKind(node as BinaryExpression) === SpecialPropertyAssignmentKind.ModuleExports) {
// module.exports = ...
@@ -2144,7 +2155,7 @@ namespace ts {
// falls through
case SyntaxKind.JSDocPropertyTag:
const propTag = node as JSDocPropertyLikeTag;
const flags = propTag.isBracketed || propTag.typeExpression.type.kind === SyntaxKind.JSDocOptionalType ?
const flags = propTag.isBracketed || propTag.typeExpression && propTag.typeExpression.type.kind === SyntaxKind.JSDocOptionalType ?
SymbolFlags.Property | SymbolFlags.Optional :
SymbolFlags.Property;
return declareSymbolAndAddToSymbolTable(propTag, flags, SymbolFlags.PropertyExcludes);
@@ -2269,16 +2280,13 @@ namespace ts {
function isExportsOrModuleExportsOrAlias(node: Node): boolean {
return isExportsIdentifier(node) ||
isModuleExportsPropertyAccessExpression(node) ||
isNameOfExportsOrModuleExportsAliasDeclaration(node);
isIdentifier(node) && isNameOfExportsOrModuleExportsAliasDeclaration(node);
}
function isNameOfExportsOrModuleExportsAliasDeclaration(node: Node) {
if (isIdentifier(node)) {
const symbol = lookupSymbolForName(node.escapedText);
return symbol && symbol.valueDeclaration && isVariableDeclaration(symbol.valueDeclaration) &&
symbol.valueDeclaration.initializer && isExportsOrModuleExportsOrAliasOrAssignment(symbol.valueDeclaration.initializer);
}
return false;
function isNameOfExportsOrModuleExportsAliasDeclaration(node: Identifier): boolean {
const symbol = lookupSymbolForName(node.escapedText);
return symbol && symbol.valueDeclaration && isVariableDeclaration(symbol.valueDeclaration) &&
symbol.valueDeclaration.initializer && isExportsOrModuleExportsOrAliasOrAssignment(symbol.valueDeclaration.initializer);
}
function isExportsOrModuleExportsOrAliasOrAssignment(node: Node): boolean {
@@ -2352,20 +2360,22 @@ namespace ts {
// Look up the function in the local scope, since prototype assignments should
// follow the function declaration
const leftSideOfAssignment = node.left as PropertyAccessExpression;
const target = leftSideOfAssignment.expression as Identifier;
const target = leftSideOfAssignment.expression;
// Fix up parent pointers since we're going to use these nodes before we bind into them
leftSideOfAssignment.parent = node;
target.parent = leftSideOfAssignment;
if (isIdentifier(target)) {
// Fix up parent pointers since we're going to use these nodes before we bind into them
leftSideOfAssignment.parent = node;
target.parent = leftSideOfAssignment;
if (isNameOfExportsOrModuleExportsAliasDeclaration(target)) {
// This can be an alias for the 'exports' or 'module.exports' names, e.g.
// var util = module.exports;
// util.property = function ...
bindExportsPropertyAssignment(node);
}
else {
bindPropertyAssignment(target.escapedText, leftSideOfAssignment, /*isPrototypeProperty*/ false);
if (isNameOfExportsOrModuleExportsAliasDeclaration(target)) {
// This can be an alias for the 'exports' or 'module.exports' names, e.g.
// var util = module.exports;
// util.property = function ...
bindExportsPropertyAssignment(node);
}
else {
bindPropertyAssignment(target.escapedText, leftSideOfAssignment, /*isPrototypeProperty*/ false);
}
}
}
@@ -2697,6 +2707,12 @@ namespace ts {
if (expression.kind === SyntaxKind.ImportKeyword) {
transformFlags |= TransformFlags.ContainsDynamicImport;
// A dynamic 'import()' call that contains a lexical 'this' will
// require a captured 'this' when emitting down-level.
if (subtreeFlags & TransformFlags.ContainsLexicalThis) {
transformFlags |= TransformFlags.ContainsCapturedLexicalThis;
}
}
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
+8 -5
View File
@@ -73,12 +73,16 @@ namespace ts {
*/
onRemoveSourceFile(path: Path): void;
/**
* Called when sourceFile is changed
* For all source files, either "onUpdateSourceFile" or "onUpdateSourceFileWithSameVersion" will be called.
* If the builder is sure that the source file needs an update, "onUpdateSourceFile" will be called;
* otherwise "onUpdateSourceFileWithSameVersion" will be called.
*/
onUpdateSourceFile(program: Program, sourceFile: SourceFile): void;
/**
* Called when source file has not changed but has some of the resolutions invalidated
* If returned true, builder will mark the file as changed (noting that something associated with file has changed)
* For all source files, either "onUpdateSourceFile" or "onUpdateSourceFileWithSameVersion" will be called.
* If the builder is sure that the source file needs an update, "onUpdateSourceFile" will be called;
* otherwise "onUpdateSourceFileWithSameVersion" will be called.
* This function should return whether the source file should be marked as changed (meaning that something associated with file has changed, e.g. module resolution)
*/
onUpdateSourceFileWithSameVersion(program: Program, sourceFile: SourceFile): boolean;
/**
@@ -161,8 +165,7 @@ namespace ts {
existingInfo.version = sourceFile.version;
emitHandler.onUpdateSourceFile(program, sourceFile);
}
else if (program.hasInvalidatedResolution(sourceFile.path) &&
emitHandler.onUpdateSourceFileWithSameVersion(program, sourceFile)) {
else if (emitHandler.onUpdateSourceFileWithSameVersion(program, sourceFile)) {
registerChangedFile(sourceFile.path, sourceFile.fileName);
}
}
+255 -116
View File
@@ -226,6 +226,23 @@ namespace ts {
return tryFindAmbientModule(moduleName, /*withAugmentations*/ false);
},
getApparentType,
getUnionType,
createAnonymousType,
createSignature,
createSymbol,
createIndexInfo,
getAnyType: () => anyType,
getStringType: () => stringType,
getNumberType: () => numberType,
createPromiseType,
createArrayType,
getBooleanType: () => booleanType,
getVoidType: () => voidType,
getUndefinedType: () => undefinedType,
getNullType: () => nullType,
getESSymbolType: () => esSymbolType,
getNeverType: () => neverType,
isSymbolAccessible,
isArrayLikeType,
getAllPossiblePropertiesOfTypes,
getSuggestionForNonexistentProperty: (node, type) => getSuggestionForNonexistentProperty(node, type),
@@ -281,6 +298,7 @@ namespace ts {
const noConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
const circularConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
const resolvingDefaultType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
const markerSuperType = <TypeParameter>createType(TypeFlags.TypeParameter);
const markerSubType = <TypeParameter>createType(TypeFlags.TypeParameter);
@@ -503,6 +521,12 @@ namespace ts {
Inferential = 2, // Inferential typing
}
const enum CallbackCheck {
None,
Bivariant,
Strict,
}
const builtinGlobals = createSymbolTable();
builtinGlobals.set(undefinedSymbol.escapedName, undefinedSymbol);
@@ -902,6 +926,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 propertyWithInvalidInitializer: Node;
const errorLocation = location;
let grandparent: Node;
@@ -1120,6 +1145,9 @@ namespace ts {
}
break;
}
if (location.kind !== SyntaxKind.Block) {
lastNonBlockLocation = location;
}
lastLocation = location;
location = location.parent;
}
@@ -1127,7 +1155,7 @@ namespace ts {
// We just climbed up parents looking for the name, meaning that we started in a descendant node of `lastLocation`.
// If `result === lastLocation.symbol`, that means that we are somewhere inside `lastLocation` 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 !== lastLocation.symbol) {
if (isUse && result && nameNotFoundMessage && noUnusedIdentifiers && result !== lastNonBlockLocation.symbol) {
result.isReferenced = true;
}
@@ -3664,6 +3692,7 @@ namespace ts {
function buildParameterDisplay(p: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) {
const parameterNode = <ParameterDeclaration>p.valueDeclaration;
if (parameterNode ? isRestParameter(parameterNode) : isTransientSymbol(p) && p.isRestParameter) {
writePunctuation(writer, SyntaxKind.DotDotDotToken);
}
@@ -4151,13 +4180,13 @@ namespace ts {
if (parentType === unknownType) {
return unknownType;
}
// If no type was specified or inferred for parent, or if the specified or inferred type is any,
// infer from the initializer of the binding element if one is present. Otherwise, go with the
// undefined or any type of the parent.
if (!parentType || isTypeAny(parentType)) {
if (declaration.initializer) {
return checkDeclarationInitializer(declaration);
}
// If no type was specified or inferred for parent,
// infer from the initializer of the binding element if one is present.
// Otherwise, go with the undefined type of the parent.
if (!parentType) {
return declaration.initializer ? checkDeclarationInitializer(declaration) : parentType;
}
if (isTypeAny(parentType)) {
return parentType;
}
@@ -4183,9 +4212,6 @@ namespace ts {
// computed properties with non-literal names are treated as 'any'
return anyType;
}
if (declaration.initializer) {
getContextualType(declaration.initializer);
}
// Use type of the specified property, or otherwise, for a numeric name, the type of the numeric index signature,
// or otherwise the type of the string index signature.
@@ -6048,27 +6074,51 @@ namespace ts {
return type.resolvedApparentType || (type.resolvedApparentType = getTypeWithThisArgument(type, type));
}
function getResolvedTypeParameterDefault(typeParameter: TypeParameter): Type | undefined {
if (!typeParameter.default) {
if (typeParameter.target) {
const targetDefault = getResolvedTypeParameterDefault(typeParameter.target);
typeParameter.default = targetDefault ? instantiateType(targetDefault, typeParameter.mapper) : noConstraintType;
}
else {
// To block recursion, set the initial value to the resolvingDefaultType.
typeParameter.default = resolvingDefaultType;
const defaultDeclaration = typeParameter.symbol && forEach(typeParameter.symbol.declarations, decl => isTypeParameterDeclaration(decl) && decl.default);
const defaultType = defaultDeclaration ? getTypeFromTypeNode(defaultDeclaration) : noConstraintType;
if (typeParameter.default === resolvingDefaultType) {
// If we have not been called recursively, set the correct default type.
typeParameter.default = defaultType;
}
}
}
else if (typeParameter.default === resolvingDefaultType) {
// If we are called recursively for this type parameter, mark the default as circular.
typeParameter.default = circularConstraintType;
}
return typeParameter.default;
}
/**
* Gets the default type for a type parameter.
*
* If the type parameter is the result of an instantiation, this gets the instantiated
* default type of its target. If the type parameter has no default type, `undefined`
* is returned.
*
* This function *does not* perform a circularity check.
* default type of its target. If the type parameter has no default type or the default is
* circular, `undefined` is returned.
*/
function getDefaultFromTypeParameter(typeParameter: TypeParameter): Type | undefined {
if (!typeParameter.default) {
if (typeParameter.target) {
const targetDefault = getDefaultFromTypeParameter(typeParameter.target);
typeParameter.default = targetDefault ? instantiateType(targetDefault, typeParameter.mapper) : noConstraintType;
}
else {
const defaultDeclaration = typeParameter.symbol && forEach(typeParameter.symbol.declarations, decl => isTypeParameterDeclaration(decl) && decl.default);
typeParameter.default = defaultDeclaration ? getTypeFromTypeNode(defaultDeclaration) : noConstraintType;
}
}
return typeParameter.default === noConstraintType ? undefined : typeParameter.default;
const defaultType = getResolvedTypeParameterDefault(typeParameter);
return defaultType !== noConstraintType && defaultType !== circularConstraintType ? defaultType : undefined;
}
function hasNonCircularTypeParameterDefault(typeParameter: TypeParameter) {
return getResolvedTypeParameterDefault(typeParameter) !== circularConstraintType;
}
/**
* Indicates whether the declaration of a typeParameter has a default type.
*/
function hasTypeParameterDefault(typeParameter: TypeParameter): boolean {
return !!(typeParameter.symbol && forEach(typeParameter.symbol.declarations, decl => isTypeParameterDeclaration(decl) && decl.default));
}
/**
@@ -6242,7 +6292,7 @@ namespace ts {
}
function getImplicitIndexTypeOfType(type: Type, kind: IndexKind): Type {
if (isObjectLiteralType(type)) {
if (isObjectTypeWithInferableIndex(type)) {
const propTypes: Type[] = [];
for (const prop of getPropertiesOfType(type)) {
if (kind === IndexKind.String || isNumericLiteralName(prop.escapedName)) {
@@ -6354,7 +6404,7 @@ namespace ts {
let minTypeArgumentCount = 0;
if (typeParameters) {
for (let i = 0; i < typeParameters.length; i++) {
if (!getDefaultFromTypeParameter(typeParameters[i])) {
if (!hasTypeParameterDefault(typeParameters[i])) {
minTypeArgumentCount = i + 1;
}
}
@@ -7846,7 +7896,7 @@ namespace ts {
* this function should be called in a left folding style, with left = previous result of getSpreadType
* and right = the new element to be spread.
*/
function getSpreadType(left: Type, right: Type): Type {
function getSpreadType(left: Type, right: Type, symbol: Symbol, propagatedFlags: TypeFlags): Type {
if (left.flags & TypeFlags.Any || right.flags & TypeFlags.Any) {
return anyType;
}
@@ -7857,10 +7907,10 @@ namespace ts {
return left;
}
if (left.flags & TypeFlags.Union) {
return mapType(left, t => getSpreadType(t, right));
return mapType(left, t => getSpreadType(t, right, symbol, propagatedFlags));
}
if (right.flags & TypeFlags.Union) {
return mapType(right, t => getSpreadType(left, t));
return mapType(right, t => getSpreadType(left, t, symbol, propagatedFlags));
}
if (right.flags & TypeFlags.NonPrimitive) {
return nonPrimitiveType;
@@ -7918,7 +7968,13 @@ namespace ts {
members.set(leftProp.escapedName, getNonReadonlySymbol(leftProp));
}
}
return createAnonymousType(undefined, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);
const spread = createAnonymousType(undefined, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);
spread.flags |= propagatedFlags;
spread.flags |= TypeFlags.FreshLiteral;
(spread as ObjectType).objectFlags |= ObjectFlags.ObjectLiteral;
spread.symbol = symbol;
return spread;
}
function getNonReadonlySymbol(prop: Symbol) {
@@ -8251,11 +8307,11 @@ namespace ts {
// The first time an anonymous type is instantiated we compute and store a list of the type
// parameters that are in scope (and therefore potentially referenced). For type literals that
// aren't the right hand side of a generic type alias declaration we optimize by reducing the
// set of type parameters to those that are actually referenced somewhere in the literal.
// set of type parameters to those that are possibly referenced in the literal.
const declaration = symbol.declarations[0];
const outerTypeParameters = getOuterTypeParameters(declaration, /*includeThisTypes*/ true) || emptyArray;
typeParameters = symbol.flags & SymbolFlags.TypeLiteral && !target.aliasTypeArguments ?
filter(outerTypeParameters, tp => isTypeParameterReferencedWithin(tp, declaration)) :
filter(outerTypeParameters, tp => isTypeParameterPossiblyReferenced(tp, declaration)) :
outerTypeParameters;
links.typeParameters = typeParameters;
if (typeParameters.length) {
@@ -8281,13 +8337,27 @@ namespace ts {
return type;
}
function isTypeParameterReferencedWithin(tp: TypeParameter, node: Node) {
return tp.isThisType ? forEachChild(node, checkThis) : forEachChild(node, checkIdentifier);
function checkThis(node: Node): boolean {
return node.kind === SyntaxKind.ThisType || forEachChild(node, checkThis);
function isTypeParameterPossiblyReferenced(tp: TypeParameter, node: Node) {
// If the type parameter doesn't have exactly one declaration, if there are invening statement blocks
// between the node and the type parameter declaration, if the node contains actual references to the
// type parameter, or if the node contains type queries, we consider the type parameter possibly referenced.
if (tp.symbol && tp.symbol.declarations && tp.symbol.declarations.length === 1) {
const container = tp.symbol.declarations[0].parent;
if (findAncestor(node, n => n.kind === SyntaxKind.Block ? "quit" : n === container)) {
return forEachChild(node, containsReference);
}
}
function checkIdentifier(node: Node): boolean {
return node.kind === SyntaxKind.Identifier && isPartOfTypeNode(node) && getTypeFromTypeNode(<TypeNode>node) === tp || forEachChild(node, checkIdentifier);
return true;
function containsReference(node: Node): boolean {
switch (node.kind) {
case SyntaxKind.ThisType:
return tp.isThisType;
case SyntaxKind.Identifier:
return !tp.isThisType && isPartOfTypeNode(node) && getTypeFromTypeNode(<TypeNode>node) === tp;
case SyntaxKind.TypeQuery:
return true;
}
return forEachChild(node, containsReference);
}
}
@@ -8513,7 +8583,7 @@ namespace ts {
function isSignatureAssignableTo(source: Signature,
target: Signature,
ignoreReturnTypes: boolean): boolean {
return compareSignaturesRelated(source, target, /*checkAsCallback*/ false, ignoreReturnTypes, /*reportErrors*/ false,
return compareSignaturesRelated(source, target, CallbackCheck.None, ignoreReturnTypes, /*reportErrors*/ false,
/*errorReporter*/ undefined, compareTypesAssignable) !== Ternary.False;
}
@@ -8524,7 +8594,7 @@ namespace ts {
*/
function compareSignaturesRelated(source: Signature,
target: Signature,
checkAsCallback: boolean,
callbackCheck: CallbackCheck,
ignoreReturnTypes: boolean,
reportErrors: boolean,
errorReporter: ErrorReporter,
@@ -8543,7 +8613,7 @@ namespace ts {
}
const kind = target.declaration ? target.declaration.kind : SyntaxKind.Unknown;
const strictVariance = strictFunctionTypes && kind !== SyntaxKind.MethodDeclaration &&
const strictVariance = !callbackCheck && strictFunctionTypes && kind !== SyntaxKind.MethodDeclaration &&
kind !== SyntaxKind.MethodSignature && kind !== SyntaxKind.Constructor;
let result = Ternary.True;
@@ -8572,21 +8642,21 @@ namespace ts {
for (let i = 0; i < checkCount; i++) {
const sourceType = i < sourceMax ? getTypeOfParameter(sourceParams[i]) : getRestTypeOfSignature(source);
const targetType = i < targetMax ? getTypeOfParameter(targetParams[i]) : getRestTypeOfSignature(target);
const sourceSig = getSingleCallSignature(getNonNullableType(sourceType));
const targetSig = getSingleCallSignature(getNonNullableType(targetType));
// In order to ensure that any generic type Foo<T> is at least co-variant with respect to T no matter
// how Foo uses T, we need to relate parameters bi-variantly (given that parameters are input positions,
// they naturally relate only contra-variantly). However, if the source and target parameters both have
// function types with a single call signature, we known we are relating two callback parameters. In
// function types with a single call signature, we know we are relating two callback parameters. In
// that case it is sufficient to only relate the parameters of the signatures co-variantly because,
// similar to return values, callback parameters are output positions. This means that a Promise<T>,
// where T is used only in callback parameter positions, will be co-variant (as opposed to bi-variant)
// with respect to T.
const sourceSig = callbackCheck ? undefined : getSingleCallSignature(getNonNullableType(sourceType));
const targetSig = callbackCheck ? undefined : getSingleCallSignature(getNonNullableType(targetType));
const callbacks = sourceSig && targetSig && !sourceSig.typePredicate && !targetSig.typePredicate &&
(getFalsyFlags(sourceType) & TypeFlags.Nullable) === (getFalsyFlags(targetType) & TypeFlags.Nullable);
const related = callbacks ?
compareSignaturesRelated(targetSig, sourceSig, /*checkAsCallback*/ true, /*ignoreReturnTypes*/ false, reportErrors, errorReporter, compareTypes) :
!checkAsCallback && !strictVariance && compareTypes(sourceType, targetType, /*reportErrors*/ false) || compareTypes(targetType, sourceType, reportErrors);
compareSignaturesRelated(targetSig, sourceSig, strictVariance ? CallbackCheck.Strict : CallbackCheck.Bivariant, /*ignoreReturnTypes*/ false, reportErrors, errorReporter, compareTypes) :
!callbackCheck && !strictVariance && compareTypes(sourceType, targetType, /*reportErrors*/ false) || compareTypes(targetType, sourceType, reportErrors);
if (!related) {
if (reportErrors) {
errorReporter(Diagnostics.Types_of_parameters_0_and_1_are_incompatible,
@@ -8621,7 +8691,7 @@ namespace ts {
// When relating callback signatures, we still need to relate return types bi-variantly as otherwise
// the containing type wouldn't be co-variant. For example, interface Foo<T> { add(cb: () => T): void }
// wouldn't be co-variant for T without this rule.
result &= checkAsCallback && compareTypes(targetReturnType, sourceReturnType, /*reportErrors*/ false) ||
result &= callbackCheck === CallbackCheck.Bivariant && compareTypes(targetReturnType, sourceReturnType, /*reportErrors*/ false) ||
compareTypes(sourceReturnType, targetReturnType, reportErrors);
}
@@ -9065,6 +9135,13 @@ namespace ts {
(isTypeSubsetOf(globalObjectType, target) || (!isComparingJsxAttributes && isEmptyObjectType(target)))) {
return false;
}
if (target.flags & TypeFlags.Union) {
const discriminantType = findMatchingDiscriminantType(source, target as UnionType);
if (discriminantType) {
// check excess properties against discriminant type only, not the entire union
return hasExcessProperties(source, discriminantType, reportErrors);
}
}
for (const prop of getPropertiesOfObjectType(source)) {
if (!isKnownProperty(target, prop.escapedName, isComparingJsxAttributes)) {
if (reportErrors) {
@@ -9141,20 +9218,24 @@ namespace ts {
}
function findMatchingDiscriminantType(source: Type, target: UnionOrIntersectionType) {
let match: Type;
const sourceProperties = getPropertiesOfObjectType(source);
if (sourceProperties) {
for (const sourceProperty of sourceProperties) {
if (isDiscriminantProperty(target, sourceProperty.escapedName)) {
const sourceType = getTypeOfSymbol(sourceProperty);
for (const type of target.types) {
const targetType = getTypeOfPropertyOfType(type, sourceProperty.escapedName);
if (targetType && isRelatedTo(sourceType, targetType)) {
return type;
const sourceProperty = findSingleDiscriminantProperty(sourceProperties, target);
if (sourceProperty) {
const sourceType = getTypeOfSymbol(sourceProperty);
for (const type of target.types) {
const targetType = getTypeOfPropertyOfType(type, sourceProperty.escapedName);
if (targetType && isRelatedTo(sourceType, targetType)) {
if (match) {
return undefined;
}
match = type;
}
}
}
}
return match;
}
function typeRelatedToEachType(source: Type, target: IntersectionType, reportErrors: boolean): Ternary {
@@ -9718,7 +9799,7 @@ namespace ts {
*/
function signatureRelatedTo(source: Signature, target: Signature, erase: boolean, reportErrors: boolean): Ternary {
return compareSignaturesRelated(erase ? getErasedSignature(source) : source, erase ? getErasedSignature(target) : target,
/*checkAsCallback*/ false, /*ignoreReturnTypes*/ false, reportErrors, reportError, isRelatedTo);
CallbackCheck.None, /*ignoreReturnTypes*/ false, reportErrors, reportError, isRelatedTo);
}
function signaturesIdenticalTo(source: Type, target: Type, kind: SignatureKind): Ternary {
@@ -9782,7 +9863,7 @@ namespace ts {
// if T is related to U.
return kind === IndexKind.String && isRelatedTo(getTemplateTypeFromMappedType(<MappedType>source), targetInfo.type, reportErrors);
}
if (isObjectLiteralType(source)) {
if (isObjectTypeWithInferableIndex(source)) {
let related = Ternary.True;
if (kind === IndexKind.String) {
const sourceNumberInfo = getIndexInfoOfType(source, IndexKind.Number);
@@ -10295,11 +10376,11 @@ namespace ts {
}
/**
* Return true if type was inferred from an object literal or written as an object type literal
* Return true if type was inferred from an object literal, written as an object type literal, or is the shape of a module
* with no call or construct signatures.
*/
function isObjectLiteralType(type: Type) {
return type.symbol && (type.symbol.flags & (SymbolFlags.ObjectLiteral | SymbolFlags.TypeLiteral)) !== 0 &&
function isObjectTypeWithInferableIndex(type: Type) {
return type.symbol && (type.symbol.flags & (SymbolFlags.ObjectLiteral | SymbolFlags.TypeLiteral | SymbolFlags.ValueModule)) !== 0 &&
getSignaturesOfType(type, SignatureKind.Call).length === 0 &&
getSignaturesOfType(type, SignatureKind.Construct).length === 0;
}
@@ -11157,6 +11238,19 @@ namespace ts {
return false;
}
function findSingleDiscriminantProperty(sourceProperties: Symbol[], target: Type): Symbol | undefined {
let result: Symbol;
for (const sourceProperty of sourceProperties) {
if (isDiscriminantProperty(target, sourceProperty.escapedName)) {
if (result) {
return undefined;
}
result = sourceProperty;
}
}
return result;
}
function isOrContainsMatchingReference(source: Node, target: Node) {
return isMatchingReference(source, target) || containsMatchingReference(source, target);
}
@@ -13858,7 +13952,7 @@ namespace ts {
checkExternalEmitHelpers(memberDecl, ExternalEmitHelpers.Assign);
}
if (propertiesArray.length > 0) {
spread = getSpreadType(spread, createObjectLiteralType());
spread = getSpreadType(spread, createObjectLiteralType(), node.symbol, propagatedFlags);
propertiesArray = [];
propertiesTable = createSymbolTable();
hasComputedStringProperty = false;
@@ -13870,7 +13964,7 @@ namespace ts {
error(memberDecl, Diagnostics.Spread_types_may_only_be_created_from_object_types);
return unknownType;
}
spread = getSpreadType(spread, type);
spread = getSpreadType(spread, type, node.symbol, propagatedFlags);
offset = i + 1;
continue;
}
@@ -13915,14 +14009,7 @@ namespace ts {
if (spread !== emptyObjectType) {
if (propertiesArray.length > 0) {
spread = getSpreadType(spread, createObjectLiteralType());
}
if (spread.flags & TypeFlags.Object) {
// only set the symbol and flags if this is a (fresh) object type
spread.flags |= propagatedFlags;
spread.flags |= TypeFlags.FreshLiteral;
(spread as ObjectType).objectFlags |= ObjectFlags.ObjectLiteral;
spread.symbol = node.symbol;
spread = getSpreadType(spread, createObjectLiteralType(), node.symbol, propagatedFlags);
}
return spread;
}
@@ -13981,7 +14068,7 @@ namespace ts {
*/
function isUnhyphenatedJsxName(name: string | __String) {
// - is the only character supported in JSX attribute names that isn't valid in JavaScript identifiers
return (name as string).indexOf("-") < 0;
return !stringContains(name as string, "-");
}
/**
@@ -14043,7 +14130,7 @@ namespace ts {
else {
Debug.assert(attributeDecl.kind === SyntaxKind.JsxSpreadAttribute);
if (attributesArray.length > 0) {
spread = getSpreadType(spread, createJsxAttributesType(attributes.symbol, attributesTable));
spread = getSpreadType(spread, createJsxAttributesType(attributes.symbol, attributesTable), openingLikeElement.symbol, /*propagatedFlags*/ 0);
attributesArray = [];
attributesTable = createSymbolTable();
}
@@ -14052,7 +14139,7 @@ namespace ts {
hasSpreadAnyType = true;
}
if (isValidSpreadType(exprType)) {
spread = getSpreadType(spread, exprType);
spread = getSpreadType(spread, exprType, openingLikeElement.symbol, /*propagatedFlags*/ 0);
}
else {
typeToIntersect = typeToIntersect ? getIntersectionType([typeToIntersect, exprType]) : exprType;
@@ -14063,7 +14150,7 @@ namespace ts {
if (!hasSpreadAnyType) {
if (spread !== emptyObjectType) {
if (attributesArray.length > 0) {
spread = getSpreadType(spread, createJsxAttributesType(attributes.symbol, attributesTable));
spread = getSpreadType(spread, createJsxAttributesType(attributes.symbol, attributesTable), openingLikeElement.symbol, /*propagatedFlags*/ 0);
}
attributesArray = getPropertiesOfType(spread);
}
@@ -14799,11 +14886,7 @@ namespace ts {
// where this references the constructor function object of a derived class,
// a super property access is permitted and must specify a public static member function of the base class.
if (languageVersion < ScriptTarget.ES2015) {
const hasNonMethodDeclaration = forEachProperty(prop, p => {
const propKind = getDeclarationKindFromSymbol(p);
return propKind !== SyntaxKind.MethodDeclaration && propKind !== SyntaxKind.MethodSignature;
});
if (hasNonMethodDeclaration) {
if (symbolHasNonMethodDeclaration(prop)) {
error(errorNode, Diagnostics.Only_public_and_protected_methods_of_the_base_class_are_accessible_via_the_super_keyword);
return false;
}
@@ -14818,6 +14901,16 @@ namespace ts {
}
}
// Referencing Abstract Properties within Constructors is not allowed
if ((flags & ModifierFlags.Abstract) && symbolHasNonMethodDeclaration(prop)) {
const declaringClassDeclaration = <ClassLikeDeclaration>getClassLikeDeclarationOfSymbol(getParentOfSymbol(prop));
if (declaringClassDeclaration && isNodeWithinConstructor(node, declaringClassDeclaration)) {
error(errorNode, Diagnostics.Abstract_property_0_in_class_1_cannot_be_accessed_in_the_constructor, symbolToString(prop), typeToString(getDeclaringClass(prop)));
return false;
}
}
// Public properties are otherwise accessible.
if (!(flags & ModifierFlags.NonPublicAccessibilityModifier)) {
return true;
@@ -14869,6 +14962,13 @@ namespace ts {
return true;
}
function symbolHasNonMethodDeclaration(symbol: Symbol) {
return forEachProperty(symbol, prop => {
const propKind = getDeclarationKindFromSymbol(prop);
return propKind !== SyntaxKind.MethodDeclaration && propKind !== SyntaxKind.MethodSignature;
});
}
function checkNonNullExpression(node: Expression | QualifiedName) {
return checkNonNullType(checkExpression(node), node);
}
@@ -15575,34 +15675,32 @@ namespace ts {
return getInferredTypes(context);
}
function checkTypeArguments(signature: Signature, typeArgumentNodes: ReadonlyArray<TypeNode>, typeArgumentTypes: Type[], reportErrors: boolean, headMessage?: DiagnosticMessage): boolean {
function checkTypeArguments(signature: Signature, typeArgumentNodes: ReadonlyArray<TypeNode>, reportErrors: boolean, headMessage?: DiagnosticMessage): Type[] | false {
const isJavascript = isInJavaScriptFile(signature.declaration);
const typeParameters = signature.typeParameters;
let typeArgumentsAreAssignable = true;
const typeArgumentTypes = fillMissingTypeArguments(map(typeArgumentNodes, getTypeFromTypeNode), typeParameters, getMinTypeArgumentCount(typeParameters), isJavascript);
let mapper: TypeMapper;
for (let i = 0; i < typeArgumentNodes.length; i++) {
if (typeArgumentsAreAssignable /* so far */) {
const constraint = getConstraintOfTypeParameter(typeParameters[i]);
if (constraint) {
let errorInfo: DiagnosticMessageChain;
let typeArgumentHeadMessage = Diagnostics.Type_0_does_not_satisfy_the_constraint_1;
if (reportErrors && headMessage) {
errorInfo = chainDiagnosticMessages(errorInfo, typeArgumentHeadMessage);
typeArgumentHeadMessage = headMessage;
}
if (!mapper) {
mapper = createTypeMapper(typeParameters, typeArgumentTypes);
}
const typeArgument = typeArgumentTypes[i];
typeArgumentsAreAssignable = checkTypeAssignableTo(
typeArgument,
getTypeWithThisArgument(instantiateType(constraint, mapper), typeArgument),
reportErrors ? typeArgumentNodes[i] : undefined,
typeArgumentHeadMessage,
errorInfo);
}
Debug.assert(typeParameters[i] !== undefined, "Should not call checkTypeArguments with too many type arguments");
const constraint = getConstraintOfTypeParameter(typeParameters[i]);
if (!constraint) continue;
const errorInfo = reportErrors && headMessage && chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Type_0_does_not_satisfy_the_constraint_1);
const typeArgumentHeadMessage = headMessage || Diagnostics.Type_0_does_not_satisfy_the_constraint_1;
if (!mapper) {
mapper = createTypeMapper(typeParameters, typeArgumentTypes);
}
const typeArgument = typeArgumentTypes[i];
if (!checkTypeAssignableTo(
typeArgument,
getTypeWithThisArgument(instantiateType(constraint, mapper), typeArgument),
reportErrors ? typeArgumentNodes[i] : undefined,
typeArgumentHeadMessage,
errorInfo)) {
return false;
}
}
return typeArgumentsAreAssignable;
return typeArgumentTypes;
}
/**
@@ -16135,8 +16233,7 @@ namespace ts {
checkApplicableSignature(node, args, candidateForArgumentError, assignableRelation, /*excludeArgument*/ undefined, /*reportErrors*/ true);
}
else if (candidateForTypeArgumentError) {
const typeArguments = (<CallExpression>node).typeArguments;
checkTypeArguments(candidateForTypeArgumentError, typeArguments, map(typeArguments, getTypeFromTypeNode), /*reportErrors*/ true, fallbackError);
checkTypeArguments(candidateForTypeArgumentError, (node as CallExpression).typeArguments, /*reportErrors*/ true, fallbackError);
}
else if (typeArguments && every(signatures, sig => length(sig.typeParameters) !== typeArguments.length)) {
let min = Number.POSITIVE_INFINITY;
@@ -16235,10 +16332,12 @@ namespace ts {
candidate = originalCandidate;
if (candidate.typeParameters) {
let typeArgumentTypes: Type[];
const isJavascript = isInJavaScriptFile(candidate.declaration);
if (typeArguments) {
typeArgumentTypes = fillMissingTypeArguments(map(typeArguments, getTypeFromTypeNode), candidate.typeParameters, getMinTypeArgumentCount(candidate.typeParameters), isJavascript);
if (!checkTypeArguments(candidate, typeArguments, typeArgumentTypes, /*reportErrors*/ false)) {
const typeArgumentResult = checkTypeArguments(candidate, typeArguments, /*reportErrors*/ false);
if (typeArgumentResult) {
typeArgumentTypes = typeArgumentResult;
}
else {
candidateForTypeArgumentError = originalCandidate;
break;
}
@@ -16246,6 +16345,7 @@ namespace ts {
else {
typeArgumentTypes = inferTypeArguments(node, candidate, args, excludeArgument, inferenceContext);
}
const isJavascript = isInJavaScriptFile(candidate.declaration);
candidate = getSignatureInstantiation(candidate, typeArgumentTypes, isJavascript);
}
if (!checkApplicableSignature(node, args, candidate, relation, excludeArgument, /*reportErrors*/ false)) {
@@ -16551,6 +16651,12 @@ namespace ts {
return resolveUntypedCall(node);
}
if (isPotentiallyUncalledDecorator(node, callSignatures)) {
const nodeStr = getTextOfNode(node.expression, /*includeTrivia*/ false);
error(node, Diagnostics._0_accepts_too_few_arguments_to_be_used_as_a_decorator_here_Did_you_mean_to_call_it_first_and_write_0, nodeStr);
return resolveErrorCall(node);
}
const headMessage = getDiagnosticHeadMessageForDecoratorResolution(node);
if (!callSignatures.length) {
let errorInfo: DiagnosticMessageChain;
@@ -16563,6 +16669,18 @@ namespace ts {
return resolveCall(node, callSignatures, candidatesOutArray, headMessage);
}
/**
* Sometimes, we have a decorator that could accept zero arguments,
* but is receiving too many arguments as part of the decorator invocation.
* In those cases, a user may have meant to *call* the expression before using it as a decorator.
*/
function isPotentiallyUncalledDecorator(decorator: Decorator, signatures: Signature[]) {
return signatures.length && every(signatures, signature =>
signature.minArgumentCount === 0 &&
!signature.hasRestParameter &&
signature.parameters.length < getEffectiveArgumentCount(decorator, /*args*/ undefined, signature));
}
/**
* This function is similar to getResolvedSignature but is exclusively for trying to resolve JSX stateless-function component.
* The main reason we have to use this function instead of getResolvedSignature because, the caller of this function will already check the type of openingLikeElement's tagName
@@ -18416,6 +18534,9 @@ namespace ts {
if (!hasNonCircularBaseConstraint(typeParameter)) {
error(node.constraint, Diagnostics.Type_parameter_0_has_a_circular_constraint, typeToString(typeParameter));
}
if (!hasNonCircularTypeParameterDefault(typeParameter)) {
error(node.default, Diagnostics.Type_parameter_0_has_a_circular_default, typeToString(typeParameter));
}
const constraintType = getConstraintOfTypeParameter(typeParameter);
const defaultType = getDefaultFromTypeParameter(typeParameter);
if (constraintType && defaultType) {
@@ -18436,9 +18557,8 @@ namespace ts {
checkGrammarDecorators(node) || checkGrammarModifiers(node);
checkVariableLikeDeclaration(node);
let func = getContainingFunction(node);
const func = getContainingFunction(node);
if (hasModifier(node, ModifierFlags.ParameterPropertyModifier)) {
func = getContainingFunction(node);
if (!(func.kind === SyntaxKind.Constructor && nodeIsPresent(func.body))) {
error(node, Diagnostics.A_parameter_property_is_only_allowed_in_a_constructor_implementation);
}
@@ -19971,18 +20091,24 @@ namespace ts {
}
function checkJSDocAugmentsTag(node: JSDocAugmentsTag): void {
const cls = getJSDocHost(node);
if (!isClassDeclaration(cls) && !isClassExpression(cls)) {
error(cls, Diagnostics.JSDoc_augments_is_not_attached_to_a_class_declaration);
const classLike = getJSDocHost(node);
if (!isClassDeclaration(classLike) && !isClassExpression(classLike)) {
error(classLike, Diagnostics.JSDoc_0_is_not_attached_to_a_class, idText(node.tagName));
return;
}
const augmentsTags = getAllJSDocTagsOfKind(classLike, SyntaxKind.JSDocAugmentsTag);
Debug.assert(augmentsTags.length > 0);
if (augmentsTags.length > 1) {
error(augmentsTags[1], Diagnostics.Class_declarations_cannot_have_more_than_one_augments_or_extends_tag);
}
const name = getIdentifierFromEntityNameExpression(node.class.expression);
const extend = getClassExtendsHeritageClauseElement(cls);
const extend = getClassExtendsHeritageClauseElement(classLike);
if (extend) {
const className = getIdentifierFromEntityNameExpression(extend.expression);
if (className && name.escapedText !== className.escapedText) {
error(name, Diagnostics.JSDoc_augments_0_does_not_match_the_extends_1_clause, idText(name), idText(className));
error(name, Diagnostics.JSDoc_0_1_does_not_match_the_extends_2_clause, idText(node.tagName), idText(name), idText(className));
}
}
}
@@ -20254,7 +20380,7 @@ namespace ts {
function checkCollisionWithArgumentsInGeneratedCode(node: SignatureDeclaration) {
// no rest parameters \ declaration context \ overload - no codegen impact
if (!hasDeclaredRestParameter(node) || isInAmbientContext(node) || nodeIsMissing((<FunctionLikeDeclaration>node).body)) {
if (!hasRestParameter(node) || isInAmbientContext(node) || nodeIsMissing((<FunctionLikeDeclaration>node).body)) {
return;
}
@@ -23095,6 +23221,19 @@ namespace ts {
return result;
}
function isNodeWithinConstructor(node: Node, classDeclaration: ClassLikeDeclaration) {
return findAncestor(node, element => {
if (isConstructorDeclaration(element) && nodeIsPresent(element.body)) {
return true;
}
else if (element === classDeclaration || isFunctionLikeDeclaration(element)) {
return "quit";
}
return false;
});
}
function isNodeWithinClass(node: Node, classDeclaration: ClassLikeDeclaration) {
return !!forEachEnclosingClass(node, n => n === classDeclaration);
}
+10 -19
View File
@@ -213,11 +213,13 @@ namespace ts {
return undefined;
}
export function zipWith<T, U>(arrayA: ReadonlyArray<T>, arrayB: ReadonlyArray<U>, callback: (a: T, b: U, index: number) => void): void {
export function zipWith<T, U, V>(arrayA: ReadonlyArray<T>, arrayB: ReadonlyArray<U>, callback: (a: T, b: U, index: number) => V): V[] {
const result: V[] = [];
Debug.assert(arrayA.length === arrayB.length);
for (let i = 0; i < arrayA.length; i++) {
callback(arrayA[i], arrayB[i], i);
result.push(callback(arrayA[i], arrayB[i], i));
}
return result;
}
export function zipToMap<T>(keys: ReadonlyArray<string>, values: ReadonlyArray<T>): Map<T> {
@@ -356,21 +358,6 @@ namespace ts {
return array;
}
export function removeWhere<T>(array: T[], f: (x: T) => boolean): boolean {
let outIndex = 0;
for (const item of array) {
if (!f(item)) {
array[outIndex] = item;
outIndex++;
}
}
if (outIndex !== array.length) {
array.length = outIndex;
return true;
}
return false;
}
export function filterMutate<T>(array: T[], f: (x: T, i: number, array: T[]) => boolean): void {
let outIndex = 0;
for (let i = 0; i < array.length; i++) {
@@ -1663,7 +1650,7 @@ namespace ts {
}
export function isUrl(path: string) {
return path && !isRootedDiskPath(path) && path.indexOf("://") !== -1;
return path && !isRootedDiskPath(path) && stringContains(path, "://");
}
export function pathIsRelative(path: string): boolean {
@@ -1932,8 +1919,12 @@ namespace ts {
return expectedPos >= 0 && str.indexOf(suffix, expectedPos) === expectedPos;
}
export function stringContains(str: string, substring: string): boolean {
return str.indexOf(substring) !== -1;
}
export function hasExtension(fileName: string): boolean {
return getBaseFileName(fileName).indexOf(".") >= 0;
return stringContains(getBaseFileName(fileName), ".");
}
export function fileExtensionIs(path: string, extension: string): boolean {
+1 -1
View File
@@ -172,7 +172,7 @@ namespace ts {
function hasInternalAnnotation(range: CommentRange) {
const comment = currentText.substring(range.pos, range.end);
return comment.indexOf("@internal") >= 0;
return stringContains(comment, "@internal");
}
function stripInternal(node: Node) {
+32 -10
View File
@@ -907,6 +907,10 @@
"category": "Error",
"code": 1328
},
"'{0}' accepts too few arguments to be used as a decorator here. Did you mean to call it first and write '@{0}()'?": {
"category": "Error",
"code": 1329
},
"Duplicate identifier '{0}'.": {
"category": "Error",
@@ -2216,6 +2220,14 @@
"category": "Error",
"code": 2714
},
"Abstract property '{0}' in class '{1}' cannot be accessed in the constructor.": {
"category": "Error",
"code": 2715
},
"Type parameter '{0}' has a circular default.": {
"category": "Error",
"code": 2716
},
"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",
@@ -3511,11 +3523,11 @@
"category": "Error",
"code": 8021
},
"JSDoc '@augments' is not attached to a class declaration.": {
"JSDoc '@{0}' is not attached to a class.": {
"category": "Error",
"code": 8022
},
"JSDoc '@augments {0}' does not match the 'extends {1}' clause.": {
"JSDoc '@{0} {1}' does not match the 'extends {2}' clause.": {
"category": "Error",
"code": 8023
},
@@ -3523,6 +3535,10 @@
"category": "Error",
"code": 8024
},
"Class declarations cannot have more than one `@augments` or `@extends` tag.": {
"category": "Error",
"code": 8025
},
"Only identifiers/qualified-names with optional type arguments are currently supported in a class 'extends' clause.": {
"category": "Error",
"code": 9002
@@ -3649,7 +3665,7 @@
"category": "Message",
"code": 90013
},
"Change {0} to {1}.": {
"Change '{0}' to '{1}'.": {
"category": "Message",
"code": 90014
},
@@ -3665,6 +3681,7 @@
"category": "Message",
"code": 90017
},
"Disable checking for this file.": {
"category": "Message",
"code": 90018
@@ -3705,7 +3722,10 @@
"category": "Message",
"code": 90027
},
"Call decorator expression.": {
"category": "Message",
"code": 90028
},
"Convert function to an ES2015 class": {
"category": "Message",
"code": 95001
@@ -3714,34 +3734,36 @@
"category": "Message",
"code": 95002
},
"Extract symbol": {
"category": "Message",
"code": 95003
},
"Extract to {0} in {1}": {
"category": "Message",
"code": 95004
},
"Extract function": {
"category": "Message",
"code": 95005
},
"Extract constant": {
"category": "Message",
"code": 95006
},
"Extract to {0} in enclosing scope": {
"category": "Message",
"code": 95007
},
"Extract to {0} in {1} scope": {
"category": "Message",
"code": 95008
},
"Infer type of '{0}' from usage.": {
"category": "Message",
"code": 95009
},
"Infer parameter types from usage.": {
"category": "Message",
"code": 95010
}
}
Executable → Regular
+1 -1
View File
@@ -1226,7 +1226,7 @@ namespace ts {
// check if numeric literal is a decimal literal that was originally written with a dot
const text = getLiteralTextOfNode(<LiteralExpression>expression);
return !expression.numericLiteralFlags
&& text.indexOf(tokenToString(SyntaxKind.DotToken)) < 0;
&& !stringContains(text, tokenToString(SyntaxKind.DotToken));
}
else if (isPropertyAccessExpression(expression) || isElementAccessExpression(expression)) {
// check if constant enum value is integer
+16 -1
View File
@@ -47,10 +47,15 @@ namespace ts {
* Creates a shallow, memberwise clone of a node with no source map location.
*/
/* @internal */
export function getSynthesizedClone<T extends Node>(node: T | undefined): T {
export function getSynthesizedClone<T extends Node>(node: T | undefined): T | undefined {
// We don't use "clone" from core.ts here, as we need to preserve the prototype chain of
// the original node. We also need to exclude specific properties and only include own-
// properties (to skip members already defined on the shared prototype).
if (node === undefined) {
return undefined;
}
const clone = <T>createSynthesizedNode(node.kind);
clone.flags |= node.flags;
setOriginalNode(clone, node);
@@ -2607,6 +2612,16 @@ namespace ts {
return node;
}
/**
* Sets flags that control emit behavior of a node.
*/
/* @internal */
export function addEmitFlags<T extends Node>(node: T, emitFlags: EmitFlags) {
const emitNode = getOrCreateEmitNode(node);
emitNode.flags = emitNode.flags | emitFlags;
return node;
}
/**
* Gets a custom text range to use when emitting source maps.
*/
+1 -1
View File
@@ -1061,7 +1061,7 @@ namespace ts {
export function getPackageNameFromAtTypesDirectory(mangledName: string): string {
const withoutAtTypePrefix = removePrefix(mangledName, "@types/");
if (withoutAtTypePrefix !== mangledName) {
return withoutAtTypePrefix.indexOf(mangledScopedPackageSeparator) !== -1 ?
return stringContains(withoutAtTypePrefix, mangledScopedPackageSeparator) ?
"@" + withoutAtTypePrefix.replace(mangledScopedPackageSeparator, ts.directorySeparator) :
withoutAtTypePrefix;
}
+2 -1
View File
@@ -6373,6 +6373,7 @@ namespace ts {
if (tagName) {
switch (tagName.escapedText) {
case "augments":
case "extends":
tag = parseAugmentsTag(atToken, tagName);
break;
case "class":
@@ -6699,7 +6700,7 @@ namespace ts {
if (typeExpression && typeExpression.type.kind === SyntaxKind.ArrayType) {
jsdocTypeLiteral.isArrayType = true;
}
typedefTag.typeExpression = childTypeTag && !isObjectOrObjectArrayTypeReference(childTypeTag.typeExpression.type) ?
typedefTag.typeExpression = childTypeTag && childTypeTag.typeExpression && !isObjectOrObjectArrayTypeReference(childTypeTag.typeExpression.type) ?
childTypeTag.typeExpression :
finishNode(jsdocTypeLiteral);
}
+11 -5
View File
@@ -663,8 +663,7 @@ namespace ts {
dropDiagnosticsProducingTypeChecker,
getSourceFileFromReference,
sourceFileToPackageName,
redirectTargetsSet,
hasInvalidatedResolution
redirectTargetsSet
};
verifyCompilerOptions();
@@ -1092,11 +1091,18 @@ namespace ts {
return true;
}
if (defaultLibraryPath && defaultLibraryPath.length !== 0) {
return containsPath(defaultLibraryPath, file.path, currentDirectory, /*ignoreCase*/ !host.useCaseSensitiveFileNames());
if (!options.noLib) {
return false;
}
return compareStrings(file.fileName, getDefaultLibraryFileName(), /*ignoreCase*/ !host.useCaseSensitiveFileNames()) === Comparison.EqualTo;
// If '--lib' is not specified, include default library file according to '--target'
// otherwise, using options specified in '--lib' instead of '--target' default library file
if (!options.lib) {
return compareStrings(file.fileName, getDefaultLibraryFileName(), /*ignoreCase*/ !host.useCaseSensitiveFileNames()) === Comparison.EqualTo;
}
else {
return forEach(options.lib, libFileName => compareStrings(file.fileName, combinePaths(defaultLibraryPath, libFileName), /*ignoreCase*/ !host.useCaseSensitiveFileNames()) === Comparison.EqualTo);
}
}
function getDiagnosticsProducingTypeChecker() {
+16 -15
View File
@@ -107,7 +107,9 @@ namespace ts {
return {
startRecordingFilesWithChangedResolutions,
finishRecordingFilesWithChangedResolutions,
startCachingPerDirectoryResolution,
// perDirectoryResolvedModuleNames and perDirectoryResolvedTypeReferenceDirectives could be non empty if there was exception during program update
// (between startCachingPerDirectoryResolution and finishCachingPerDirectoryResolution)
startCachingPerDirectoryResolution: clearPerDirectoryResolutions,
finishCachingPerDirectoryResolution,
resolveModuleNames,
resolveTypeReferenceDirectives,
@@ -141,7 +143,9 @@ namespace ts {
resolvedModuleNames.clear();
resolvedTypeReferenceDirectives.clear();
allFilesHaveInvalidatedResolution = false;
Debug.assert(perDirectoryResolvedModuleNames.size === 0 && perDirectoryResolvedTypeReferenceDirectives.size === 0);
// perDirectoryResolvedModuleNames and perDirectoryResolvedTypeReferenceDirectives could be non empty if there was exception during program update
// (between startCachingPerDirectoryResolution and finishCachingPerDirectoryResolution)
clearPerDirectoryResolutions();
}
function startRecordingFilesWithChangedResolutions() {
@@ -165,8 +169,9 @@ namespace ts {
return path => collected && collected.has(path);
}
function startCachingPerDirectoryResolution() {
Debug.assert(perDirectoryResolvedModuleNames.size === 0 && perDirectoryResolvedTypeReferenceDirectives.size === 0);
function clearPerDirectoryResolutions() {
perDirectoryResolvedModuleNames.clear();
perDirectoryResolvedTypeReferenceDirectives.clear();
}
function finishCachingPerDirectoryResolution() {
@@ -178,8 +183,7 @@ namespace ts {
}
});
perDirectoryResolvedModuleNames.clear();
perDirectoryResolvedTypeReferenceDirectives.clear();
clearPerDirectoryResolutions();
}
function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
@@ -323,17 +327,14 @@ namespace ts {
let dir = getDirectoryPath(getNormalizedAbsolutePath(failedLookupLocation, getCurrentDirectory()));
let dirPath = getDirectoryPath(failedLookupLocationPath);
// If the directory is node_modules use it to watch
if (isNodeModulesDirectory(dirPath)) {
return { dir, dirPath };
// If directory path contains node module, get the most parent node_modules directory for watching
while (stringContains(dirPath, "/node_modules/")) {
dir = getDirectoryPath(dir);
dirPath = getDirectoryPath(dirPath);
}
// If directory path contains node module, get the node_modules directory for watching
if (dirPath.indexOf("/node_modules/") !== -1) {
while (!isNodeModulesDirectory(dirPath)) {
dir = getDirectoryPath(dir);
dirPath = getDirectoryPath(dirPath);
}
// If the directory is node_modules use it to watch
if (isNodeModulesDirectory(dirPath)) {
return { dir, dirPath };
}
+1 -2
View File
@@ -1112,7 +1112,7 @@ namespace ts {
}
function visitCallExpression(node: CallExpression) {
if (forEach(node.arguments, containsYield)) {
if (!isImportCall(node) && forEach(node.arguments, containsYield)) {
// [source]
// a.b(1, yield, 2);
//
@@ -1123,7 +1123,6 @@ namespace ts {
// .yield resumeLabel
// .mark resumeLabel
// _b.apply(_a, _c.concat([%sent%, 2]));
const { target, thisArg } = createCallBinding(node.expression, hoistVariableDeclaration, languageVersion, /*cacheIdentifiers*/ true);
return setOriginalNode(
createFunctionApply(
+93 -36
View File
@@ -21,7 +21,8 @@ namespace ts {
const {
startLexicalEnvironment,
endLexicalEnvironment
endLexicalEnvironment,
hoistVariableDeclaration
} = context;
const compilerOptions = context.getCompilerOptions();
@@ -519,18 +520,20 @@ namespace ts {
}
function visitImportCallExpression(node: ImportCall): Expression {
const argument = visitNode(firstOrUndefined(node.arguments), importCallExpressionVisitor);
const containsLexicalThis = !!(node.transformFlags & TransformFlags.ContainsLexicalThis);
switch (compilerOptions.module) {
case ModuleKind.AMD:
return transformImportCallExpressionAMD(node);
return createImportCallExpressionAMD(argument, containsLexicalThis);
case ModuleKind.UMD:
return transformImportCallExpressionUMD(node);
return createImportCallExpressionUMD(argument, containsLexicalThis);
case ModuleKind.CommonJS:
default:
return transformImportCallExpressionCommonJS(node);
return createImportCallExpressionCommonJS(argument, containsLexicalThis);
}
}
function transformImportCallExpressionUMD(node: ImportCall): Expression {
function createImportCallExpressionUMD(arg: Expression | undefined, containsLexicalThis: boolean): Expression {
// (function (factory) {
// ... (regular UMD)
// }
@@ -545,14 +548,25 @@ namespace ts {
// : new Promise(function (_a, _b) { require([x], _a, _b); }); /*Amd Require*/
// });
needUMDDynamicImportHelper = true;
return createConditional(
/*condition*/ createIdentifier("__syncRequire"),
/*whenTrue*/ transformImportCallExpressionCommonJS(node),
/*whenFalse*/ transformImportCallExpressionAMD(node)
);
if (isSimpleCopiableExpression(arg)) {
const argClone = isGeneratedIdentifier(arg) ? arg : isStringLiteral(arg) ? createLiteral(arg) : setEmitFlags(setTextRange(getSynthesizedClone(arg), arg), EmitFlags.NoComments);
return createConditional(
/*condition*/ createIdentifier("__syncRequire"),
/*whenTrue*/ createImportCallExpressionCommonJS(arg, containsLexicalThis),
/*whenFalse*/ createImportCallExpressionAMD(argClone, containsLexicalThis)
);
}
else {
const temp = createTempVariable(hoistVariableDeclaration);
return createComma(createAssignment(temp, arg), createConditional(
/*condition*/ createIdentifier("__syncRequire"),
/*whenTrue*/ createImportCallExpressionCommonJS(temp, containsLexicalThis),
/*whenFalse*/ createImportCallExpressionAMD(temp, containsLexicalThis)
));
}
}
function transformImportCallExpressionAMD(node: ImportCall): Expression {
function createImportCallExpressionAMD(arg: Expression | undefined, containsLexicalThis: boolean): Expression {
// improt("./blah")
// emit as
// define(["require", "exports", "blah"], function (require, exports) {
@@ -561,46 +575,89 @@ namespace ts {
// });
const resolve = createUniqueName("resolve");
const reject = createUniqueName("reject");
return createNew(
createIdentifier("Promise"),
/*typeArguments*/ undefined,
[createFunctionExpression(
const parameters = [
createParameter(/*decorator*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, /*name*/ resolve),
createParameter(/*decorator*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, /*name*/ reject)
];
const body = createBlock([
createStatement(
createCall(
createIdentifier("require"),
/*typeArguments*/ undefined,
[createArrayLiteral([arg || createOmittedExpression()]), resolve, reject]
)
)
]);
let func: FunctionExpression | ArrowFunction;
if (languageVersion >= ScriptTarget.ES2015) {
func = createArrowFunction(
/*modifiers*/ undefined,
/*typeParameters*/ undefined,
parameters,
/*type*/ undefined,
/*equalsGreaterThanToken*/ undefined,
body);
}
else {
func = createFunctionExpression(
/*modifiers*/ undefined,
/*asteriskToken*/ undefined,
/*name*/ undefined,
/*typeParameters*/ undefined,
[createParameter(/*decorator*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, /*name*/ resolve),
createParameter(/*decorator*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, /*name*/ reject)],
parameters,
/*type*/ undefined,
createBlock([createStatement(
createCall(
createIdentifier("require"),
/*typeArguments*/ undefined,
[createArrayLiteral([firstOrUndefined(node.arguments) || createOmittedExpression()]), resolve, reject]
))])
)]);
body);
// if there is a lexical 'this' in the import call arguments, ensure we indicate
// that this new function expression indicates it captures 'this' so that the
// es2015 transformer will properly substitute 'this' with '_this'.
if (containsLexicalThis) {
setEmitFlags(func, EmitFlags.CapturesThis);
}
}
return createNew(createIdentifier("Promise"), /*typeArguments*/ undefined, [func]);
}
function transformImportCallExpressionCommonJS(node: ImportCall): Expression {
function createImportCallExpressionCommonJS(arg: Expression | undefined, containsLexicalThis: boolean): Expression {
// import("./blah")
// emit as
// Promise.resolve().then(function () { return require(x); }) /*CommonJs Require*/
// We have to wrap require in then callback so that require is done in asynchronously
// if we simply do require in resolve callback in Promise constructor. We will execute the loading immediately
return createCall(
createPropertyAccess(
createCall(createPropertyAccess(createIdentifier("Promise"), "resolve"), /*typeArguments*/ undefined, /*argumentsArray*/ []),
"then"),
/*typeArguments*/ undefined,
[createFunctionExpression(
const promiseResolveCall = createCall(createPropertyAccess(createIdentifier("Promise"), "resolve"), /*typeArguments*/ undefined, /*argumentsArray*/ []);
const requireCall = createCall(createIdentifier("require"), /*typeArguments*/ undefined, arg ? [arg] : []);
let func: FunctionExpression | ArrowFunction;
if (languageVersion >= ScriptTarget.ES2015) {
func = createArrowFunction(
/*modifiers*/ undefined,
/*typeParameters*/ undefined,
/*parameters*/ [],
/*type*/ undefined,
/*equalsGreaterThanToken*/ undefined,
requireCall);
}
else {
func = createFunctionExpression(
/*modifiers*/ undefined,
/*asteriskToken*/ undefined,
/*name*/ undefined,
/*typeParameters*/ undefined,
/*parameters*/ undefined,
/*parameters*/ [],
/*type*/ undefined,
createBlock([createReturn(createCall(createIdentifier("require"), /*typeArguments*/ undefined, node.arguments))])
)]);
createBlock([createReturn(requireCall)]));
// if there is a lexical 'this' in the import call arguments, ensure we indicate
// that this new function expression indicates it captures 'this' so that the
// es2015 transformer will properly substitute 'this' with '_this'.
if (containsLexicalThis) {
setEmitFlags(func, EmitFlags.CapturesThis);
}
}
return createCall(createPropertyAccess(promiseResolveCall, "then"), /*typeArguments*/ undefined, [func]);
}
/**
@@ -861,10 +918,10 @@ namespace ts {
if (original && hasAssociatedEndOfDeclarationMarker(original)) {
// Defer exports until we encounter an EndOfDeclarationMarker node
const id = getOriginalNodeId(node);
deferredExports[id] = appendExportStatement(deferredExports[id], createIdentifier("default"), node.expression, /*location*/ node, /*allowComments*/ true);
deferredExports[id] = appendExportStatement(deferredExports[id], createIdentifier("default"), visitNode(node.expression, importCallExpressionVisitor), /*location*/ node, /*allowComments*/ true);
}
else {
statements = appendExportStatement(statements, createIdentifier("default"), node.expression, /*location*/ node, /*allowComments*/ true);
statements = appendExportStatement(statements, createIdentifier("default"), visitNode(node.expression, importCallExpressionVisitor), /*location*/ node, /*allowComments*/ true);
}
return singleOrMany(statements);
+1 -1
View File
@@ -1495,7 +1495,7 @@ namespace ts {
createIdentifier("import")
),
/*typeArguments*/ undefined,
node.arguments
some(node.arguments) ? [visitNode(node.arguments[0], destructuringAndImportCallVisitor)] : []
);
}
+26 -16
View File
@@ -26,7 +26,7 @@ namespace ts {
IsExportOfNamespace = 1 << 3,
IsNamedExternalExport = 1 << 4,
IsDefaultExternalExport = 1 << 5,
HasExtendsClause = 1 << 6,
IsDerivedClass = 1 << 6,
UseImmediatelyInvokedFunctionExpression = 1 << 7,
HasAnyDecorators = HasConstructorDecorators | HasMemberDecorators,
@@ -45,6 +45,7 @@ namespace ts {
const resolver = context.getEmitResolver();
const compilerOptions = context.getCompilerOptions();
const strictNullChecks = typeof compilerOptions.strictNullChecks === "undefined" ? compilerOptions.strict : compilerOptions.strictNullChecks;
const languageVersion = getEmitScriptTarget(compilerOptions);
const moduleKind = getEmitModuleKind(compilerOptions);
@@ -553,7 +554,8 @@ namespace ts {
function getClassFacts(node: ClassDeclaration, staticProperties: ReadonlyArray<PropertyDeclaration>) {
let facts = ClassFacts.None;
if (some(staticProperties)) facts |= ClassFacts.HasStaticInitializedProperties;
if (getClassExtendsHeritageClauseElement(node)) facts |= ClassFacts.HasExtendsClause;
const extendsClauseElement = getClassExtendsHeritageClauseElement(node);
if (extendsClauseElement && skipOuterExpressions(extendsClauseElement.expression).kind !== SyntaxKind.NullKeyword) facts |= ClassFacts.IsDerivedClass;
if (shouldEmitDecorateCallForClass(node)) facts |= ClassFacts.HasConstructorDecorators;
if (childIsDecorated(node)) facts |= ClassFacts.HasMemberDecorators;
if (isExportOfNamespace(node)) facts |= ClassFacts.IsExportOfNamespace;
@@ -699,7 +701,7 @@ namespace ts {
name,
/*typeParameters*/ undefined,
visitNodes(node.heritageClauses, visitor, isHeritageClause),
transformClassMembers(node, (facts & ClassFacts.HasExtendsClause) !== 0)
transformClassMembers(node, (facts & ClassFacts.IsDerivedClass) !== 0)
);
// To better align with the old emitter, we should not emit a trailing source map
@@ -814,7 +816,7 @@ namespace ts {
// ${members}
// }
const heritageClauses = visitNodes(node.heritageClauses, visitor, isHeritageClause);
const members = transformClassMembers(node, (facts & ClassFacts.HasExtendsClause) !== 0);
const members = transformClassMembers(node, (facts & ClassFacts.IsDerivedClass) !== 0);
const classExpression = createClassExpression(/*modifiers*/ undefined, name, /*typeParameters*/ undefined, heritageClauses, members);
setOriginalNode(classExpression, node);
setTextRange(classExpression, location);
@@ -887,11 +889,11 @@ namespace ts {
* Transforms the members of a class.
*
* @param node The current class.
* @param hasExtendsClause A value indicating whether the class has an extends clause.
* @param isDerivedClass A value indicating whether the class has an extends clause that does not extend 'null'.
*/
function transformClassMembers(node: ClassDeclaration | ClassExpression, hasExtendsClause: boolean) {
function transformClassMembers(node: ClassDeclaration | ClassExpression, isDerivedClass: boolean) {
const members: ClassElement[] = [];
const constructor = transformConstructor(node, hasExtendsClause);
const constructor = transformConstructor(node, isDerivedClass);
if (constructor) {
members.push(constructor);
}
@@ -904,9 +906,9 @@ namespace ts {
* Transforms (or creates) a constructor for a class.
*
* @param node The current class.
* @param hasExtendsClause A value indicating whether the class has an extends clause.
* @param isDerivedClass A value indicating whether the class has an extends clause that does not extend 'null'.
*/
function transformConstructor(node: ClassDeclaration | ClassExpression, hasExtendsClause: boolean) {
function transformConstructor(node: ClassDeclaration | ClassExpression, isDerivedClass: boolean) {
// Check if we have property assignment inside class declaration.
// If there is a property assignment, we need to emit constructor whether users define it or not
// If there is no property assignment, we can omit constructor if users do not define it
@@ -921,7 +923,7 @@ namespace ts {
}
const parameters = transformConstructorParameters(constructor);
const body = transformConstructorBody(node, constructor, hasExtendsClause);
const body = transformConstructorBody(node, constructor, isDerivedClass);
// constructor(${parameters}) {
// ${body}
@@ -947,7 +949,6 @@ namespace ts {
* parameter property assignments or instance property initializers.
*
* @param constructor The constructor declaration.
* @param hasExtendsClause A value indicating whether the class has an extends clause.
*/
function transformConstructorParameters(constructor: ConstructorDeclaration) {
// The ES2015 spec specifies in 14.5.14. Runtime Semantics: ClassDefinitionEvaluation:
@@ -975,9 +976,9 @@ namespace ts {
*
* @param node The current class.
* @param constructor The current class constructor.
* @param hasExtendsClause A value indicating whether the class has an extends clause.
* @param isDerivedClass A value indicating whether the class has an extends clause that does not extend 'null'.
*/
function transformConstructorBody(node: ClassExpression | ClassDeclaration, constructor: ConstructorDeclaration, hasExtendsClause: boolean) {
function transformConstructorBody(node: ClassExpression | ClassDeclaration, constructor: ConstructorDeclaration, isDerivedClass: boolean) {
let statements: Statement[] = [];
let indexOfFirstStatement = 0;
@@ -1001,7 +1002,7 @@ namespace ts {
const propertyAssignments = getParametersWithPropertyAssignments(constructor);
addRange(statements, map(propertyAssignments, transformParameterWithPropertyAssignment));
}
else if (hasExtendsClause) {
else if (isDerivedClass) {
// Add a synthetic `super` call:
//
// super(...arguments);
@@ -1869,7 +1870,16 @@ namespace ts {
// Note when updating logic here also update getEntityNameForDecoratorMetadata
// so that aliases can be marked as referenced
let serializedUnion: SerializedTypeNode;
for (const typeNode of node.types) {
for (let typeNode of node.types) {
while (typeNode.kind === SyntaxKind.ParenthesizedType) {
typeNode = (typeNode as ParenthesizedTypeNode).type; // Skip parens if need be
}
if (typeNode.kind === SyntaxKind.NeverKeyword) {
continue; // Always elide `never` from the union/intersection if possible
}
if (!strictNullChecks && (typeNode.kind === SyntaxKind.NullKeyword || typeNode.kind === SyntaxKind.UndefinedKeyword)) {
continue; // Elide null and undefined from unions for metadata, just like what we did prior to the implementation of strict null checks
}
const serializedIndividual = serializeTypeNode(typeNode);
if (isIdentifier(serializedIndividual) && serializedIndividual.escapedText === "Object") {
@@ -1893,7 +1903,7 @@ namespace ts {
}
// If we were able to find common type, use it
return serializedUnion;
return serializedUnion || createVoidZero(); // Fallback is only hit if all union constituients are null/undefined/never
}
/**
+13
View File
@@ -178,4 +178,17 @@ namespace ts {
}
return values;
}
/**
* Used in the module transformer to check if an expression is reasonably without sideeffect,
* and thus better to copy into multiple places rather than to cache in a temporary variable
* - this is mostly subjective beyond the requirement that the expression not be sideeffecting
*/
export function isSimpleCopiableExpression(expression: Expression) {
return expression.kind === SyntaxKind.StringLiteral ||
expression.kind === SyntaxKind.NumericLiteral ||
expression.kind === SyntaxKind.NoSubstitutionTemplateLiteral ||
isKeyword(expression.kind) ||
isIdentifier(expression);
}
}
+27 -3
View File
@@ -2159,6 +2159,10 @@ namespace ts {
kind: SyntaxKind.JSDocTag;
}
/**
* Note that `@extends` is a synonym of `@augments`.
* Both tags are represented by this interface.
*/
export interface JSDocAugmentsTag extends JSDocTag {
kind: SyntaxKind.JSDocAugmentsTag;
class: ExpressionWithTypeArguments & { expression: Identifier | PropertyAccessEntityNameExpression };
@@ -2194,7 +2198,7 @@ namespace ts {
export interface JSDocPropertyLikeTag extends JSDocTag, Declaration {
parent: JSDoc;
name: EntityName;
typeExpression: JSDocTypeExpression;
typeExpression?: JSDocTypeExpression;
/** Whether the property name came before the type -- non-standard for JSDoc, but Typescript-like */
isNameFirst: boolean;
isBracketed: boolean;
@@ -2525,8 +2529,6 @@ namespace ts {
/* @internal */ sourceFileToPackageName: Map<string>;
/** Set of all source files that some other source file redirects to. */
/* @internal */ redirectTargetsSet: Map<true>;
/** Returns true when file in the program had invalidated resolution at the time of program creation. */
/* @internal */ hasInvalidatedResolution: HasInvalidatedResolution;
}
/* @internal */
@@ -2649,6 +2651,10 @@ namespace ts {
signatureToString(signature: Signature, enclosingDeclaration?: Node, flags?: TypeFormatFlags, kind?: SignatureKind): string;
typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string;
symbolToString(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): string;
/**
* @deprecated Use the createX factory functions or XToY typechecker methods and `createPrinter` or the `xToString` methods instead
* This will be removed in a future version.
*/
getSymbolDisplayBuilder(): SymbolDisplayBuilder;
getFullyQualifiedName(symbol: Symbol): string;
getAugmentedPropertiesOfType(type: Type): Symbol[];
@@ -2689,6 +2695,24 @@ namespace ts {
getSuggestionForNonexistentSymbol(location: Node, name: string, meaning: SymbolFlags): string | undefined;
/* @internal */ getBaseConstraintOfType(type: Type): Type | undefined;
/* @internal */ getAnyType(): Type;
/* @internal */ getStringType(): Type;
/* @internal */ getNumberType(): Type;
/* @internal */ getBooleanType(): Type;
/* @internal */ getVoidType(): Type;
/* @internal */ getUndefinedType(): Type;
/* @internal */ getNullType(): Type;
/* @internal */ getESSymbolType(): Type;
/* @internal */ getNeverType(): Type;
/* @internal */ getUnionType(types: Type[], subtypeReduction?: boolean): Type;
/* @internal */ createArrayType(elementType: Type): Type;
/* @internal */ createPromiseType(type: Type): Type;
/* @internal */ createAnonymousType(symbol: Symbol, members: SymbolTable, callSignatures: Signature[], constructSignatures: Signature[], stringIndexInfo: IndexInfo, numberIndexInfo: IndexInfo): Type;
/* @internal */ createSignature(declaration: SignatureDeclaration, typeParameters: TypeParameter[], thisParameter: Symbol | undefined, parameters: Symbol[], resolvedReturnType: Type, typePredicate: TypePredicate, minArgumentCount: number, hasRestParameter: boolean, hasLiteralTypes: boolean): Signature;
/* @internal */ createSymbol(flags: SymbolFlags, name: __String): TransientSymbol;
/* @internal */ createIndexInfo(type: Type, isReadonly: boolean, declaration?: SignatureDeclaration): IndexInfo;
/* @internal */ isSymbolAccessible(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags, shouldComputeAliasToMarkVisible: boolean): SymbolAccessibilityResult;
/* @internal */ tryFindAmbientModuleWithoutAugmentations(moduleName: string): Symbol | undefined;
/* @internal */ getSymbolWalker(accept?: (symbol: Symbol) => boolean): SymbolWalker;
+45 -37
View File
@@ -1604,26 +1604,12 @@ namespace ts {
}
export function hasRestParameter(s: SignatureDeclaration): boolean {
return isRestParameter(lastOrUndefined(s.parameters));
const last = lastOrUndefined(s.parameters);
return last && isRestParameter(last);
}
export function hasDeclaredRestParameter(s: SignatureDeclaration): boolean {
return isDeclaredRestParam(lastOrUndefined(s.parameters));
}
export function isRestParameter(node: ParameterDeclaration) {
if (isInJavaScriptFile(node)) {
if (node.type && node.type.kind === SyntaxKind.JSDocVariadicType ||
forEach(getJSDocParameterTags(node),
t => t.typeExpression && t.typeExpression.type.kind === SyntaxKind.JSDocVariadicType)) {
return true;
}
}
return isDeclaredRestParam(node);
}
export function isDeclaredRestParam(node: ParameterDeclaration) {
return node && node.dotDotDotToken !== undefined;
export function isRestParameter(node: ParameterDeclaration): boolean {
return node.dotDotDotToken !== undefined;
}
export const enum AssignmentKind {
@@ -4112,27 +4098,35 @@ namespace ts {
if (!declaration) {
return undefined;
}
if (isJSDocPropertyLikeTag(declaration) && declaration.name.kind === SyntaxKind.QualifiedName) {
return declaration.name.right;
}
if (declaration.kind === SyntaxKind.BinaryExpression) {
const expr = declaration as BinaryExpression;
switch (getSpecialPropertyAssignmentKind(expr)) {
case SpecialPropertyAssignmentKind.ExportsProperty:
case SpecialPropertyAssignmentKind.ThisProperty:
case SpecialPropertyAssignmentKind.Property:
case SpecialPropertyAssignmentKind.PrototypeProperty:
return (expr.left as PropertyAccessExpression).name;
default:
return undefined;
switch (declaration.kind) {
case SyntaxKind.JSDocPropertyTag:
case SyntaxKind.JSDocParameterTag: {
const { name } = declaration as JSDocPropertyLikeTag;
if (name.kind === SyntaxKind.QualifiedName) {
return name.right;
}
break;
}
case SyntaxKind.BinaryExpression: {
const expr = declaration as BinaryExpression;
switch (getSpecialPropertyAssignmentKind(expr)) {
case SpecialPropertyAssignmentKind.ExportsProperty:
case SpecialPropertyAssignmentKind.ThisProperty:
case SpecialPropertyAssignmentKind.Property:
case SpecialPropertyAssignmentKind.PrototypeProperty:
return (expr.left as PropertyAccessExpression).name;
default:
return undefined;
}
}
case SyntaxKind.JSDocTypedefTag:
return getNameOfJSDocTypedef(declaration as JSDocTypedefTag);
case SyntaxKind.ExportAssignment: {
const { expression } = declaration as ExportAssignment;
return isIdentifier(expression) ? expression : undefined;
}
}
else if (declaration.kind === SyntaxKind.JSDocTypedefTag) {
return getNameOfJSDocTypedef(declaration as JSDocTypedefTag);
}
else {
return (declaration as NamedDeclaration).name;
}
return (declaration as NamedDeclaration).name;
}
/**
@@ -4247,6 +4241,12 @@ namespace ts {
return find(tags, doc => doc.kind === kind);
}
/** Gets all JSDoc tags of a specified kind, or undefined if not present. */
export function getAllJSDocTagsOfKind(node: Node, kind: SyntaxKind): ReadonlyArray<JSDocTag> | undefined {
const tags = getJSDocTags(node);
return filter(tags, doc => doc.kind === kind);
}
}
// Simple node tests of the form `node.kind === SyntaxKind.Foo`.
@@ -5649,6 +5649,14 @@ namespace ts {
return node.kind >= SyntaxKind.FirstJSDocTagNode && node.kind <= SyntaxKind.LastJSDocTagNode;
}
export function isSetAccessor(node: Node): node is SetAccessorDeclaration {
return node.kind === SyntaxKind.SetAccessor;
}
export function isGetAccessor(node: Node): node is GetAccessorDeclaration {
return node.kind === SyntaxKind.GetAccessor;
}
/** True if has jsdoc nodes attached to it. */
/* @internal */
export function hasJSDocNodes(node: Node): node is HasJSDoc {
+9 -9
View File
@@ -249,10 +249,10 @@ namespace ts {
let hasChangedAutomaticTypeDirectiveNames = false; // True if the automatic type directives have changed
const loggingEnabled = compilerOptions.diagnostics || compilerOptions.extendedDiagnostics;
const writeLog: (s: string) => void = loggingEnabled ? s => system.write(s) : noop;
const watchFile = loggingEnabled ? ts.addFileWatcherWithLogging : ts.addFileWatcher;
const watchFilePath = loggingEnabled ? ts.addFilePathWatcherWithLogging : ts.addFilePathWatcher;
const watchDirectoryWorker = loggingEnabled ? ts.addDirectoryWatcherWithLogging : ts.addDirectoryWatcher;
const writeLog: (s: string) => void = loggingEnabled ? s => { system.write(s); system.write(system.newLine); } : noop;
const watchFile = compilerOptions.extendedDiagnostics ? ts.addFileWatcherWithLogging : loggingEnabled ? ts.addFileWatcherWithOnlyTriggerLogging : ts.addFileWatcher;
const watchFilePath = compilerOptions.extendedDiagnostics ? ts.addFilePathWatcherWithLogging : ts.addFilePathWatcher;
const watchDirectoryWorker = compilerOptions.extendedDiagnostics ? ts.addDirectoryWatcherWithLogging : ts.addDirectoryWatcher;
watchingHost = watchingHost || createWatchingSystemHost(compilerOptions.pretty);
const { system, parseConfigFile, reportDiagnostic, reportWatchDiagnostic, beforeCompile, afterCompile } = watchingHost;
@@ -322,6 +322,9 @@ namespace ts {
if (hasChangedCompilerOptions) {
newLine = getNewLineCharacter(compilerOptions, system);
if (program && changesAffectModuleResolution(program.getCompilerOptions(), compilerOptions)) {
resolutionCache.clear();
}
}
const hasInvalidatedResolution = resolutionCache.createHasInvalidatedResolution();
@@ -329,14 +332,11 @@ namespace ts {
return;
}
if (hasChangedCompilerOptions && changesAffectModuleResolution(program && program.getCompilerOptions(), compilerOptions)) {
resolutionCache.clear();
}
const needsUpdateInTypeRootWatch = hasChangedCompilerOptions || !program;
hasChangedCompilerOptions = false;
beforeCompile(compilerOptions);
// Compile the program
const needsUpdateInTypeRootWatch = hasChangedCompilerOptions || !program;
hasChangedCompilerOptions = false;
resolutionCache.startCachingPerDirectoryResolution();
compilerHost.hasInvalidatedResolution = hasInvalidatedResolution;
compilerHost.hasChangedAutomaticTypeDirectiveNames = hasChangedAutomaticTypeDirectiveNames;
+25 -6
View File
@@ -82,7 +82,12 @@ namespace ts {
export function addFileWatcherWithLogging(host: System, file: string, cb: FileWatcherCallback, log: (s: string) => void): FileWatcher {
const watcherCaption = `FileWatcher:: `;
return createWatcherWithLogging(addFileWatcher, watcherCaption, log, host, file, cb);
return createWatcherWithLogging(addFileWatcher, watcherCaption, log, /*logOnlyTrigger*/ false, host, file, cb);
}
export function addFileWatcherWithOnlyTriggerLogging(host: System, file: string, cb: FileWatcherCallback, log: (s: string) => void): FileWatcher {
const watcherCaption = `FileWatcher:: `;
return createWatcherWithLogging(addFileWatcher, watcherCaption, log, /*logOnlyTrigger*/ true, host, file, cb);
}
export type FilePathWatcherCallback = (fileName: string, eventKind: FileWatcherEventKind, filePath: Path) => void;
@@ -92,7 +97,12 @@ namespace ts {
export function addFilePathWatcherWithLogging(host: System, file: string, cb: FilePathWatcherCallback, path: Path, log: (s: string) => void): FileWatcher {
const watcherCaption = `FileWatcher:: `;
return createWatcherWithLogging(addFileWatcher, watcherCaption, log, host, file, cb, path);
return createWatcherWithLogging(addFileWatcher, watcherCaption, log, /*logOnlyTrigger*/ false, host, file, cb, path);
}
export function addFilePathWatcherWithOnlyTriggerLogging(host: System, file: string, cb: FilePathWatcherCallback, path: Path, log: (s: string) => void): FileWatcher {
const watcherCaption = `FileWatcher:: `;
return createWatcherWithLogging(addFileWatcher, watcherCaption, log, /*logOnlyTrigger*/ true, host, file, cb, path);
}
export function addDirectoryWatcher(host: System, directory: string, cb: DirectoryWatcherCallback, flags: WatchDirectoryFlags): FileWatcher {
@@ -102,14 +112,21 @@ namespace ts {
export function addDirectoryWatcherWithLogging(host: System, directory: string, cb: DirectoryWatcherCallback, flags: WatchDirectoryFlags, log: (s: string) => void): FileWatcher {
const watcherCaption = `DirectoryWatcher ${(flags & WatchDirectoryFlags.Recursive) !== 0 ? "recursive" : ""}:: `;
return createWatcherWithLogging(addDirectoryWatcher, watcherCaption, log, host, directory, cb, flags);
return createWatcherWithLogging(addDirectoryWatcher, watcherCaption, log, /*logOnlyTrigger*/ false, host, directory, cb, flags);
}
export function addDirectoryWatcherWithOnlyTriggerLogging(host: System, directory: string, cb: DirectoryWatcherCallback, flags: WatchDirectoryFlags, log: (s: string) => void): FileWatcher {
const watcherCaption = `DirectoryWatcher ${(flags & WatchDirectoryFlags.Recursive) !== 0 ? "recursive" : ""}:: `;
return createWatcherWithLogging(addDirectoryWatcher, watcherCaption, log, /*logOnlyTrigger*/ true, host, directory, cb, flags);
}
type WatchCallback<T, U> = (fileName: string, cbOptional1?: T, optional?: U) => void;
type AddWatch<T, U> = (host: System, file: string, cb: WatchCallback<T, U>, optional?: U) => FileWatcher;
function createWatcherWithLogging<T, U>(addWatch: AddWatch<T, U>, watcherCaption: string, log: (s: string) => void, host: System, file: string, cb: WatchCallback<T, U>, optional?: U): FileWatcher {
function createWatcherWithLogging<T, U>(addWatch: AddWatch<T, U>, watcherCaption: string, log: (s: string) => void, logOnlyTrigger: boolean, host: System, file: string, cb: WatchCallback<T, U>, optional?: U): FileWatcher {
const info = `PathInfo: ${file}`;
log(`${watcherCaption}Added: ${info}`);
if (!logOnlyTrigger) {
log(`${watcherCaption}Added: ${info}`);
}
const watcher = addWatch(host, file, (fileName, cbOptional1?) => {
const optionalInfo = cbOptional1 !== undefined ? ` ${cbOptional1}` : "";
log(`${watcherCaption}Trigger: ${fileName}${optionalInfo} ${info}`);
@@ -120,7 +137,9 @@ namespace ts {
}, optional);
return {
close: () => {
log(`${watcherCaption}Close: ${info}`);
if (!logOnlyTrigger) {
log(`${watcherCaption}Close: ${info}`);
}
watcher.close();
}
};
+1 -1
View File
@@ -2381,7 +2381,7 @@ Actual: ${stringify(fullActual)}`);
}));
return ts.flatMap(ts.deduplicate(diagnosticsForCodeFix, ts.equalOwnProperties), diagnostic => {
if (errorCode && errorCode !== diagnostic.code) {
if (errorCode !== undefined && errorCode !== diagnostic.code) {
return;
}
+37 -13
View File
@@ -38,20 +38,45 @@ namespace Harness.Parallel.Host {
return undefined;
}
function hashName(runner: TestRunnerKind, test: string) {
function hashName(runner: TestRunnerKind | "unittest", test: string) {
return `tsrunner-${runner}://${test}`;
}
let tasks: { runner: TestRunnerKind | "unittest", file: string, size: number }[] = [];
const newTasks: { runner: TestRunnerKind | "unittest", file: string, size: number }[] = [];
let unknownValue: string | undefined;
export function start() {
initializeProgressBarsDependencies();
console.log("Discovering tests...");
const discoverStart = +(new Date());
const { statSync }: { statSync(path: string): { size: number }; } = require("fs");
let tasks: { runner: TestRunnerKind, file: string, size: number }[] = [];
const newTasks: { runner: TestRunnerKind, file: string, size: number }[] = [];
const perfData = readSavedPerfData(configOption);
let totalCost = 0;
let unknownValue: string | undefined;
if (runUnitTests) {
(global as any).describe = (suiteName: string) => {
// Note, sub-suites are not indexed (we assume such granularity is not required)
let size = 0;
if (perfData) {
size = perfData[hashName("unittest", suiteName)];
if (size === undefined) {
newTasks.push({ runner: "unittest", file: suiteName, size: 0 });
unknownValue = suiteName;
return;
}
}
tasks.push({ runner: "unittest", file: suiteName, size });
totalCost += size;
};
}
else {
(global as any).describe = ts.noop;
}
setTimeout(() => startDelayed(perfData, totalCost), 0); // Do real startup on next tick, so all unit tests have been collected
}
function startDelayed(perfData: {[testHash: string]: number}, totalCost: number) {
initializeProgressBarsDependencies();
console.log(`Discovered ${tasks.length} unittest suites` + (newTasks.length ? ` and ${newTasks.length} new suites.` : "."));
console.log("Discovering runner-based tests...");
const discoverStart = +(new Date());
const { statSync }: { statSync(path: string): { size: number }; } = require("fs");
for (const runner of runners) {
const files = runner.enumerateTestFiles();
for (const file of files) {
@@ -87,8 +112,7 @@ namespace Harness.Parallel.Host {
}
tasks.sort((a, b) => a.size - b.size);
tasks = tasks.concat(newTasks);
// 1 fewer batches than threads to account for unittests running on the final thread
const batchCount = runners.length === 1 ? workerCount : workerCount - 1;
const batchCount = workerCount;
const packfraction = 0.9;
const chunkSize = 1000; // ~1KB or 1s for sending batches near the end of a test
const batchSize = (totalCost / workerCount) * packfraction; // Keep spare tests for unittest thread in reserve
@@ -113,7 +137,7 @@ namespace Harness.Parallel.Host {
let closedWorkers = 0;
for (let i = 0; i < workerCount; i++) {
// TODO: Just send the config over the IPC channel or in the command line arguments
const config: TestConfig = { light: Harness.lightMode, listenForWork: true, runUnitTests: runners.length === 1 ? false : i === workerCount - 1 };
const config: TestConfig = { light: Harness.lightMode, listenForWork: true, runUnitTests: runners.length !== 1 };
const configPath = ts.combinePaths(taskConfigsFolder, `task-config${i}.json`);
Harness.IO.writeFile(configPath, JSON.stringify(config));
const child = fork(__filename, [`--config="${configPath}"`]);
@@ -187,7 +211,7 @@ namespace Harness.Parallel.Host {
// It's only really worth doing an initial batching if there are a ton of files to go through
if (totalFiles > 1000) {
console.log("Batching initial test lists...");
const batches: { runner: TestRunnerKind, file: string, size: number }[][] = new Array(batchCount);
const batches: { runner: TestRunnerKind | "unittest", file: string, size: number }[][] = new Array(batchCount);
const doneBatching = new Array(batchCount);
let scheduledTotal = 0;
batcher: while (true) {
@@ -230,7 +254,7 @@ namespace Harness.Parallel.Host {
if (payload) {
worker.send({ type: "batch", payload });
}
else { // Unittest thread - send off just one test
else { // Out of batches, send off just one test
const payload = tasks.pop();
ts.Debug.assert(!!payload); // The reserve kept above should ensure there is always an initial task available, even in suboptimal scenarios
worker.send({ type: "test", payload });
+2 -2
View File
@@ -1,14 +1,14 @@
/// <reference path="./host.ts" />
/// <reference path="./worker.ts" />
namespace Harness.Parallel {
export type ParallelTestMessage = { type: "test", payload: { runner: TestRunnerKind, file: string } } | never;
export type ParallelTestMessage = { type: "test", payload: { runner: TestRunnerKind | "unittest", file: string } } | never;
export type ParallelBatchMessage = { type: "batch", payload: ParallelTestMessage["payload"][] } | never;
export type ParallelCloseMessage = { type: "close" } | never;
export type ParallelHostMessage = ParallelTestMessage | ParallelCloseMessage | ParallelBatchMessage;
export type ParallelErrorMessage = { type: "error", payload: { error: string, stack: string, name?: string[] } } | never;
export type ErrorInfo = ParallelErrorMessage["payload"] & { name: string[] };
export type ParallelResultMessage = { type: "result", payload: { passing: number, errors: ErrorInfo[], duration: number, runner: TestRunnerKind, file: string } } | never;
export type ParallelResultMessage = { type: "result", payload: { passing: number, errors: ErrorInfo[], duration: number, runner: TestRunnerKind | "unittest", file: string } } | never;
export type ParallelBatchProgressMessage = { type: "progress", payload: ParallelResultMessage["payload"] } | never;
export type ParallelClientMessage = ParallelErrorMessage | ParallelResultMessage | ParallelBatchProgressMessage;
}
+42 -18
View File
@@ -1,22 +1,13 @@
namespace Harness.Parallel.Worker {
let errors: ErrorInfo[] = [];
let passing = 0;
let reportedUnitTests = false;
type Executor = {name: string, callback: Function, kind: "suite" | "test"} | never;
function resetShimHarnessAndExecute(runner: RunnerBase) {
if (reportedUnitTests) {
errors = [];
passing = 0;
testList.length = 0;
}
reportedUnitTests = true;
if (testList.length) {
// Execute unit tests
testList.forEach(({ name, callback, kind }) => executeCallback(name, callback, kind));
testList.length = 0;
}
errors = [];
passing = 0;
testList.length = 0;
const start = +(new Date());
runner.initializeTests();
testList.forEach(({ name, callback, kind }) => executeCallback(name, callback, kind));
@@ -226,13 +217,46 @@ namespace Harness.Parallel.Worker {
shimMochaHarness();
}
function handleTest(runner: TestRunnerKind, file: string) {
if (!runners.has(runner)) {
runners.set(runner, createRunner(runner));
function handleTest(runner: TestRunnerKind | "unittest", file: string) {
collectUnitTestsIfNeeded();
if (runner === unittest) {
return executeUnitTest(file);
}
else {
if (!runners.has(runner)) {
runners.set(runner, createRunner(runner));
}
const instance = runners.get(runner);
instance.tests = [file];
return { ...resetShimHarnessAndExecute(instance), runner, file };
}
const instance = runners.get(runner);
instance.tests = [file];
return { ...resetShimHarnessAndExecute(instance), runner, file };
}
}
const unittest: "unittest" = "unittest";
let unitTests: {[name: string]: Function};
function collectUnitTestsIfNeeded() {
if (!unitTests && testList.length) {
unitTests = {};
for (const test of testList) {
unitTests[test.name] = test.callback;
}
testList.length = 0;
}
}
function executeUnitTest(name: string) {
if (!unitTests) {
throw new Error(`Asked to run unit test ${name}, but no unit tests were discovered!`);
}
if (unitTests[name]) {
errors = [];
passing = 0;
const start = +(new Date());
executeSuiteCallback(name, unitTests[name]);
delete unitTests[name];
return { file: name, runner: unittest, errors, passing, duration: +(new Date()) - start };
}
throw new Error(`Unit test with name "${name}" was asked to be run, but such a test does not exist!`);
}
}
+44 -1
View File
@@ -563,7 +563,7 @@ namespace ts.projectSystem {
path: "/a/b/file3.js",
content: "console.log('file3');"
};
const externalProjectName = "externalproject";
const externalProjectName = "/a/b/externalproject";
const host = createServerHost([file1, file2, file3, libFile]);
const session = createSession(host);
const projectService = session.getProjectService();
@@ -588,5 +588,48 @@ namespace ts.projectSystem {
assert.isTrue(outFileContent.indexOf(file2.content) === -1);
assert.isTrue(outFileContent.indexOf(file3.content) === -1);
});
it("should use project root as current directory so that compile on save results in correct file mapping", () => {
const inputFileName = "Foo.ts";
const file1 = {
path: `/root/TypeScriptProject3/TypeScriptProject3/${inputFileName}`,
content: "consonle.log('file1');"
};
const externalProjectName = "/root/TypeScriptProject3/TypeScriptProject3/TypeScriptProject3.csproj";
const host = createServerHost([file1, libFile]);
const session = createSession(host);
const projectService = session.getProjectService();
const outFileName = "bar.js";
projectService.openExternalProject({
rootFiles: toExternalFiles([file1.path]),
options: {
outFile: outFileName,
sourceMap: true,
compileOnSave: true
},
projectFileName: externalProjectName
});
const emitRequest = makeSessionRequest<server.protocol.CompileOnSaveEmitFileRequestArgs>(CommandNames.CompileOnSaveEmitFile, { file: file1.path });
session.executeCommand(emitRequest);
// Verify js file
const expectedOutFileName = "/root/TypeScriptProject3/TypeScriptProject3/" + outFileName;
assert.isTrue(host.fileExists(expectedOutFileName));
const outFileContent = host.readFile(expectedOutFileName);
verifyContentHasString(outFileContent, file1.content);
verifyContentHasString(outFileContent, `//# ${"sourceMappingURL"}=${outFileName}.map`); // Sometimes tools can sometimes see this line as a source mapping url comment, so we obfuscate it a little
// Verify map file
const expectedMapFileName = expectedOutFileName + ".map";
assert.isTrue(host.fileExists(expectedMapFileName));
const mapFileContent = host.readFile(expectedMapFileName);
verifyContentHasString(mapFileContent, `"sources":["${inputFileName}"]`);
function verifyContentHasString(content: string, string: string) {
assert.isTrue(content.indexOf(string) !== -1, `Expected "${content}" to have "${string}"`);
}
});
});
}
+32
View File
@@ -223,6 +223,14 @@ const f = () => {
testExtractConstant("extractConstant_ArrowFunction_Expression",
`const f = () => [#|2 + 1|];`);
testExtractConstant("extractConstant_PreserveTrivia", `
// a
var q = /*b*/ //c
/*d*/ [#|1 /*e*/ //f
/*g*/ + /*h*/ //i
/*j*/ 2|] /*k*/ //l
/*m*/; /*n*/ //o`);
testExtractConstantFailed("extractConstant_Void", `
function f(): void { }
[#|f();|]`);
@@ -230,6 +238,30 @@ function f(): void { }
testExtractConstantFailed("extractConstant_Never", `
function f(): never { }
[#|f();|]`);
testExtractConstant("extractConstant_This_Constructor", `
class C {
constructor() {
[#|this.m2()|];
}
m2() { return 1; }
}`);
testExtractConstant("extractConstant_This_Method", `
class C {
m1() {
[#|this.m2()|];
}
m2() { return 1; }
}`);
testExtractConstant("extractConstant_This_Property", `
namespace N { // Force this test to be TS-only
class C {
x = 1;
y = [#|this.x|];
}
}`);
});
function testExtractConstant(caption: string, text: string) {
+180
View File
@@ -360,6 +360,186 @@ function parsePrimaryExpression(): any {
export const j = 10;
export const y = [#|j * j|];
}`);
testExtractFunction("extractFunction_VariableDeclaration_Var", `
[#|var x = 1;|]
x;
`);
testExtractFunction("extractFunction_VariableDeclaration_Let_Type", `
[#|let x: number = 1;|]
x;
`);
testExtractFunction("extractFunction_VariableDeclaration_Let_NoType", `
[#|let x = 1;|]
x;
`);
testExtractFunction("extractFunction_VariableDeclaration_Const_Type", `
[#|const x: number = 1;|]
x;
`);
testExtractFunction("extractFunction_VariableDeclaration_Const_NoType", `
[#|const x = 1;|]
x;
`);
testExtractFunction("extractFunction_VariableDeclaration_Multiple1", `
[#|const x = 1, y: string = "a";|]
x; y;
`);
testExtractFunction("extractFunction_VariableDeclaration_Multiple2", `
[#|const x = 1, y = "a";
const z = 3;|]
x; y; z;
`);
testExtractFunction("extractFunction_VariableDeclaration_Multiple3", `
[#|const x = 1, y: string = "a";
let z = 3;|]
x; y; z;
`);
testExtractFunction("extractFunction_VariableDeclaration_ConsumedTwice", `
[#|const x: number = 1;|]
x; x;
`);
testExtractFunction("extractFunction_VariableDeclaration_DeclaredTwice", `
[#|var x = 1;
var x = 2;|]
x;
`);
testExtractFunction("extractFunction_VariableDeclaration_Writes_Var", `
function f() {
let a = 1;
[#|var x = 1;
a++;|]
a; x;
}`);
testExtractFunction("extractFunction_VariableDeclaration_Writes_Let_NoType", `
function f() {
let a = 1;
[#|let x = 1;
a++;|]
a; x;
}`);
testExtractFunction("extractFunction_VariableDeclaration_Writes_Let_Type", `
function f() {
let a = 1;
[#|let x: number = 1;
a++;|]
a; x;
}`);
// We propagate numericLiteralFlags, but it's not consumed by the emitter,
// so everything comes out decimal. It would be nice to improve this.
testExtractFunction("extractFunction_VariableDeclaration_Writes_Let_LiteralType1", `
function f() {
let a = 1;
[#|let x: 0o10 | 10 | 0b10 = 10;
a++;|]
a; x;
}`);
testExtractFunction("extractFunction_VariableDeclaration_Writes_Let_LiteralType2", `
function f() {
let a = 1;
[#|let x: "a" | 'b' = 'a';
a++;|]
a; x;
}`);
// We propagate numericLiteralFlags, but it's not consumed by the emitter,
// so everything comes out decimal. It would be nice to improve this.
testExtractFunction("extractFunction_VariableDeclaration_Writes_Let_LiteralType1", `
function f() {
let a = 1;
[#|let x: 0o10 | 10 | 0b10 = 10;
a++;|]
a; x;
}`);
testExtractFunction("extractFunction_VariableDeclaration_Writes_Let_TypeWithComments", `
function f() {
let a = 1;
[#|let x: /*A*/ "a" /*B*/ | /*C*/ 'b' /*D*/ = 'a';
a++;|]
a; x;
}`);
testExtractFunction("extractFunction_VariableDeclaration_Writes_Const_NoType", `
function f() {
let a = 1;
[#|const x = 1;
a++;|]
a; x;
}`);
testExtractFunction("extractFunction_VariableDeclaration_Writes_Const_Type", `
function f() {
let a = 1;
[#|const x: number = 1;
a++;|]
a; x;
}`);
testExtractFunction("extractFunction_VariableDeclaration_Writes_Mixed1", `
function f() {
let a = 1;
[#|const x = 1;
let y = 2;
a++;|]
a; x; y;
}`);
testExtractFunction("extractFunction_VariableDeclaration_Writes_Mixed2", `
function f() {
let a = 1;
[#|var x = 1;
let y = 2;
a++;|]
a; x; y;
}`);
testExtractFunction("extractFunction_VariableDeclaration_Writes_Mixed3", `
function f() {
let a = 1;
[#|let x: number = 1;
let y = 2;
a++;|]
a; x; y;
}`);
testExtractFunction("extractFunction_VariableDeclaration_Writes_UnionUndefined", `
function f() {
let a = 1;
[#|let x: number | undefined = 1;
let y: undefined | number = 2;
let z: (undefined | number) = 3;
a++;|]
a; x; y; z;
}`);
testExtractFunction("extractFunction_VariableDeclaration_ShorthandProperty", `
function f() {
[#|let x;|]
return { x };
}`);
testExtractFunction("extractFunction_PreserveTrivia", `
// a
var q = /*b*/ //c
/*d*/ [#|1 /*e*/ //f
/*g*/ + /*h*/ //i
/*j*/ 2|] /*k*/ //l
/*m*/; /*n*/ //o`);
});
function testExtractFunction(caption: string, text: string) {
+27
View File
@@ -152,6 +152,16 @@ namespace ts {
}
}
`);
testExtractRange(`
function f(x: number) {
[#|[$|try {
x++;
}
finally {
return 1;
}|]|]
}
`);
});
testExtractRangeFailed("extractRangeFailed1",
@@ -313,6 +323,23 @@ switch (x) {
refactor.extractSymbol.Messages.CannotExtractRange.message
]);
testExtractRangeFailed("extractRangeFailed11",
`
function f(x: number) {
while (true) {
[#|try {
x++;
}
finally {
break;
}|]
}
}
`,
[
refactor.extractSymbol.Messages.CannotExtractRangeContainingConditionalBreakOrContinueStatements.message
]);
testExtractRangeFailed("extract-method-not-for-token-expression-statement", `[#|a|]`, [refactor.extractSymbol.Messages.CannotExtractIdentifier.message]);
});
}
+29 -5
View File
@@ -1,10 +1,34 @@
/// <reference path="../harness.ts" />
describe("Public APIs", () => {
it("for the language service and compiler should be acknowledged when they change", () => {
Harness.Baseline.runBaseline("api/typescript.d.ts", () => Harness.IO.readFile("built/local/typescript.d.ts"));
function verifyApi(fileName: string) {
const builtFile = `built/local/${fileName}`;
const api = `api/${fileName}`;
let fileContent: string;
before(() => {
fileContent = Harness.IO.readFile(builtFile);
});
it("should be acknowledged when they change", () => {
Harness.Baseline.runBaseline(api, () => fileContent);
});
it("should compile", () => {
const testFile: Harness.Compiler.TestFile = {
unitName: builtFile,
content: fileContent
};
const inputFiles = [testFile];
const output = Harness.Compiler.compileFiles(inputFiles, [], /*harnessSettings*/ undefined, /*options*/ {}, /*currentDirectory*/ undefined);
assert(!output.result.errors || !output.result.errors.length, Harness.Compiler.minimalDiagnosticsToString(output.result.errors, /*pretty*/ true));
});
}
describe("for the language service and compiler", () => {
verifyApi("typescript.d.ts");
});
it("for the language server should be acknowledged when they change", () => {
Harness.Baseline.runBaseline("api/tsserverlibrary.d.ts", () => Harness.IO.readFile("built/local/tsserverlibrary.d.ts"));
describe("for the language server", () => {
verifyApi("tsserverlibrary.d.ts");
});
});
});
+57 -2
View File
@@ -16,8 +16,8 @@ namespace ts.server {
directoryExists: () => false,
getDirectories: () => [],
createDirectory: noop,
getExecutingFilePath(): string { return void 0; },
getCurrentDirectory(): string { return void 0; },
getExecutingFilePath(): string { return ""; },
getCurrentDirectory(): string { return ""; },
getEnvironmentVariable(): string { return ""; },
readDirectory() { return []; },
exit: noop,
@@ -386,6 +386,61 @@ namespace ts.server {
});
});
describe("exceptions", () => {
const command = "testhandler";
class TestSession extends Session {
lastSent: protocol.Message;
private exceptionRaisingHandler(_request: protocol.Request): { response?: any, responseRequired: boolean } {
f1();
return;
function f1() {
throw new Error("myMessage");
}
}
constructor() {
super({
host: mockHost,
cancellationToken: nullCancellationToken,
useSingleInferredProject: false,
useInferredProjectPerProjectRoot: false,
typingsInstaller: undefined,
byteLength: Utils.byteLength,
hrtime: process.hrtime,
logger: projectSystem.nullLogger,
canUseEvents: true
});
this.addProtocolHandler(command, this.exceptionRaisingHandler);
}
send(msg: protocol.Message) {
this.lastSent = msg;
}
}
it("raised in a protocol handler generate an event", () => {
const session = new TestSession();
const request = {
command,
seq: 0,
type: "request"
};
session.onMessage(JSON.stringify(request));
const lastSent = session.lastSent as protocol.Response;
expect(lastSent).to.contain({
seq: 0,
type: "response",
command,
success: false
});
expect(lastSent.message).has.string("myMessage").and.has.string("f1");
});
});
describe("how Session is extendable via subclassing", () => {
class TestSession extends Session {
lastSent: protocol.Message;
+12 -68
View File
@@ -5,9 +5,9 @@ namespace ts.projectSystem {
describe("project telemetry", () => {
it("does nothing for inferred project", () => {
const file = makeFile("/a.js");
const et = new EventTracker([file]);
const et = new TestServerEventManager([file]);
et.service.openClientFile(file.path);
assert.equal(et.getEvents().length, 0);
et.hasZeroEvent(ts.server.ProjectInfoTelemetryEvent);
});
it("only sends an event once", () => {
@@ -15,7 +15,7 @@ namespace ts.projectSystem {
const file2 = makeFile("/b.ts");
const tsconfig = makeFile("/a/tsconfig.json", {});
const et = new EventTracker([file, file2, tsconfig]);
const et = new TestServerEventManager([file, file2, tsconfig]);
et.service.openClientFile(file.path);
et.assertProjectInfoTelemetryEvent({}, tsconfig.path);
@@ -25,12 +25,12 @@ namespace ts.projectSystem {
et.service.openClientFile(file2.path);
checkNumberOfProjects(et.service, { inferredProjects: 1 });
assert.equal(et.getEvents().length, 0);
et.hasZeroEvent(ts.server.ProjectInfoTelemetryEvent);
et.service.openClientFile(file.path);
checkNumberOfProjects(et.service, { configuredProjects: 1, inferredProjects: 1 });
assert.equal(et.getEvents().length, 0);
et.hasZeroEvent(ts.server.ProjectInfoTelemetryEvent);
});
it("counts files by extension", () => {
@@ -39,7 +39,7 @@ namespace ts.projectSystem {
const compilerOptions: ts.CompilerOptions = { allowJs: true };
const tsconfig = makeFile("/tsconfig.json", { compilerOptions, include: ["src"] });
const et = new EventTracker([...files, notIncludedFile, tsconfig]);
const et = new TestServerEventManager([...files, notIncludedFile, tsconfig]);
et.service.openClientFile(files[0].path);
et.assertProjectInfoTelemetryEvent({
fileStats: { ts: 2, tsx: 1, js: 1, jsx: 1, dts: 1 },
@@ -50,7 +50,7 @@ namespace ts.projectSystem {
it("works with external project", () => {
const file1 = makeFile("/a.ts");
const et = new EventTracker([file1]);
const et = new TestServerEventManager([file1]);
const compilerOptions: ts.server.protocol.CompilerOptions = { strict: true };
const projectFileName = "/hunter2/foo.csproj";
@@ -148,7 +148,7 @@ namespace ts.projectSystem {
(compilerOptions as any).unknownCompilerOption = "hunter2"; // These are always ignored.
const tsconfig = makeFile("/tsconfig.json", { compilerOptions, files: ["/a.ts"] });
const et = new EventTracker([file, tsconfig]);
const et = new TestServerEventManager([file, tsconfig]);
et.service.openClientFile(file.path);
et.assertProjectInfoTelemetryEvent({
@@ -168,7 +168,7 @@ namespace ts.projectSystem {
compileOnSave: true,
});
const et = new EventTracker([tsconfig, file]);
const et = new TestServerEventManager([tsconfig, file]);
et.service.openClientFile(file.path);
et.assertProjectInfoTelemetryEvent({
extends: true,
@@ -198,7 +198,7 @@ namespace ts.projectSystem {
exclude: [],
},
});
const et = new EventTracker([jsconfig, file]);
const et = new TestServerEventManager([jsconfig, file]);
et.service.openClientFile(file.path);
et.assertProjectInfoTelemetryEvent({
projectId: Harness.mockHash("/jsconfig.json"),
@@ -216,10 +216,10 @@ namespace ts.projectSystem {
it("detects whether language service was disabled", () => {
const file = makeFile("/a.js");
const tsconfig = makeFile("/jsconfig.json", {});
const et = new EventTracker([tsconfig, file]);
const et = new TestServerEventManager([tsconfig, file]);
et.host.getFileSize = () => server.maxProgramSizeForNonTsFiles + 1;
et.service.openClientFile(file.path);
et.getEvent<server.ProjectLanguageServiceStateEvent>(server.ProjectLanguageServiceStateEvent, /*mayBeMore*/ true);
et.getEvent<server.ProjectLanguageServiceStateEvent>(server.ProjectLanguageServiceStateEvent);
et.assertProjectInfoTelemetryEvent({
projectId: Harness.mockHash("/jsconfig.json"),
fileStats: fileStats({ js: 1 }),
@@ -235,63 +235,7 @@ namespace ts.projectSystem {
});
});
class EventTracker {
private events: server.ProjectServiceEvent[] = [];
readonly service: TestProjectService;
readonly host: projectSystem.TestServerHost;
constructor(files: projectSystem.FileOrFolder[]) {
this.host = createServerHost(files);
this.service = createProjectService(this.host, {
eventHandler: event => {
this.events.push(event);
},
});
}
getEvents(): ReadonlyArray<server.ProjectServiceEvent> {
const events = this.events;
this.events = [];
return events;
}
assertProjectInfoTelemetryEvent(partial: Partial<server.ProjectInfoTelemetryEventData>, configFile?: string): void {
assert.deepEqual(this.getEvent<server.ProjectInfoTelemetryEvent>(ts.server.ProjectInfoTelemetryEvent), {
projectId: Harness.mockHash(configFile || "/tsconfig.json"),
fileStats: fileStats({ ts: 1 }),
compilerOptions: {},
extends: false,
files: false,
include: false,
exclude: false,
compileOnSave: false,
typeAcquisition: {
enable: false,
exclude: false,
include: false,
},
configFileName: "tsconfig.json",
projectType: "configured",
languageServiceEnabled: true,
version: ts.version,
...partial,
});
}
getEvent<T extends server.ProjectServiceEvent>(eventName: T["eventName"], mayBeMore = false): T["data"] {
if (mayBeMore) { assert(this.events.length !== 0); }
else { assert.equal(this.events.length, 1); }
const event = this.events.shift();
assert.equal(event.eventName, eventName);
return event.data;
}
}
function makeFile(path: string, content: {} = ""): projectSystem.FileOrFolder {
return { path, content: isString(content) ? "" : JSON.stringify(content) };
}
function fileStats(nonZeroStats: Partial<server.FileStats>): server.FileStats {
return { ts: 0, tsx: 0, dts: 0, js: 0, jsx: 0, ...nonZeroStats };
}
}
+260 -97
View File
@@ -80,6 +80,92 @@ namespace ts.tscWatch {
checkOutputDoesNotContain(host, expectedNonAffectedFiles);
}
function checkOutputErrors(host: WatchedSystem, errors?: ReadonlyArray<Diagnostic>, isInitial?: true, skipWaiting?: true) {
const outputs = host.getOutput();
const expectedOutputCount = (isInitial ? 0 : 1) + (errors ? errors.length : 0) + (skipWaiting ? 0 : 1);
assert.equal(outputs.length, expectedOutputCount, "Outputs = " + outputs.toString());
let index = 0;
if (!isInitial) {
assertWatchDiagnosticAt(host, index, Diagnostics.File_change_detected_Starting_incremental_compilation);
index++;
}
forEach(errors, error => {
assertDiagnosticAt(host, index, error);
index++;
});
if (!skipWaiting) {
assertWatchDiagnosticAt(host, index, Diagnostics.Compilation_complete_Watching_for_file_changes);
}
host.clearOutput();
}
function assertDiagnosticAt(host: WatchedSystem, outputAt: number, diagnostic: Diagnostic) {
const output = host.getOutput()[outputAt];
assert.equal(output, formatDiagnostic(diagnostic, host), "outputs[" + outputAt + "] is " + output);
}
function assertWatchDiagnosticAt(host: WatchedSystem, outputAt: number, diagnosticMessage: DiagnosticMessage) {
const output = host.getOutput()[outputAt];
assert.isTrue(endsWith(output, getWatchDiagnosticWithoutDate(host, diagnosticMessage)), "outputs[" + outputAt + "] is " + output);
}
function getWatchDiagnosticWithoutDate(host: WatchedSystem, diagnosticMessage: DiagnosticMessage) {
return ` - ${flattenDiagnosticMessageText(getLocaleSpecificMessage(diagnosticMessage), host.newLine)}${host.newLine + host.newLine + host.newLine}`;
}
function getDiagnosticOfFileFrom(file: SourceFile, text: string, start: number, length: number, message: DiagnosticMessage): Diagnostic {
return {
file,
start,
length,
messageText: text,
category: message.category,
code: message.code,
};
}
function getDiagnosticWithoutFile(message: DiagnosticMessage, ..._args: (string | number)[]): Diagnostic {
let text = getLocaleSpecificMessage(message);
if (arguments.length > 1) {
text = formatStringFromArgs(text, arguments, 1);
}
return getDiagnosticOfFileFrom(/*file*/ undefined, text, /*start*/ undefined, /*length*/ undefined, message);
}
function getDiagnosticOfFile(file: SourceFile, start: number, length: number, message: DiagnosticMessage, ..._args: (string | number)[]): Diagnostic {
let text = getLocaleSpecificMessage(message);
if (arguments.length > 4) {
text = formatStringFromArgs(text, arguments, 4);
}
return getDiagnosticOfFileFrom(file, text, start, length, message);
}
function getUnknownCompilerOption(program: Program, configFile: FileOrFolder, option: string) {
const quotedOption = `"${option}"`;
return getDiagnosticOfFile(program.getCompilerOptions().configFile, configFile.content.indexOf(quotedOption), quotedOption.length, Diagnostics.Unknown_compiler_option_0, option);
}
function getDiagnosticOfFileFromProgram(program: Program, filePath: string, start: number, length: number, message: DiagnosticMessage, ..._args: (string | number)[]): Diagnostic {
let text = getLocaleSpecificMessage(message);
if (arguments.length > 5) {
text = formatStringFromArgs(text, arguments, 5);
}
return getDiagnosticOfFileFrom(program.getSourceFileByPath(toPath(filePath, program.getCurrentDirectory(), s => s.toLowerCase())),
text, start, length, message);
}
function getDiagnosticModuleNotFoundOfFile(program: Program, file: FileOrFolder, moduleName: string) {
const quotedModuleName = `"${moduleName}"`;
return getDiagnosticOfFileFromProgram(program, file.path, file.content.indexOf(quotedModuleName), quotedModuleName.length, Diagnostics.Cannot_find_module_0, moduleName);
}
describe("tsc-watch program updates", () => {
const commonFile1: FileOrFolder = {
path: "/a/b/commonFile1.ts",
@@ -233,9 +319,10 @@ namespace ts.tscWatch {
});
it("handles the missing files - that were added to program because they were added with ///<ref", () => {
const commonFile2Name = "commonFile2.ts";
const file1: FileOrFolder = {
path: "/a/b/commonFile1.ts",
content: `/// <reference path="commonFile2.ts"/>
content: `/// <reference path="${commonFile2Name}"/>
let x = y`
};
const host = createWatchedSystem([file1, libFile]);
@@ -243,18 +330,16 @@ namespace ts.tscWatch {
checkProgramRootFiles(watch(), [file1.path]);
checkProgramActualFiles(watch(), [file1.path, libFile.path]);
const errors = [
`a/b/commonFile1.ts(1,22): error TS6053: File '${commonFile2.path}' not found.${host.newLine}`,
`a/b/commonFile1.ts(2,29): error TS2304: Cannot find name 'y'.${host.newLine}`
];
checkOutputContains(host, errors);
host.clearOutput();
checkOutputErrors(host, [
getDiagnosticOfFileFromProgram(watch(), file1.path, file1.content.indexOf(commonFile2Name), commonFile2Name.length, Diagnostics.File_0_not_found, commonFile2.path),
getDiagnosticOfFileFromProgram(watch(), file1.path, file1.content.indexOf("y"), 1, Diagnostics.Cannot_find_name_0, "y")
], /*isInitial*/ true);
host.reloadFS([file1, commonFile2, libFile]);
host.runQueuedTimeoutCallbacks();
checkProgramRootFiles(watch(), [file1.path]);
checkProgramActualFiles(watch(), [file1.path, libFile.path, commonFile2.path]);
checkOutputDoesNotContain(host, errors);
checkOutputErrors(host);
});
it("should reflect change in config file", () => {
@@ -578,17 +663,19 @@ namespace ts.tscWatch {
path: "/a/b/tsconfig.json",
content: JSON.stringify({ compilerOptions: {} })
};
const host = createWatchedSystem([file1, file2, config]);
const host = createWatchedSystem([file1, file2, libFile, config]);
const watch = createWatchModeWithConfigFile(config.path, host);
checkProgramActualFiles(watch(), [file1.path, file2.path]);
checkProgramActualFiles(watch(), [file1.path, file2.path, libFile.path]);
checkOutputErrors(host, emptyArray, /*isInitial*/ true);
host.clearOutput();
host.reloadFS([file1, file2]);
host.reloadFS([file1, file2, libFile]);
host.checkTimeoutQueueLengthAndRun(1);
assert.equal(host.exitCode, ExitStatus.DiagnosticsPresent_OutputsSkipped);
checkOutputContains(host, [`error TS6053: File '${config.path}' not found.${host.newLine}`]);
checkOutputErrors(host, [
getDiagnosticWithoutFile(Diagnostics.File_0_not_found, config.path)
], /*isInitial*/ undefined, /*skipWaiting*/ true);
});
it("Proper errors: document is not contained in project", () => {
@@ -687,25 +774,25 @@ namespace ts.tscWatch {
};
const file1 = {
path: "/a/b/file1.ts",
content: "import * as T from './moduleFile'; T.bar();"
content: 'import * as T from "./moduleFile"; T.bar();'
};
const host = createWatchedSystem([moduleFile, file1, libFile]);
createWatchModeWithoutConfigFile([file1.path], host);
const error = "a/b/file1.ts(1,20): error TS2307: Cannot find module \'./moduleFile\'.\n";
checkOutputDoesNotContain(host, [error]);
const watch = createWatchModeWithoutConfigFile([file1.path], host);
checkOutputErrors(host, emptyArray, /*isInitial*/ true);
const moduleFileOldPath = moduleFile.path;
const moduleFileNewPath = "/a/b/moduleFile1.ts";
moduleFile.path = moduleFileNewPath;
host.reloadFS([moduleFile, file1, libFile]);
host.runQueuedTimeoutCallbacks();
checkOutputContains(host, [error]);
checkOutputErrors(host, [
getDiagnosticModuleNotFoundOfFile(watch(), file1, "./moduleFile")
]);
host.clearOutput();
moduleFile.path = moduleFileOldPath;
host.reloadFS([moduleFile, file1, libFile]);
host.runQueuedTimeoutCallbacks();
checkOutputDoesNotContain(host, [error]);
checkOutputErrors(host);
});
it("rename a module file and rename back should restore the states for configured projects", () => {
@@ -715,31 +802,29 @@ namespace ts.tscWatch {
};
const file1 = {
path: "/a/b/file1.ts",
content: "import * as T from './moduleFile'; T.bar();"
content: 'import * as T from "./moduleFile"; T.bar();'
};
const configFile = {
path: "/a/b/tsconfig.json",
content: `{}`
};
const host = createWatchedSystem([moduleFile, file1, configFile, libFile]);
createWatchModeWithConfigFile(configFile.path, host);
const error = "a/b/file1.ts(1,20): error TS2307: Cannot find module \'./moduleFile\'.\n";
checkOutputDoesNotContain(host, [error]);
const watch = createWatchModeWithConfigFile(configFile.path, host);
checkOutputErrors(host, emptyArray, /*isInitial*/ true);
const moduleFileOldPath = moduleFile.path;
const moduleFileNewPath = "/a/b/moduleFile1.ts";
moduleFile.path = moduleFileNewPath;
host.clearOutput();
host.reloadFS([moduleFile, file1, configFile, libFile]);
host.runQueuedTimeoutCallbacks();
checkOutputContains(host, [error]);
checkOutputErrors(host, [
getDiagnosticModuleNotFoundOfFile(watch(), file1, "./moduleFile")
]);
host.clearOutput();
moduleFile.path = moduleFileOldPath;
host.reloadFS([moduleFile, file1, configFile, libFile]);
host.runQueuedTimeoutCallbacks();
checkOutputDoesNotContain(host, [error]);
checkOutputErrors(host);
});
it("types should load from config file path if config exists", () => {
@@ -771,18 +856,18 @@ namespace ts.tscWatch {
};
const file1 = {
path: "/a/b/file1.ts",
content: "import * as T from './moduleFile'; T.bar();"
content: 'import * as T from "./moduleFile"; T.bar();'
};
const host = createWatchedSystem([file1, libFile]);
createWatchModeWithoutConfigFile([file1.path], host);
const watch = createWatchModeWithoutConfigFile([file1.path], host);
const error = `a/b/file1.ts(1,20): error TS2307: Cannot find module \'./moduleFile\'.${host.newLine}`;
checkOutputContains(host, [error]);
host.clearOutput();
checkOutputErrors(host, [
getDiagnosticModuleNotFoundOfFile(watch(), file1, "./moduleFile")
], /*isInitial*/ true);
host.reloadFS([file1, moduleFile, libFile]);
host.runQueuedTimeoutCallbacks();
checkOutputDoesNotContain(host, [error]);
checkOutputErrors(host);
});
it("Configure file diagnostics events are generated when the config file has errors", () => {
@@ -801,14 +886,14 @@ namespace ts.tscWatch {
};
const host = createWatchedSystem([file, configFile, libFile]);
createWatchModeWithConfigFile(configFile.path, host);
checkOutputContains(host, [
`a/b/tsconfig.json(3,29): error TS5023: Unknown compiler option \'foo\'.${host.newLine}`,
`a/b/tsconfig.json(4,29): error TS5023: Unknown compiler option \'allowJS\'.${host.newLine}`
]);
const watch = createWatchModeWithConfigFile(configFile.path, host);
checkOutputErrors(host, [
getUnknownCompilerOption(watch(), configFile, "foo"),
getUnknownCompilerOption(watch(), configFile, "allowJS")
], /*isInitial*/ true);
});
it("Configure file diagnostics events are generated when the config file doesn't have errors", () => {
it("If config file doesnt have errors, they are not reported", () => {
const file = {
path: "/a/b/app.ts",
content: "let x = 10"
@@ -822,13 +907,10 @@ namespace ts.tscWatch {
const host = createWatchedSystem([file, configFile, libFile]);
createWatchModeWithConfigFile(configFile.path, host);
checkOutputDoesNotContain(host, [
`a/b/tsconfig.json(3,29): error TS5023: Unknown compiler option \'foo\'.${host.newLine}`,
`a/b/tsconfig.json(4,29): error TS5023: Unknown compiler option \'allowJS\'.${host.newLine}`
]);
checkOutputErrors(host, emptyArray, /*isInitial*/ true);
});
it("Configure file diagnostics events are generated when the config file changes", () => {
it("Reports errors when the config file changes", () => {
const file = {
path: "/a/b/app.ts",
content: "let x = 10"
@@ -841,9 +923,8 @@ namespace ts.tscWatch {
};
const host = createWatchedSystem([file, configFile, libFile]);
createWatchModeWithConfigFile(configFile.path, host);
const error = `a/b/tsconfig.json(3,25): error TS5023: Unknown compiler option 'haha'.${host.newLine}`;
checkOutputDoesNotContain(host, [error]);
const watch = createWatchModeWithConfigFile(configFile.path, host);
checkOutputErrors(host, emptyArray, /*isInitial*/ true);
configFile.content = `{
"compilerOptions": {
@@ -852,15 +933,16 @@ namespace ts.tscWatch {
}`;
host.reloadFS([file, configFile, libFile]);
host.runQueuedTimeoutCallbacks();
checkOutputContains(host, [error]);
checkOutputErrors(host, [
getUnknownCompilerOption(watch(), configFile, "haha")
]);
host.clearOutput();
configFile.content = `{
"compilerOptions": {}
}`;
host.reloadFS([file, configFile, libFile]);
host.runQueuedTimeoutCallbacks();
checkOutputDoesNotContain(host, [error]);
checkOutputErrors(host);
});
it("non-existing directories listed in config file input array should be tolerated without crashing the server", () => {
@@ -935,29 +1017,28 @@ namespace ts.tscWatch {
}`;
const configFileContentWithComment = configFileContentBeforeComment + configFileContentComment + configFileContentAfterComment;
const configFileContentWithoutCommentLine = configFileContentBeforeComment + configFileContentAfterComment;
const line = 5;
const errors = (line: number) => [
`a/b/tsconfig.json(${line},25): error TS5053: Option \'allowJs\' cannot be specified with option \'declaration\'.\n`,
`a/b/tsconfig.json(${line + 1},25): error TS5053: Option \'allowJs\' cannot be specified with option \'declaration\'.\n`
];
const configFile = {
path: "/a/b/tsconfig.json",
content: configFileContentWithComment
};
const host = createWatchedSystem([file, libFile, configFile]);
createWatchModeWithConfigFile(configFile.path, host);
checkOutputContains(host, errors(line));
checkOutputDoesNotContain(host, errors(line - 2));
host.clearOutput();
const files = [file, libFile, configFile];
const host = createWatchedSystem(files);
const watch = createWatchModeWithConfigFile(configFile.path, host);
const errors = () => [
getDiagnosticOfFile(watch().getCompilerOptions().configFile, configFile.content.indexOf('"allowJs"'), '"allowJs"'.length, Diagnostics.Option_0_cannot_be_specified_with_option_1, "allowJs", "declaration"),
getDiagnosticOfFile(watch().getCompilerOptions().configFile, configFile.content.indexOf('"declaration"'), '"declaration"'.length, Diagnostics.Option_0_cannot_be_specified_with_option_1, "allowJs", "declaration")
];
const intialErrors = errors();
checkOutputErrors(host, intialErrors, /*isInitial*/ true);
configFile.content = configFileContentWithoutCommentLine;
host.reloadFS([file, configFile]);
host.reloadFS(files);
host.runQueuedTimeoutCallbacks();
checkOutputContains(host, errors(line - 2));
checkOutputDoesNotContain(host, errors(line));
const nowErrors = errors();
checkOutputErrors(host, nowErrors);
assert.equal(nowErrors[0].start, intialErrors[0].start - configFileContentComment.length);
assert.equal(nowErrors[1].start, intialErrors[1].start - configFileContentComment.length);
});
});
@@ -1485,23 +1566,20 @@ namespace ts.tscWatch {
path: "/a/d/f0.ts",
content: `import {x} from "f1"`
};
const imported = {
path: "/a/f1.ts",
content: `foo()`
};
const f1IsNotModule = `a/d/f0.ts(1,17): error TS2306: File '${imported.path}' is not a module.\n`;
const cannotFindFoo = `a/f1.ts(1,1): error TS2304: Cannot find name 'foo'.\n`;
const cannotAssignValue = "a/d/f0.ts(2,21): error TS2322: Type '1' is not assignable to type 'string'.\n";
const files = [root, imported, libFile];
const host = createWatchedSystem(files);
createWatchModeWithoutConfigFile([root.path], host, { module: ModuleKind.AMD });
const watch = createWatchModeWithoutConfigFile([root.path], host, { module: ModuleKind.AMD });
const f1IsNotModule = getDiagnosticOfFileFromProgram(watch(), root.path, root.content.indexOf('"f1"'), '"f1"'.length, Diagnostics.File_0_is_not_a_module, imported.path);
const cannotFindFoo = getDiagnosticOfFileFromProgram(watch(), imported.path, imported.content.indexOf("foo"), "foo".length, Diagnostics.Cannot_find_name_0, "foo");
// ensure that imported file was found
checkOutputContains(host, [f1IsNotModule, cannotFindFoo]);
host.clearOutput();
checkOutputErrors(host, [f1IsNotModule, cannotFindFoo], /*isInitial*/ true);
const originalFileExists = host.fileExists;
{
@@ -1517,8 +1595,11 @@ namespace ts.tscWatch {
host.runQueuedTimeoutCallbacks();
// ensure file has correct number of errors after edit
checkOutputContains(host, [f1IsNotModule, cannotAssignValue]);
host.clearOutput();
checkOutputErrors(host, [
f1IsNotModule,
getDiagnosticOfFileFromProgram(watch(), root.path, newContent.indexOf("var x") + "var ".length, "x".length, Diagnostics.Type_0_is_not_assignable_to_type_1, 1, "string"),
cannotFindFoo
]);
}
{
let fileExistsIsCalled = false;
@@ -1534,13 +1615,13 @@ namespace ts.tscWatch {
root.content = `import {x} from "f2"`;
host.reloadFS(files);
// trigger synchronization to make sure that LSHost will try to find 'f2' module on disk
host.runQueuedTimeoutCallbacks();
// trigger synchronization to make sure that LSHost will try to find 'f2' module on disk
host.runQueuedTimeoutCallbacks();
// ensure file has correct number of errors after edit
const cannotFindModuleF2 = `a/d/f0.ts(1,17): error TS2307: Cannot find module 'f2'.\n`;
checkOutputContains(host, [cannotFindModuleF2]);
host.clearOutput();
// ensure file has correct number of errors after edit
checkOutputErrors(host, [
getDiagnosticModuleNotFoundOfFile(watch(), root, "f2")
]);
assert.isTrue(fileExistsIsCalled);
}
@@ -1561,7 +1642,7 @@ namespace ts.tscWatch {
host.reloadFS(files);
host.runQueuedTimeoutCallbacks();
checkOutputContains(host, [f1IsNotModule, cannotFindFoo]);
checkOutputErrors(host, [f1IsNotModule, cannotFindFoo]);
assert.isTrue(fileExistsCalled);
}
});
@@ -1593,12 +1674,12 @@ namespace ts.tscWatch {
return originalFileExists.call(host, fileName);
};
createWatchModeWithoutConfigFile([root.path], host, { module: ModuleKind.AMD });
const watch = createWatchModeWithoutConfigFile([root.path], host, { module: ModuleKind.AMD });
const barNotFound = `a/foo.ts(1,17): error TS2307: Cannot find module 'bar'.\n`;
assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called");
checkOutputContains(host, [barNotFound]);
host.clearOutput();
checkOutputErrors(host, [
getDiagnosticModuleNotFoundOfFile(watch(), root, "bar")
], /*isInitial*/ true);
fileExistsCalledForBar = false;
root.content = `import {y} from "bar"`;
@@ -1606,7 +1687,7 @@ namespace ts.tscWatch {
host.runQueuedTimeoutCallbacks();
assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called.");
checkOutputDoesNotContain(host, [barNotFound]);
checkOutputErrors(host);
});
it("should compile correctly when resolved module goes missing and then comes back (module is not part of the root)", () => {
@@ -1617,7 +1698,7 @@ namespace ts.tscWatch {
const imported = {
path: `/a/bar.d.ts`,
content: `export const y = 1;`
content: `export const y = 1;export const x = 10;`
};
const files = [root, libFile];
@@ -1635,25 +1716,107 @@ namespace ts.tscWatch {
return originalFileExists.call(host, fileName);
};
createWatchModeWithoutConfigFile([root.path], host, { module: ModuleKind.AMD });
const watch = createWatchModeWithoutConfigFile([root.path], host, { module: ModuleKind.AMD });
const barNotFound = `a/foo.ts(1,17): error TS2307: Cannot find module 'bar'.\n`;
assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called");
checkOutputDoesNotContain(host, [barNotFound]);
host.clearOutput();
checkOutputErrors(host, emptyArray, /*isInitial*/ true);
fileExistsCalledForBar = false;
host.reloadFS(files);
host.runQueuedTimeoutCallbacks();
assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called.");
checkOutputContains(host, [barNotFound]);
host.clearOutput();
checkOutputErrors(host, [
getDiagnosticModuleNotFoundOfFile(watch(), root, "bar")
]);
fileExistsCalledForBar = false;
host.reloadFS(filesWithImported);
host.checkTimeoutQueueLengthAndRun(1);
assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called.");
checkOutputDoesNotContain(host, [barNotFound]);
checkOutputErrors(host);
});
it("works when module resolution changes to ambient module", () => {
const root = {
path: "/a/b/foo.ts",
content: `import * as fs from "fs";`
};
const packageJson = {
path: "/a/b/node_modules/@types/node/package.json",
content: `
{
"main": ""
}
`
};
const nodeType = {
path: "/a/b/node_modules/@types/node/index.d.ts",
content: `
declare module "fs" {
export interface Stats {
isFile(): boolean;
}
}`
};
const files = [root, libFile];
const filesWithNodeType = files.concat(packageJson, nodeType);
const host = createWatchedSystem(files, { currentDirectory: "/a/b" });
const watch = createWatchModeWithoutConfigFile([root.path], host, { });
checkOutputErrors(host, [
getDiagnosticModuleNotFoundOfFile(watch(), root, "fs")
], /*isInitial*/ true);
host.reloadFS(filesWithNodeType);
host.runQueuedTimeoutCallbacks();
checkOutputErrors(host);
});
it("works when included file with ambient module changes", () => {
const root = {
path: "/a/b/foo.ts",
content: `
import * as fs from "fs";
import * as u from "url";
`
};
const file = {
path: "/a/b/bar.d.ts",
content: `
declare module "url" {
export interface Url {
href?: string;
}
}
`
};
const fileContentWithFS = `
declare module "fs" {
export interface Stats {
isFile(): boolean;
}
}
`;
const files = [root, file, libFile];
const host = createWatchedSystem(files, { currentDirectory: "/a/b" });
const watch = createWatchModeWithoutConfigFile([root.path, file.path], host, {});
checkOutputErrors(host, [
getDiagnosticModuleNotFoundOfFile(watch(), root, "fs")
], /*isInitial*/ true);
file.content += fileContentWithFS;
host.reloadFS(files);
host.runQueuedTimeoutCallbacks();
checkOutputErrors(host);
});
});
}
+331 -61
View File
@@ -132,16 +132,77 @@ namespace ts.projectSystem {
return map(fileNames, toExternalFile);
}
class TestServerEventManager {
public events: server.ProjectServiceEvent[] = [];
export function fileStats(nonZeroStats: Partial<server.FileStats>): server.FileStats {
return { ts: 0, tsx: 0, dts: 0, js: 0, jsx: 0, ...nonZeroStats };
}
handler: server.ProjectServiceEventHandler = (event: server.ProjectServiceEvent) => {
this.events.push(event);
export class TestServerEventManager {
private events: server.ProjectServiceEvent[] = [];
readonly session: TestSession;
readonly service: server.ProjectService;
readonly host: projectSystem.TestServerHost;
constructor(files: projectSystem.FileOrFolder[]) {
this.host = createServerHost(files);
this.session = createSession(this.host, {
canUseEvents: true,
eventHandler: event => this.events.push(event),
});
this.service = this.session.getProjectService();
}
checkEventCountOfType(eventType: "configFileDiag", expectedCount: number) {
const eventsOfType = filter(this.events, e => e.eventName === eventType);
assert.equal(eventsOfType.length, expectedCount, `The actual event counts of type ${eventType} is ${eventsOfType.length}, while expected ${expectedCount}`);
getEvents(): ReadonlyArray<server.ProjectServiceEvent> {
const events = this.events;
this.events = [];
return events;
}
getEvent<T extends server.ProjectServiceEvent>(eventName: T["eventName"]): T["data"] {
let eventData: T["data"];
filterMutate(this.events, e => {
if (e.eventName === eventName) {
if (eventData !== undefined) {
assert(false, "more than one event found");
}
eventData = e.data;
return false;
}
return true;
});
assert.isDefined(eventData);
return eventData;
}
hasZeroEvent<T extends server.ProjectServiceEvent>(eventName: T["eventName"]) {
this.events.forEach(event => assert.notEqual(event.eventName, eventName));
}
checkSingleConfigFileDiagEvent(configFileName: string, triggerFile: string) {
const eventData = this.getEvent<server.ConfigFileDiagEvent>(server.ConfigFileDiagEvent);
assert.equal(eventData.configFileName, configFileName);
assert.equal(eventData.triggerFile, triggerFile);
}
assertProjectInfoTelemetryEvent(partial: Partial<server.ProjectInfoTelemetryEventData>, configFile?: string): void {
assert.deepEqual(this.getEvent<server.ProjectInfoTelemetryEvent>(ts.server.ProjectInfoTelemetryEvent), {
projectId: Harness.mockHash(configFile || "/tsconfig.json"),
fileStats: fileStats({ ts: 1 }),
compilerOptions: {},
extends: false,
files: false,
include: false,
exclude: false,
compileOnSave: false,
typeAcquisition: {
enable: false,
exclude: false,
include: false,
},
configFileName: "tsconfig.json",
projectType: "configured",
languageServiceEnabled: true,
version: ts.version,
...partial,
});
}
}
@@ -220,11 +281,11 @@ namespace ts.projectSystem {
checkNumberOfProjects(this, count);
}
}
export function createProjectService(host: server.ServerHost, parameters: CreateProjectServiceParameters = {}) {
export function createProjectService(host: server.ServerHost, parameters: CreateProjectServiceParameters = {}, options?: Partial<server.ProjectServiceOptions>) {
const cancellationToken = parameters.cancellationToken || server.nullCancellationToken;
const logger = parameters.logger || nullLogger;
const useSingleInferredProject = parameters.useSingleInferredProject !== undefined ? parameters.useSingleInferredProject : false;
return new TestProjectService(host, logger, cancellationToken, useSingleInferredProject, parameters.typingsInstaller, parameters.eventHandler);
return new TestProjectService(host, logger, cancellationToken, useSingleInferredProject, parameters.typingsInstaller, parameters.eventHandler, options);
}
export function checkNumberOfConfiguredProjects(projectService: server.ProjectService, expected: number) {
@@ -352,8 +413,6 @@ namespace ts.projectSystem {
verifyDiagnostics(actual, []);
}
const typeRootFromTsserverLocation = "/node_modules/@types";
export function getTypeRootsFromLocation(currentDirectory: string) {
currentDirectory = normalizePath(currentDirectory);
const result: string[] = [];
@@ -401,7 +460,7 @@ namespace ts.projectSystem {
const configFiles = flatMap(configFileLocations, location => [location + "tsconfig.json", location + "jsconfig.json"]);
checkWatchedFiles(host, configFiles.concat(libFile.path, moduleFile.path));
checkWatchedDirectories(host, [], /*recursive*/ false);
checkWatchedDirectories(host, ["/a/b/c", typeRootFromTsserverLocation], /*recursive*/ true);
checkWatchedDirectories(host, ["/a/b/c", ...getTypeRootsFromLocation(getDirectoryPath(appFile.path))], /*recursive*/ true);
});
it("can handle tsconfig file name with difference casing", () => {
@@ -463,7 +522,7 @@ namespace ts.projectSystem {
const { configFileName, configFileErrors } = projectService.openClientFile(file1.path);
assert(configFileName, "should find config file");
assert.isTrue(!configFileErrors, `expect no errors in config file, got ${JSON.stringify(configFileErrors)}`);
assert.isTrue(!configFileErrors || configFileErrors.length === 0, `expect no errors in config file, got ${JSON.stringify(configFileErrors)}`);
checkNumberOfInferredProjects(projectService, 0);
checkNumberOfConfiguredProjects(projectService, 1);
@@ -503,7 +562,7 @@ namespace ts.projectSystem {
const { configFileName, configFileErrors } = projectService.openClientFile(file1.path);
assert(configFileName, "should find config file");
assert.isTrue(!configFileErrors, `expect no errors in config file, got ${JSON.stringify(configFileErrors)}`);
assert.isTrue(!configFileErrors || configFileErrors.length === 0, `expect no errors in config file, got ${JSON.stringify(configFileErrors)}`);
checkNumberOfInferredProjects(projectService, 0);
checkNumberOfConfiguredProjects(projectService, 1);
@@ -2399,6 +2458,43 @@ namespace ts.projectSystem {
checkWatchedDirectories(host, watchedRecursiveDirectories, /*recursive*/ true);
});
it("Failed lookup locations are uses parent most node_modules directory", () => {
const file1: FileOrFolder = {
path: "/a/b/src/file1.ts",
content: 'import { classc } from "module1"'
};
const module1: FileOrFolder = {
path: "/a/b/node_modules/module1/index.d.ts",
content: `import { class2 } from "module2";
export classc { method2a(): class2; }`
};
const module2: FileOrFolder = {
path: "/a/b/node_modules/module2/index.d.ts",
content: "export class2 { method2() { return 10; } }"
};
const module3: FileOrFolder = {
path: "/a/b/node_modules/module/node_modules/module3/index.d.ts",
content: "export class3 { method2() { return 10; } }"
};
const configFile: FileOrFolder = {
path: "/a/b/src/tsconfig.json",
content: JSON.stringify({ files: [file1.path] })
};
const files = [file1, module1, module2, module3, configFile, libFile];
const host = createServerHost(files);
const projectService = createProjectService(host);
projectService.openClientFile(file1.path);
checkNumberOfProjects(projectService, { configuredProjects: 1 });
const project = projectService.configuredProjects.get(configFile.path);
assert.isDefined(project);
checkProjectActualFiles(project, [file1.path, libFile.path, module1.path, module2.path, configFile.path]);
checkWatchedFiles(host, [libFile.path, module1.path, module2.path, configFile.path]);
checkWatchedDirectories(host, [], /*recursive*/ false);
const watchedRecursiveDirectories = getTypeRootsFromLocation("/a/b/src");
watchedRecursiveDirectories.push("/a/b/src", "/a/b/node_modules");
checkWatchedDirectories(host, watchedRecursiveDirectories, /*recursive*/ true);
});
});
describe("Proper errors", () => {
@@ -2882,6 +2978,52 @@ namespace ts.projectSystem {
function checkSnapLength(snap: IScriptSnapshot, expectedLength: number) {
assert.equal(snap.getLength(), expectedLength, "Incorrect snapshot size");
}
function verifyOpenFileWorks(useCaseSensitiveFileNames: boolean) {
const file1: FileOrFolder = {
path: "/a/b/src/app.ts",
content: "let x = 10;"
};
const file2: FileOrFolder = {
path: "/a/B/lib/module2.ts",
content: "let z = 10;"
};
const configFile: FileOrFolder = {
path: "/a/b/tsconfig.json",
content: ""
};
const configFile2: FileOrFolder = {
path: "/a/tsconfig.json",
content: ""
};
const host = createServerHost([file1, file2, configFile, configFile2], {
useCaseSensitiveFileNames
});
const service = createProjectService(host);
// Open file1 -> configFile
verifyConfigFileName(file1, "/a", configFile);
verifyConfigFileName(file1, "/a/b", configFile);
verifyConfigFileName(file1, "/a/B", useCaseSensitiveFileNames ? undefined : configFile);
// Open file2 use root "/a/b"
verifyConfigFileName(file2, "/a", useCaseSensitiveFileNames ? configFile2 : configFile);
verifyConfigFileName(file2, "/a/b", useCaseSensitiveFileNames ? undefined : configFile);
verifyConfigFileName(file2, "/a/B", useCaseSensitiveFileNames ? undefined : configFile);
function verifyConfigFileName(file: FileOrFolder, projectRoot: string, expectedConfigFile: FileOrFolder | undefined) {
const { configFileName } = service.openClientFile(file.path, /*fileContent*/ undefined, /*scriptKind*/ undefined, projectRoot);
assert.equal(configFileName, expectedConfigFile && expectedConfigFile.path);
service.closeClientFile(file.path);
}
}
it("works when project root is used with case-sensitive system", () => {
verifyOpenFileWorks(/*useCaseSensitiveFileNames*/ true);
});
it("works when project root is used with case-insensitive system", () => {
verifyOpenFileWorks(/*useCaseSensitiveFileNames*/ false);
});
});
describe("Language service", () => {
@@ -3078,7 +3220,6 @@ namespace ts.projectSystem {
describe("Configure file diagnostics events", () => {
it("are generated when the config file has errors", () => {
const serverEventManager = new TestServerEventManager();
const file = {
path: "/a/b/app.ts",
content: "let x = 10"
@@ -3092,26 +3233,12 @@ namespace ts.projectSystem {
}
}`
};
const host = createServerHost([file, configFile]);
const session = createSession(host, {
canUseEvents: true,
eventHandler: serverEventManager.handler
});
openFilesForSession([file], session);
serverEventManager.checkEventCountOfType("configFileDiag", 1);
for (const event of serverEventManager.events) {
if (event.eventName === "configFileDiag") {
assert.equal(event.data.configFileName, configFile.path);
assert.equal(event.data.triggerFile, file.path);
return;
}
}
const serverEventManager = new TestServerEventManager([file, configFile]);
openFilesForSession([file], serverEventManager.session);
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, file.path);
});
it("are generated when the config file doesn't have errors", () => {
const serverEventManager = new TestServerEventManager();
const file = {
path: "/a/b/app.ts",
content: "let x = 10"
@@ -3122,18 +3249,12 @@ namespace ts.projectSystem {
"compilerOptions": {}
}`
};
const host = createServerHost([file, configFile]);
const session = createSession(host, {
canUseEvents: true,
eventHandler: serverEventManager.handler
});
openFilesForSession([file], session);
serverEventManager.checkEventCountOfType("configFileDiag", 1);
const serverEventManager = new TestServerEventManager([file, configFile]);
openFilesForSession([file], serverEventManager.session);
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, file.path);
});
it("are generated when the config file changes", () => {
const serverEventManager = new TestServerEventManager();
const file = {
path: "/a/b/app.ts",
content: "let x = 10"
@@ -3145,29 +3266,70 @@ namespace ts.projectSystem {
}`
};
const host = createServerHost([file, configFile]);
const session = createSession(host, {
canUseEvents: true,
eventHandler: serverEventManager.handler
});
openFilesForSession([file], session);
serverEventManager.checkEventCountOfType("configFileDiag", 1);
const serverEventManager = new TestServerEventManager([file, configFile]);
openFilesForSession([file], serverEventManager.session);
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, file.path);
configFile.content = `{
"compilerOptions": {
"haha": 123
}
}`;
host.reloadFS([file, configFile]);
host.runQueuedTimeoutCallbacks();
serverEventManager.checkEventCountOfType("configFileDiag", 2);
serverEventManager.host.reloadFS([file, configFile]);
serverEventManager.host.runQueuedTimeoutCallbacks();
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, configFile.path);
configFile.content = `{
"compilerOptions": {}
}`;
host.reloadFS([file, configFile]);
host.runQueuedTimeoutCallbacks();
serverEventManager.checkEventCountOfType("configFileDiag", 3);
serverEventManager.host.reloadFS([file, configFile]);
serverEventManager.host.runQueuedTimeoutCallbacks();
serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, configFile.path);
});
it("are not generated when the config file doesnot include file opened and config file has errors", () => {
const file = {
path: "/a/b/app.ts",
content: "let x = 10"
};
const file2 = {
path: "/a/b/test.ts",
content: "let x = 10"
};
const configFile = {
path: "/a/b/tsconfig.json",
content: `{
"compilerOptions": {
"foo": "bar",
"allowJS": true
},
"files": ["app.ts"]
}`
};
const serverEventManager = new TestServerEventManager([file, file2, libFile, configFile]);
openFilesForSession([file2], serverEventManager.session);
serverEventManager.hasZeroEvent("configFileDiag");
});
it("are not generated when the config file doesnot include file opened and doesnt contain any errors", () => {
const file = {
path: "/a/b/app.ts",
content: "let x = 10"
};
const file2 = {
path: "/a/b/test.ts",
content: "let x = 10"
};
const configFile = {
path: "/a/b/tsconfig.json",
content: `{
"files": ["app.ts"]
}`
};
const serverEventManager = new TestServerEventManager([file, file2, libFile, configFile]);
openFilesForSession([file2], serverEventManager.session);
serverEventManager.hasZeroEvent("configFileDiag");
});
});
@@ -3622,6 +3784,113 @@ namespace ts.projectSystem {
assert.equal(projectService.inferredProjects[1].getCompilationSettings().target, ScriptTarget.ESNext);
assert.equal(projectService.inferredProjects[2].getCompilationSettings().target, ScriptTarget.ES2015);
});
function checkInferredProject(inferredProject: server.InferredProject, actualFiles: FileOrFolder[], target: ScriptTarget) {
checkProjectActualFiles(inferredProject, actualFiles.map(f => f.path));
assert.equal(inferredProject.getCompilationSettings().target, target);
}
function verifyProjectRootWithCaseSensitivity(useCaseSensitiveFileNames: boolean) {
const files: [FileOrFolder, FileOrFolder, FileOrFolder, FileOrFolder] = [
{ path: "/a/file1.ts", content: "let x = 1;" },
{ path: "/A/file2.ts", content: "let y = 2;" },
{ path: "/b/file2.ts", content: "let x = 3;" },
{ path: "/c/file3.ts", content: "let z = 4;" }
];
const host = createServerHost(files, { useCaseSensitiveFileNames });
const projectService = createProjectService(host, { useSingleInferredProject: true, }, { useInferredProjectPerProjectRoot: true });
projectService.setCompilerOptionsForInferredProjects({
allowJs: true,
target: ScriptTarget.ESNext
});
projectService.setCompilerOptionsForInferredProjects({
allowJs: true,
target: ScriptTarget.ES2015
}, "/a");
openClientFiles(["/a", "/a", "/b", undefined]);
verifyInferredProjectsState([
[[files[3]], ScriptTarget.ESNext],
[[files[0], files[1]], ScriptTarget.ES2015],
[[files[2]], ScriptTarget.ESNext]
]);
closeClientFiles();
openClientFiles(["/a", "/A", "/b", undefined]);
if (useCaseSensitiveFileNames) {
verifyInferredProjectsState([
[[files[3]], ScriptTarget.ESNext],
[[files[0]], ScriptTarget.ES2015],
[[files[1]], ScriptTarget.ESNext],
[[files[2]], ScriptTarget.ESNext]
]);
}
else {
verifyInferredProjectsState([
[[files[3]], ScriptTarget.ESNext],
[[files[0], files[1]], ScriptTarget.ES2015],
[[files[2]], ScriptTarget.ESNext]
]);
}
closeClientFiles();
projectService.setCompilerOptionsForInferredProjects({
allowJs: true,
target: ScriptTarget.ES2017
}, "/A");
openClientFiles(["/a", "/a", "/b", undefined]);
verifyInferredProjectsState([
[[files[3]], ScriptTarget.ESNext],
[[files[0], files[1]], useCaseSensitiveFileNames ? ScriptTarget.ES2015 : ScriptTarget.ES2017],
[[files[2]], ScriptTarget.ESNext]
]);
closeClientFiles();
openClientFiles(["/a", "/A", "/b", undefined]);
if (useCaseSensitiveFileNames) {
verifyInferredProjectsState([
[[files[3]], ScriptTarget.ESNext],
[[files[0]], ScriptTarget.ES2015],
[[files[1]], ScriptTarget.ES2017],
[[files[2]], ScriptTarget.ESNext]
]);
}
else {
verifyInferredProjectsState([
[[files[3]], ScriptTarget.ESNext],
[[files[0], files[1]], ScriptTarget.ES2017],
[[files[2]], ScriptTarget.ESNext]
]);
}
closeClientFiles();
function openClientFiles(projectRoots: [string | undefined, string | undefined, string | undefined, string | undefined]) {
files.forEach((file, index) => {
projectService.openClientFile(file.path, file.content, ScriptKind.JS, projectRoots[index]);
});
}
function closeClientFiles() {
files.forEach(file => projectService.closeClientFile(file.path));
}
function verifyInferredProjectsState(expected: [FileOrFolder[], ScriptTarget][]) {
checkNumberOfProjects(projectService, { inferredProjects: expected.length });
projectService.inferredProjects.forEach((p, index) => {
const [actualFiles, target] = expected[index];
checkInferredProject(p, actualFiles, target);
});
}
}
it("inferred projects per project root with case sensitive system", () => {
verifyProjectRootWithCaseSensitivity(/*useCaseSensitiveFileNames*/ true);
});
it("inferred projects per project root with case insensitive system", () => {
verifyProjectRootWithCaseSensitivity(/*useCaseSensitiveFileNames*/ false);
});
});
describe("No overwrite emit error", () => {
@@ -4250,14 +4519,14 @@ namespace ts.projectSystem {
fileName: "/a.ts",
textChanges: [
{
start: { line: 2, offset: 1 },
end: { line: 3, offset: 1 },
newText: " newFunction();\n",
start: { line: 2, offset: 3 },
end: { line: 2, offset: 5 },
newText: "newFunction();",
},
{
start: { line: 3, offset: 2 },
end: { line: 3, offset: 2 },
newText: "\nfunction newFunction() {\n 1;\n}\n",
newText: "\n\nfunction newFunction() {\n 1;\n}\n",
},
]
}
@@ -4331,7 +4600,7 @@ namespace ts.projectSystem {
function verifyCalledOnEachEntry(callback: CalledMaps, expectedKeys: Map<number>) {
const calledMap = calledMaps[callback];
assert.equal(calledMap.size, expectedKeys.size, `${callback}: incorrect size of map: Actual keys: ${arrayFrom(calledMap.keys())} Expected: ${arrayFrom(expectedKeys.keys())}`);
ts.TestFSWithWatch.verifyMapSize(callback, calledMap, arrayFrom(expectedKeys.keys()));
expectedKeys.forEach((called, name) => {
assert.isTrue(calledMap.has(name), `${callback} is expected to contain ${name}, actual keys: ${arrayFrom(calledMap.keys())}`);
assert.equal(calledMap.get(name).length, called, `${callback} is expected to be called ${called} times with ${name}. Actual entry: ${calledMap.get(name)}`);
@@ -4413,6 +4682,7 @@ namespace ts.projectSystem {
}
const f2Lookups = getLocationsForModuleLookup("f2");
callsTrackingHost.verifyCalledOnEachEntryNTimes(CalledMapsWithSingleArg.fileExists, f2Lookups, 1);
const typeRootLocations = getTypeRootsFromLocation(getDirectoryPath(root.path));
const f2DirLookups = getLocationsForDirectoryLookup();
callsTrackingHost.verifyCalledOnEachEntry(CalledMapsWithSingleArg.directoryExists, f2DirLookups);
callsTrackingHost.verifyNoCall(CalledMapsWithSingleArg.getDirectories);
@@ -4423,7 +4693,7 @@ namespace ts.projectSystem {
verifyImportedDiagnostics();
const f1Lookups = f2Lookups.map(s => s.replace("f2", "f1"));
f1Lookups.length = f1Lookups.indexOf(imported.path) + 1;
const f1DirLookups = ["/c/d", "/c", typeRootFromTsserverLocation];
const f1DirLookups = ["/c/d", "/c", ...typeRootLocations];
vertifyF1Lookups();
// setting compiler options discards module resolution cache
@@ -4475,7 +4745,7 @@ namespace ts.projectSystem {
function getLocationsForDirectoryLookup() {
const result = createMap<number>();
// Type root
result.set(typeRootFromTsserverLocation, 1);
typeRootLocations.forEach(location => result.set(location, 1));
forEachAncestorDirectory(getDirectoryPath(root.path), ancestor => {
// To resolve modules
result.set(ancestor, 2);
+14 -6
View File
@@ -95,7 +95,7 @@ namespace ts.TestFSWithWatch {
}
}
function getDiffInKeys(map: Map<any>, expectedKeys: ReadonlyArray<string>) {
function getDiffInKeys<T>(map: Map<T>, expectedKeys: ReadonlyArray<string>) {
if (map.size === expectedKeys.length) {
return "";
}
@@ -122,8 +122,12 @@ namespace ts.TestFSWithWatch {
return `\n\nNotInActual: ${notInActual}\nDuplicates: ${duplicates}\nInActualButNotInExpected: ${inActualNotExpected}`;
}
function checkMapKeys(caption: string, map: Map<any>, expectedKeys: ReadonlyArray<string>) {
export function verifyMapSize(caption: string, map: Map<any>, expectedKeys: ReadonlyArray<string>) {
assert.equal(map.size, expectedKeys.length, `${caption}: incorrect size of map: Actual keys: ${arrayFrom(map.keys())} Expected: ${expectedKeys}${getDiffInKeys(map, expectedKeys)}`);
}
function checkMapKeys(caption: string, map: Map<any>, expectedKeys: ReadonlyArray<string>) {
verifyMapSize(caption, map, expectedKeys);
for (const name of expectedKeys) {
assert.isTrue(map.has(name), `${caption} is expected to contain ${name}, actual keys: ${arrayFrom(map.keys())}`);
}
@@ -212,13 +216,13 @@ namespace ts.TestFSWithWatch {
directoryName: string;
}
export class TestServerHost implements server.ServerHost {
export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost {
args: string[] = [];
private readonly output: string[] = [];
private fs: Map<FSEntry> = createMap<FSEntry>();
private getCanonicalFileName: (s: string) => string;
getCanonicalFileName: (s: string) => string;
private toPath: (f: string) => Path;
private timeoutCallbacks = new Callbacks();
private immediateCallbacks = new Callbacks();
@@ -234,6 +238,10 @@ namespace ts.TestFSWithWatch {
this.reloadFS(fileOrFolderList);
}
getNewLine() {
return this.newLine;
}
toNormalizedAbsolutePath(s: string) {
return getNormalizedAbsolutePath(s, this.currentDirectory);
}
@@ -548,7 +556,7 @@ namespace ts.TestFSWithWatch {
const folder = this.toFolder(directoryName);
// base folder has to be present
const base = getDirectoryPath(folder.fullPath);
const base = getDirectoryPath(folder.path);
const baseFolder = this.fs.get(base) as Folder;
Debug.assert(isFolder(baseFolder));
@@ -560,7 +568,7 @@ namespace ts.TestFSWithWatch {
const file = this.toFile({ path, content });
// base folder has to be present
const base = getDirectoryPath(file.fullPath);
const base = getDirectoryPath(file.path);
const folder = this.fs.get(base) as Folder;
Debug.assert(isFolder(folder));
+2
View File
@@ -10,6 +10,7 @@ interface Array<T> {
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
find<S extends T>(predicate: (this: void, value: T, index: number, obj: T[]) => value is S, thisArg?: any): S | undefined;
find(predicate: (value: T, index: number, obj: T[]) => boolean, thisArg?: any): T | undefined;
/**
@@ -350,6 +351,7 @@ interface ReadonlyArray<T> {
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
find<S extends T>(predicate: (this: void, value: T, index: number, obj: ReadonlyArray<T>) => value is S, thisArg?: any): S | undefined;
find(predicate: (value: T, index: number, obj: ReadonlyArray<T>) => boolean, thisArg?: any): T | undefined;
/**
+2 -2
View File
@@ -3,7 +3,7 @@ interface ObjectConstructor {
* Returns an array of values of the enumerable properties of an object
* @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object.
*/
values<T>(o: { [s: string]: T }): T[];
values<T>(o: { [s: string]: T } | { [n: number]: T }): T[];
/**
* Returns an array of values of the enumerable properties of an object
@@ -15,7 +15,7 @@ interface ObjectConstructor {
* Returns an array of key/values of the enumerable properties of an object
* @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object.
*/
entries<T>(o: { [s: string]: T }): [string, T][];
entries<T>(o: { [s: string]: T } | { [n: number]: T }): [string, T][];
/**
* Returns an array of key/values of the enumerable properties of an object
+44 -22
View File
@@ -1050,7 +1050,8 @@ interface ReadonlyArray<T> {
* @param callbackfn A function that accepts up to four arguments. The reduce method calls the callbackfn function one time for each element in the array.
* @param initialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value.
*/
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: ReadonlyArray<T>) => T, initialValue?: T): T;
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: ReadonlyArray<T>) => T): T;
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: ReadonlyArray<T>) => T, initialValue: T): T;
/**
* Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function.
* @param callbackfn A function that accepts up to four arguments. The reduce method calls the callbackfn function one time for each element in the array.
@@ -1062,7 +1063,8 @@ interface ReadonlyArray<T> {
* @param callbackfn A function that accepts up to four arguments. The reduceRight method calls the callbackfn function one time for each element in the array.
* @param initialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value.
*/
reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: ReadonlyArray<T>) => T, initialValue?: T): T;
reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: ReadonlyArray<T>) => T): T;
reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: ReadonlyArray<T>) => T, initialValue: T): T;
/**
* Calls the specified callback function for all the elements in an array, in descending order. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function.
* @param callbackfn A function that accepts up to four arguments. The reduceRight method calls the callbackfn function one time for each element in the array.
@@ -1200,7 +1202,8 @@ interface Array<T> {
* @param callbackfn A function that accepts up to four arguments. The reduce method calls the callbackfn function one time for each element in the array.
* @param initialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value.
*/
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue?: T): T;
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T;
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T;
/**
* Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function.
* @param callbackfn A function that accepts up to four arguments. The reduce method calls the callbackfn function one time for each element in the array.
@@ -1212,7 +1215,8 @@ interface Array<T> {
* @param callbackfn A function that accepts up to four arguments. The reduceRight method calls the callbackfn function one time for each element in the array.
* @param initialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value.
*/
reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue?: T): T;
reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T;
reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T;
/**
* Calls the specified callback function for all the elements in an array, in descending order. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function.
* @param callbackfn A function that accepts up to four arguments. The reduceRight method calls the callbackfn function one time for each element in the array.
@@ -1647,7 +1651,8 @@ interface Int8Array {
* the accumulation. The first call to the callbackfn function provides this value as an argument
* instead of an array value.
*/
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int8Array) => number, initialValue?: number): number;
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int8Array) => number): number;
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int8Array) => number, initialValue: number): number;
/**
* Calls the specified callback function for all the elements in an array. The return value of
@@ -1671,7 +1676,8 @@ interface Int8Array {
* the accumulation. The first call to the callbackfn function provides this value as an
* argument instead of an array value.
*/
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int8Array) => number, initialValue?: number): number;
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int8Array) => number): number;
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int8Array) => number, initialValue: number): number;
/**
* Calls the specified callback function for all the elements in an array, in descending order.
@@ -1914,7 +1920,8 @@ interface Uint8Array {
* the accumulation. The first call to the callbackfn function provides this value as an argument
* instead of an array value.
*/
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint8Array) => number, initialValue?: number): number;
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint8Array) => number): number;
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint8Array) => number, initialValue: number): number;
/**
* Calls the specified callback function for all the elements in an array. The return value of
@@ -1938,7 +1945,8 @@ interface Uint8Array {
* the accumulation. The first call to the callbackfn function provides this value as an
* argument instead of an array value.
*/
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint8Array) => number, initialValue?: number): number;
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint8Array) => number): number;
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint8Array) => number, initialValue: number): number;
/**
* Calls the specified callback function for all the elements in an array, in descending order.
@@ -2181,7 +2189,8 @@ interface Uint8ClampedArray {
* the accumulation. The first call to the callbackfn function provides this value as an argument
* instead of an array value.
*/
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint8ClampedArray) => number, initialValue?: number): number;
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint8ClampedArray) => number): number;
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint8ClampedArray) => number, initialValue: number): number;
/**
* Calls the specified callback function for all the elements in an array. The return value of
@@ -2205,7 +2214,8 @@ interface Uint8ClampedArray {
* the accumulation. The first call to the callbackfn function provides this value as an
* argument instead of an array value.
*/
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint8ClampedArray) => number, initialValue?: number): number;
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint8ClampedArray) => number): number;
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint8ClampedArray) => number, initialValue: number): number;
/**
* Calls the specified callback function for all the elements in an array, in descending order.
@@ -2446,7 +2456,8 @@ interface Int16Array {
* the accumulation. The first call to the callbackfn function provides this value as an argument
* instead of an array value.
*/
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int16Array) => number, initialValue?: number): number;
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int16Array) => number): number;
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int16Array) => number, initialValue: number): number;
/**
* Calls the specified callback function for all the elements in an array. The return value of
@@ -2470,7 +2481,8 @@ interface Int16Array {
* the accumulation. The first call to the callbackfn function provides this value as an
* argument instead of an array value.
*/
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int16Array) => number, initialValue?: number): number;
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int16Array) => number): number;
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int16Array) => number, initialValue: number): number;
/**
* Calls the specified callback function for all the elements in an array, in descending order.
@@ -2714,7 +2726,8 @@ interface Uint16Array {
* the accumulation. The first call to the callbackfn function provides this value as an argument
* instead of an array value.
*/
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint16Array) => number, initialValue?: number): number;
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint16Array) => number): number;
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint16Array) => number, initialValue: number): number;
/**
* Calls the specified callback function for all the elements in an array. The return value of
@@ -2738,7 +2751,8 @@ interface Uint16Array {
* the accumulation. The first call to the callbackfn function provides this value as an
* argument instead of an array value.
*/
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint16Array) => number, initialValue?: number): number;
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint16Array) => number): number;
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint16Array) => number, initialValue: number): number;
/**
* Calls the specified callback function for all the elements in an array, in descending order.
@@ -2981,7 +2995,8 @@ interface Int32Array {
* the accumulation. The first call to the callbackfn function provides this value as an argument
* instead of an array value.
*/
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int32Array) => number, initialValue?: number): number;
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int32Array) => number): number;
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int32Array) => number, initialValue: number): number;
/**
* Calls the specified callback function for all the elements in an array. The return value of
@@ -3005,7 +3020,8 @@ interface Int32Array {
* the accumulation. The first call to the callbackfn function provides this value as an
* argument instead of an array value.
*/
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int32Array) => number, initialValue?: number): number;
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int32Array) => number): number;
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Int32Array) => number, initialValue: number): number;
/**
* Calls the specified callback function for all the elements in an array, in descending order.
@@ -3247,7 +3263,8 @@ interface Uint32Array {
* the accumulation. The first call to the callbackfn function provides this value as an argument
* instead of an array value.
*/
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint32Array) => number, initialValue?: number): number;
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint32Array) => number): number;
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint32Array) => number, initialValue: number): number;
/**
* Calls the specified callback function for all the elements in an array. The return value of
@@ -3271,7 +3288,8 @@ interface Uint32Array {
* the accumulation. The first call to the callbackfn function provides this value as an
* argument instead of an array value.
*/
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint32Array) => number, initialValue?: number): number;
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint32Array) => number): number;
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Uint32Array) => number, initialValue: number): number;
/**
* Calls the specified callback function for all the elements in an array, in descending order.
@@ -3514,7 +3532,8 @@ interface Float32Array {
* the accumulation. The first call to the callbackfn function provides this value as an argument
* instead of an array value.
*/
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Float32Array) => number, initialValue?: number): number;
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Float32Array) => number): number;
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Float32Array) => number, initialValue: number): number;
/**
* Calls the specified callback function for all the elements in an array. The return value of
@@ -3538,7 +3557,8 @@ interface Float32Array {
* the accumulation. The first call to the callbackfn function provides this value as an
* argument instead of an array value.
*/
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Float32Array) => number, initialValue?: number): number;
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Float32Array) => number): number;
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Float32Array) => number, initialValue: number): number;
/**
* Calls the specified callback function for all the elements in an array, in descending order.
@@ -3782,7 +3802,8 @@ interface Float64Array {
* the accumulation. The first call to the callbackfn function provides this value as an argument
* instead of an array value.
*/
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Float64Array) => number, initialValue?: number): number;
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Float64Array) => number): number;
reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Float64Array) => number, initialValue: number): number;
/**
* Calls the specified callback function for all the elements in an array. The return value of
@@ -3806,7 +3827,8 @@ interface Float64Array {
* the accumulation. The first call to the callbackfn function provides this value as an
* argument instead of an array value.
*/
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Float64Array) => number, initialValue?: number): number;
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Float64Array) => number): number;
reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: Float64Array) => number, initialValue: number): number;
/**
* Calls the specified callback function for all the elements in an array, in descending order.
+49 -21
View File
@@ -403,7 +403,7 @@ namespace ts.server {
this.globalPlugins = opts.globalPlugins || emptyArray;
this.pluginProbeLocations = opts.pluginProbeLocations || emptyArray;
this.allowLocalPluginLoads = !!opts.allowLocalPluginLoads;
this.typesMapLocation = (opts.typesMapLocation === undefined) ? combinePaths(this.host.getExecutingFilePath(), "../typesMap.json") : opts.typesMapLocation;
this.typesMapLocation = (opts.typesMapLocation === undefined) ? combinePaths(this.getExecutingFilePath(), "../typesMap.json") : opts.typesMapLocation;
Debug.assert(!!this.host.createHash, "'ServerHost.createHash' is required for ProjectService");
@@ -431,6 +431,11 @@ namespace ts.server {
this.watchFilePath = (host, file, cb, path, watchType, project) => ts.addFilePathWatcherWithLogging(host, file, cb, path, this.createWatcherLog(watchType, project));
this.watchDirectory = (host, dir, cb, flags, watchType, project) => ts.addDirectoryWatcherWithLogging(host, dir, cb, flags, this.createWatcherLog(watchType, project));
}
else if (this.logger.loggingEnabled()) {
this.watchFile = (host, file, cb, watchType, project) => ts.addFileWatcherWithOnlyTriggerLogging(host, file, cb, this.createWatcherLog(watchType, project));
this.watchFilePath = (host, file, cb, path, watchType, project) => ts.addFilePathWatcherWithOnlyTriggerLogging(host, file, cb, path, this.createWatcherLog(watchType, project));
this.watchDirectory = (host, dir, cb, flags, watchType, project) => ts.addDirectoryWatcherWithOnlyTriggerLogging(host, dir, cb, flags, this.createWatcherLog(watchType, project));
}
else {
this.watchFile = ts.addFileWatcher;
this.watchFilePath = ts.addFilePathWatcher;
@@ -447,6 +452,16 @@ namespace ts.server {
return toPath(fileName, this.currentDirectory, this.toCanonicalFileName);
}
/*@internal*/
getExecutingFilePath() {
return this.getNormalizedAbsolutePath(this.host.getExecutingFilePath());
}
/*@internal*/
getNormalizedAbsolutePath(fileName: string) {
return getNormalizedAbsolutePath(fileName, this.host.getCurrentDirectory());
}
/* @internal */
getChangedFiles_TestOnly() {
return this.changedFiles;
@@ -575,9 +590,9 @@ namespace ts.server {
// always set 'allowNonTsExtensions' for inferred projects since user cannot configure it from the outside
// previously we did not expose a way for user to change these settings and this option was enabled by default
compilerOptions.allowNonTsExtensions = true;
if (projectRootPath) {
this.compilerOptionsForInferredProjectsPerProjectRoot.set(projectRootPath, compilerOptions);
const canonicalProjectRootPath = projectRootPath && this.toCanonicalFileName(projectRootPath);
if (canonicalProjectRootPath) {
this.compilerOptionsForInferredProjectsPerProjectRoot.set(canonicalProjectRootPath, compilerOptions);
}
else {
this.compilerOptionsForInferredProjects = compilerOptions;
@@ -593,9 +608,9 @@ namespace ts.server {
// root path
// - Inferred projects with a projectRootPath, if the new options apply to that
// project root path.
if (projectRootPath ?
project.projectRootPath === projectRootPath :
!project.projectRootPath || !this.compilerOptionsForInferredProjectsPerProjectRoot.has(project.projectRootPath)) {
if (canonicalProjectRootPath ?
project.projectRootPath === canonicalProjectRootPath :
!project.projectRootPath || !this.compilerOptionsForInferredProjectsPerProjectRoot.has(project.projectRootPath)) {
project.setCompilerOptions(compilerOptions);
project.compileOnSaveEnabled = compilerOptions.compileOnSave;
project.markAsDirty();
@@ -827,6 +842,9 @@ namespace ts.server {
this.logger.info(`remove project: ${project.getRootFiles().toString()}`);
project.close();
if (Debug.shouldAssert(AssertionLevel.Normal)) {
this.filenameToScriptInfo.forEach(info => Debug.assert(!info.isAttached(project)));
}
// Remove the project from pending project updates
this.pendingProjectUpdates.delete(project.getProjectName());
@@ -1200,7 +1218,7 @@ namespace ts.server {
projectRootPath?: NormalizedPath) {
let searchPath = asNormalizedPath(getDirectoryPath(info.fileName));
while (!projectRootPath || searchPath.indexOf(projectRootPath) >= 0) {
while (!projectRootPath || containsPath(projectRootPath, searchPath, this.currentDirectory, !this.host.useCaseSensitiveFileNames)) {
const canonicalSearchPath = normalizedPathToPath(searchPath, this.currentDirectory, this.toCanonicalFileName);
const tsconfigFileName = asNormalizedPath(combinePaths(searchPath, "tsconfig.json"));
let result = action(tsconfigFileName, combinePaths(canonicalSearchPath, "tsconfig.json"));
@@ -1561,14 +1579,17 @@ namespace ts.server {
project.watchWildcards(projectOptions.wildcardDirectories);
}
this.updateNonInferredProject(project, projectOptions.files, fileNamePropertyReader, projectOptions.compilerOptions, projectOptions.typeAcquisition, projectOptions.compileOnSave);
this.sendConfigFileDiagEvent(project, configFileName);
}
private sendConfigFileDiagEvent(project: ConfiguredProject, triggerFile: NormalizedPath) {
if (!this.eventHandler) {
return;
}
this.eventHandler(<ConfigFileDiagEvent>{
eventName: ConfigFileDiagEvent,
data: { configFileName, diagnostics: project.getGlobalProjectErrors() || [], triggerFile: configFileName }
data: { configFileName: project.getConfigFilePath(), diagnostics: project.getAllProjectErrors(), triggerFile }
});
}
@@ -1578,9 +1599,10 @@ namespace ts.server {
}
if (projectRootPath) {
const canonicalProjectRootPath = this.toCanonicalFileName(projectRootPath);
// if we have an explicit project root path, find (or create) the matching inferred project.
for (const project of this.inferredProjects) {
if (project.projectRootPath === projectRootPath) {
if (project.projectRootPath === canonicalProjectRootPath) {
return project;
}
}
@@ -1621,12 +1643,13 @@ namespace ts.server {
return this.inferredProjects[0];
}
return this.createInferredProject(/*rootDirectoryForResolution*/ undefined, /*isSingleInferredProject*/ true);
// Single inferred project does not have a project root and hence no current directory
return this.createInferredProject(/*currentDirectory*/ undefined, /*isSingleInferredProject*/ true);
}
private createInferredProject(rootDirectoryForResolution: string | undefined, isSingleInferredProject?: boolean, projectRootPath?: string): InferredProject {
private createInferredProject(currentDirectory: string | undefined, isSingleInferredProject?: boolean, projectRootPath?: string): InferredProject {
const compilerOptions = projectRootPath && this.compilerOptionsForInferredProjectsPerProjectRoot.get(projectRootPath) || this.compilerOptionsForInferredProjects;
const project = new InferredProject(this, this.documentRegistry, compilerOptions, projectRootPath, rootDirectoryForResolution);
const project = new InferredProject(this, this.documentRegistry, compilerOptions, projectRootPath, currentDirectory);
if (isSingleInferredProject) {
this.inferredProjects.unshift(project);
}
@@ -1888,6 +1911,7 @@ 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, fileContent, scriptKind, hasMixedContent);
@@ -1898,14 +1922,8 @@ namespace ts.server {
project = this.findConfiguredProjectByProjectName(configFileName);
if (!project) {
project = this.createConfiguredProject(configFileName);
// even if opening config file was successful, it could still
// contain errors that were tolerated.
const errors = project.getGlobalProjectErrors();
if (errors && errors.length > 0) {
// set configFileErrors only when the errors array is non-empty
configFileErrors = errors;
}
// Send the event only if the project got created as part of this open request
sendConfigFileDiagEvent = true;
}
}
}
@@ -1919,10 +1937,19 @@ namespace ts.server {
// 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);
}
this.addToListOfOpenFiles(info);
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
@@ -1938,6 +1965,7 @@ namespace ts.server {
// the file from that old project is reopened because of opening file from here.
this.deleteOrphanScriptInfoNotInAnyProject();
this.printProjects();
return { configFileName, configFileErrors };
}
+50 -54
View File
@@ -195,6 +195,9 @@ namespace ts.server {
return result.module;
}
/*@internal*/
readonly currentDirectory: string;
/*@internal*/
constructor(
/*@internal*/readonly projectName: string,
@@ -206,7 +209,8 @@ namespace ts.server {
private compilerOptions: CompilerOptions,
public compileOnSaveEnabled: boolean,
/*@internal*/public directoryStructureHost: DirectoryStructureHost,
rootDirectoryForResolution: string | undefined) {
currentDirectory: string | undefined) {
this.currentDirectory = this.projectService.getNormalizedAbsolutePath(currentDirectory || "");
this.cancellationToken = new ThrottledCancellationToken(this.projectService.cancellationToken, this.projectService.throttleWaitMilliseconds);
if (!this.compilerOptions) {
@@ -229,8 +233,9 @@ namespace ts.server {
this.realpath = path => host.realpath(path);
}
// Use the current directory as resolution root only if the project created using current directory string
this.resolutionCache = createResolutionCache(this, currentDirectory && this.currentDirectory);
this.languageService = createLanguageService(this, this.documentRegistry);
this.resolutionCache = createResolutionCache(this, rootDirectoryForResolution);
if (!languageServiceEnabled) {
this.disableLanguageService();
}
@@ -296,16 +301,16 @@ namespace ts.server {
}
}
getCancellationToken() {
getCancellationToken(): HostCancellationToken {
return this.cancellationToken;
}
getCurrentDirectory(): string {
return this.directoryStructureHost.getCurrentDirectory();
return this.currentDirectory;
}
getDefaultLibFileName() {
const nodeModuleBinDir = getDirectoryPath(normalizePath(this.projectService.host.getExecutingFilePath()));
const nodeModuleBinDir = getDirectoryPath(normalizePath(this.projectService.getExecutingFilePath()));
return combinePaths(nodeModuleBinDir, getDefaultLibFileName(this.compilerOptions));
}
@@ -448,9 +453,8 @@ namespace ts.server {
this.ensureBuilder();
const { emitSkipped, outputFiles } = this.builder.emitFile(this.program, scriptInfo.path);
if (!emitSkipped) {
const projectRootPath = this.getProjectRootPath();
for (const outputFile of outputFiles) {
const outputFileAbsoluteFileName = getNormalizedAbsolutePath(outputFile.name, projectRootPath ? projectRootPath : getDirectoryPath(scriptInfo.fileName));
const outputFileAbsoluteFileName = getNormalizedAbsolutePath(outputFile.name, this.currentDirectory);
writeFile(outputFileAbsoluteFileName, outputFile.text, outputFile.writeByteOrderMark);
}
}
@@ -479,7 +483,6 @@ namespace ts.server {
getProjectName() {
return this.projectName;
}
abstract getProjectRootPath(): string | undefined;
abstract getTypeAcquisition(): TypeAcquisition;
getExternalFiles(): SortedReadonlyArray<string> {
@@ -495,25 +498,23 @@ namespace ts.server {
close() {
if (this.program) {
// if we have a program - release all files that are enlisted in program
// if we have a program - release all files that are enlisted in program but arent root
// The releasing of the roots happens later
// The project could have pending update remaining and hence the info could be in the files but not in program graph
for (const f of this.program.getSourceFiles()) {
const info = this.projectService.getScriptInfo(f.fileName);
// We might not find the script info in case its not associated with the project any more
// and project graph was not updated (eg delayed update graph in case of files changed/deleted on the disk)
if (info) {
info.detachFromProject(this);
}
this.detachScriptInfoIfNotRoot(f.fileName);
}
}
if (!this.program || !this.languageServiceEnabled) {
// release all root files either if there is no program or language service is disabled.
// in the latter case set of root files can be larger than the set of files in program.
for (const root of this.rootFiles) {
root.detachFromProject(this);
}
// Release external files
forEach(this.externalFiles, externalFile => this.detachScriptInfoIfNotRoot(externalFile));
// Always remove root files from the project
for (const root of this.rootFiles) {
root.detachFromProject(this);
}
this.rootFiles = undefined;
this.rootFilesMap = undefined;
this.externalFiles = undefined;
this.program = undefined;
this.builder = undefined;
this.resolutionCache.clear();
@@ -532,6 +533,15 @@ namespace ts.server {
this.languageService = undefined;
}
private detachScriptInfoIfNotRoot(uncheckedFilename: string) {
const info = this.projectService.getScriptInfo(uncheckedFilename);
// We might not find the script info in case its not associated with the project any more
// and project graph was not updated (eg delayed update graph in case of files changed/deleted on the disk)
if (info && !this.isRoot(info)) {
info.detachFromProject(this);
}
}
isClosed() {
return this.rootFiles === undefined;
}
@@ -561,7 +571,7 @@ namespace ts.server {
return map(this.program.getSourceFiles(), sourceFile => {
const scriptInfo = this.projectService.getScriptInfoForPath(sourceFile.path);
if (!scriptInfo) {
Debug.fail(`scriptInfo for a file '${sourceFile.fileName}' is missing.`);
Debug.fail(`scriptInfo for a file '${sourceFile.fileName}' Path: '${sourceFile.path}' is missing.`);
}
return scriptInfo;
});
@@ -732,7 +742,6 @@ namespace ts.server {
*/
updateGraph(): boolean {
this.resolutionCache.startRecordingFilesWithChangedResolutions();
this.hasInvalidatedResolution = this.resolutionCache.createHasInvalidatedResolution();
let hasChanges = this.updateGraphWorker();
@@ -792,9 +801,10 @@ namespace ts.server {
private updateGraphWorker() {
const oldProgram = this.program;
Debug.assert(!this.isClosed(), "Called update graph worker of closed project");
this.writeLog(`Starting updateGraphWorker: Project: ${this.getProjectName()}`);
const start = timestamp();
this.hasInvalidatedResolution = this.resolutionCache.createHasInvalidatedResolution();
this.resolutionCache.startCachingPerDirectoryResolution();
this.program = this.languageService.getProgram();
this.resolutionCache.finishCachingPerDirectoryResolution();
@@ -1037,13 +1047,16 @@ namespace ts.server {
super.setCompilerOptions(newOptions);
}
/** this is canonical project root path */
readonly projectRootPath: string | undefined;
/*@internal*/
constructor(
projectService: ProjectService,
documentRegistry: DocumentRegistry,
compilerOptions: CompilerOptions,
public readonly projectRootPath: string | undefined,
rootDirectoryForResolution: string | undefined) {
projectRootPath: string | undefined,
currentDirectory: string | undefined) {
super(InferredProject.newName(),
ProjectKind.Inferred,
projectService,
@@ -1053,7 +1066,8 @@ namespace ts.server {
compilerOptions,
/*compileOnSaveEnabled*/ false,
projectService.host,
rootDirectoryForResolution);
currentDirectory);
this.projectRootPath = projectRootPath && projectService.toCanonicalFileName(projectRootPath);
}
addRoot(info: ScriptInfo) {
@@ -1082,12 +1096,6 @@ namespace ts.server {
this.getRootScriptInfos().length === 1;
}
getProjectRootPath() {
return this.projectRootPath ||
// Single inferred project does not have a project root.
!this.projectService.useSingleInferredProject && getDirectoryPath(this.getRootFiles()[0]);
}
close() {
forEach(this.getRootScriptInfos(), info => this.projectService.stopWatchingConfigFilesForInferredProjectRoot(info));
super.close();
@@ -1183,7 +1191,7 @@ namespace ts.server {
// Search our peer node_modules, then any globally-specified probe paths
// ../../.. to walk from X/node_modules/typescript/lib/tsserver.js to X/node_modules/
const searchPaths = [combinePaths(host.getExecutingFilePath(), "../../.."), ...this.projectService.pluginProbeLocations];
const searchPaths = [combinePaths(this.projectService.getExecutingFilePath(), "../../.."), ...this.projectService.pluginProbeLocations];
if (this.projectService.allowLocalPluginLoads) {
const local = getDirectoryPath(this.canonicalConfigFilePath);
@@ -1263,22 +1271,18 @@ namespace ts.server {
}
}
getProjectRootPath() {
return getDirectoryPath(this.getConfigFilePath());
}
/**
* Get the errors that dont have any file name associated
*/
getGlobalProjectErrors(): ReadonlyArray<Diagnostic> {
return filter(this.projectErrors, diagnostic => !diagnostic.file);
return filter(this.projectErrors, diagnostic => !diagnostic.file) || emptyArray;
}
/**
* Get all the project errors
*/
getAllProjectErrors(): ReadonlyArray<Diagnostic> {
return this.projectErrors;
return this.projectErrors || emptyArray;
}
setProjectErrors(projectErrors: Diagnostic[]) {
@@ -1327,14 +1331,15 @@ namespace ts.server {
}
close() {
super.close();
if (this.configFileWatcher) {
this.configFileWatcher.close();
this.configFileWatcher = undefined;
}
this.stopWatchingWildCards();
this.projectErrors = undefined;
this.configFileSpecs = undefined;
super.close();
}
addOpenRef() {
@@ -1379,13 +1384,14 @@ namespace ts.server {
compilerOptions: CompilerOptions,
languageServiceEnabled: boolean,
public compileOnSaveEnabled: boolean,
private readonly projectFilePath?: string) {
projectFilePath?: string) {
super(externalProjectName,
ProjectKind.External,
projectService,
documentRegistry,
/*hasExplicitListOfFiles*/ true,
languageServiceEnabled, compilerOptions,
languageServiceEnabled,
compilerOptions,
compileOnSaveEnabled,
projectService.host,
getDirectoryPath(projectFilePath || normalizeSlashes(externalProjectName)));
@@ -1395,16 +1401,6 @@ namespace ts.server {
return this.excludedFiles;
}
getProjectRootPath() {
if (this.projectFilePath) {
return getDirectoryPath(this.projectFilePath);
}
// if the projectFilePath is not given, we make the assumption that the project name
// is the path of the project file. AS the project name is provided by VS, we need to
// normalize slashes before using it as a file name.
return getDirectoryPath(normalizeSlashes(this.getProjectName()));
}
getTypeAcquisition() {
return this.typeAcquisition;
}
+19 -3
View File
@@ -753,10 +753,23 @@ namespace ts.server {
const sys = <ServerHost>ts.sys;
// use watchGuard process on Windows when node version is 4 or later
const useWatchGuard = process.platform === "win32" && getNodeMajorVersion() >= 4;
const originalWatchDirectory: ServerHost["watchDirectory"] = sys.watchDirectory.bind(sys);
const noopWatcher: FileWatcher = { close: noop };
// This is the function that catches the exceptions when watching directory, and yet lets project service continue to function
// Eg. on linux the number of watches are limited and one could easily exhaust watches and the exception ENOSPC is thrown when creating watcher at that point
function watchDirectorySwallowingException(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher {
try {
return originalWatchDirectory(path, callback, recursive);
}
catch (e) {
logger.info(`Exception when creating directory watcher: ${e.message}`);
return noopWatcher;
}
}
if (useWatchGuard) {
const currentDrive = extractWatchDirectoryCacheKey(sys.resolvePath(sys.getCurrentDirectory()), /*currentDriveKey*/ undefined);
const statusCache = createMap<boolean>();
const originalWatchDirectory = sys.watchDirectory;
sys.watchDirectory = function (path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher {
const cacheKey = extractWatchDirectoryCacheKey(path, currentDrive);
let status = cacheKey && statusCache.get(cacheKey);
@@ -790,14 +803,17 @@ namespace ts.server {
}
if (status) {
// this drive is safe to use - call real 'watchDirectory'
return originalWatchDirectory.call(sys, path, callback, recursive);
return watchDirectorySwallowingException(path, callback, recursive);
}
else {
// this drive is unsafe - return no-op watcher
return { close() { } };
return noopWatcher;
}
};
}
else {
sys.watchDirectory = watchDirectorySwallowingException;
}
// Override sys.write because fs.writeSync is not reliable on Node 4
sys.write = (s: string) => writeMessage(new Buffer(s, "utf8"));
+2 -8
View File
@@ -969,13 +969,7 @@ namespace ts.server {
* @param fileContent is a version of the file content that is known to be more up to date than the one on disk
*/
private openClientFile(fileName: NormalizedPath, fileContent?: string, scriptKind?: ScriptKind, projectRootPath?: NormalizedPath) {
const { configFileName, configFileErrors } = this.projectService.openClientFileWithNormalizedPath(fileName, fileContent, scriptKind, /*hasMixedContent*/ false, projectRootPath);
if (this.eventHandler) {
this.eventHandler({
eventName: "configFileDiag",
data: { triggerFile: fileName, configFileName, diagnostics: configFileErrors || emptyArray }
});
}
this.projectService.openClientFileWithNormalizedPath(fileName, fileContent, scriptKind, /*hasMixedContent*/ false, projectRootPath);
}
private getPosition(args: protocol.FileLocationRequestArgs, scriptInfo: ScriptInfo): number {
@@ -1608,7 +1602,7 @@ namespace ts.server {
}
// No need to analyze lib.d.ts
const fileNamesInProject = fileNames.filter(value => value.indexOf("lib.d.ts") < 0);
const fileNamesInProject = fileNames.filter(value => !stringContains(value, "lib.d.ts"));
if (fileNamesInProject.length === 0) {
return;
}
@@ -68,10 +68,14 @@ namespace ts.server.typingsInstaller {
return combinePaths(normalizeSlashes(globalTypingsCacheLocation), `node_modules/${TypesRegistryPackageName}/index.json`);
}
type ExecSync = (command: string, options: { cwd: string, stdio?: "ignore" }) => any;
interface ExecSyncOptions {
cwd: string;
encoding: "utf-8";
}
type ExecSync = (command: string, options: ExecSyncOptions) => string;
export class NodeTypingsInstaller extends TypingsInstaller {
private readonly execSync: ExecSync;
private readonly nodeExecSync: ExecSync;
private readonly npmPath: string;
readonly typesRegistry: Map<void>;
@@ -88,14 +92,14 @@ namespace ts.server.typingsInstaller {
this.npmPath = npmLocation !== undefined ? npmLocation : getDefaultNPMLocation(process.argv[0]);
// If the NPM path contains spaces and isn't wrapped in quotes, do so.
if (this.npmPath.indexOf(" ") !== -1 && this.npmPath[0] !== `"`) {
if (stringContains(this.npmPath, " ") && this.npmPath[0] !== `"`) {
this.npmPath = `"${this.npmPath}"`;
}
if (this.log.isEnabled()) {
this.log.writeLine(`Process id: ${process.pid}`);
this.log.writeLine(`NPM location: ${this.npmPath} (explicit '${Arguments.NpmLocation}' ${npmLocation === undefined ? "not " : ""} provided)`);
}
({ execSync: this.execSync } = require("child_process"));
({ execSync: this.nodeExecSync } = require("child_process"));
this.ensurePackageDirectoryExists(globalTypingsCacheLocation);
@@ -103,7 +107,7 @@ namespace ts.server.typingsInstaller {
if (this.log.isEnabled()) {
this.log.writeLine(`Updating ${TypesRegistryPackageName} npm package...`);
}
this.execSync(`${this.npmPath} install --ignore-scripts ${TypesRegistryPackageName}`, { cwd: globalTypingsCacheLocation, stdio: "ignore" });
this.execSyncAndLog(`${this.npmPath} install --ignore-scripts ${TypesRegistryPackageName}`, { cwd: globalTypingsCacheLocation });
if (this.log.isEnabled()) {
this.log.writeLine(`Updated ${TypesRegistryPackageName} npm package`);
}
@@ -155,22 +159,31 @@ namespace ts.server.typingsInstaller {
}
const command = `${this.npmPath} install --ignore-scripts ${args.join(" ")} --save-dev --user-agent="typesInstaller/${version}"`;
const start = Date.now();
let stdout: Buffer;
let stderr: Buffer;
let hasError = false;
try {
stdout = this.execSync(command, { cwd });
}
catch (e) {
stdout = e.stdout;
stderr = e.stderr;
hasError = true;
}
const hasError = this.execSyncAndLog(command, { cwd });
if (this.log.isEnabled()) {
this.log.writeLine(`npm install #${requestId} took: ${Date.now() - start} ms${sys.newLine}stdout: ${stdout && stdout.toString()}${sys.newLine}stderr: ${stderr && stderr.toString()}`);
this.log.writeLine(`npm install #${requestId} took: ${Date.now() - start} ms`);
}
onRequestCompleted(!hasError);
}
/** Returns 'true' in case of error. */
private execSyncAndLog(command: string, options: Pick<ExecSyncOptions, "cwd">): boolean {
if (this.log.isEnabled()) {
this.log.writeLine(`Exec: ${command}`);
}
try {
const stdout = this.nodeExecSync(command, { ...options, encoding: "utf-8" });
if (this.log.isEnabled()) {
this.log.writeLine(` Succeeded. stdout:${indent(sys.newLine, stdout)}`);
}
return false;
}
catch (error) {
const { stdout, stderr } = error;
this.log.writeLine(` Failed. stdout:${indent(sys.newLine, stdout)}${sys.newLine} stderr:${indent(sys.newLine, stderr)}`);
return true;
}
}
}
const logFilePath = findArgument(server.Arguments.LogFile);
@@ -193,4 +206,8 @@ namespace ts.server.typingsInstaller {
});
const installer = new NodeTypingsInstaller(globalTypingsCacheLocation, typingSafeListLocation, typesMapLocation, npmLocation, /*throttleLimit*/5, log);
installer.listen();
function indent(newline: string, string: string): string {
return `${newline} ` + string.replace(/\r?\n/, `${newline} `);
}
}
@@ -0,0 +1,20 @@
/* @internal */
namespace ts.codefix {
registerCodeFix({
errorCodes: [Diagnostics._0_accepts_too_few_arguments_to_be_used_as_a_decorator_here_Did_you_mean_to_call_it_first_and_write_0.code],
getCodeActions: (context: CodeFixContext) => {
const sourceFile = context.sourceFile;
const token = getTokenAtPosition(sourceFile, context.span.start, /*includeJsDocComment*/ false);
const decorator = getAncestor(token, SyntaxKind.Decorator) as Decorator;
Debug.assert(!!decorator, "Expected position to be owned by a decorator.");
const replacement = createCall(decorator.expression, /*typeArguments*/ undefined, /*argumentsArray*/ undefined);
const changeTracker = textChanges.ChangeTracker.fromContext(context);
changeTracker.replaceNode(sourceFile, decorator.expression, replacement);
return [{
description: getLocaleSpecificMessage(Diagnostics.Call_decorator_expression),
changes: changeTracker.getChanges()
}];
}
});
}
@@ -92,7 +92,7 @@ namespace ts.codefix {
classDeclarationSourceFile,
classDeclaration,
staticInitialization,
{ suffix: context.newLineCharacter });
{ prefix: context.newLineCharacter, suffix: context.newLineCharacter });
const initializeStaticAction = {
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Initialize_static_property_0), [tokenName]),
changes: staticInitializationChangeTracker.getChanges()
@@ -112,11 +112,11 @@ namespace ts.codefix {
createIdentifier("undefined")));
const propertyInitializationChangeTracker = textChanges.ChangeTracker.fromContext(context);
propertyInitializationChangeTracker.insertNodeAt(
propertyInitializationChangeTracker.insertNodeBefore(
classDeclarationSourceFile,
classConstructor.body.getEnd() - 1,
classConstructor.body.getLastToken(),
propertyInitialization,
{ prefix: context.newLineCharacter, suffix: context.newLineCharacter });
{ suffix: context.newLineCharacter });
const initializeAction = {
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Initialize_property_0_in_the_constructor), [tokenName]),
+2
View File
@@ -1,3 +1,4 @@
/// <reference path="addMissingInvocationForDecorator.ts" />
/// <reference path="correctQualifiedNameToIndexedAccessType.ts" />
/// <reference path="fixClassIncorrectlyImplementsInterface.ts" />
/// <reference path="fixAddMissingMember.ts" />
@@ -12,3 +13,4 @@
/// <reference path='importFixes.ts' />
/// <reference path='disableJsDiagnostics.ts' />
/// <reference path='helpers.ts' />
/// <reference path='inferFromUsage.ts' />
+1 -5
View File
@@ -16,7 +16,7 @@ namespace ts.codefix {
moduleSpecifier?: string;
}
enum ModuleSpecifierComparison {
const enum ModuleSpecifierComparison {
Better,
Equal,
Worse
@@ -26,10 +26,6 @@ namespace ts.codefix {
private symbolIdToActionMap: ImportCodeAction[][] = [];
addAction(symbolId: number, newAction: ImportCodeAction) {
if (!newAction) {
return;
}
const actions = this.symbolIdToActionMap[symbolId];
if (!actions) {
this.symbolIdToActionMap[symbolId] = [newAction];
+653
View File
@@ -0,0 +1,653 @@
/* @internal */
namespace ts.codefix {
registerCodeFix({
errorCodes: [
// Variable declarations
Diagnostics.Variable_0_implicitly_has_type_1_in_some_locations_where_its_type_cannot_be_determined.code,
// Variable uses
Diagnostics.Variable_0_implicitly_has_an_1_type.code,
// Parameter declarations
Diagnostics.Parameter_0_implicitly_has_an_1_type.code,
Diagnostics.Rest_parameter_0_implicitly_has_an_any_type.code,
// Get Accessor declarations
Diagnostics.Property_0_implicitly_has_type_any_because_its_get_accessor_lacks_a_return_type_annotation.code,
Diagnostics._0_which_lacks_return_type_annotation_implicitly_has_an_1_return_type.code,
// Set Accessor declarations
Diagnostics.Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_parameter_type_annotation.code,
// Property declarations
Diagnostics.Member_0_implicitly_has_an_1_type.code,
],
getCodeActions: getActionsForAddExplicitTypeAnnotation
});
function getActionsForAddExplicitTypeAnnotation({ sourceFile, program, span: { start }, errorCode, cancellationToken }: CodeFixContext): CodeAction[] | undefined {
const token = getTokenAtPosition(sourceFile, start, /*includeJsDocComment*/ false);
let writer: StringSymbolWriter;
if (isInJavaScriptFile(token)) {
return undefined;
}
switch (token.kind) {
case SyntaxKind.Identifier:
case SyntaxKind.DotDotDotToken:
case SyntaxKind.PublicKeyword:
case SyntaxKind.PrivateKeyword:
case SyntaxKind.ProtectedKeyword:
case SyntaxKind.ReadonlyKeyword:
// Allowed
break;
default:
return undefined;
}
const containingFunction = getContainingFunction(token);
const checker = program.getTypeChecker();
switch (errorCode) {
// 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);
case Diagnostics.Variable_0_implicitly_has_an_1_type.code:
return getCodeActionForVariableUsage(<Identifier>token);
// Parameter declarations
case Diagnostics.Parameter_0_implicitly_has_an_1_type.code:
if (isSetAccessor(containingFunction)) {
return getCodeActionForSetAccessor(containingFunction);
}
// falls through
case Diagnostics.Rest_parameter_0_implicitly_has_an_any_type.code:
return getCodeActionForParameters(<ParameterDeclaration>token.parent);
// Get Accessor declarations
case Diagnostics.Property_0_implicitly_has_type_any_because_its_get_accessor_lacks_a_return_type_annotation.code:
case Diagnostics._0_which_lacks_return_type_annotation_implicitly_has_an_1_return_type.code:
return isGetAccessor(containingFunction) ? getCodeActionForGetAccessor(containingFunction) : undefined;
// 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) : undefined;
}
return undefined;
function getCodeActionForVariableDeclaration(declaration: VariableDeclaration | PropertyDeclaration | PropertySignature) {
if (!isIdentifier(declaration.name)) {
return undefined;
}
const type = inferTypeForVariableFromUsage(declaration.name);
const typeString = type && typeToString(type, declaration);
if (!typeString) {
return undefined;
}
return createCodeActions(declaration.name.getText(), declaration.name.getEnd(), `: ${typeString}`);
}
function getCodeActionForVariableUsage(token: Identifier) {
const symbol = checker.getSymbolAtLocation(token);
return symbol && symbol.valueDeclaration && getCodeActionForVariableDeclaration(<VariableDeclaration>symbol.valueDeclaration);
}
function isApplicableFunctionForInference(declaration: FunctionLike): declaration is MethodDeclaration | FunctionDeclaration | ConstructorDeclaration {
switch (declaration.kind) {
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.Constructor:
return true;
case SyntaxKind.FunctionExpression:
return !!(declaration as FunctionExpression).name;
}
return false;
}
function getCodeActionForParameters(parameterDeclaration: ParameterDeclaration): CodeAction[] {
if (!isIdentifier(parameterDeclaration.name) || !isApplicableFunctionForInference(containingFunction)) {
return undefined;
}
const types = inferTypeForParametersFromUsage(containingFunction) ||
map(containingFunction.parameters, p => isIdentifier(p.name) && inferTypeForVariableFromUsage(p.name));
if (!types) {
return undefined;
}
const textChanges: TextChange[] = zipWith(containingFunction.parameters, types, (parameter, type) => {
if (type && !parameter.type && !parameter.initializer) {
const typeString = typeToString(type, containingFunction);
return typeString ? {
span: { start: parameter.end, length: 0 },
newText: `: ${typeString}`
} : undefined;
}
}).filter(c => !!c);
return textChanges.length ? [{
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Infer_parameter_types_from_usage), [parameterDeclaration.name.getText()]),
changes: [{
fileName: sourceFile.fileName,
textChanges
}]
}] : undefined;
}
function getCodeActionForSetAccessor(setAccessorDeclaration: SetAccessorDeclaration) {
const setAccessorParameter = setAccessorDeclaration.parameters[0];
if (!setAccessorParameter || !isIdentifier(setAccessorDeclaration.name) || !isIdentifier(setAccessorParameter.name)) {
return undefined;
}
const type = inferTypeForVariableFromUsage(setAccessorDeclaration.name) ||
inferTypeForVariableFromUsage(setAccessorParameter.name);
const typeString = type && typeToString(type, containingFunction);
if (!typeString) {
return undefined;
}
return createCodeActions(setAccessorDeclaration.name.getText(), setAccessorParameter.name.getEnd(), `: ${typeString}`);
}
function getCodeActionForGetAccessor(getAccessorDeclaration: GetAccessorDeclaration) {
if (!isIdentifier(getAccessorDeclaration.name)) {
return undefined;
}
const type = inferTypeForVariableFromUsage(getAccessorDeclaration.name);
const typeString = type && typeToString(type, containingFunction);
if (!typeString) {
return undefined;
}
const closeParenToken = getFirstChildOfKind(getAccessorDeclaration, sourceFile, SyntaxKind.CloseParenToken);
return createCodeActions(getAccessorDeclaration.name.getText(), closeParenToken.getEnd(), `: ${typeString}`);
}
function createCodeActions(name: string, start: number, typeString: string) {
return [{
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Infer_type_of_0_from_usage), [name]),
changes: [{
fileName: sourceFile.fileName,
textChanges: [{
span: { start, length: 0 },
newText: typeString
}]
}]
}];
}
function getReferences(token: PropertyName | Token<SyntaxKind.ConstructorKeyword>) {
const references = FindAllReferences.findReferencedSymbols(
program,
cancellationToken,
program.getSourceFiles(),
token.getSourceFile(),
token.getStart());
Debug.assert(!!references, "Found no references!");
Debug.assert(references.length === 1, "Found more references than expected");
return map(references[0].references, r => <Identifier>getTokenAtPosition(program.getSourceFile(r.fileName), r.textSpan.start, /*includeJsDocComment*/ false));
}
function inferTypeForVariableFromUsage(token: Identifier) {
return InferFromReference.inferTypeFromReferences(getReferences(token), checker, cancellationToken);
}
function inferTypeForParametersFromUsage(containingFunction: FunctionLikeDeclaration) {
switch (containingFunction.kind) {
case SyntaxKind.Constructor:
case SyntaxKind.FunctionExpression:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.MethodDeclaration:
const isConstructor = containingFunction.kind === SyntaxKind.Constructor;
const searchToken = isConstructor ?
<Token<SyntaxKind.ConstructorKeyword>>getFirstChildOfKind(containingFunction, sourceFile, SyntaxKind.ConstructorKeyword) :
containingFunction.name;
if (searchToken) {
return InferFromReference.inferTypeForParametersFromReferences(getReferences(searchToken), containingFunction, checker, cancellationToken);
}
}
}
function getTypeAccessiblityWriter() {
if (!writer) {
let str = "";
let typeIsAccessible = true;
const writeText: (text: string) => void = text => str += text;
writer = {
string: () => typeIsAccessible ? str : undefined,
writeKeyword: writeText,
writeOperator: writeText,
writePunctuation: writeText,
writeSpace: writeText,
writeStringLiteral: writeText,
writeParameter: writeText,
writeProperty: writeText,
writeSymbol: writeText,
writeLine: () => str += " ",
increaseIndent: noop,
decreaseIndent: noop,
clear: () => { str = ""; typeIsAccessible = true; },
trackSymbol: (symbol, declaration, meaning) => {
if (checker.isSymbolAccessible(symbol, declaration, meaning, /*shouldComputeAliasToMarkVisible*/ false).accessibility !== SymbolAccessibility.Accessible) {
typeIsAccessible = false;
}
},
reportInaccessibleThisError: () => { typeIsAccessible = false; },
reportPrivateInBaseOfClassExpression: () => { typeIsAccessible = false; },
};
}
writer.clear();
return writer;
}
function typeToString(type: Type, enclosingDeclaration: Declaration) {
const writer = getTypeAccessiblityWriter();
checker.getSymbolDisplayBuilder().buildTypeDisplay(type, writer, enclosingDeclaration);
return writer.string();
}
function getFirstChildOfKind(node: Node, sourcefile: SourceFile, kind: SyntaxKind) {
for (const child of node.getChildren(sourcefile)) {
if (child.kind === kind) return child;
}
return undefined;
}
}
namespace InferFromReference {
interface CallContext {
argumentTypes: Type[];
returnType: UsageContext;
}
interface UsageContext {
isNumber?: boolean;
isString?: boolean;
isNumberOrString?: boolean;
candidateTypes?: Type[];
properties?: UnderscoreEscapedMap<UsageContext>;
callContexts?: CallContext[];
constructContexts?: CallContext[];
numberIndexContext?: UsageContext;
stringIndexContext?: UsageContext;
}
export function inferTypeFromReferences(references: Identifier[], checker: TypeChecker, cancellationToken: CancellationToken): Type | undefined {
const usageContext: UsageContext = {};
for (const reference of references) {
cancellationToken.throwIfCancellationRequested();
inferTypeFromContext(reference, checker, usageContext);
}
return getTypeFromUsageContext(usageContext, checker);
}
export function inferTypeForParametersFromReferences(references: Identifier[], declaration: FunctionLikeDeclaration, checker: TypeChecker, cancellationToken: CancellationToken): (Type | undefined)[] | 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, /*subtypeReduction*/ true));
paramTypes[parameterIndex] = isRestParameter ? checker.createArrayType(type) : type;
}
}
return paramTypes;
}
}
return undefined;
}
function inferTypeFromContext(node: Expression, checker: TypeChecker, usageContext: UsageContext): void {
while (isRightSideOfQualifiedNameOrPropertyAccess(node)) {
node = <Expression>node.parent;
}
switch (node.parent.kind) {
case SyntaxKind.PostfixUnaryExpression:
usageContext.isNumber = true;
break;
case SyntaxKind.PrefixUnaryExpression:
inferTypeFromPrefixUnaryExpressionContext(<PrefixUnaryExpression>node.parent, usageContext);
break;
case SyntaxKind.BinaryExpression:
inferTypeFromBinaryExpressionContext(node, <BinaryExpression>node.parent, checker, usageContext);
break;
case SyntaxKind.CaseClause:
case SyntaxKind.DefaultClause:
inferTypeFromSwitchStatementLabelContext(<CaseOrDefaultClause>node.parent, checker, usageContext);
break;
case SyntaxKind.CallExpression:
case SyntaxKind.NewExpression:
if ((<CallExpression | NewExpression>node.parent).expression === node) {
inferTypeFromCallExpressionContext(<CallExpression | NewExpression>node.parent, checker, usageContext);
}
else {
inferTypeFromContextualType(node, checker, usageContext);
}
break;
case SyntaxKind.PropertyAccessExpression:
inferTypeFromPropertyAccessExpressionContext(<PropertyAccessExpression>node.parent, checker, usageContext);
break;
case SyntaxKind.ElementAccessExpression:
inferTypeFromPropertyElementExpressionContext(<ElementAccessExpression>node.parent, node, checker, usageContext);
break;
default:
return inferTypeFromContextualType(node, checker, usageContext);
}
}
function inferTypeFromContextualType(node: Expression, checker: TypeChecker, usageContext: UsageContext): void {
if (isPartOfExpression(node)) {
addCandidateType(usageContext, checker.getContextualType(node));
}
}
function inferTypeFromPrefixUnaryExpressionContext(node: PrefixUnaryExpression, usageContext: UsageContext): void {
switch (node.operator) {
case SyntaxKind.PlusPlusToken:
case SyntaxKind.MinusMinusToken:
case SyntaxKind.MinusToken:
case SyntaxKind.TildeToken:
usageContext.isNumber = true;
break;
case SyntaxKind.PlusToken:
usageContext.isNumberOrString = true;
break;
// case SyntaxKind.ExclamationToken:
// no inferences here;
}
}
function inferTypeFromBinaryExpressionContext(node: Expression, parent: BinaryExpression, checker: TypeChecker, usageContext: UsageContext): void {
switch (parent.operatorToken.kind) {
// ExponentiationOperator
case SyntaxKind.AsteriskAsteriskToken:
// MultiplicativeOperator
case SyntaxKind.AsteriskToken:
case SyntaxKind.SlashToken:
case SyntaxKind.PercentToken:
// ShiftOperator
case SyntaxKind.LessThanLessThanToken:
case SyntaxKind.GreaterThanGreaterThanToken:
case SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
// BitwiseOperator
case SyntaxKind.AmpersandToken:
case SyntaxKind.BarToken:
case SyntaxKind.CaretToken:
// CompoundAssignmentOperator
case SyntaxKind.MinusEqualsToken:
case SyntaxKind.AsteriskAsteriskEqualsToken:
case SyntaxKind.AsteriskEqualsToken:
case SyntaxKind.SlashEqualsToken:
case SyntaxKind.PercentEqualsToken:
case SyntaxKind.AmpersandEqualsToken:
case SyntaxKind.BarEqualsToken:
case SyntaxKind.CaretEqualsToken:
case SyntaxKind.LessThanLessThanEqualsToken:
case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken:
case SyntaxKind.GreaterThanGreaterThanEqualsToken:
// AdditiveOperator
case SyntaxKind.MinusToken:
// RelationalOperator
case SyntaxKind.LessThanToken:
case SyntaxKind.LessThanEqualsToken:
case SyntaxKind.GreaterThanToken:
case SyntaxKind.GreaterThanEqualsToken:
const operandType = checker.getTypeAtLocation(parent.left === node ? parent.right : parent.left);
if (operandType.flags & TypeFlags.EnumLike) {
addCandidateType(usageContext, operandType);
}
else {
usageContext.isNumber = true;
}
break;
case SyntaxKind.PlusEqualsToken:
case SyntaxKind.PlusToken:
const otherOperandType = checker.getTypeAtLocation(parent.left === node ? parent.right : parent.left);
if (otherOperandType.flags & TypeFlags.EnumLike) {
addCandidateType(usageContext, otherOperandType);
}
else if (otherOperandType.flags & TypeFlags.NumberLike) {
usageContext.isNumber = true;
}
else if (otherOperandType.flags & TypeFlags.StringLike) {
usageContext.isString = true;
}
else {
usageContext.isNumberOrString = true;
}
break;
// AssignmentOperators
case SyntaxKind.EqualsToken:
case SyntaxKind.EqualsEqualsToken:
case SyntaxKind.EqualsEqualsEqualsToken:
case SyntaxKind.ExclamationEqualsEqualsToken:
case SyntaxKind.ExclamationEqualsToken:
addCandidateType(usageContext, checker.getTypeAtLocation(parent.left === node ? parent.right : parent.left));
break;
case SyntaxKind.InKeyword:
if (node === parent.left) {
usageContext.isString = true;
}
break;
// LogicalOperator
case SyntaxKind.BarBarToken:
if (node === parent.left &&
(node.parent.parent.kind === SyntaxKind.VariableDeclaration || isAssignmentExpression(node.parent.parent, /*excludeCompoundAssignment*/ true))) {
// var x = x || {};
// TODO: use getFalsyflagsOfType
addCandidateType(usageContext, checker.getTypeAtLocation(parent.right));
}
break;
case SyntaxKind.AmpersandAmpersandToken:
case SyntaxKind.CommaToken:
case SyntaxKind.InstanceOfKeyword:
// nothing to infer here
break;
}
}
function inferTypeFromSwitchStatementLabelContext(parent: CaseOrDefaultClause, checker: TypeChecker, usageContext: UsageContext): void {
addCandidateType(usageContext, checker.getTypeAtLocation((<SwitchStatement>parent.parent.parent).expression));
}
function inferTypeFromCallExpressionContext(parent: CallExpression | NewExpression, checker: TypeChecker, usageContext: UsageContext): void {
const callContext: CallContext = {
argumentTypes: [],
returnType: {}
};
if (parent.arguments) {
for (const argument of parent.arguments) {
callContext.argumentTypes.push(checker.getTypeAtLocation(argument));
}
}
inferTypeFromContext(parent, checker, callContext.returnType);
if (parent.kind === SyntaxKind.CallExpression) {
(usageContext.callContexts || (usageContext.callContexts = [])).push(callContext);
}
else {
(usageContext.constructContexts || (usageContext.constructContexts = [])).push(callContext);
}
}
function inferTypeFromPropertyAccessExpressionContext(parent: PropertyAccessExpression, checker: TypeChecker, usageContext: UsageContext): void {
const name = escapeLeadingUnderscores(parent.name.text);
if (!usageContext.properties) {
usageContext.properties = createUnderscoreEscapedMap<UsageContext>();
}
const propertyUsageContext = {};
inferTypeFromContext(parent, checker, propertyUsageContext);
usageContext.properties.set(name, propertyUsageContext);
}
function inferTypeFromPropertyElementExpressionContext(parent: ElementAccessExpression, node: Expression, checker: TypeChecker, usageContext: UsageContext): void {
if (node === parent.argumentExpression) {
usageContext.isNumberOrString = true;
return;
}
else {
const indexType = checker.getTypeAtLocation(parent);
const indexUsageContext = {};
inferTypeFromContext(parent, checker, indexUsageContext);
if (indexType.flags & TypeFlags.NumberLike) {
usageContext.numberIndexContext = indexUsageContext;
}
else {
usageContext.stringIndexContext = indexUsageContext;
}
}
}
function getTypeFromUsageContext(usageContext: UsageContext, checker: TypeChecker): Type | undefined {
if (usageContext.isNumberOrString && !usageContext.isNumber && !usageContext.isString) {
return checker.getUnionType([checker.getNumberType(), checker.getStringType()]);
}
else if (usageContext.isNumber) {
return checker.getNumberType();
}
else if (usageContext.isString) {
return checker.getStringType();
}
else if (usageContext.candidateTypes) {
return checker.getWidenedType(checker.getUnionType(map(usageContext.candidateTypes, t => checker.getBaseTypeOfLiteralType(t)), /*subtypeReduction*/ true));
}
else if (usageContext.properties && hasCallContext(usageContext.properties.get("then" as __String))) {
const paramType = getParameterTypeFromCallContexts(0, usageContext.properties.get("then" as __String).callContexts, /*isRestParameter*/ false, checker);
const types = paramType.getCallSignatures().map(c => c.getReturnType());
return checker.createPromiseType(types.length ? checker.getUnionType(types, /*subtypeReduction*/ true) : checker.getAnyType());
}
else if (usageContext.properties && hasCallContext(usageContext.properties.get("push" as __String))) {
return checker.createArrayType(getParameterTypeFromCallContexts(0, usageContext.properties.get("push" as __String).callContexts, /*isRestParameter*/ false, checker));
}
else if (usageContext.properties || usageContext.callContexts || usageContext.constructContexts || usageContext.numberIndexContext || usageContext.stringIndexContext) {
const members = createUnderscoreEscapedMap<Symbol>();
const callSignatures: Signature[] = [];
const constructSignatures: Signature[] = [];
let stringIndexInfo: IndexInfo;
let numberIndexInfo: IndexInfo;
if (usageContext.properties) {
usageContext.properties.forEach((context, name) => {
const symbol = checker.createSymbol(SymbolFlags.Property, name);
symbol.type = getTypeFromUsageContext(context, checker);
members.set(name, symbol);
});
}
if (usageContext.callContexts) {
for (const callContext of usageContext.callContexts) {
callSignatures.push(getSignatureFromCallContext(callContext, checker));
}
}
if (usageContext.constructContexts) {
for (const constructContext of usageContext.constructContexts) {
constructSignatures.push(getSignatureFromCallContext(constructContext, checker));
}
}
if (usageContext.numberIndexContext) {
numberIndexInfo = checker.createIndexInfo(getTypeFromUsageContext(usageContext.numberIndexContext, checker), /*isReadonly*/ false);
}
if (usageContext.stringIndexContext) {
stringIndexInfo = checker.createIndexInfo(getTypeFromUsageContext(usageContext.stringIndexContext, checker), /*isReadonly*/ false);
}
return checker.createAnonymousType(/*symbol*/ undefined, members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo);
}
else {
return undefined;
}
}
function getParameterTypeFromCallContexts(parameterIndex: number, callContexts: CallContext[], isRestParameter: boolean, checker: TypeChecker) {
let types: Type[] = [];
if (callContexts) {
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, /*subtypeReduction*/ true));
return isRestParameter ? checker.createArrayType(type) : type;
}
return undefined;
}
function getSignatureFromCallContext(callContext: CallContext, checker: TypeChecker): Signature {
const parameters: Symbol[] = [];
for (let i = 0; i < callContext.argumentTypes.length; i++) {
const symbol = checker.createSymbol(SymbolFlags.FunctionScopedVariable, escapeLeadingUnderscores(`arg${i}`));
symbol.type = checker.getWidenedType(checker.getBaseTypeOfLiteralType(callContext.argumentTypes[i]));
parameters.push(symbol);
}
const returnType = getTypeFromUsageContext(callContext.returnType, checker);
return checker.createSignature(/*declaration*/ undefined, /*typeParameters*/ undefined, /*thisParameter*/ undefined, parameters, returnType, /*typePredicate*/ undefined, callContext.argumentTypes.length, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
}
function addCandidateType(context: UsageContext, type: Type) {
if (type && !(type.flags & TypeFlags.Any) && !(type.flags & TypeFlags.Never)) {
(context.candidateTypes || (context.candidateTypes = [])).push(type);
}
}
function hasCallContext(usageContext: UsageContext) {
return usageContext && usageContext.callContexts;
}
}
}
+5
View File
@@ -1195,6 +1195,11 @@ namespace ts.Completions {
if (isClassLike(location)) {
return location;
}
// class c { method() { } b| }
if (isFromClassElementDeclaration(location) &&
(location.parent as ClassElement).name === location) {
return location.parent.parent as ClassLikeDeclaration;
}
break;
default:
+4 -1
View File
@@ -482,7 +482,10 @@ namespace ts.FindAllReferences.Core {
/** @param allSearchSymbols set of additinal symbols for use by `includes`. */
createSearch(location: Node, symbol: Symbol, comingFrom: ImportExport | undefined, searchOptions: { text?: string, allSearchSymbols?: Symbol[] } = {}): Search {
// Note: if this is an external module symbol, the name doesn't include quotes.
const { text = stripQuotes(getDeclaredName(this.checker, symbol, location)), allSearchSymbols = undefined } = searchOptions;
const {
text = stripQuotes(unescapeLeadingUnderscores((getLocalSymbolForExportDefault(symbol) || symbol).escapedName)),
allSearchSymbols = undefined,
} = searchOptions;
const escapedText = escapeLeadingUnderscores(text);
const parents = this.options.implementations && getParentSymbolsOfPropertyAccess(location, symbol, this.checker);
return {
-3
View File
@@ -609,9 +609,6 @@ namespace ts.FindAllReferences {
}
return forEach(symbol.declarations, decl => {
if (isExportAssignment(decl)) {
return isIdentifier(decl.expression) ? decl.expression.escapedText : undefined;
}
const name = getNameOfDeclaration(decl);
return name && name.kind === SyntaxKind.Identifier && name.escapedText;
});
+2 -2
View File
@@ -195,7 +195,7 @@ namespace ts.Completions.PathCompletions {
const normalizedPrefixDirectory = getDirectoryPath(normalizedPrefix);
const normalizedPrefixBase = getBaseFileName(normalizedPrefix);
const fragmentHasPath = fragment.indexOf(directorySeparator) !== -1;
const fragmentHasPath = stringContains(fragment, directorySeparator);
// Try and expand the prefix to include any path from the fragment so that we can limit the readDirectory call
const expandedPrefixDirectory = fragmentHasPath ? combinePaths(normalizedPrefixDirectory, normalizedPrefixBase + getDirectoryPath(fragment)) : normalizedPrefixDirectory;
@@ -235,7 +235,7 @@ namespace ts.Completions.PathCompletions {
function enumeratePotentialNonRelativeModules(fragment: string, scriptPath: string, options: CompilerOptions, typeChecker: TypeChecker, host: LanguageServiceHost): string[] {
// Check If this is a nested module
const isNestedModule = fragment.indexOf(directorySeparator) !== -1;
const isNestedModule = stringContains(fragment, directorySeparator);
const moduleNameFragment = isNestedModule ? fragment.substr(0, fragment.lastIndexOf(directorySeparator)) : undefined;
// Get modules that the type checker picked up
@@ -172,7 +172,8 @@ namespace ts.refactor.convertFunctionToES6Class {
switch (assignmentBinaryExpression.right.kind) {
case SyntaxKind.FunctionExpression: {
const functionExpression = assignmentBinaryExpression.right as FunctionExpression;
const method = createMethod(/*decorators*/ undefined, modifiers, /*asteriskToken*/ undefined, memberDeclaration.name, /*questionToken*/ undefined,
const fullModifiers = concatenate(modifiers, getModifierKindFromSource(functionExpression, SyntaxKind.AsyncKeyword));
const method = createMethod(/*decorators*/ undefined, fullModifiers, /*asteriskToken*/ undefined, memberDeclaration.name, /*questionToken*/ undefined,
/*typeParameters*/ undefined, functionExpression.parameters, /*type*/ undefined, functionExpression.body);
copyComments(assignmentBinaryExpression, method);
return method;
@@ -192,7 +193,8 @@ namespace ts.refactor.convertFunctionToES6Class {
const expression = arrowFunctionBody as Expression;
bodyBlock = createBlock([createReturn(expression)]);
}
const method = createMethod(/*decorators*/ undefined, modifiers, /*asteriskToken*/ undefined, memberDeclaration.name, /*questionToken*/ undefined,
const fullModifiers = concatenate(modifiers, getModifierKindFromSource(arrowFunction, SyntaxKind.AsyncKeyword));
const method = createMethod(/*decorators*/ undefined, fullModifiers, /*asteriskToken*/ undefined, memberDeclaration.name, /*questionToken*/ undefined,
/*typeParameters*/ undefined, arrowFunction.parameters, /*type*/ undefined, bodyBlock);
copyComments(assignmentBinaryExpression, method);
return method;
@@ -243,7 +245,8 @@ namespace ts.refactor.convertFunctionToES6Class {
memberElements.unshift(createConstructor(/*decorators*/ undefined, /*modifiers*/ undefined, initializer.parameters, initializer.body));
}
const cls = createClassDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, node.name,
const modifiers = getModifierKindFromSource(precedingNode, SyntaxKind.ExportKeyword);
const cls = createClassDeclaration(/*decorators*/ undefined, modifiers, node.name,
/*typeParameters*/ undefined, /*heritageClauses*/ undefined, memberElements);
// Don't call copyComments here because we'll already leave them in place
return cls;
@@ -255,10 +258,15 @@ namespace ts.refactor.convertFunctionToES6Class {
memberElements.unshift(createConstructor(/*decorators*/ undefined, /*modifiers*/ undefined, node.parameters, node.body));
}
const cls = createClassDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, node.name,
const modifiers = getModifierKindFromSource(node, SyntaxKind.ExportKeyword);
const cls = createClassDeclaration(/*decorators*/ undefined, modifiers, node.name,
/*typeParameters*/ undefined, /*heritageClauses*/ undefined, memberElements);
// Don't call copyComments here because we'll already leave them in place
return cls;
}
function getModifierKindFromSource(source: Node, kind: SyntaxKind) {
return filter(source.modifiers, modifier => modifier.kind === kind);
}
}
}
+221 -84
View File
@@ -137,7 +137,7 @@ namespace ts.refactor.extractSymbol {
export const FunctionWillNotBeVisibleInTheNewScope = createMessage("Function will not visible in the new scope.");
export const CannotExtractIdentifier = createMessage("Select more than a single identifier.");
export const CannotExtractExportedEntity = createMessage("Cannot extract exported declaration");
export const CannotCombineWritesAndReturns = createMessage("Cannot combine writes and returns");
export const CannotWriteInExpression = createMessage("Cannot write back side-effects when extracting an expression");
export const CannotExtractReadonlyPropertyInitializerOutsideConstructor = createMessage("Cannot move initialization of read-only class property outside of the constructor");
export const CannotExtractAmbientBlock = createMessage("Cannot extract code from ambient contexts");
export const CannotAccessVariablesFromNestedScopes = createMessage("Cannot access variables from nested scopes");
@@ -375,7 +375,7 @@ namespace ts.refactor.extractSymbol {
permittedJumps = PermittedJumps.None;
break;
case SyntaxKind.Block:
if (node.parent && node.parent.kind === SyntaxKind.TryStatement && (<TryStatement>node).finallyBlock === node) {
if (node.parent && node.parent.kind === SyntaxKind.TryStatement && (<TryStatement>node.parent).finallyBlock === node) {
// allow unconditional returns from finally blocks
permittedJumps = PermittedJumps.Return;
}
@@ -476,7 +476,10 @@ namespace ts.refactor.extractSymbol {
// if range uses this as keyword or as type inside the class then it can only be extracted to a method of the containing class
const containingClass = getContainingClass(current);
if (containingClass) {
return [containingClass];
const containingFunction = findAncestor(current, isFunctionLikeDeclaration);
return containingFunction
? [containingFunction, containingClass]
: [containingClass];
}
}
@@ -507,15 +510,16 @@ namespace ts.refactor.extractSymbol {
}
function getFunctionExtractionAtIndex(targetRange: TargetRange, context: RefactorContext, requestedChangesIndex: number): RefactorEditInfo {
const { scopes, readsAndWrites: { target, usagesPerScope, functionErrorsPerScope } } = getPossibleExtractionsWorker(targetRange, context);
const { scopes, readsAndWrites: { target, usagesPerScope, functionErrorsPerScope, exposedVariableDeclarations } } = getPossibleExtractionsWorker(targetRange, context);
Debug.assert(!functionErrorsPerScope[requestedChangesIndex].length, "The extraction went missing? How?");
context.cancellationToken.throwIfCancellationRequested();
return extractFunctionInScope(target, scopes[requestedChangesIndex], usagesPerScope[requestedChangesIndex], targetRange, context);
return extractFunctionInScope(target, scopes[requestedChangesIndex], usagesPerScope[requestedChangesIndex], exposedVariableDeclarations, targetRange, context);
}
function getConstantExtractionAtIndex(targetRange: TargetRange, context: RefactorContext, requestedChangesIndex: number): RefactorEditInfo {
const { scopes, readsAndWrites: { target, usagesPerScope, constantErrorsPerScope } } = getPossibleExtractionsWorker(targetRange, context);
const { scopes, readsAndWrites: { target, usagesPerScope, constantErrorsPerScope, exposedVariableDeclarations } } = getPossibleExtractionsWorker(targetRange, context);
Debug.assert(!constantErrorsPerScope[requestedChangesIndex].length, "The extraction went missing? How?");
Debug.assert(exposedVariableDeclarations.length === 0, "Extract constant accepted a range containing a variable declaration?");
context.cancellationToken.throwIfCancellationRequested();
const expression = isExpression(target)
? target
@@ -660,7 +664,7 @@ namespace ts.refactor.extractSymbol {
function getUniqueName(baseName: string, fileText: string): string {
let nameText = baseName;
for (let i = 1; fileText.indexOf(nameText) !== -1; i++) {
for (let i = 1; stringContains(fileText, nameText); i++) {
nameText = `${baseName}_${i}`;
}
return nameText;
@@ -674,6 +678,7 @@ namespace ts.refactor.extractSymbol {
node: Statement | Expression | Block,
scope: Scope,
{ usages: usagesInScope, typeParameterUsages, substitutions }: ScopeUsages,
exposedVariableDeclarations: ReadonlyArray<VariableDeclaration>,
range: TargetRange,
context: RefactorContext): RefactorEditInfo {
@@ -731,10 +736,12 @@ namespace ts.refactor.extractSymbol {
// to avoid problems when there are literal types present
if (isExpression(node) && !isJS) {
const contextualType = checker.getContextualType(node);
returnType = checker.typeToTypeNode(contextualType);
returnType = checker.typeToTypeNode(contextualType, scope, NodeBuilderFlags.NoTruncation);
}
const { body, returnValueProperty } = transformFunctionBody(node, writes, substitutions, !!(range.facts & RangeFacts.HasReturn));
const { body, returnValueProperty } = transformFunctionBody(node, exposedVariableDeclarations, writes, substitutions, !!(range.facts & RangeFacts.HasReturn));
suppressLeadingAndTrailingTrivia(body);
let newFunction: MethodDeclaration | FunctionDeclaration;
if (isClassLike(scope)) {
@@ -778,7 +785,10 @@ namespace ts.refactor.extractSymbol {
changeTracker.insertNodeBefore(context.file, nodeToInsertBefore, newFunction, { suffix: context.newLineCharacter + context.newLineCharacter });
}
else {
changeTracker.insertNodeBefore(context.file, scope.getLastToken(), newFunction, { prefix: context.newLineCharacter, suffix: context.newLineCharacter });
changeTracker.insertNodeBefore(context.file, scope.getLastToken(), newFunction, {
prefix: isLineBreak(file.text.charCodeAt(scope.getLastToken().pos)) ? context.newLineCharacter : context.newLineCharacter + context.newLineCharacter,
suffix: context.newLineCharacter
});
}
const newNodes: Node[] = [];
@@ -796,38 +806,114 @@ namespace ts.refactor.extractSymbol {
call = createAwait(call);
}
if (writes) {
if (exposedVariableDeclarations.length && !writes) {
// No need to mix declarations and writes.
// How could any variables be exposed if there's a return statement?
Debug.assert(!returnValueProperty);
Debug.assert(!(range.facts & RangeFacts.HasReturn));
if (exposedVariableDeclarations.length === 1) {
// Declaring exactly one variable: let x = newFunction();
const variableDeclaration = exposedVariableDeclarations[0];
newNodes.push(createVariableStatement(
/*modifiers*/ undefined,
createVariableDeclarationList(
[createVariableDeclaration(getSynthesizedDeepClone(variableDeclaration.name), /*type*/ getSynthesizedDeepClone(variableDeclaration.type), /*initializer*/ call)], // TODO (acasey): test binding patterns
variableDeclaration.parent.flags)));
}
else {
// Declaring multiple variables / return properties:
// let {x, y} = newFunction();
const bindingElements: BindingElement[] = [];
const typeElements: TypeElement[] = [];
let commonNodeFlags = exposedVariableDeclarations[0].parent.flags;
let sawExplicitType = false;
for (const variableDeclaration of exposedVariableDeclarations) {
bindingElements.push(createBindingElement(
/*dotDotDotToken*/ undefined,
/*propertyName*/ undefined,
/*name*/ getSynthesizedDeepClone(variableDeclaration.name)));
// Being returned through an object literal will have widened the type.
const variableType: TypeNode = checker.typeToTypeNode(
checker.getBaseTypeOfLiteralType(checker.getTypeAtLocation(variableDeclaration)),
scope,
NodeBuilderFlags.NoTruncation);
typeElements.push(createPropertySignature(
/*modifiers*/ undefined,
/*name*/ variableDeclaration.symbol.name,
/*questionToken*/ undefined,
/*type*/ variableType,
/*initializer*/ undefined));
sawExplicitType = sawExplicitType || variableDeclaration.type !== undefined;
commonNodeFlags = commonNodeFlags & variableDeclaration.parent.flags;
}
const typeLiteral: TypeLiteralNode | undefined = sawExplicitType ? createTypeLiteralNode(typeElements) : undefined;
if (typeLiteral) {
setEmitFlags(typeLiteral, EmitFlags.SingleLine);
}
newNodes.push(createVariableStatement(
/*modifiers*/ undefined,
createVariableDeclarationList(
[createVariableDeclaration(
createObjectBindingPattern(bindingElements),
/*type*/ typeLiteral,
/*initializer*/call)],
commonNodeFlags)));
}
}
else if (exposedVariableDeclarations.length || writes) {
if (exposedVariableDeclarations.length) {
// CONSIDER: we're going to create one statement per variable, but we could actually preserve their original grouping.
for (const variableDeclaration of exposedVariableDeclarations) {
let flags: NodeFlags = variableDeclaration.parent.flags;
if (flags & NodeFlags.Const) {
flags = (flags & ~NodeFlags.Const) | NodeFlags.Let;
}
newNodes.push(createVariableStatement(
/*modifiers*/ undefined,
createVariableDeclarationList(
[createVariableDeclaration(variableDeclaration.symbol.name, getTypeDeepCloneUnionUndefined(variableDeclaration.type))],
flags)));
}
}
if (returnValueProperty) {
// has both writes and return, need to create variable declaration to hold return value;
newNodes.push(createVariableStatement(
/*modifiers*/ undefined,
[createVariableDeclaration(returnValueProperty, createKeywordTypeNode(SyntaxKind.AnyKeyword))]
));
createVariableDeclarationList(
[createVariableDeclaration(returnValueProperty, getTypeDeepCloneUnionUndefined(returnType))],
NodeFlags.Let)));
}
const assignments = getPropertyAssignmentsForWrites(writes);
const assignments = getPropertyAssignmentsForWritesAndVariableDeclarations(exposedVariableDeclarations, writes);
if (returnValueProperty) {
assignments.unshift(createShorthandPropertyAssignment(returnValueProperty));
}
// propagate writes back
if (assignments.length === 1) {
if (returnValueProperty) {
newNodes.push(createReturn(createIdentifier(returnValueProperty)));
}
else {
newNodes.push(createStatement(createBinary(assignments[0].name, SyntaxKind.EqualsToken, call)));
// We would only have introduced a return value property if there had been
// other assignments to make.
Debug.assert(!returnValueProperty);
if (range.facts & RangeFacts.HasReturn) {
newNodes.push(createReturn());
}
newNodes.push(createStatement(createAssignment(assignments[0].name, call)));
if (range.facts & RangeFacts.HasReturn) {
newNodes.push(createReturn());
}
}
else {
// emit e.g.
// { a, b, __return } = newFunction(a, b);
// return __return;
newNodes.push(createStatement(createBinary(createObjectLiteral(assignments), SyntaxKind.EqualsToken, call)));
newNodes.push(createStatement(createAssignment(createObjectLiteral(assignments), call)));
if (returnValueProperty) {
newNodes.push(createReturn(createIdentifier(returnValueProperty)));
}
@@ -845,15 +931,10 @@ namespace ts.refactor.extractSymbol {
}
}
if (isReadonlyArray(range.range)) {
changeTracker.replaceNodesWithNodes(context.file, range.range, newNodes, {
nodeSeparator: context.newLineCharacter,
suffix: context.newLineCharacter // insert newline only when replacing statements
});
}
else {
changeTracker.replaceNodeWithNodes(context.file, range.range, newNodes, { nodeSeparator: context.newLineCharacter });
}
const replacementRange = isReadonlyArray(range.range)
? { pos: first(range.range).getStart(), end: last(range.range).end }
: { pos: range.range.getStart(), end: range.range.end };
changeTracker.replaceRangeWithNodes(context.file, replacementRange, newNodes, { nodeSeparator: context.newLineCharacter });
const edits = changeTracker.getChanges();
const renameRange = isReadonlyArray(range.range) ? first(range.range) : range.range;
@@ -861,6 +942,21 @@ namespace ts.refactor.extractSymbol {
const renameFilename = renameRange.getSourceFile().fileName;
const renameLocation = getRenameLocation(edits, renameFilename, functionNameText, /*isDeclaredBeforeUse*/ false);
return { renameFilename, renameLocation, edits };
function getTypeDeepCloneUnionUndefined(typeNode: TypeNode | undefined): TypeNode | undefined {
if (typeNode === undefined) {
return undefined;
}
const clone = getSynthesizedDeepClone(typeNode);
let withoutParens = clone;
while (isParenthesizedTypeNode(withoutParens)) {
withoutParens = withoutParens.type;
}
return isUnionTypeNode(withoutParens) && find(withoutParens.types, t => t.kind === SyntaxKind.UndefinedKeyword)
? clone
: createUnionTypeNode([clone, createKeywordTypeNode(SyntaxKind.UndefinedKeyword)]);
}
}
/**
@@ -883,9 +979,10 @@ namespace ts.refactor.extractSymbol {
const variableType = isJS
? undefined
: checker.typeToTypeNode(checker.getContextualType(node));
: checker.typeToTypeNode(checker.getContextualType(node), scope, NodeBuilderFlags.NoTruncation);
const initializer = transformConstantInitializer(node, substitutions);
suppressLeadingAndTrailingTrivia(initializer);
const changeTracker = textChanges.ChangeTracker.fromContext(context);
@@ -918,7 +1015,7 @@ namespace ts.refactor.extractSymbol {
changeTracker.insertNodeBefore(context.file, nodeToInsertBefore, newVariable, { suffix: context.newLineCharacter + context.newLineCharacter });
// Consume
changeTracker.replaceNodeWithNodes(context.file, node, [localReference], { nodeSeparator: context.newLineCharacter });
changeTracker.replaceRange(context.file, { pos: node.getStart(), end: node.end }, localReference);
}
else {
const newVariableDeclaration = createVariableDeclaration(localNameText, variableType, initializer);
@@ -1088,21 +1185,22 @@ namespace ts.refactor.extractSymbol {
}
}
function transformFunctionBody(body: Node, writes: ReadonlyArray<UsageEntry>, substitutions: ReadonlyMap<() => Node>, hasReturn: boolean): { body: Block, returnValueProperty: string } {
if (isBlock(body) && !writes && substitutions.size === 0) {
// already block, no writes to propagate back, no substitutions - can use node as is
function transformFunctionBody(body: Node, exposedVariableDeclarations: ReadonlyArray<VariableDeclaration>, writes: ReadonlyArray<UsageEntry>, substitutions: ReadonlyMap<Node>, hasReturn: boolean): { body: Block, returnValueProperty: string } {
const hasWritesOrVariableDeclarations = writes !== undefined || exposedVariableDeclarations.length > 0;
if (isBlock(body) && !hasWritesOrVariableDeclarations && substitutions.size === 0) {
// already block, no declarations or writes to propagate back, no substitutions - can use node as is
return { body: createBlock(body.statements, /*multLine*/ true), returnValueProperty: undefined };
}
let returnValueProperty: string;
let ignoreReturns = false;
const statements = createNodeArray(isBlock(body) ? body.statements.slice(0) : [isStatement(body) ? body : createReturn(<Expression>body)]);
// rewrite body if either there are writes that should be propagated back via return statements or there are substitutions
if (writes || substitutions.size) {
if (hasWritesOrVariableDeclarations || substitutions.size) {
const rewrittenStatements = visitNodes(statements, visitor).slice();
if (writes && !hasReturn && isStatement(body)) {
if (hasWritesOrVariableDeclarations && !hasReturn && isStatement(body)) {
// add return at the end to propagate writes back in case if control flow falls out of the function body
// it is ok to know that range has at least one return since it we only allow unconditional returns
const assignments = getPropertyAssignmentsForWrites(writes);
const assignments = getPropertyAssignmentsForWritesAndVariableDeclarations(exposedVariableDeclarations, writes);
if (assignments.length === 1) {
rewrittenStatements.push(createReturn(assignments[0].name));
}
@@ -1117,8 +1215,8 @@ namespace ts.refactor.extractSymbol {
}
function visitor(node: Node): VisitResult<Node> {
if (!ignoreReturns && node.kind === SyntaxKind.ReturnStatement && writes) {
const assignments: ObjectLiteralElementLike[] = getPropertyAssignmentsForWrites(writes);
if (!ignoreReturns && node.kind === SyntaxKind.ReturnStatement && hasWritesOrVariableDeclarations) {
const assignments: ObjectLiteralElementLike[] = getPropertyAssignmentsForWritesAndVariableDeclarations(exposedVariableDeclarations, writes);
if ((<ReturnStatement>node).expression) {
if (!returnValueProperty) {
returnValueProperty = "__return";
@@ -1136,21 +1234,21 @@ namespace ts.refactor.extractSymbol {
const oldIgnoreReturns = ignoreReturns;
ignoreReturns = ignoreReturns || isFunctionLikeDeclaration(node) || isClassLike(node);
const substitution = substitutions.get(getNodeId(node).toString());
const result = substitution ? substitution() : visitEachChild(node, visitor, nullTransformationContext);
const result = substitution ? getSynthesizedDeepClone(substitution) : visitEachChild(node, visitor, nullTransformationContext);
ignoreReturns = oldIgnoreReturns;
return result;
}
}
}
function transformConstantInitializer(initializer: Expression, substitutions: ReadonlyMap<() => Node>): Expression {
function transformConstantInitializer(initializer: Expression, substitutions: ReadonlyMap<Node>): Expression {
return substitutions.size
? visitor(initializer) as Expression
: initializer;
function visitor(node: Node): VisitResult<Node> {
const substitution = substitutions.get(getNodeId(node).toString());
return substitution ? substitution() : visitEachChild(node, visitor, nullTransformationContext);
return substitution ? getSynthesizedDeepClone(substitution) : visitEachChild(node, visitor, nullTransformationContext);
}
}
@@ -1240,8 +1338,18 @@ namespace ts.refactor.extractSymbol {
}
}
function getPropertyAssignmentsForWrites(writes: ReadonlyArray<UsageEntry>): ShorthandPropertyAssignment[] {
return writes.map(w => createShorthandPropertyAssignment(w.symbol.name));
function getPropertyAssignmentsForWritesAndVariableDeclarations(
exposedVariableDeclarations: ReadonlyArray<ts.VariableDeclaration>,
writes: ReadonlyArray<UsageEntry>) {
const variableAssignments = map(exposedVariableDeclarations, v => createShorthandPropertyAssignment(v.symbol.name));
const writeAssignments = map(writes, w => createShorthandPropertyAssignment(w.symbol.name));
return variableAssignments === undefined
? writeAssignments
: writeAssignments === undefined
? variableAssignments
: variableAssignments.concat(writeAssignments);
}
function isReadonlyArray(v: any): v is ReadonlyArray<any> {
@@ -1279,7 +1387,7 @@ namespace ts.refactor.extractSymbol {
interface ScopeUsages {
readonly usages: Map<UsageEntry>;
readonly typeParameterUsages: Map<TypeParameter>; // Key is type ID
readonly substitutions: Map<() => Node>;
readonly substitutions: Map<Node>;
}
interface ReadsAndWrites {
@@ -1287,6 +1395,7 @@ namespace ts.refactor.extractSymbol {
readonly usagesPerScope: ReadonlyArray<ScopeUsages>;
readonly functionErrorsPerScope: ReadonlyArray<ReadonlyArray<Diagnostic>>;
readonly constantErrorsPerScope: ReadonlyArray<ReadonlyArray<Diagnostic>>;
readonly exposedVariableDeclarations: ReadonlyArray<VariableDeclaration>;
}
function collectReadsAndWrites(
targetRange: TargetRange,
@@ -1298,10 +1407,13 @@ namespace ts.refactor.extractSymbol {
const allTypeParameterUsages = createMap<TypeParameter>(); // Key is type ID
const usagesPerScope: ScopeUsages[] = [];
const substitutionsPerScope: Map<() => Node>[] = [];
const substitutionsPerScope: Map<Node>[] = [];
const functionErrorsPerScope: Diagnostic[][] = [];
const constantErrorsPerScope: Diagnostic[][] = [];
const visibleDeclarationsInExtractedRange: Symbol[] = [];
const visibleDeclarationsInExtractedRange: NamedDeclaration[] = [];
const exposedVariableSymbolSet = createMap<true>(); // Key is symbol ID
const exposedVariableDeclarations: VariableDeclaration[] = [];
let firstExposedNonVariableDeclaration: NamedDeclaration | undefined = undefined;
const expression = !isReadonlyArray(targetRange.range)
? targetRange.range
@@ -1322,8 +1434,8 @@ namespace ts.refactor.extractSymbol {
// initialize results
for (const scope of scopes) {
usagesPerScope.push({ usages: createMap<UsageEntry>(), typeParameterUsages: createMap<TypeParameter>(), substitutions: createMap<() => Expression>() });
substitutionsPerScope.push(createMap<() => Expression>());
usagesPerScope.push({ usages: createMap<UsageEntry>(), typeParameterUsages: createMap<TypeParameter>(), substitutions: createMap<Expression>() });
substitutionsPerScope.push(createMap<Expression>());
functionErrorsPerScope.push(
isFunctionLikeDeclaration(scope) && scope.kind !== SyntaxKind.FunctionDeclaration
@@ -1346,7 +1458,6 @@ namespace ts.refactor.extractSymbol {
const seenUsages = createMap<Usage>();
const target = isReadonlyArray(targetRange.range) ? createBlock(<Statement[]>targetRange.range) : targetRange.range;
const containingLexicalScopeOfExtraction = isBlockScope(scopes[0], scopes[0].parent) ? scopes[0] : getEnclosingBlockScopeContainer(scopes[0]);
const unmodifiedNode = isReadonlyArray(targetRange.range) ? first(targetRange.range) : targetRange.range;
const inGenericContext = isInGenericContext(unmodifiedNode);
@@ -1392,6 +1503,15 @@ namespace ts.refactor.extractSymbol {
Debug.assert(i === scopes.length);
}
// If there are any declarations in the extracted block that are used in the same enclosing
// lexical scope, we can't move the extraction "up" as those declarations will become unreachable
if (visibleDeclarationsInExtractedRange.length) {
const containingLexicalScopeOfExtraction = isBlockScope(scopes[0], scopes[0].parent)
? scopes[0]
: getEnclosingBlockScopeContainer(scopes[0]);
forEachChild(containingLexicalScopeOfExtraction, checkForUsedDeclarations);
}
for (let i = 0; i < scopes.length; i++) {
const scopeUsages = usagesPerScope[i];
// Special case: in the innermost scope, all usages are available.
@@ -1415,8 +1535,11 @@ namespace ts.refactor.extractSymbol {
}
});
if (hasWrite && !isReadonlyArray(targetRange.range) && isExpression(targetRange.range)) {
const diag = createDiagnosticForNode(targetRange.range, Messages.CannotCombineWritesAndReturns);
// If an expression was extracted, then there shouldn't have been any variable declarations.
Debug.assert(isReadonlyArray(targetRange.range) || exposedVariableDeclarations.length === 0);
if (hasWrite && !isReadonlyArray(targetRange.range)) {
const diag = createDiagnosticForNode(targetRange.range, Messages.CannotWriteInExpression);
functionErrorsPerScope[i].push(diag);
constantErrorsPerScope[i].push(diag);
}
@@ -1425,15 +1548,14 @@ namespace ts.refactor.extractSymbol {
functionErrorsPerScope[i].push(diag);
constantErrorsPerScope[i].push(diag);
}
else if (firstExposedNonVariableDeclaration) {
const diag = createDiagnosticForNode(firstExposedNonVariableDeclaration, Messages.CannotExtractExportedEntity);
functionErrorsPerScope[i].push(diag);
constantErrorsPerScope[i].push(diag);
}
}
// If there are any declarations in the extracted block that are used in the same enclosing
// lexical scope, we can't move the extraction "up" as those declarations will become unreachable
if (visibleDeclarationsInExtractedRange.length) {
forEachChild(containingLexicalScopeOfExtraction, checkForUsedDeclarations);
}
return { target, usagesPerScope, functionErrorsPerScope, constantErrorsPerScope };
return { target, usagesPerScope, functionErrorsPerScope, constantErrorsPerScope, exposedVariableDeclarations };
function hasTypeParameters(node: Node) {
return isDeclarationWithTypeParameters(node) &&
@@ -1472,7 +1594,7 @@ namespace ts.refactor.extractSymbol {
}
if (isDeclaration(node) && node.symbol) {
visibleDeclarationsInExtractedRange.push(node.symbol);
visibleDeclarationsInExtractedRange.push(node);
}
if (isAssignmentExpression(node)) {
@@ -1518,11 +1640,7 @@ namespace ts.refactor.extractSymbol {
}
function recordUsagebySymbol(identifier: Identifier, usage: Usage, isTypeName: boolean) {
// If the identifier is both a property name and its value, we're only interested in its value
// (since the name is a declaration and will be included in the extracted range).
const symbol = identifier.parent && isShorthandPropertyAssignment(identifier.parent) && identifier.parent.name === identifier
? checker.getShorthandAssignmentValueSymbol(identifier.parent)
: checker.getSymbolAtLocation(identifier);
const symbol = getSymbolReferencedByIdentifier(identifier);
if (!symbol) {
// cannot find symbol - do nothing
return undefined;
@@ -1606,36 +1724,55 @@ namespace ts.refactor.extractSymbol {
}
// Otherwise check and recurse.
const sym = checker.getSymbolAtLocation(node);
if (sym && visibleDeclarationsInExtractedRange.some(d => d === sym)) {
const diag = createDiagnosticForNode(node, Messages.CannotExtractExportedEntity);
for (const errors of functionErrorsPerScope) {
errors.push(diag);
const sym = isIdentifier(node)
? getSymbolReferencedByIdentifier(node)
: checker.getSymbolAtLocation(node);
if (sym) {
const decl = find(visibleDeclarationsInExtractedRange, d => d.symbol === sym);
if (decl) {
if (isVariableDeclaration(decl)) {
const idString = decl.symbol.id.toString();
if (!exposedVariableSymbolSet.has(idString)) {
exposedVariableDeclarations.push(decl);
exposedVariableSymbolSet.set(idString, true);
}
}
else {
// CONSIDER: this includes binding elements, which we could
// expose in the same way as variables.
firstExposedNonVariableDeclaration = firstExposedNonVariableDeclaration || decl;
}
}
for (const errors of constantErrorsPerScope) {
errors.push(diag);
}
return true;
}
else {
forEachChild(node, checkForUsedDeclarations);
}
forEachChild(node, checkForUsedDeclarations);
}
function tryReplaceWithQualifiedNameOrPropertyAccess(symbol: Symbol, scopeDecl: Node, isTypeNode: boolean): () => (PropertyAccessExpression | EntityName) {
/**
* Return the symbol referenced by an identifier (even if it declares a different symbol).
*/
function getSymbolReferencedByIdentifier(identifier: Identifier) {
// If the identifier is both a property name and its value, we're only interested in its value
// (since the name is a declaration and will be included in the extracted range).
return identifier.parent && isShorthandPropertyAssignment(identifier.parent) && identifier.parent.name === identifier
? checker.getShorthandAssignmentValueSymbol(identifier.parent)
: checker.getSymbolAtLocation(identifier);
}
function tryReplaceWithQualifiedNameOrPropertyAccess(symbol: Symbol, scopeDecl: Node, isTypeNode: boolean): PropertyAccessExpression | EntityName {
if (!symbol) {
return undefined;
}
if (symbol.getDeclarations().some(d => d.parent === scopeDecl)) {
return () => createIdentifier(symbol.name);
return createIdentifier(symbol.name);
}
const prefix = tryReplaceWithQualifiedNameOrPropertyAccess(symbol.parent, scopeDecl, isTypeNode);
if (prefix === undefined) {
return undefined;
}
return isTypeNode
? () => createQualifiedName(<EntityName>prefix(), createIdentifier(symbol.name))
: () => createPropertyAccess(<Expression>prefix(), symbol.name);
? createQualifiedName(<EntityName>prefix, createIdentifier(symbol.name))
: createPropertyAccess(<Expression>prefix, symbol.name);
}
}
+1 -1
View File
@@ -1952,7 +1952,7 @@ namespace ts {
function isNodeModulesFile(path: string): boolean {
const node_modulesFolderName = "/node_modules/";
return path.indexOf(node_modulesFolderName) !== -1;
return stringContains(path, node_modulesFolderName);
}
}
+16 -16
View File
@@ -16,7 +16,7 @@
/// <reference path='services.ts' />
/* @internal */
let debugObjectHost = (function (this: any) { return this; })();
let debugObjectHost: { CollectGarbage(): void } = (function (this: any) { return this; })();
// We need to use 'null' to interface with the managed side.
/* tslint:disable:no-null-keyword */
@@ -119,13 +119,13 @@ namespace ts {
}
export interface Shim {
dispose(_dummy: any): void;
dispose(_dummy: {}): void;
}
export interface LanguageServiceShim extends Shim {
languageService: LanguageService;
dispose(_dummy: any): void;
dispose(_dummy: {}): void;
refresh(throwOnError: boolean): void;
@@ -417,7 +417,7 @@ namespace ts {
return this.shimHost.getScriptVersion(fileName);
}
public getLocalizedDiagnosticMessages(): any {
public getLocalizedDiagnosticMessages() {
const diagnosticMessagesJson = this.shimHost.getLocalizedDiagnosticMessages();
if (diagnosticMessagesJson === null || diagnosticMessagesJson === "") {
return null;
@@ -515,7 +515,7 @@ namespace ts {
}
}
function simpleForwardCall(logger: Logger, actionDescription: string, action: () => any, logPerformance: boolean): any {
function simpleForwardCall(logger: Logger, actionDescription: string, action: () => {}, logPerformance: boolean): {} {
let start: number;
if (logPerformance) {
logger.log(actionDescription);
@@ -539,14 +539,14 @@ namespace ts {
return result;
}
function forwardJSONCall(logger: Logger, actionDescription: string, action: () => any, logPerformance: boolean): string {
function forwardJSONCall(logger: Logger, actionDescription: string, action: () => {}, logPerformance: boolean): string {
return <string>forwardCall(logger, actionDescription, /*returnJson*/ true, action, logPerformance);
}
function forwardCall<T>(logger: Logger, actionDescription: string, returnJson: boolean, action: () => T, logPerformance: boolean): T | string {
try {
const result = simpleForwardCall(logger, actionDescription, action, logPerformance);
return returnJson ? JSON.stringify({ result }) : result;
return returnJson ? JSON.stringify({ result }) : result as T;
}
catch (err) {
if (err instanceof OperationCanceledException) {
@@ -563,7 +563,7 @@ namespace ts {
constructor(private factory: ShimFactory) {
factory.registerShim(this);
}
public dispose(_dummy: any): void {
public dispose(_dummy: {}): void {
this.factory.unregisterShim(this);
}
}
@@ -601,7 +601,7 @@ namespace ts {
this.logger = this.host;
}
public forwardJSONCall(actionDescription: string, action: () => any): string {
public forwardJSONCall(actionDescription: string, action: () => {}): string {
return forwardJSONCall(this.logger, actionDescription, action, this.logPerformance);
}
@@ -611,7 +611,7 @@ namespace ts {
* Ensure (almost) deterministic release of internal Javascript resources when
* some external native objects holds onto us (e.g. Com/Interop).
*/
public dispose(dummy: any): void {
public dispose(dummy: {}): void {
this.logger.log("dispose()");
this.languageService.dispose();
this.languageService = null;
@@ -635,7 +635,7 @@ namespace ts {
public refresh(throwOnError: boolean): void {
this.forwardJSONCall(
`refresh(${throwOnError})`,
() => <any>null
() => null
);
}
@@ -644,7 +644,7 @@ namespace ts {
"cleanupSemanticCache()",
() => {
this.languageService.cleanupSemanticCache();
return <any>null;
return null;
});
}
@@ -980,13 +980,13 @@ namespace ts {
);
}
public getEmitOutputObject(fileName: string): any {
public getEmitOutputObject(fileName: string): EmitOutput {
return forwardCall(
this.logger,
`getEmitOutput('${fileName}')`,
/*returnJson*/ false,
() => this.languageService.getEmitOutput(fileName),
this.logPerformance);
this.logPerformance) as EmitOutput;
}
}
@@ -1030,7 +1030,7 @@ namespace ts {
super(factory);
}
private forwardJSONCall(actionDescription: string, action: () => any): any {
private forwardJSONCall(actionDescription: string, action: () => {}): string {
return forwardJSONCall(this.logger, actionDescription, action, this.logPerformance);
}
@@ -1221,7 +1221,7 @@ namespace ts {
// Here we expose the TypeScript services as an external module
// so that it may be consumed easily like a node module.
declare const module: any;
declare const module: { exports: {} };
if (typeof module !== "undefined" && module.exports) {
module.exports = ts;
}
+13 -7
View File
@@ -341,13 +341,19 @@ namespace ts.SymbolDisplay {
}
if (symbolFlags & SymbolFlags.Alias) {
addNewLineIfDisplayPartsExist();
if (symbol.declarations[0].kind === SyntaxKind.NamespaceExportDeclaration) {
displayParts.push(keywordPart(SyntaxKind.ExportKeyword));
displayParts.push(spacePart());
displayParts.push(keywordPart(SyntaxKind.NamespaceKeyword));
}
else {
displayParts.push(keywordPart(SyntaxKind.ImportKeyword));
switch (symbol.declarations[0].kind) {
case SyntaxKind.NamespaceExportDeclaration:
displayParts.push(keywordPart(SyntaxKind.ExportKeyword));
displayParts.push(spacePart());
displayParts.push(keywordPart(SyntaxKind.NamespaceKeyword));
break;
case SyntaxKind.ExportAssignment:
displayParts.push(keywordPart(SyntaxKind.ExportKeyword));
displayParts.push(spacePart());
displayParts.push(keywordPart((symbol.declarations[0] as ExportAssignment).isExportEquals ? SyntaxKind.EqualsToken : SyntaxKind.DefaultKeyword));
break;
default:
displayParts.push(keywordPart(SyntaxKind.ImportKeyword));
}
displayParts.push(spacePart());
addFullSymbolName(symbol);
+70
View File
@@ -1334,4 +1334,74 @@ namespace ts {
}
return position;
}
/**
* Creates a deep, memberwise clone of a node with no source map location.
*
* WARNING: This is an expensive operation and is only intended to be used in refactorings
* and code fixes (because those are triggered by explicit user actions).
*/
export function getSynthesizedDeepClone<T extends Node>(node: T | undefined): T | undefined {
if (node === undefined) {
return undefined;
}
const visited = visitEachChild(node, getSynthesizedDeepClone, nullTransformationContext);
if (visited === node) {
// This only happens for leaf nodes - internal nodes always see their children change.
const clone = getSynthesizedClone(node);
if (isStringLiteral(clone)) {
clone.textSourceNode = node as any;
}
else if (isNumericLiteral(clone)) {
clone.numericLiteralFlags = (node as any).numericLiteralFlags;
}
clone.pos = node.pos;
clone.end = node.end;
return clone;
}
// PERF: As an optimization, rather than calling getSynthesizedClone, we'll update
// the new node created by visitEachChild with the extra changes getSynthesizedClone
// would have made.
visited.parent = undefined;
return visited;
}
/**
* Sets EmitFlags to suppress leading and trailing trivia on the node.
*/
/* @internal */
export function suppressLeadingAndTrailingTrivia(node: Node) {
Debug.assert(node !== undefined);
suppressLeading(node);
suppressTrailing(node);
function suppressLeading(node: Node) {
addEmitFlags(node, EmitFlags.NoLeadingComments);
const firstChild = forEachChild(node, child => child);
firstChild && suppressLeading(firstChild);
}
function suppressTrailing(node: Node) {
addEmitFlags(node, EmitFlags.NoTrailingComments);
let lastChild: Node = undefined;
forEachChild(
node,
child => (lastChild = child, undefined),
children => {
// As an optimization, jump straight to the end of the list.
if (children.length) {
lastChild = last(children);
}
return undefined;
});
lastChild && suppressTrailing(lastChild);
}
}
}
@@ -0,0 +1,37 @@
tests/cases/compiler/abstractPropertyInConstructor.ts(4,24): error TS2715: Abstract property 'prop' in class 'AbstractClass' cannot be accessed in the constructor.
tests/cases/compiler/abstractPropertyInConstructor.ts(7,18): error TS2715: Abstract property 'prop' in class 'AbstractClass' cannot be accessed in the constructor.
tests/cases/compiler/abstractPropertyInConstructor.ts(9,14): error TS2715: Abstract property 'cb' in class 'AbstractClass' cannot be accessed in the constructor.
==== tests/cases/compiler/abstractPropertyInConstructor.ts (3 errors) ====
abstract class AbstractClass {
constructor(str: string) {
this.method(parseInt(str));
let val = this.prop.toLowerCase();
~~~~
!!! error TS2715: Abstract property 'prop' in class 'AbstractClass' cannot be accessed in the constructor.
if (!str) {
this.prop = "Hello World";
~~~~
!!! error TS2715: Abstract property 'prop' in class 'AbstractClass' cannot be accessed in the constructor.
}
this.cb(str);
~~
!!! error TS2715: Abstract property 'cb' in class 'AbstractClass' cannot be accessed in the constructor.
const innerFunction = () => {
return this.prop;
}
}
abstract prop: string;
abstract cb: (s: string) => void;
abstract method(num: number): void;
method2() {
this.prop = this.prop + "!";
}
}
@@ -0,0 +1,46 @@
//// [abstractPropertyInConstructor.ts]
abstract class AbstractClass {
constructor(str: string) {
this.method(parseInt(str));
let val = this.prop.toLowerCase();
if (!str) {
this.prop = "Hello World";
}
this.cb(str);
const innerFunction = () => {
return this.prop;
}
}
abstract prop: string;
abstract cb: (s: string) => void;
abstract method(num: number): void;
method2() {
this.prop = this.prop + "!";
}
}
//// [abstractPropertyInConstructor.js]
var AbstractClass = /** @class */ (function () {
function AbstractClass(str) {
var _this = this;
this.method(parseInt(str));
var val = this.prop.toLowerCase();
if (!str) {
this.prop = "Hello World";
}
this.cb(str);
var innerFunction = function () {
return _this.prop;
};
}
AbstractClass.prototype.method2 = function () {
this.prop = this.prop + "!";
};
return AbstractClass;
}());
@@ -0,0 +1,70 @@
=== tests/cases/compiler/abstractPropertyInConstructor.ts ===
abstract class AbstractClass {
>AbstractClass : Symbol(AbstractClass, Decl(abstractPropertyInConstructor.ts, 0, 0))
constructor(str: string) {
>str : Symbol(str, Decl(abstractPropertyInConstructor.ts, 1, 16))
this.method(parseInt(str));
>this.method : Symbol(AbstractClass.method, Decl(abstractPropertyInConstructor.ts, 16, 37))
>this : Symbol(AbstractClass, Decl(abstractPropertyInConstructor.ts, 0, 0))
>method : Symbol(AbstractClass.method, Decl(abstractPropertyInConstructor.ts, 16, 37))
>parseInt : Symbol(parseInt, Decl(lib.d.ts, --, --))
>str : Symbol(str, Decl(abstractPropertyInConstructor.ts, 1, 16))
let val = this.prop.toLowerCase();
>val : Symbol(val, Decl(abstractPropertyInConstructor.ts, 3, 11))
>this.prop.toLowerCase : Symbol(String.toLowerCase, Decl(lib.d.ts, --, --))
>this.prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 13, 5))
>this : Symbol(AbstractClass, Decl(abstractPropertyInConstructor.ts, 0, 0))
>prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 13, 5))
>toLowerCase : Symbol(String.toLowerCase, Decl(lib.d.ts, --, --))
if (!str) {
>str : Symbol(str, Decl(abstractPropertyInConstructor.ts, 1, 16))
this.prop = "Hello World";
>this.prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 13, 5))
>this : Symbol(AbstractClass, Decl(abstractPropertyInConstructor.ts, 0, 0))
>prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 13, 5))
}
this.cb(str);
>this.cb : Symbol(AbstractClass.cb, Decl(abstractPropertyInConstructor.ts, 15, 26))
>this : Symbol(AbstractClass, Decl(abstractPropertyInConstructor.ts, 0, 0))
>cb : Symbol(AbstractClass.cb, Decl(abstractPropertyInConstructor.ts, 15, 26))
>str : Symbol(str, Decl(abstractPropertyInConstructor.ts, 1, 16))
const innerFunction = () => {
>innerFunction : Symbol(innerFunction, Decl(abstractPropertyInConstructor.ts, 10, 13))
return this.prop;
>this.prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 13, 5))
>this : Symbol(AbstractClass, Decl(abstractPropertyInConstructor.ts, 0, 0))
>prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 13, 5))
}
}
abstract prop: string;
>prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 13, 5))
abstract cb: (s: string) => void;
>cb : Symbol(AbstractClass.cb, Decl(abstractPropertyInConstructor.ts, 15, 26))
>s : Symbol(s, Decl(abstractPropertyInConstructor.ts, 16, 18))
abstract method(num: number): void;
>method : Symbol(AbstractClass.method, Decl(abstractPropertyInConstructor.ts, 16, 37))
>num : Symbol(num, Decl(abstractPropertyInConstructor.ts, 18, 20))
method2() {
>method2 : Symbol(AbstractClass.method2, Decl(abstractPropertyInConstructor.ts, 18, 39))
this.prop = this.prop + "!";
>this.prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 13, 5))
>this : Symbol(AbstractClass, Decl(abstractPropertyInConstructor.ts, 0, 0))
>prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 13, 5))
>this.prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 13, 5))
>this : Symbol(AbstractClass, Decl(abstractPropertyInConstructor.ts, 0, 0))
>prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 13, 5))
}
}
@@ -0,0 +1,81 @@
=== tests/cases/compiler/abstractPropertyInConstructor.ts ===
abstract class AbstractClass {
>AbstractClass : AbstractClass
constructor(str: string) {
>str : string
this.method(parseInt(str));
>this.method(parseInt(str)) : void
>this.method : (num: number) => void
>this : this
>method : (num: number) => void
>parseInt(str) : number
>parseInt : (s: string, radix?: number) => number
>str : string
let val = this.prop.toLowerCase();
>val : string
>this.prop.toLowerCase() : string
>this.prop.toLowerCase : () => string
>this.prop : string
>this : this
>prop : string
>toLowerCase : () => string
if (!str) {
>!str : boolean
>str : string
this.prop = "Hello World";
>this.prop = "Hello World" : "Hello World"
>this.prop : string
>this : this
>prop : string
>"Hello World" : "Hello World"
}
this.cb(str);
>this.cb(str) : void
>this.cb : (s: string) => void
>this : this
>cb : (s: string) => void
>str : string
const innerFunction = () => {
>innerFunction : () => string
>() => { return this.prop; } : () => string
return this.prop;
>this.prop : string
>this : this
>prop : string
}
}
abstract prop: string;
>prop : string
abstract cb: (s: string) => void;
>cb : (s: string) => void
>s : string
abstract method(num: number): void;
>method : (num: number) => void
>num : number
method2() {
>method2 : () => void
this.prop = this.prop + "!";
>this.prop = this.prop + "!" : string
>this.prop : string
>this : this
>prop : string
>this.prop + "!" : string
>this.prop : string
>this : this
>prop : string
>"!" : "!"
}
}
@@ -3,9 +3,9 @@ var paired: any[];
>paired : Symbol(paired, Decl(anyInferenceAnonymousFunctions.ts, 0, 3))
paired.reduce(function (a1, a2) {
>paired.reduce : Symbol(Array.reduce, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>paired.reduce : Symbol(Array.reduce, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>paired : Symbol(paired, Decl(anyInferenceAnonymousFunctions.ts, 0, 3))
>reduce : Symbol(Array.reduce, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>reduce : Symbol(Array.reduce, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>a1 : Symbol(a1, Decl(anyInferenceAnonymousFunctions.ts, 2, 24))
>a2 : Symbol(a2, Decl(anyInferenceAnonymousFunctions.ts, 2, 27))
@@ -15,9 +15,9 @@ paired.reduce(function (a1, a2) {
} , []);
paired.reduce((b1, b2) => {
>paired.reduce : Symbol(Array.reduce, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>paired.reduce : Symbol(Array.reduce, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>paired : Symbol(paired, Decl(anyInferenceAnonymousFunctions.ts, 0, 3))
>reduce : Symbol(Array.reduce, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>reduce : Symbol(Array.reduce, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>b1 : Symbol(b1, Decl(anyInferenceAnonymousFunctions.ts, 8, 15))
>b2 : Symbol(b2, Decl(anyInferenceAnonymousFunctions.ts, 8, 18))
@@ -27,9 +27,9 @@ paired.reduce((b1, b2) => {
} , []);
paired.reduce((b3, b4) => b3.concat({}), []);
>paired.reduce : Symbol(Array.reduce, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>paired.reduce : Symbol(Array.reduce, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>paired : Symbol(paired, Decl(anyInferenceAnonymousFunctions.ts, 0, 3))
>reduce : Symbol(Array.reduce, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>reduce : Symbol(Array.reduce, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>b3 : Symbol(b3, Decl(anyInferenceAnonymousFunctions.ts, 13, 15))
>b4 : Symbol(b4, Decl(anyInferenceAnonymousFunctions.ts, 13, 18))
>b3 : Symbol(b3, Decl(anyInferenceAnonymousFunctions.ts, 13, 15))
@@ -4,9 +4,9 @@ var paired: any[];
paired.reduce(function (a1, a2) {
>paired.reduce(function (a1, a2) { return a1.concat({});} , []) : any
>paired.reduce : { (callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: any[]) => any, initialValue?: any): any; <U>(callbackfn: (previousValue: U, currentValue: any, currentIndex: number, array: any[]) => U, initialValue: U): U; }
>paired.reduce : { (callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: any[]) => any): any; (callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: any[]) => any, initialValue: any): any; <U>(callbackfn: (previousValue: U, currentValue: any, currentIndex: number, array: any[]) => U, initialValue: U): U; }
>paired : any[]
>reduce : { (callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: any[]) => any, initialValue?: any): any; <U>(callbackfn: (previousValue: U, currentValue: any, currentIndex: number, array: any[]) => U, initialValue: U): U; }
>reduce : { (callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: any[]) => any): any; (callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: any[]) => any, initialValue: any): any; <U>(callbackfn: (previousValue: U, currentValue: any, currentIndex: number, array: any[]) => U, initialValue: U): U; }
>function (a1, a2) { return a1.concat({});} : (a1: any, a2: any) => any
>a1 : any
>a2 : any
@@ -23,9 +23,9 @@ paired.reduce(function (a1, a2) {
paired.reduce((b1, b2) => {
>paired.reduce((b1, b2) => { return b1.concat({});} , []) : any
>paired.reduce : { (callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: any[]) => any, initialValue?: any): any; <U>(callbackfn: (previousValue: U, currentValue: any, currentIndex: number, array: any[]) => U, initialValue: U): U; }
>paired.reduce : { (callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: any[]) => any): any; (callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: any[]) => any, initialValue: any): any; <U>(callbackfn: (previousValue: U, currentValue: any, currentIndex: number, array: any[]) => U, initialValue: U): U; }
>paired : any[]
>reduce : { (callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: any[]) => any, initialValue?: any): any; <U>(callbackfn: (previousValue: U, currentValue: any, currentIndex: number, array: any[]) => U, initialValue: U): U; }
>reduce : { (callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: any[]) => any): any; (callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: any[]) => any, initialValue: any): any; <U>(callbackfn: (previousValue: U, currentValue: any, currentIndex: number, array: any[]) => U, initialValue: U): U; }
>(b1, b2) => { return b1.concat({});} : (b1: any, b2: any) => any
>b1 : any
>b2 : any
@@ -42,9 +42,9 @@ paired.reduce((b1, b2) => {
paired.reduce((b3, b4) => b3.concat({}), []);
>paired.reduce((b3, b4) => b3.concat({}), []) : any
>paired.reduce : { (callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: any[]) => any, initialValue?: any): any; <U>(callbackfn: (previousValue: U, currentValue: any, currentIndex: number, array: any[]) => U, initialValue: U): U; }
>paired.reduce : { (callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: any[]) => any): any; (callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: any[]) => any, initialValue: any): any; <U>(callbackfn: (previousValue: U, currentValue: any, currentIndex: number, array: any[]) => U, initialValue: U): U; }
>paired : any[]
>reduce : { (callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: any[]) => any, initialValue?: any): any; <U>(callbackfn: (previousValue: U, currentValue: any, currentIndex: number, array: any[]) => U, initialValue: U): U; }
>reduce : { (callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: any[]) => any): any; (callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: any[]) => any, initialValue: any): any; <U>(callbackfn: (previousValue: U, currentValue: any, currentIndex: number, array: any[]) => U, initialValue: U): U; }
>(b3, b4) => b3.concat({}) : (b3: any, b4: any) => any
>b3 : any
>b4 : any
+19 -9
View File
@@ -1442,6 +1442,10 @@ declare namespace ts {
interface JSDocUnknownTag extends JSDocTag {
kind: SyntaxKind.JSDocTag;
}
/**
* Note that `@extends` is a synonym of `@augments`.
* Both tags are represented by this interface.
*/
interface JSDocAugmentsTag extends JSDocTag {
kind: SyntaxKind.JSDocAugmentsTag;
class: ExpressionWithTypeArguments & {
@@ -1473,7 +1477,7 @@ declare namespace ts {
interface JSDocPropertyLikeTag extends JSDocTag, Declaration {
parent: JSDoc;
name: EntityName;
typeExpression: JSDocTypeExpression;
typeExpression?: JSDocTypeExpression;
/** Whether the property name came before the type -- non-standard for JSDoc, but Typescript-like */
isNameFirst: boolean;
isBracketed: boolean;
@@ -1727,6 +1731,10 @@ declare namespace ts {
signatureToString(signature: Signature, enclosingDeclaration?: Node, flags?: TypeFormatFlags, kind?: SignatureKind): string;
typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string;
symbolToString(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): string;
/**
* @deprecated Use the createX factory functions or XToY typechecker methods and `createPrinter` or the `xToString` methods instead
* This will be removed in a future version.
*/
getSymbolDisplayBuilder(): SymbolDisplayBuilder;
getFullyQualifiedName(symbol: Symbol): string;
getAugmentedPropertiesOfType(type: Type): Symbol[];
@@ -2866,6 +2874,8 @@ declare namespace ts {
function getJSDocReturnType(node: Node): TypeNode | undefined;
/** Get all JSDoc tags related to a node, including those on parent nodes. */
function getJSDocTags(node: Node): ReadonlyArray<JSDocTag> | undefined;
/** Gets all JSDoc tags of a specified kind, or undefined if not present. */
function getAllJSDocTagsOfKind(node: Node, kind: SyntaxKind): ReadonlyArray<JSDocTag> | undefined;
}
declare namespace ts {
function isNumericLiteral(node: Node): node is NumericLiteral;
@@ -3058,6 +3068,8 @@ declare namespace ts {
function isCaseOrDefaultClause(node: Node): node is CaseOrDefaultClause;
/** True if node is of a kind that may contain comment text. */
function isJSDocCommentContainingNode(node: Node): boolean;
function isSetAccessor(node: Node): node is SetAccessorDeclaration;
function isGetAccessor(node: Node): node is GetAccessorDeclaration;
}
declare namespace ts {
interface ErrorCallback {
@@ -7104,7 +7116,7 @@ declare namespace ts.server {
getScriptKind(fileName: string): ScriptKind;
getScriptVersion(filename: string): string;
getScriptSnapshot(filename: string): IScriptSnapshot;
getCancellationToken(): ThrottledCancellationToken;
getCancellationToken(): HostCancellationToken;
getCurrentDirectory(): string;
getDefaultLibFileName(): string;
useCaseSensitiveFileNames(): boolean;
@@ -7131,11 +7143,11 @@ declare namespace ts.server {
enableLanguageService(): void;
disableLanguageService(): void;
getProjectName(): string;
abstract getProjectRootPath(): string | undefined;
abstract getTypeAcquisition(): TypeAcquisition;
getExternalFiles(): SortedReadonlyArray<string>;
getSourceFile(path: Path): SourceFile;
close(): void;
private detachScriptInfoIfNotRoot(uncheckedFilename);
isClosed(): boolean;
hasRoots(): boolean;
getRootFiles(): NormalizedPath[];
@@ -7176,15 +7188,15 @@ declare namespace ts.server {
* the file and its imports/references are put into an InferredProject.
*/
class InferredProject extends Project {
readonly projectRootPath: string | undefined;
private static readonly newName;
private _isJsInferredProject;
toggleJsInferredProject(isJsInferredProject: boolean): void;
setCompilerOptions(options?: CompilerOptions): void;
/** this is canonical project root path */
readonly projectRootPath: string | undefined;
addRoot(info: ScriptInfo): void;
removeRoot(info: ScriptInfo): void;
isProjectWithSingleRoot(): boolean;
getProjectRootPath(): string;
close(): void;
getTypeAcquisition(): TypeAcquisition;
}
@@ -7211,7 +7223,6 @@ declare namespace ts.server {
enablePlugins(): void;
private enablePlugin(pluginConfigEntry, searchPaths);
private enableProxy(pluginModuleFactory, configEntry);
getProjectRootPath(): string;
/**
* Get the errors that dont have any file name associated
*/
@@ -7237,11 +7248,9 @@ declare namespace ts.server {
class ExternalProject extends Project {
externalProjectName: string;
compileOnSaveEnabled: boolean;
private readonly projectFilePath;
excludedFiles: ReadonlyArray<NormalizedPath>;
private typeAcquisition;
getExcludedFiles(): ReadonlyArray<NormalizedPath>;
getProjectRootPath(): string;
getTypeAcquisition(): TypeAcquisition;
setTypeAcquisition(newTypeAcquisition: TypeAcquisition): void;
}
@@ -7517,9 +7526,10 @@ declare namespace ts.server {
private createConfiguredProject(configFileName);
private updateNonInferredProjectFiles<T>(project, files, propertyReader);
private updateNonInferredProject<T>(project, newUncheckedFiles, propertyReader, newOptions, newTypeAcquisition, compileOnSave);
private sendConfigFileDiagEvent(project, triggerFile);
private getOrCreateInferredProjectForProjectRootPathIfEnabled(info, projectRootPath);
private getOrCreateSingleInferredProjectIfEnabled();
private createInferredProject(rootDirectoryForResolution, isSingleInferredProject?, projectRootPath?);
private createInferredProject(currentDirectory, isSingleInferredProject?, projectRootPath?);
getScriptInfo(uncheckedFileName: string): ScriptInfo;
private watchClosedScriptInfo(info);
private stopWatchingScriptInfo(info);
+13 -1
View File
@@ -1442,6 +1442,10 @@ declare namespace ts {
interface JSDocUnknownTag extends JSDocTag {
kind: SyntaxKind.JSDocTag;
}
/**
* Note that `@extends` is a synonym of `@augments`.
* Both tags are represented by this interface.
*/
interface JSDocAugmentsTag extends JSDocTag {
kind: SyntaxKind.JSDocAugmentsTag;
class: ExpressionWithTypeArguments & {
@@ -1473,7 +1477,7 @@ declare namespace ts {
interface JSDocPropertyLikeTag extends JSDocTag, Declaration {
parent: JSDoc;
name: EntityName;
typeExpression: JSDocTypeExpression;
typeExpression?: JSDocTypeExpression;
/** Whether the property name came before the type -- non-standard for JSDoc, but Typescript-like */
isNameFirst: boolean;
isBracketed: boolean;
@@ -1727,6 +1731,10 @@ declare namespace ts {
signatureToString(signature: Signature, enclosingDeclaration?: Node, flags?: TypeFormatFlags, kind?: SignatureKind): string;
typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string;
symbolToString(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): string;
/**
* @deprecated Use the createX factory functions or XToY typechecker methods and `createPrinter` or the `xToString` methods instead
* This will be removed in a future version.
*/
getSymbolDisplayBuilder(): SymbolDisplayBuilder;
getFullyQualifiedName(symbol: Symbol): string;
getAugmentedPropertiesOfType(type: Type): Symbol[];
@@ -2921,6 +2929,8 @@ declare namespace ts {
function getJSDocReturnType(node: Node): TypeNode | undefined;
/** Get all JSDoc tags related to a node, including those on parent nodes. */
function getJSDocTags(node: Node): ReadonlyArray<JSDocTag> | undefined;
/** Gets all JSDoc tags of a specified kind, or undefined if not present. */
function getAllJSDocTagsOfKind(node: Node, kind: SyntaxKind): ReadonlyArray<JSDocTag> | undefined;
}
declare namespace ts {
function isNumericLiteral(node: Node): node is NumericLiteral;
@@ -3113,6 +3123,8 @@ declare namespace ts {
function isCaseOrDefaultClause(node: Node): node is CaseOrDefaultClause;
/** True if node is of a kind that may contain comment text. */
function isJSDocCommentContainingNode(node: Node): boolean;
function isSetAccessor(node: Node): node is SetAccessorDeclaration;
function isGetAccessor(node: Node): node is GetAccessorDeclaration;
}
declare namespace ts {
function createNode(kind: SyntaxKind, pos?: number, end?: number): Node;
+22
View File
@@ -0,0 +1,22 @@
//// [arrayFind.ts]
// test fix for #18112, type guard predicates should narrow returned element
function isNumber(x: any): x is number {
return typeof x === "number";
}
const arrayOfStringsNumbersAndBooleans = ["string", false, 0, "strung", 1, true];
const foundNumber: number | undefined = arrayOfStringsNumbersAndBooleans.find(isNumber);
const readonlyArrayOfStringsNumbersAndBooleans = arrayOfStringsNumbersAndBooleans as ReadonlyArray<string | number | boolean>;
const readonlyFoundNumber: number | undefined = readonlyArrayOfStringsNumbersAndBooleans.find(isNumber);
//// [arrayFind.js]
// test fix for #18112, type guard predicates should narrow returned element
function isNumber(x) {
return typeof x === "number";
}
var arrayOfStringsNumbersAndBooleans = ["string", false, 0, "strung", 1, true];
var foundNumber = arrayOfStringsNumbersAndBooleans.find(isNumber);
var readonlyArrayOfStringsNumbersAndBooleans = arrayOfStringsNumbersAndBooleans;
var readonlyFoundNumber = readonlyArrayOfStringsNumbersAndBooleans.find(isNumber);
@@ -0,0 +1,33 @@
=== tests/cases/compiler/arrayFind.ts ===
// test fix for #18112, type guard predicates should narrow returned element
function isNumber(x: any): x is number {
>isNumber : Symbol(isNumber, Decl(arrayFind.ts, 0, 0))
>x : Symbol(x, Decl(arrayFind.ts, 1, 18))
>x : Symbol(x, Decl(arrayFind.ts, 1, 18))
return typeof x === "number";
>x : Symbol(x, Decl(arrayFind.ts, 1, 18))
}
const arrayOfStringsNumbersAndBooleans = ["string", false, 0, "strung", 1, true];
>arrayOfStringsNumbersAndBooleans : Symbol(arrayOfStringsNumbersAndBooleans, Decl(arrayFind.ts, 5, 5))
const foundNumber: number | undefined = arrayOfStringsNumbersAndBooleans.find(isNumber);
>foundNumber : Symbol(foundNumber, Decl(arrayFind.ts, 6, 5))
>arrayOfStringsNumbersAndBooleans.find : Symbol(Array.find, Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --))
>arrayOfStringsNumbersAndBooleans : Symbol(arrayOfStringsNumbersAndBooleans, Decl(arrayFind.ts, 5, 5))
>find : Symbol(Array.find, Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --))
>isNumber : Symbol(isNumber, Decl(arrayFind.ts, 0, 0))
const readonlyArrayOfStringsNumbersAndBooleans = arrayOfStringsNumbersAndBooleans as ReadonlyArray<string | number | boolean>;
>readonlyArrayOfStringsNumbersAndBooleans : Symbol(readonlyArrayOfStringsNumbersAndBooleans, Decl(arrayFind.ts, 8, 5))
>arrayOfStringsNumbersAndBooleans : Symbol(arrayOfStringsNumbersAndBooleans, Decl(arrayFind.ts, 5, 5))
>ReadonlyArray : Symbol(ReadonlyArray, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --))
const readonlyFoundNumber: number | undefined = readonlyArrayOfStringsNumbersAndBooleans.find(isNumber);
>readonlyFoundNumber : Symbol(readonlyFoundNumber, Decl(arrayFind.ts, 9, 5))
>readonlyArrayOfStringsNumbersAndBooleans.find : Symbol(ReadonlyArray.find, Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --))
>readonlyArrayOfStringsNumbersAndBooleans : Symbol(readonlyArrayOfStringsNumbersAndBooleans, Decl(arrayFind.ts, 8, 5))
>find : Symbol(ReadonlyArray.find, Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --))
>isNumber : Symbol(isNumber, Decl(arrayFind.ts, 0, 0))
+46
View File
@@ -0,0 +1,46 @@
=== tests/cases/compiler/arrayFind.ts ===
// test fix for #18112, type guard predicates should narrow returned element
function isNumber(x: any): x is number {
>isNumber : (x: any) => x is number
>x : any
>x : any
return typeof x === "number";
>typeof x === "number" : boolean
>typeof x : "string" | "number" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : any
>"number" : "number"
}
const arrayOfStringsNumbersAndBooleans = ["string", false, 0, "strung", 1, true];
>arrayOfStringsNumbersAndBooleans : (string | number | boolean)[]
>["string", false, 0, "strung", 1, true] : (string | number | boolean)[]
>"string" : "string"
>false : false
>0 : 0
>"strung" : "strung"
>1 : 1
>true : true
const foundNumber: number | undefined = arrayOfStringsNumbersAndBooleans.find(isNumber);
>foundNumber : number
>arrayOfStringsNumbersAndBooleans.find(isNumber) : number
>arrayOfStringsNumbersAndBooleans.find : { <S extends string | number | boolean>(predicate: (this: void, value: string | number | boolean, index: number, obj: (string | number | boolean)[]) => value is S, thisArg?: any): S; (predicate: (value: string | number | boolean, index: number, obj: (string | number | boolean)[]) => boolean, thisArg?: any): string | number | boolean; }
>arrayOfStringsNumbersAndBooleans : (string | number | boolean)[]
>find : { <S extends string | number | boolean>(predicate: (this: void, value: string | number | boolean, index: number, obj: (string | number | boolean)[]) => value is S, thisArg?: any): S; (predicate: (value: string | number | boolean, index: number, obj: (string | number | boolean)[]) => boolean, thisArg?: any): string | number | boolean; }
>isNumber : (x: any) => x is number
const readonlyArrayOfStringsNumbersAndBooleans = arrayOfStringsNumbersAndBooleans as ReadonlyArray<string | number | boolean>;
>readonlyArrayOfStringsNumbersAndBooleans : ReadonlyArray<string | number | boolean>
>arrayOfStringsNumbersAndBooleans as ReadonlyArray<string | number | boolean> : ReadonlyArray<string | number | boolean>
>arrayOfStringsNumbersAndBooleans : (string | number | boolean)[]
>ReadonlyArray : ReadonlyArray<T>
const readonlyFoundNumber: number | undefined = readonlyArrayOfStringsNumbersAndBooleans.find(isNumber);
>readonlyFoundNumber : number
>readonlyArrayOfStringsNumbersAndBooleans.find(isNumber) : number
>readonlyArrayOfStringsNumbersAndBooleans.find : { <S extends string | number | boolean>(predicate: (this: void, value: string | number | boolean, index: number, obj: ReadonlyArray<string | number | boolean>) => value is S, thisArg?: any): S; (predicate: (value: string | number | boolean, index: number, obj: ReadonlyArray<string | number | boolean>) => boolean, thisArg?: any): string | number | boolean; }
>readonlyArrayOfStringsNumbersAndBooleans : ReadonlyArray<string | number | boolean>
>find : { <S extends string | number | boolean>(predicate: (this: void, value: string | number | boolean, index: number, obj: ReadonlyArray<string | number | boolean>) => value is S, thisArg?: any): S; (predicate: (value: string | number | boolean, index: number, obj: ReadonlyArray<string | number | boolean>) => boolean, thisArg?: any): string | number | boolean; }
>isNumber : (x: any) => x is number
@@ -0,0 +1,58 @@
//// [asyncImportNestedYield.ts]
async function* foo() {
import((await import(yield "foo")).default);
}
//// [asyncImportNestedYield.js]
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [0, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var g = generator.apply(thisArg, _arguments || []), i, q = [];
return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i;
function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
function fulfill(value) { resume("next", value); }
function reject(value) { resume("throw", value); }
function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
};
function foo() {
return __asyncGenerator(this, arguments, function foo_1() {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, "foo"];
case 1: return [4 /*yield*/, __await.apply(void 0, [Promise.resolve().then(function () { return require(_a.sent()); })])];
case 2:
Promise.resolve().then(function () { return require((_a.sent())["default"]); });
return [2 /*return*/];
}
});
});
}
@@ -0,0 +1,6 @@
=== tests/cases/compiler/asyncImportNestedYield.ts ===
async function* foo() {
>foo : Symbol(foo, Decl(asyncImportNestedYield.ts, 0, 0))
import((await import(yield "foo")).default);
}
@@ -0,0 +1,14 @@
=== tests/cases/compiler/asyncImportNestedYield.ts ===
async function* foo() {
>foo : () => AsyncIterableIterator<"foo">
import((await import(yield "foo")).default);
>import((await import(yield "foo")).default) : Promise<any>
>(await import(yield "foo")).default : any
>(await import(yield "foo")) : any
>await import(yield "foo") : any
>import(yield "foo") : Promise<any>
>yield "foo" : any
>"foo" : "foo"
>default : any
}
@@ -0,0 +1,5 @@
//// [castFunctionExpressionShouldBeParenthesized.ts]
(function a() { } as any)().foo()
//// [castFunctionExpressionShouldBeParenthesized.js]
(function a() { }().foo());
@@ -0,0 +1,4 @@
=== tests/cases/compiler/castFunctionExpressionShouldBeParenthesized.ts ===
(function a() { } as any)().foo()
>a : Symbol(a, Decl(castFunctionExpressionShouldBeParenthesized.ts, 0, 1))
@@ -0,0 +1,11 @@
=== tests/cases/compiler/castFunctionExpressionShouldBeParenthesized.ts ===
(function a() { } as any)().foo()
>(function a() { } as any)().foo() : any
>(function a() { } as any)().foo : any
>(function a() { } as any)() : any
>(function a() { } as any) : any
>function a() { } as any : any
>function a() { } : () => void
>a : () => void
>foo : any
@@ -1,7 +1,8 @@
//// [classExtendingNull.ts]
class C1 extends null { }
class C2 extends (null) { }
class C3 extends null { x = 1; }
class C4 extends (null) { x = 1; }
//// [classExtendingNull.js]
var __extends = (this && this.__extends) || (function () {
@@ -26,3 +27,17 @@ var C2 = /** @class */ (function (_super) {
}
return C2;
}((null)));
var C3 = /** @class */ (function (_super) {
__extends(C3, _super);
function C3() {
this.x = 1;
}
return C3;
}(null));
var C4 = /** @class */ (function (_super) {
__extends(C4, _super);
function C4() {
this.x = 1;
}
return C4;
}((null)));
@@ -5,3 +5,11 @@ class C1 extends null { }
class C2 extends (null) { }
>C2 : Symbol(C2, Decl(classExtendingNull.ts, 0, 25))
class C3 extends null { x = 1; }
>C3 : Symbol(C3, Decl(classExtendingNull.ts, 1, 27))
>x : Symbol(C3.x, Decl(classExtendingNull.ts, 2, 23))
class C4 extends (null) { x = 1; }
>C4 : Symbol(C4, Decl(classExtendingNull.ts, 2, 32))
>x : Symbol(C4.x, Decl(classExtendingNull.ts, 3, 25))
@@ -8,3 +8,16 @@ class C2 extends (null) { }
>(null) : null
>null : null
class C3 extends null { x = 1; }
>C3 : C3
>null : null
>x : number
>1 : 1
class C4 extends (null) { x = 1; }
>C4 : C4
>(null) : null
>null : null
>x : number
>1 : 1
@@ -1,18 +1,18 @@
tests/cases/compiler/immutable.d.ts(341,22): error TS2430: Interface 'Keyed<K, V>' incorrectly extends interface 'Collection<K, V>'.
tests/cases/compiler/immutable.ts(341,22): error TS2430: Interface 'Keyed<K, V>' incorrectly extends interface 'Collection<K, V>'.
Types of property 'toSeq' are incompatible.
Type '() => Keyed<K, V>' is not assignable to type '() => this'.
Type 'Keyed<K, V>' is not assignable to type 'this'.
tests/cases/compiler/immutable.d.ts(359,22): error TS2430: Interface 'Indexed<T>' incorrectly extends interface 'Collection<number, T>'.
tests/cases/compiler/immutable.ts(359,22): error TS2430: Interface 'Indexed<T>' incorrectly extends interface 'Collection<number, T>'.
Types of property 'toSeq' are incompatible.
Type '() => Indexed<T>' is not assignable to type '() => this'.
Type 'Indexed<T>' is not assignable to type 'this'.
tests/cases/compiler/immutable.d.ts(391,22): error TS2430: Interface 'Set<T>' incorrectly extends interface 'Collection<never, T>'.
tests/cases/compiler/immutable.ts(391,22): error TS2430: Interface 'Set<T>' incorrectly extends interface 'Collection<never, T>'.
Types of property 'toSeq' are incompatible.
Type '() => Set<T>' is not assignable to type '() => this'.
Type 'Set<T>' is not assignable to type 'this'.
==== tests/cases/compiler/complex.d.ts (0 errors) ====
==== tests/cases/compiler/complex.ts (0 errors) ====
interface Ara<T> { t: T }
interface Collection<K, V> {
map<M>(mapper: (value: V, key: K, iter: this) => M): Collection<K, M>;
@@ -33,7 +33,7 @@ tests/cases/compiler/immutable.d.ts(391,22): error TS2430: Interface 'Set<T>' in
flatMap<M>(mapper: (value: T, key: void, iter: this) => Ara<M>, context?: any): N2<M>;
toSeq(): N2<T>;
}
==== tests/cases/compiler/immutable.d.ts (3 errors) ====
==== tests/cases/compiler/immutable.ts (3 errors) ====
// Test that complex recursive collections can pass the `extends` assignability check without
// running out of memory. This bug was exposed in Typescript 2.4 when more generic signatures
// started being checked.
@@ -0,0 +1,538 @@
//// [tests/cases/compiler/complexRecursiveCollections.ts] ////
//// [complex.ts]
interface Ara<T> { t: T }
interface Collection<K, V> {
map<M>(mapper: (value: V, key: K, iter: this) => M): Collection<K, M>;
flatMap<M>(mapper: (value: V, key: K, iter: this) => Ara<M>, context?: any): Collection<K, M>;
// these seem necessary to push it over the top for memory usage
reduce<R>(reducer: (reduction: R, value: V, key: K, iter: this) => R, initialReduction: R, context?: any): R;
reduce<R>(reducer: (reduction: V | R, value: V, key: K, iter: this) => R): R;
toSeq(): Seq<K, V>;
}
interface Seq<K, V> extends Collection<K, V> {
}
interface N1<T> extends Collection<void, T> {
map<M>(mapper: (value: T, key: void, iter: this) => M): N1<M>;
flatMap<M>(mapper: (value: T, key: void, iter: this) => Ara<M>, context?: any): N1<M>;
}
interface N2<T> extends N1<T> {
map<M>(mapper: (value: T, key: void, iter: this) => M): N2<M>;
flatMap<M>(mapper: (value: T, key: void, iter: this) => Ara<M>, context?: any): N2<M>;
toSeq(): N2<T>;
}
//// [immutable.ts]
// Test that complex recursive collections can pass the `extends` assignability check without
// running out of memory. This bug was exposed in Typescript 2.4 when more generic signatures
// started being checked.
declare module Immutable {
export function fromJS(jsValue: any, reviver?: (key: string | number, sequence: Collection.Keyed<string, any> | Collection.Indexed<any>, path?: Array<string | number>) => any): any;
export function is(first: any, second: any): boolean;
export function hash(value: any): number;
export function isImmutable(maybeImmutable: any): maybeImmutable is Collection<any, any>;
export function isCollection(maybeCollection: any): maybeCollection is Collection<any, any>;
export function isKeyed(maybeKeyed: any): maybeKeyed is Collection.Keyed<any, any>;
export function isIndexed(maybeIndexed: any): maybeIndexed is Collection.Indexed<any>;
export function isAssociative(maybeAssociative: any): maybeAssociative is Collection.Keyed<any, any> | Collection.Indexed<any>;
export function isOrdered(maybeOrdered: any): boolean;
export function isValueObject(maybeValue: any): maybeValue is ValueObject;
export interface ValueObject {
equals(other: any): boolean;
hashCode(): number;
}
export module List {
function isList(maybeList: any): maybeList is List<any>;
function of<T>(...values: Array<T>): List<T>;
}
export function List(): List<any>;
export function List<T>(): List<T>;
export function List<T>(collection: Iterable<T>): List<T>;
export interface List<T> extends Collection.Indexed<T> {
// Persistent changes
set(index: number, value: T): List<T>;
delete(index: number): List<T>;
remove(index: number): List<T>;
insert(index: number, value: T): List<T>;
clear(): List<T>;
push(...values: Array<T>): List<T>;
pop(): List<T>;
unshift(...values: Array<T>): List<T>;
shift(): List<T>;
update(index: number, notSetValue: T, updater: (value: T) => T): this;
update(index: number, updater: (value: T) => T): this;
update<R>(updater: (value: this) => R): R;
merge(...collections: Array<Collection.Indexed<T> | Array<T>>): this;
mergeWith(merger: (oldVal: T, newVal: T, key: number) => T, ...collections: Array<Collection.Indexed<T> | Array<T>>): this;
mergeDeep(...collections: Array<Collection.Indexed<T> | Array<T>>): this;
mergeDeepWith(merger: (oldVal: T, newVal: T, key: number) => T, ...collections: Array<Collection.Indexed<T> | Array<T>>): this;
setSize(size: number): List<T>;
// Deep persistent changes
setIn(keyPath: Iterable<any>, value: any): this;
deleteIn(keyPath: Iterable<any>): this;
removeIn(keyPath: Iterable<any>): this;
updateIn(keyPath: Iterable<any>, notSetValue: any, updater: (value: any) => any): this;
updateIn(keyPath: Iterable<any>, updater: (value: any) => any): this;
mergeIn(keyPath: Iterable<any>, ...collections: Array<any>): this;
mergeDeepIn(keyPath: Iterable<any>, ...collections: Array<any>): this;
// Transient changes
withMutations(mutator: (mutable: this) => any): this;
asMutable(): this;
asImmutable(): this;
// Sequence algorithms
concat<C>(...valuesOrCollections: Array<Iterable<C> | C>): List<T | C>;
map<M>(mapper: (value: T, key: number, iter: this) => M, context?: any): List<M>;
flatMap<M>(mapper: (value: T, key: number, iter: this) => Iterable<M>, context?: any): List<M>;
filter<F extends T>(predicate: (value: T, index: number, iter: this) => value is F, context?: any): List<F>;
filter(predicate: (value: T, index: number, iter: this) => any, context?: any): this;
}
export module Map {
function isMap(maybeMap: any): maybeMap is Map<any, any>;
function of(...keyValues: Array<any>): Map<any, any>;
}
export function Map<K, V>(collection: Iterable<[K, V]>): Map<K, V>;
export function Map<T>(collection: Iterable<Iterable<T>>): Map<T, T>;
export function Map<V>(obj: {[key: string]: V}): Map<string, V>;
export function Map<K, V>(): Map<K, V>;
export function Map(): Map<any, any>;
export interface Map<K, V> extends Collection.Keyed<K, V> {
// Persistent changes
set(key: K, value: V): this;
delete(key: K): this;
remove(key: K): this;
deleteAll(keys: Iterable<K>): this;
removeAll(keys: Iterable<K>): this;
clear(): this;
update(key: K, notSetValue: V, updater: (value: V) => V): this;
update(key: K, updater: (value: V) => V): this;
update<R>(updater: (value: this) => R): R;
merge(...collections: Array<Collection<K, V> | {[key: string]: V}>): this;
mergeWith(merger: (oldVal: V, newVal: V, key: K) => V, ...collections: Array<Collection<K, V> | {[key: string]: V}>): this;
mergeDeep(...collections: Array<Collection<K, V> | {[key: string]: V}>): this;
mergeDeepWith(merger: (oldVal: V, newVal: V, key: K) => V, ...collections: Array<Collection<K, V> | {[key: string]: V}>): this;
// Deep persistent changes
setIn(keyPath: Iterable<any>, value: any): this;
deleteIn(keyPath: Iterable<any>): this;
removeIn(keyPath: Iterable<any>): this;
updateIn(keyPath: Iterable<any>, notSetValue: any, updater: (value: any) => any): this;
updateIn(keyPath: Iterable<any>, updater: (value: any) => any): this;
mergeIn(keyPath: Iterable<any>, ...collections: Array<any>): this;
mergeDeepIn(keyPath: Iterable<any>, ...collections: Array<any>): this;
// Transient changes
withMutations(mutator: (mutable: this) => any): this;
asMutable(): this;
asImmutable(): this;
// Sequence algorithms
concat<KC, VC>(...collections: Array<Iterable<[KC, VC]>>): Map<K | KC, V | VC>;
concat<C>(...collections: Array<{[key: string]: C}>): Map<K | string, V | C>;
map<M>(mapper: (value: V, key: K, iter: this) => M, context?: any): Map<K, M>;
mapKeys<M>(mapper: (key: K, value: V, iter: this) => M, context?: any): Map<M, V>;
mapEntries<KM, VM>(mapper: (entry: [K, V], index: number, iter: this) => [KM, VM], context?: any): Map<KM, VM>;
flatMap<M>(mapper: (value: V, key: K, iter: this) => Iterable<M>, context?: any): Map<any, any>;
filter<F extends V>(predicate: (value: V, key: K, iter: this) => value is F, context?: any): Map<K, F>;
filter(predicate: (value: V, key: K, iter: this) => any, context?: any): this;
}
export module OrderedMap {
function isOrderedMap(maybeOrderedMap: any): maybeOrderedMap is OrderedMap<any, any>;
}
export function OrderedMap<K, V>(collection: Iterable<[K, V]>): OrderedMap<K, V>;
export function OrderedMap<T>(collection: Iterable<Iterable<T>>): OrderedMap<T, T>;
export function OrderedMap<V>(obj: {[key: string]: V}): OrderedMap<string, V>;
export function OrderedMap<K, V>(): OrderedMap<K, V>;
export function OrderedMap(): OrderedMap<any, any>;
export interface OrderedMap<K, V> extends Map<K, V> {
// Sequence algorithms
concat<KC, VC>(...collections: Array<Iterable<[KC, VC]>>): OrderedMap<K | KC, V | VC>;
concat<C>(...collections: Array<{[key: string]: C}>): OrderedMap<K | string, V | C>;
map<M>(mapper: (value: V, key: K, iter: this) => M, context?: any): OrderedMap<K, M>;
mapKeys<M>(mapper: (key: K, value: V, iter: this) => M, context?: any): OrderedMap<M, V>;
mapEntries<KM, VM>(mapper: (entry: [K, V], index: number, iter: this) => [KM, VM], context?: any): OrderedMap<KM, VM>;
flatMap<M>(mapper: (value: V, key: K, iter: this) => Iterable<M>, context?: any): OrderedMap<any, any>;
filter<F extends V>(predicate: (value: V, key: K, iter: this) => value is F, context?: any): OrderedMap<K, F>;
filter(predicate: (value: V, key: K, iter: this) => any, context?: any): this;
}
export module Set {
function isSet(maybeSet: any): maybeSet is Set<any>;
function of<T>(...values: Array<T>): Set<T>;
function fromKeys<T>(iter: Collection<T, any>): Set<T>;
function fromKeys(obj: {[key: string]: any}): Set<string>;
function intersect<T>(sets: Iterable<Iterable<T>>): Set<T>;
function union<T>(sets: Iterable<Iterable<T>>): Set<T>;
}
export function Set(): Set<any>;
export function Set<T>(): Set<T>;
export function Set<T>(collection: Iterable<T>): Set<T>;
export interface Set<T> extends Collection.Set<T> {
// Persistent changes
add(value: T): this;
delete(value: T): this;
remove(value: T): this;
clear(): this;
union(...collections: Array<Collection<any, T> | Array<T>>): this;
merge(...collections: Array<Collection<any, T> | Array<T>>): this;
intersect(...collections: Array<Collection<any, T> | Array<T>>): this;
subtract(...collections: Array<Collection<any, T> | Array<T>>): this;
// Transient changes
withMutations(mutator: (mutable: this) => any): this;
asMutable(): this;
asImmutable(): this;
// Sequence algorithms
concat<C>(...valuesOrCollections: Array<Iterable<C> | C>): Set<T | C>;
map<M>(mapper: (value: T, key: never, iter: this) => M, context?: any): Set<M>;
flatMap<M>(mapper: (value: T, key: never, iter: this) => Iterable<M>, context?: any): Set<M>;
filter<F extends T>(predicate: (value: T, key: never, iter: this) => value is F, context?: any): Set<F>;
filter(predicate: (value: T, key: never, iter: this) => any, context?: any): this;
}
export module OrderedSet {
function isOrderedSet(maybeOrderedSet: any): boolean;
function of<T>(...values: Array<T>): OrderedSet<T>;
function fromKeys<T>(iter: Collection<T, any>): OrderedSet<T>;
function fromKeys(obj: {[key: string]: any}): OrderedSet<string>;
}
export function OrderedSet(): OrderedSet<any>;
export function OrderedSet<T>(): OrderedSet<T>;
export function OrderedSet<T>(collection: Iterable<T>): OrderedSet<T>;
export interface OrderedSet<T> extends Set<T> {
// Sequence algorithms
concat<C>(...valuesOrCollections: Array<Iterable<C> | C>): OrderedSet<T | C>;
map<M>(mapper: (value: T, key: never, iter: this) => M, context?: any): OrderedSet<M>;
flatMap<M>(mapper: (value: T, key: never, iter: this) => Iterable<M>, context?: any): OrderedSet<M>;
filter<F extends T>(predicate: (value: T, key: never, iter: this) => value is F, context?: any): OrderedSet<F>;
filter(predicate: (value: T, key: never, iter: this) => any, context?: any): this;
zip(...collections: Array<Collection<any, any>>): OrderedSet<any>;
zipWith<U, Z>(zipper: (value: T, otherValue: U) => Z, otherCollection: Collection<any, U>): OrderedSet<Z>;
zipWith<U, V, Z>(zipper: (value: T, otherValue: U, thirdValue: V) => Z, otherCollection: Collection<any, U>, thirdCollection: Collection<any, V>): OrderedSet<Z>;
zipWith<Z>(zipper: (...any: Array<any>) => Z, ...collections: Array<Collection<any, any>>): OrderedSet<Z>;
}
export module Stack {
function isStack(maybeStack: any): maybeStack is Stack<any>;
function of<T>(...values: Array<T>): Stack<T>;
}
export function Stack(): Stack<any>;
export function Stack<T>(): Stack<T>;
export function Stack<T>(collection: Iterable<T>): Stack<T>;
export interface Stack<T> extends Collection.Indexed<T> {
// Reading values
peek(): T | undefined;
// Persistent changes
clear(): Stack<T>;
unshift(...values: Array<T>): Stack<T>;
unshiftAll(iter: Iterable<T>): Stack<T>;
shift(): Stack<T>;
push(...values: Array<T>): Stack<T>;
pushAll(iter: Iterable<T>): Stack<T>;
pop(): Stack<T>;
// Transient changes
withMutations(mutator: (mutable: this) => any): this;
asMutable(): this;
asImmutable(): this;
// Sequence algorithms
concat<C>(...valuesOrCollections: Array<Iterable<C> | C>): Stack<T | C>;
map<M>(mapper: (value: T, key: number, iter: this) => M, context?: any): Stack<M>;
flatMap<M>(mapper: (value: T, key: number, iter: this) => Iterable<M>, context?: any): Stack<M>;
filter<F extends T>(predicate: (value: T, index: number, iter: this) => value is F, context?: any): Set<F>;
filter(predicate: (value: T, index: number, iter: this) => any, context?: any): this;
}
export function Range(start?: number, end?: number, step?: number): Seq.Indexed<number>;
export function Repeat<T>(value: T, times?: number): Seq.Indexed<T>;
export module Record {
export function isRecord(maybeRecord: any): maybeRecord is Record.Instance<any>;
export function getDescriptiveName(record: Instance<any>): string;
export interface Class<T extends Object> {
(values?: Partial<T> | Iterable<[string, any]>): Instance<T> & Readonly<T>;
new (values?: Partial<T> | Iterable<[string, any]>): Instance<T> & Readonly<T>;
}
export interface Instance<T extends Object> {
readonly size: number;
// Reading values
has(key: string): boolean;
get<K extends keyof T>(key: K): T[K];
// Reading deep values
hasIn(keyPath: Iterable<any>): boolean;
getIn(keyPath: Iterable<any>): any;
// Value equality
equals(other: any): boolean;
hashCode(): number;
// Persistent changes
set<K extends keyof T>(key: K, value: T[K]): this;
update<K extends keyof T>(key: K, updater: (value: T[K]) => T[K]): this;
merge(...collections: Array<Partial<T> | Iterable<[string, any]>>): this;
mergeDeep(...collections: Array<Partial<T> | Iterable<[string, any]>>): this;
mergeWith(merger: (oldVal: any, newVal: any, key: keyof T) => any, ...collections: Array<Partial<T> | Iterable<[string, any]>>): this;
mergeDeepWith(merger: (oldVal: any, newVal: any, key: any) => any, ...collections: Array<Partial<T> | Iterable<[string, any]>>): this;
delete<K extends keyof T>(key: K): this;
remove<K extends keyof T>(key: K): this;
clear(): this;
// Deep persistent changes
setIn(keyPath: Iterable<any>, value: any): this;
updateIn(keyPath: Iterable<any>, updater: (value: any) => any): this;
mergeIn(keyPath: Iterable<any>, ...collections: Array<any>): this;
mergeDeepIn(keyPath: Iterable<any>, ...collections: Array<any>): this;
deleteIn(keyPath: Iterable<any>): this;
removeIn(keyPath: Iterable<any>): this;
// Conversion to JavaScript types
toJS(): { [K in keyof T]: any };
toJSON(): T;
toObject(): T;
// Transient changes
withMutations(mutator: (mutable: this) => any): this;
asMutable(): this;
asImmutable(): this;
// Sequence algorithms
toSeq(): Seq.Keyed<keyof T, T[keyof T]>;
[Symbol.iterator](): IterableIterator<[keyof T, T[keyof T]]>;
}
}
export function Record<T>(defaultValues: T, name?: string): Record.Class<T>;
export module Seq {
function isSeq(maybeSeq: any): maybeSeq is Seq.Indexed<any> | Seq.Keyed<any, any>;
function of<T>(...values: Array<T>): Seq.Indexed<T>;
export module Keyed {}
export function Keyed<K, V>(collection: Iterable<[K, V]>): Seq.Keyed<K, V>;
export function Keyed<V>(obj: {[key: string]: V}): Seq.Keyed<string, V>;
export function Keyed<K, V>(): Seq.Keyed<K, V>;
export function Keyed(): Seq.Keyed<any, any>;
export interface Keyed<K, V> extends Seq<K, V>, Collection.Keyed<K, V> {
toJS(): Object;
toJSON(): { [key: string]: V };
toSeq(): this;
concat<KC, VC>(...collections: Array<Iterable<[KC, VC]>>): Seq.Keyed<K | KC, V | VC>;
concat<C>(...collections: Array<{[key: string]: C}>): Seq.Keyed<K | string, V | C>;
map<M>(mapper: (value: V, key: K, iter: this) => M, context?: any): Seq.Keyed<K, M>;
mapKeys<M>(mapper: (key: K, value: V, iter: this) => M, context?: any): Seq.Keyed<M, V>;
mapEntries<KM, VM>(mapper: (entry: [K, V], index: number, iter: this) => [KM, VM], context?: any): Seq.Keyed<KM, VM>;
flatMap<M>(mapper: (value: V, key: K, iter: this) => Iterable<M>, context?: any): Seq.Keyed<any, any>;
filter<F extends V>(predicate: (value: V, key: K, iter: this) => value is F, context?: any): Seq.Keyed<K, F>;
filter(predicate: (value: V, key: K, iter: this) => any, context?: any): this;
}
module Indexed {
function of<T>(...values: Array<T>): Seq.Indexed<T>;
}
export function Indexed(): Seq.Indexed<any>;
export function Indexed<T>(): Seq.Indexed<T>;
export function Indexed<T>(collection: Iterable<T>): Seq.Indexed<T>;
export interface Indexed<T> extends Seq<number, T>, Collection.Indexed<T> {
toJS(): Array<any>;
toJSON(): Array<T>;
toSeq(): this;
concat<C>(...valuesOrCollections: Array<Iterable<C> | C>): Seq.Indexed<T | C>;
map<M>(mapper: (value: T, key: number, iter: this) => M, context?: any): Seq.Indexed<M>;
flatMap<M>(mapper: (value: T, key: number, iter: this) => Iterable<M>, context?: any): Seq.Indexed<M>;
filter<F extends T>(predicate: (value: T, index: number, iter: this) => value is F, context?: any): Seq.Indexed<F>;
filter(predicate: (value: T, index: number, iter: this) => any, context?: any): this;
}
export module Set {
function of<T>(...values: Array<T>): Seq.Set<T>;
}
export function Set(): Seq.Set<any>;
export function Set<T>(): Seq.Set<T>;
export function Set<T>(collection: Iterable<T>): Seq.Set<T>;
export interface Set<T> extends Seq<never, T>, Collection.Set<T> {
toJS(): Array<any>;
toJSON(): Array<T>;
toSeq(): this;
concat<C>(...valuesOrCollections: Array<Iterable<C> | C>): Seq.Set<T | C>;
map<M>(mapper: (value: T, key: never, iter: this) => M, context?: any): Seq.Set<M>;
flatMap<M>(mapper: (value: T, key: never, iter: this) => Iterable<M>, context?: any): Seq.Set<M>;
filter<F extends T>(predicate: (value: T, key: never, iter: this) => value is F, context?: any): Seq.Set<F>;
filter(predicate: (value: T, key: never, iter: this) => any, context?: any): this;
}
}
export function Seq<S extends Seq<any, any>>(seq: S): S;
export function Seq<K, V>(collection: Collection.Keyed<K, V>): Seq.Keyed<K, V>;
export function Seq<T>(collection: Collection.Indexed<T>): Seq.Indexed<T>;
export function Seq<T>(collection: Collection.Set<T>): Seq.Set<T>;
export function Seq<T>(collection: Iterable<T>): Seq.Indexed<T>;
export function Seq<V>(obj: {[key: string]: V}): Seq.Keyed<string, V>;
export function Seq(): Seq<any, any>;
export interface Seq<K, V> extends Collection<K, V> {
readonly size: number | undefined;
// Force evaluation
cacheResult(): this;
// Sequence algorithms
map<M>(mapper: (value: V, key: K, iter: this) => M, context?: any): Seq<K, M>;
flatMap<M>(mapper: (value: V, key: K, iter: this) => Iterable<M>, context?: any): Seq<K, M>;
filter<F extends V>(predicate: (value: V, key: K, iter: this) => value is F, context?: any): Seq<K, F>;
filter(predicate: (value: V, key: K, iter: this) => any, context?: any): this;
}
export module Collection {
function isKeyed(maybeKeyed: any): maybeKeyed is Collection.Keyed<any, any>;
function isIndexed(maybeIndexed: any): maybeIndexed is Collection.Indexed<any>;
function isAssociative(maybeAssociative: any): maybeAssociative is Collection.Keyed<any, any> | Collection.Indexed<any>;
function isOrdered(maybeOrdered: any): boolean;
export module Keyed {}
export function Keyed<K, V>(collection: Iterable<[K, V]>): Collection.Keyed<K, V>;
export function Keyed<V>(obj: {[key: string]: V}): Collection.Keyed<string, V>;
export interface Keyed<K, V> extends Collection<K, V> {
toJS(): Object;
toJSON(): { [key: string]: V };
toSeq(): Seq.Keyed<K, V>;
// Sequence functions
flip(): this;
concat<KC, VC>(...collections: Array<Iterable<[KC, VC]>>): Collection.Keyed<K | KC, V | VC>;
concat<C>(...collections: Array<{[key: string]: C}>): Collection.Keyed<K | string, V | C>;
map<M>(mapper: (value: V, key: K, iter: this) => M, context?: any): Collection.Keyed<K, M>;
mapKeys<M>(mapper: (key: K, value: V, iter: this) => M, context?: any): Collection.Keyed<M, V>;
mapEntries<KM, VM>(mapper: (entry: [K, V], index: number, iter: this) => [KM, VM], context?: any): Collection.Keyed<KM, VM>;
flatMap<M>(mapper: (value: V, key: K, iter: this) => Iterable<M>, context?: any): Collection.Keyed<any, any>;
filter<F extends V>(predicate: (value: V, key: K, iter: this) => value is F, context?: any): Collection.Keyed<K, F>;
filter(predicate: (value: V, key: K, iter: this) => any, context?: any): this;
[Symbol.iterator](): IterableIterator<[K, V]>;
}
export module Indexed {}
export function Indexed<T>(collection: Iterable<T>): Collection.Indexed<T>;
export interface Indexed<T> extends Collection<number, T> {
toJS(): Array<any>;
toJSON(): Array<T>;
// Reading values
get<NSV>(index: number, notSetValue: NSV): T | NSV;
get(index: number): T | undefined;
// Conversion to Seq
toSeq(): Seq.Indexed<T>;
fromEntrySeq(): Seq.Keyed<any, any>;
// Combination
interpose(separator: T): this;
interleave(...collections: Array<Collection<any, T>>): this;
splice(index: number, removeNum: number, ...values: Array<T>): this;
zip(...collections: Array<Collection<any, any>>): Collection.Indexed<any>;
zipWith<U, Z>(zipper: (value: T, otherValue: U) => Z, otherCollection: Collection<any, U>): Collection.Indexed<Z>;
zipWith<U, V, Z>(zipper: (value: T, otherValue: U, thirdValue: V) => Z, otherCollection: Collection<any, U>, thirdCollection: Collection<any, V>): Collection.Indexed<Z>;
zipWith<Z>(zipper: (...any: Array<any>) => Z, ...collections: Array<Collection<any, any>>): Collection.Indexed<Z>;
// Search for value
indexOf(searchValue: T): number;
lastIndexOf(searchValue: T): number;
findIndex(predicate: (value: T, index: number, iter: this) => boolean, context?: any): number;
findLastIndex(predicate: (value: T, index: number, iter: this) => boolean, context?: any): number;
// Sequence algorithms
concat<C>(...valuesOrCollections: Array<Iterable<C> | C>): Collection.Indexed<T | C>;
map<M>(mapper: (value: T, key: number, iter: this) => M, context?: any): Collection.Indexed<M>;
flatMap<M>(mapper: (value: T, key: number, iter: this) => Iterable<M>, context?: any): Collection.Indexed<M>;
filter<F extends T>(predicate: (value: T, index: number, iter: this) => value is F, context?: any): Collection.Indexed<F>;
filter(predicate: (value: T, index: number, iter: this) => any, context?: any): this;
[Symbol.iterator](): IterableIterator<T>;
}
export module Set {}
export function Set<T>(collection: Iterable<T>): Collection.Set<T>;
export interface Set<T> extends Collection<never, T> {
toJS(): Array<any>;
toJSON(): Array<T>;
toSeq(): Seq.Set<T>;
// Sequence algorithms
concat<C>(...valuesOrCollections: Array<Iterable<C> | C>): Collection.Set<T | C>;
map<M>(mapper: (value: T, key: never, iter: this) => M, context?: any): Collection.Set<M>;
flatMap<M>(mapper: (value: T, key: never, iter: this) => Iterable<M>, context?: any): Collection.Set<M>;
filter<F extends T>(predicate: (value: T, key: never, iter: this) => value is F, context?: any): Collection.Set<F>;
filter(predicate: (value: T, key: never, iter: this) => any, context?: any): this;
[Symbol.iterator](): IterableIterator<T>;
}
}
export function Collection<I extends Collection<any, any>>(collection: I): I;
export function Collection<T>(collection: Iterable<T>): Collection.Indexed<T>;
export function Collection<V>(obj: {[key: string]: V}): Collection.Keyed<string, V>;
export interface Collection<K, V> extends ValueObject {
// Value equality
equals(other: any): boolean;
hashCode(): number;
// Reading values
get<NSV>(key: K, notSetValue: NSV): V | NSV;
get(key: K): V | undefined;
has(key: K): boolean;
includes(value: V): boolean;
contains(value: V): boolean;
first(): V | undefined;
last(): V | undefined;
// Reading deep values
getIn(searchKeyPath: Iterable<any>, notSetValue?: any): any;
hasIn(searchKeyPath: Iterable<any>): boolean;
// Persistent changes
update<R>(updater: (value: this) => R): R;
// Conversion to JavaScript types
toJS(): Array<any> | { [key: string]: any };
toJSON(): Array<V> | { [key: string]: V };
toArray(): Array<V>;
toObject(): { [key: string]: V };
// Conversion to Collections
toMap(): Map<K, V>;
toOrderedMap(): OrderedMap<K, V>;
toSet(): Set<V>;
toOrderedSet(): OrderedSet<V>;
toList(): List<V>;
toStack(): Stack<V>;
// Conversion to Seq
toSeq(): this;
toKeyedSeq(): Seq.Keyed<K, V>;
toIndexedSeq(): Seq.Indexed<V>;
toSetSeq(): Seq.Set<V>;
// Iterators
keys(): IterableIterator<K>;
values(): IterableIterator<V>;
entries(): IterableIterator<[K, V]>;
// Collections (Seq)
keySeq(): Seq.Indexed<K>;
valueSeq(): Seq.Indexed<V>;
entrySeq(): Seq.Indexed<[K, V]>;
// Sequence algorithms
map<M>(mapper: (value: V, key: K, iter: this) => M, context?: any): Collection<K, M>;
filter<F extends V>(predicate: (value: V, key: K, iter: this) => value is F, context?: any): Collection<K, F>;
filter(predicate: (value: V, key: K, iter: this) => any, context?: any): this;
filterNot(predicate: (value: V, key: K, iter: this) => boolean, context?: any): this;
reverse(): this;
sort(comparator?: (valueA: V, valueB: V) => number): this;
sortBy<C>(comparatorValueMapper: (value: V, key: K, iter: this) => C, comparator?: (valueA: C, valueB: C) => number): this;
groupBy<G>(grouper: (value: V, key: K, iter: this) => G, context?: any): /*Map*/Seq.Keyed<G, /*this*/Collection<K, V>>;
// Side effects
forEach(sideEffect: (value: V, key: K, iter: this) => any, context?: any): number;
// Creating subsets
slice(begin?: number, end?: number): this;
rest(): this;
butLast(): this;
skip(amount: number): this;
skipLast(amount: number): this;
skipWhile(predicate: (value: V, key: K, iter: this) => boolean, context?: any): this;
skipUntil(predicate: (value: V, key: K, iter: this) => boolean, context?: any): this;
take(amount: number): this;
takeLast(amount: number): this;
takeWhile(predicate: (value: V, key: K, iter: this) => boolean, context?: any): this;
takeUntil(predicate: (value: V, key: K, iter: this) => boolean, context?: any): this;
// Combination
concat(...valuesOrCollections: Array<any>): Collection<any, any>;
flatten(depth?: number): Collection<any, any>;
flatten(shallow?: boolean): Collection<any, any>;
flatMap<M>(mapper: (value: V, key: K, iter: this) => Iterable<M>, context?: any): Collection<K, M>;
// Reducing a value
reduce<R>(reducer: (reduction: R, value: V, key: K, iter: this) => R, initialReduction: R, context?: any): R;
reduce<R>(reducer: (reduction: V | R, value: V, key: K, iter: this) => R): R;
reduceRight<R>(reducer: (reduction: R, value: V, key: K, iter: this) => R, initialReduction: R, context?: any): R;
reduceRight<R>(reducer: (reduction: V | R, value: V, key: K, iter: this) => R): R;
every(predicate: (value: V, key: K, iter: this) => boolean, context?: any): boolean;
some(predicate: (value: V, key: K, iter: this) => boolean, context?: any): boolean;
join(separator?: string): string;
isEmpty(): boolean;
count(): number;
count(predicate: (value: V, key: K, iter: this) => boolean, context?: any): number;
countBy<G>(grouper: (value: V, key: K, iter: this) => G, context?: any): Map<G, number>;
// Search for value
find(predicate: (value: V, key: K, iter: this) => boolean, context?: any, notSetValue?: V): V | undefined;
findLast(predicate: (value: V, key: K, iter: this) => boolean, context?: any, notSetValue?: V): V | undefined;
findEntry(predicate: (value: V, key: K, iter: this) => boolean, context?: any, notSetValue?: V): [K, V] | undefined;
findLastEntry(predicate: (value: V, key: K, iter: this) => boolean, context?: any, notSetValue?: V): [K, V] | undefined;
findKey(predicate: (value: V, key: K, iter: this) => boolean, context?: any): K | undefined;
findLastKey(predicate: (value: V, key: K, iter: this) => boolean, context?: any): K | undefined;
keyOf(searchValue: V): K | undefined;
lastKeyOf(searchValue: V): K | undefined;
max(comparator?: (valueA: V, valueB: V) => number): V | undefined;
maxBy<C>(comparatorValueMapper: (value: V, key: K, iter: this) => C, comparator?: (valueA: C, valueB: C) => number): V | undefined;
min(comparator?: (valueA: V, valueB: V) => number): V | undefined;
minBy<C>(comparatorValueMapper: (value: V, key: K, iter: this) => C, comparator?: (valueA: C, valueB: C) => number): V | undefined;
// Comparison
isSubset(iter: Iterable<V>): boolean;
isSuperset(iter: Iterable<V>): boolean;
readonly size: number;
}
}
declare module "immutable" {
export = Immutable
}
//// [complex.js]
//// [immutable.js]
File diff suppressed because it is too large Load Diff
@@ -1,4 +1,4 @@
=== tests/cases/compiler/complex.d.ts ===
=== tests/cases/compiler/complex.ts ===
interface Ara<T> { t: T }
>Ara : Ara<T>
>T : T
@@ -156,7 +156,7 @@ interface N2<T> extends N1<T> {
>N2 : N2<T>
>T : T
}
=== tests/cases/compiler/immutable.d.ts ===
=== tests/cases/compiler/immutable.ts ===
// Test that complex recursive collections can pass the `extends` assignability check without
// running out of memory. This bug was exposed in Typescript 2.4 when more generic signatures
// started being checked.
@@ -0,0 +1,32 @@
//// [decoratorMetadataNoStrictNull.ts]
const dec = (obj: {}, prop: string) => undefined
class Foo {
@dec public foo: string | null;
@dec public bar: string;
}
//// [decoratorMetadataNoStrictNull.js]
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var dec = function (obj, prop) { return undefined; };
var Foo = /** @class */ (function () {
function Foo() {
}
__decorate([
dec,
__metadata("design:type", String)
], Foo.prototype, "foo");
__decorate([
dec,
__metadata("design:type", String)
], Foo.prototype, "bar");
return Foo;
}());
@@ -0,0 +1,18 @@
=== tests/cases/compiler/decoratorMetadataNoStrictNull.ts ===
const dec = (obj: {}, prop: string) => undefined
>dec : Symbol(dec, Decl(decoratorMetadataNoStrictNull.ts, 0, 5))
>obj : Symbol(obj, Decl(decoratorMetadataNoStrictNull.ts, 0, 13))
>prop : Symbol(prop, Decl(decoratorMetadataNoStrictNull.ts, 0, 21))
>undefined : Symbol(undefined)
class Foo {
>Foo : Symbol(Foo, Decl(decoratorMetadataNoStrictNull.ts, 0, 48))
@dec public foo: string | null;
>dec : Symbol(dec, Decl(decoratorMetadataNoStrictNull.ts, 0, 5))
>foo : Symbol(Foo.foo, Decl(decoratorMetadataNoStrictNull.ts, 2, 11))
@dec public bar: string;
>dec : Symbol(dec, Decl(decoratorMetadataNoStrictNull.ts, 0, 5))
>bar : Symbol(Foo.bar, Decl(decoratorMetadataNoStrictNull.ts, 3, 33))
}
@@ -0,0 +1,20 @@
=== tests/cases/compiler/decoratorMetadataNoStrictNull.ts ===
const dec = (obj: {}, prop: string) => undefined
>dec : (obj: {}, prop: string) => any
>(obj: {}, prop: string) => undefined : (obj: {}, prop: string) => any
>obj : {}
>prop : string
>undefined : undefined
class Foo {
>Foo : Foo
@dec public foo: string | null;
>dec : (obj: {}, prop: string) => any
>foo : string
>null : null
@dec public bar: string;
>dec : (obj: {}, prop: string) => any
>bar : string
}
@@ -1,4 +1,4 @@
tests/cases/conformance/decorators/class/method/decoratorOnClassMethod6.ts(4,5): error TS1241: Unable to resolve signature of method decorator when called as an expression.
tests/cases/conformance/decorators/class/method/decoratorOnClassMethod6.ts(4,5): error TS1329: 'dec' accepts too few arguments to be used as a decorator here. Did you mean to call it first and write '@dec()'?
==== tests/cases/conformance/decorators/class/method/decoratorOnClassMethod6.ts (1 errors) ====
@@ -7,5 +7,5 @@ tests/cases/conformance/decorators/class/method/decoratorOnClassMethod6.ts(4,5):
class C {
@dec ["method"]() {}
~~~~
!!! error TS1241: Unable to resolve signature of method decorator when called as an expression.
!!! error TS1329: 'dec' accepts too few arguments to be used as a decorator here. Did you mean to call it first and write '@dec()'?
}
@@ -1,4 +1,4 @@
tests/cases/conformance/decorators/class/property/decoratorOnClassProperty11.ts(4,5): error TS1240: Unable to resolve signature of property decorator when called as an expression.
tests/cases/conformance/decorators/class/property/decoratorOnClassProperty11.ts(4,5): error TS1329: 'dec' accepts too few arguments to be used as a decorator here. Did you mean to call it first and write '@dec()'?
==== tests/cases/conformance/decorators/class/property/decoratorOnClassProperty11.ts (1 errors) ====
@@ -7,5 +7,5 @@ tests/cases/conformance/decorators/class/property/decoratorOnClassProperty11.ts(
class C {
@dec prop;
~~~~
!!! error TS1240: Unable to resolve signature of property decorator when called as an expression.
!!! error TS1329: 'dec' accepts too few arguments to be used as a decorator here. Did you mean to call it first and write '@dec()'?
}
@@ -28,9 +28,9 @@ var [a0, a1]: any = undefined;
>undefined : undefined
var [a2 = false, a3 = 1]: any = undefined;
>a2 : boolean
>a2 : any
>false : false
>a3 : number
>a3 : any
>1 : 1
>undefined : undefined
@@ -28,9 +28,9 @@ var [a0, a1]: any = undefined;
>undefined : undefined
var [a2 = false, a3 = 1]: any = undefined;
>a2 : boolean
>a2 : any
>false : false
>a3 : number
>a3 : any
>1 : 1
>undefined : undefined
@@ -28,9 +28,9 @@ var [a0, a1]: any = undefined;
>undefined : undefined
var [a2 = false, a3 = 1]: any = undefined;
>a2 : boolean
>a2 : any
>false : false
>a3 : number
>a3 : any
>1 : 1
>undefined : undefined
@@ -39,7 +39,7 @@ var {1: b3} = { 1: "string" };
>"string" : "string"
var {b4 = 1}: any = { b4: 100000 };
>b4 : number
>b4 : any
>1 : 1
>{ b4: 100000 } : { b4: number; }
>b4 : number
@@ -39,7 +39,7 @@ var {1: b3} = { 1: "string" };
>"string" : "string"
var {b4 = 1}: any = { b4: 100000 };
>b4 : number
>b4 : any
>1 : 1
>{ b4: 100000 } : { b4: number; }
>b4 : number
@@ -1,20 +1,20 @@
=== tests/cases/conformance/es6/destructuring/destructuringObjectBindingPatternAndAssignment4.ts ===
const {
a = 1,
>a : 1
>a : any
>1 : 1
b = 2,
>b : 2
>b : any
>2 : 2
c = b, // ok
>c : 2
>b : 2
>c : any
>b : any
d = a, // ok
>d : 1
>a : 1
>d : any
>a : any
e = f, // error
>e : any
@@ -1,6 +1,5 @@
tests/cases/compiler/discriminatedUnionErrorMessage.ts(8,5): error TS2322: Type '{ kind: "sq"; x: number; y: number; }' is not assignable to type 'Shape'.
Type '{ kind: "sq"; x: number; y: number; }' is not assignable to type 'Square'.
Property 'size' is missing in type '{ kind: "sq"; x: number; y: number; }'.
tests/cases/compiler/discriminatedUnionErrorMessage.ts(10,5): error TS2322: Type '{ kind: "sq"; x: number; y: number; }' is not assignable to type 'Shape'.
Object literal may only specify known properties, and 'x' does not exist in type 'Square'.
==== tests/cases/compiler/discriminatedUnionErrorMessage.ts (1 errors) ====
@@ -12,12 +11,11 @@ tests/cases/compiler/discriminatedUnionErrorMessage.ts(8,5): error TS2322: Type
| Rectangle
| Circle;
let shape: Shape = {
~~~~~
!!! error TS2322: Type '{ kind: "sq"; x: number; y: number; }' is not assignable to type 'Shape'.
!!! error TS2322: Type '{ kind: "sq"; x: number; y: number; }' is not assignable to type 'Square'.
!!! error TS2322: Property 'size' is missing in type '{ kind: "sq"; x: number; y: number; }'.
kind: "sq",
x: 12,
~~~~~
!!! error TS2322: Type '{ kind: "sq"; x: number; y: number; }' is not assignable to type 'Shape'.
!!! error TS2322: Object literal may only specify known properties, and 'x' does not exist in type 'Square'.
y: 13,
}

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