diff --git a/scripts/tslint/booleanTriviaRule.ts b/scripts/tslint/booleanTriviaRule.ts index 189dafac77e..c498131be16 100644 --- a/scripts/tslint/booleanTriviaRule.ts +++ b/scripts/tslint/booleanTriviaRule.ts @@ -34,6 +34,7 @@ function walk(ctx: Lint.WalkContext): void { switch (methodName) { case "apply": case "assert": + case "assertEqual": case "call": case "equal": case "fail": @@ -69,7 +70,7 @@ function walk(ctx: Lint.WalkContext): void { const ranges = ts.getTrailingCommentRanges(sourceFile.text, arg.pos) || ts.getLeadingCommentRanges(sourceFile.text, arg.pos); if (ranges === undefined || ranges.length !== 1 || ranges[0].kind !== ts.SyntaxKind.MultiLineCommentTrivia) { - ctx.addFailureAtNode(arg, "Tag boolean argument with parameter name"); + ctx.addFailureAtNode(arg, "Tag argument with parameter name"); return; } diff --git a/scripts/tslint/debugAssertRule.ts b/scripts/tslint/debugAssertRule.ts new file mode 100644 index 00000000000..933b27697b0 --- /dev/null +++ b/scripts/tslint/debugAssertRule.ts @@ -0,0 +1,45 @@ +import * as Lint from "tslint/lib"; +import * as ts from "typescript"; + +export class Rule extends Lint.Rules.AbstractRule { + public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + return this.applyWithFunction(sourceFile, ctx => walk(ctx)); + } +} + +function walk(ctx: Lint.WalkContext): void { + ts.forEachChild(ctx.sourceFile, function recur(node) { + if (ts.isCallExpression(node)) { + checkCall(node); + } + ts.forEachChild(node, recur); + }); + + function checkCall(node: ts.CallExpression) { + if (!isDebugAssert(node.expression) || node.arguments.length < 2) { + return; + } + + const message = node.arguments[1]; + if (!ts.isStringLiteral(message)) { + ctx.addFailureAtNode(message, "Second argument to 'Debug.assert' should be a string literal."); + } + + if (node.arguments.length < 3) { + return; + } + + const message2 = node.arguments[2]; + if (!ts.isStringLiteral(message2) && !ts.isArrowFunction(message2)) { + ctx.addFailureAtNode(message, "Third argument to 'Debug.assert' should be a string literal or arrow function."); + } + } + + function isDebugAssert(expr: ts.Node): boolean { + return ts.isPropertyAccessExpression(expr) && isName(expr.expression, "Debug") && isName(expr.name, "assert"); + } + + function isName(expr: ts.Node, text: string): boolean { + return ts.isIdentifier(expr) && expr.text === text; + } +} diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4643ee36685..0fc2b927515 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1086,7 +1086,10 @@ namespace ts { location = location.parent; } - if (result && nameNotFoundMessage && noUnusedIdentifiers) { + // 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 (result && nameNotFoundMessage && noUnusedIdentifiers && result !== lastLocation.symbol) { result.isReferenced = true; } @@ -2583,10 +2586,8 @@ namespace ts { } function createTypeNodeFromObjectType(type: ObjectType): TypeNode { - if (type.objectFlags & ObjectFlags.Mapped) { - if (getConstraintTypeFromMappedType(type).flags & (TypeFlags.TypeParameter | TypeFlags.Index)) { - return createMappedTypeNodeFromType(type); - } + if (isGenericMappedType(type)) { + return createMappedTypeNodeFromType(type); } const resolved = resolveStructuredTypeMembers(type); @@ -3489,11 +3490,9 @@ namespace ts { } function writeLiteralType(type: ObjectType, flags: TypeFormatFlags) { - if (type.objectFlags & ObjectFlags.Mapped) { - if (getConstraintTypeFromMappedType(type).flags & (TypeFlags.TypeParameter | TypeFlags.Index)) { - writeMappedType(type); - return; - } + if (isGenericMappedType(type)) { + writeMappedType(type); + return; } const resolved = resolveStructuredTypeMembers(type); @@ -5792,8 +5791,7 @@ namespace ts { } function isGenericMappedType(type: Type) { - return getObjectFlags(type) & ObjectFlags.Mapped && - maybeTypeOfKind(getConstraintTypeFromMappedType(type), TypeFlags.TypeVariable | TypeFlags.Index); + return getObjectFlags(type) & ObjectFlags.Mapped && isGenericIndexType(getConstraintTypeFromMappedType(type)); } function resolveStructuredTypeMembers(type: StructuredType): ResolvedType { @@ -5905,6 +5903,10 @@ namespace ts { } function getConstraintOfIndexedAccess(type: IndexedAccessType) { + const transformed = getTransformedIndexedAccessType(type); + if (transformed) { + return transformed; + } const baseObjectType = getBaseConstraintOfType(type.objectType); const baseIndexType = getBaseConstraintOfType(type.indexType); return baseObjectType || baseIndexType ? getIndexedAccessType(baseObjectType || type.objectType, baseIndexType || type.indexType) : undefined; @@ -5976,11 +5978,18 @@ namespace ts { return stringType; } if (t.flags & TypeFlags.IndexedAccess) { + const transformed = getTransformedIndexedAccessType(t); + if (transformed) { + return getBaseConstraint(transformed); + } const baseObjectType = getBaseConstraint((t).objectType); const baseIndexType = getBaseConstraint((t).indexType); const baseIndexedAccess = baseObjectType && baseIndexType ? getIndexedAccessType(baseObjectType, baseIndexType) : undefined; return baseIndexedAccess && baseIndexedAccess !== unknownType ? getBaseConstraint(baseIndexedAccess) : undefined; } + if (isGenericMappedType(t)) { + return emptyObjectType; + } return t; } } @@ -7592,36 +7601,89 @@ namespace ts { } function getIndexedAccessForMappedType(type: MappedType, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode) { - const accessExpression = accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression ? accessNode : undefined; - if (accessExpression && isAssignmentTarget(accessExpression) && type.declaration.readonlyToken) { - error(accessExpression, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(type)); - return unknownType; + if (accessNode) { + // Check if the index type is assignable to 'keyof T' for the object type. + if (!isTypeAssignableTo(indexType, getIndexType(type))) { + error(accessNode, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(indexType), typeToString(type)); + return unknownType; + } + if (accessNode.kind === SyntaxKind.ElementAccessExpression && isAssignmentTarget(accessNode) && type.declaration.readonlyToken) { + error(accessNode, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(type)); + return unknownType; + } } const mapper = createTypeMapper([getTypeParameterFromMappedType(type)], [indexType]); const templateMapper = type.mapper ? combineTypeMappers(type.mapper, mapper) : mapper; return instantiateType(getTemplateTypeFromMappedType(type), templateMapper); } - function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode) { - // If the index type is generic, if the object type is generic and doesn't originate in an expression, - // or if the object type is a mapped type with a generic constraint, we are performing a higher-order - // index access where we cannot meaningfully access the properties of the object type. Note that for a - // generic T and a non-generic K, we eagerly resolve T[K] if it originates in an expression. This is to - // preserve backwards compatibility. For example, an element access 'this["foo"]' has always been resolved - // eagerly using the constraint type of 'this' at the given location. - if (maybeTypeOfKind(indexType, TypeFlags.TypeVariable | TypeFlags.Index) || - maybeTypeOfKind(objectType, TypeFlags.TypeVariable) && !(accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression) || - isGenericMappedType(objectType)) { + function isGenericObjectType(type: Type): boolean { + return type.flags & TypeFlags.TypeVariable ? true : + getObjectFlags(type) & ObjectFlags.Mapped ? isGenericIndexType(getConstraintTypeFromMappedType(type)) : + type.flags & TypeFlags.UnionOrIntersection ? forEach((type).types, isGenericObjectType) : + false; + } + + function isGenericIndexType(type: Type): boolean { + return type.flags & (TypeFlags.TypeVariable | TypeFlags.Index) ? true : + type.flags & TypeFlags.UnionOrIntersection ? forEach((type).types, isGenericIndexType) : + false; + } + + // Return true if the given type is a non-generic object type with a string index signature and no + // other members. + function isStringIndexOnlyType(type: Type) { + if (type.flags & TypeFlags.Object && !isGenericMappedType(type)) { + const t = resolveStructuredTypeMembers(type); + return t.properties.length === 0 && + t.callSignatures.length === 0 && t.constructSignatures.length === 0 && + t.stringIndexInfo && !t.numberIndexInfo; + } + return false; + } + + // Given an indexed access type T[K], if T is an intersection containing one or more generic types and one or + // more object types with only a string index signature, e.g. '(U & V & { [x: string]: D })[K]', return a + // transformed type of the form '(U & V)[K] | D'. This allows us to properly reason about higher order indexed + // access types with default property values as expressed by D. + function getTransformedIndexedAccessType(type: IndexedAccessType): Type { + const objectType = type.objectType; + if (objectType.flags & TypeFlags.Intersection && isGenericObjectType(objectType) && some((objectType).types, isStringIndexOnlyType)) { + const regularTypes: Type[] = []; + const stringIndexTypes: Type[] = []; + for (const t of (objectType).types) { + if (isStringIndexOnlyType(t)) { + stringIndexTypes.push(getIndexTypeOfType(t, IndexKind.String)); + } + else { + regularTypes.push(t); + } + } + return getUnionType([ + getIndexedAccessType(getIntersectionType(regularTypes), type.indexType), + getIntersectionType(stringIndexTypes) + ]); + } + return undefined; + } + + function getIndexedAccessType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode): Type { + // If the object type is a mapped type { [P in K]: E }, where K is generic, we instantiate E using a mapper + // that substitutes the index type for P. For example, for an index access { [P in K]: Box }[X], we + // construct the type Box. + if (isGenericMappedType(objectType)) { + return getIndexedAccessForMappedType(objectType, indexType, accessNode); + } + // Otherwise, if the index type is generic, or if the object type is generic and doesn't originate in an + // expression, we are performing a higher-order index access where we cannot meaningfully access the properties + // of the object type. Note that for a generic T and a non-generic K, we eagerly resolve T[K] if it originates + // in an expression. This is to preserve backwards compatibility. For example, an element access 'this["foo"]' + // has always been resolved eagerly using the constraint type of 'this' at the given location. + if (isGenericIndexType(indexType) || !(accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression) && isGenericObjectType(objectType)) { if (objectType.flags & TypeFlags.Any) { return objectType; } - // If the object type is a mapped type { [P in K]: E }, we instantiate E using a mapper that substitutes - // the index type for P. For example, for an index access { [P in K]: Box }[X], we construct the - // type Box. - if (isGenericMappedType(objectType)) { - return getIndexedAccessForMappedType(objectType, indexType, accessNode); - } - // Otherwise we defer the operation by creating an indexed access type. + // Defer the operation by creating an indexed access type. const id = objectType.id + "," + indexType.id; let type = indexedAccessTypes.get(id); if (!type) { @@ -8015,7 +8077,7 @@ namespace ts { function cloneTypeMapper(mapper: TypeMapper): TypeMapper { return mapper && isInferenceContext(mapper) ? - createInferenceContext(mapper.signature, mapper.flags | InferenceFlags.NoDefault, mapper.inferences) : + createInferenceContext(mapper.signature, mapper.flags | InferenceFlags.NoDefault, mapper.compareTypes, mapper.inferences) : mapper; } @@ -8456,7 +8518,7 @@ namespace ts { ignoreReturnTypes: boolean, reportErrors: boolean, errorReporter: ErrorReporter, - compareTypes: (s: Type, t: Type, reportErrors?: boolean) => Ternary): Ternary { + compareTypes: TypeComparer): Ternary { // TODO (drosen): De-duplicate code between related functions. if (source === target) { return Ternary.True; @@ -8466,7 +8528,7 @@ namespace ts { } if (source.typeParameters) { - source = instantiateSignatureInContextOf(source, target); + source = instantiateSignatureInContextOf(source, target, /*contextualMapper*/ undefined, compareTypes); } let result = Ternary.True; @@ -8879,11 +8941,21 @@ namespace ts { !(target.flags & TypeFlags.Union) && !isIntersectionConstituent && source !== globalObjectType && - getPropertiesOfType(source).length > 0 && + (getPropertiesOfType(source).length > 0 || + getSignaturesOfType(source, SignatureKind.Call).length > 0 || + getSignaturesOfType(source, SignatureKind.Construct).length > 0) && isWeakType(target) && !hasCommonProperties(source, target)) { if (reportErrors) { - reportError(Diagnostics.Type_0_has_no_properties_in_common_with_type_1, typeToString(source), typeToString(target)); + const calls = getSignaturesOfType(source, SignatureKind.Call); + const constructs = getSignaturesOfType(source, SignatureKind.Construct); + if (calls.length > 0 && isRelatedTo(getReturnTypeOfSignature(calls[0]), target, /*reportErrors*/ false) || + constructs.length > 0 && isRelatedTo(getReturnTypeOfSignature(constructs[0]), target, /*reportErrors*/ false)) { + reportError(Diagnostics.Value_of_type_0_has_no_properties_in_common_with_type_1_Did_you_mean_to_call_it, typeToString(source), typeToString(target)); + } + else { + reportError(Diagnostics.Type_0_has_no_properties_in_common_with_type_1, typeToString(source), typeToString(target)); + } } return Ternary.False; } @@ -9612,6 +9684,11 @@ namespace ts { if (sourceInfo) { return indexInfoRelatedTo(sourceInfo, targetInfo, reportErrors); } + if (isGenericMappedType(source)) { + // A generic mapped type { [P in K]: T } is related to an index signature { [x: string]: U } + // if T is related to U. + return kind === IndexKind.String && isRelatedTo(getTemplateTypeFromMappedType(source), targetInfo.type, reportErrors); + } if (isObjectLiteralType(source)) { let related = Ternary.True; if (kind === IndexKind.String) { @@ -10214,13 +10291,14 @@ namespace ts { } } - function createInferenceContext(signature: Signature, flags: InferenceFlags, baseInferences?: InferenceInfo[]): InferenceContext { + function createInferenceContext(signature: Signature, flags: InferenceFlags, compareTypes?: TypeComparer, baseInferences?: InferenceInfo[]): InferenceContext { const inferences = baseInferences ? map(baseInferences, cloneInferenceInfo) : map(signature.typeParameters, createInferenceInfo); const context = mapper as InferenceContext; context.mappedTypes = signature.typeParameters; context.signature = signature; context.inferences = inferences; context.flags = flags; + context.compareTypes = compareTypes || compareTypesAssignable; return context; function mapper(t: Type): Type { @@ -10323,6 +10401,19 @@ namespace ts { } } + function isPossiblyAssignableTo(source: Type, target: Type) { + const properties = getPropertiesOfObjectType(target); + for (const targetProp of properties) { + if (!(targetProp.flags & (SymbolFlags.Optional | SymbolFlags.Prototype))) { + const sourceProp = getPropertyOfObjectType(source, targetProp.escapedName); + if (!sourceProp) { + return false; + } + } + } + return true; + } + function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0) { let symbolStack: Symbol[]; let visited: Map; @@ -10516,10 +10607,14 @@ namespace ts { return; } } - inferFromProperties(source, target); - inferFromSignatures(source, target, SignatureKind.Call); - inferFromSignatures(source, target, SignatureKind.Construct); - inferFromIndexTypes(source, target); + // Infer from the members of source and target only if the two types are possibly related. We check + // in both directions because we may be inferring for a co-variant or a contra-variant position. + if (isPossiblyAssignableTo(source, target) || isPossiblyAssignableTo(target, source)) { + inferFromProperties(source, target); + inferFromSignatures(source, target, SignatureKind.Call); + inferFromSignatures(source, target, SignatureKind.Construct); + inferFromIndexTypes(source, target); + } } function inferFromProperties(source: Type, target: Type) { @@ -10651,7 +10746,7 @@ namespace ts { const constraint = getConstraintOfTypeParameter(context.signature.typeParameters[index]); if (constraint) { const instantiatedConstraint = instantiateType(constraint, context); - if (!isTypeAssignableTo(inferredType, getTypeWithThisArgument(instantiatedConstraint, inferredType))) { + if (!context.compareTypes(inferredType, getTypeWithThisArgument(instantiatedConstraint, inferredType))) { inference.inferredType = inferredType = instantiatedConstraint; } } @@ -10718,17 +10813,6 @@ namespace ts { return undefined; } - function getLeftmostIdentifierOrThis(node: Node): Node { - switch (node.kind) { - case SyntaxKind.Identifier: - case SyntaxKind.ThisKeyword: - return node; - case SyntaxKind.PropertyAccessExpression: - return getLeftmostIdentifierOrThis((node).expression); - } - return undefined; - } - function getBindingElementNameText(element: BindingElement): string | undefined { if (element.parent.kind === SyntaxKind.ObjectBindingPattern) { const name = element.propertyName || element.name; @@ -14020,11 +14104,8 @@ namespace ts { */ function resolveCustomJsxElementAttributesType(openingLikeElement: JsxOpeningLikeElement, shouldIncludeAllStatelessAttributesType: boolean, - elementType?: Type, + elementType: Type = checkExpression(openingLikeElement.tagName), elementClassType?: Type): Type { - if (!elementType) { - elementType = checkExpression(openingLikeElement.tagName); - } if (elementType.flags & TypeFlags.Union) { const types = (elementType as UnionType).types; @@ -14158,11 +14239,12 @@ namespace ts { */ function getCustomJsxElementAttributesType(node: JsxOpeningLikeElement, shouldIncludeAllStatelessAttributesType: boolean): Type { const links = getNodeLinks(node); - if (!links.resolvedJsxElementAttributesType) { + const linkLocation = shouldIncludeAllStatelessAttributesType ? "resolvedJsxElementAllAttributesType" : "resolvedJsxElementAttributesType"; + if (!links[linkLocation]) { const elemClassType = getJsxGlobalElementClassType(); - return links.resolvedJsxElementAttributesType = resolveCustomJsxElementAttributesType(node, shouldIncludeAllStatelessAttributesType, /*elementType*/ undefined, elemClassType); + return links[linkLocation] = resolveCustomJsxElementAttributesType(node, shouldIncludeAllStatelessAttributesType, /*elementType*/ undefined, elemClassType); } - return links.resolvedJsxElementAttributesType; + return links[linkLocation]; } /** @@ -15049,8 +15131,8 @@ namespace ts { } // Instantiate a generic signature in the context of a non-generic signature (section 3.8.5 in TypeScript spec) - function instantiateSignatureInContextOf(signature: Signature, contextualSignature: Signature, contextualMapper?: TypeMapper): Signature { - const context = createInferenceContext(signature, InferenceFlags.InferUnionTypes); + function instantiateSignatureInContextOf(signature: Signature, contextualSignature: Signature, contextualMapper?: TypeMapper, compareTypes?: TypeComparer): Signature { + const context = createInferenceContext(signature, InferenceFlags.InferUnionTypes, compareTypes); forEachMatchingParameterType(contextualSignature, signature, (source, target) => { // Type parameters from outer context referenced by source type are fixed by instantiation of the source type inferTypes(context.inferences, instantiateType(source, contextualMapper || identityMapper), target); @@ -16965,8 +17047,13 @@ namespace ts { if (operandType === silentNeverType) { return silentNeverType; } - if (node.operator === SyntaxKind.MinusToken && node.operand.kind === SyntaxKind.NumericLiteral) { - return getFreshTypeOfLiteralType(getLiteralType(-(node.operand).text)); + if (node.operand.kind === SyntaxKind.NumericLiteral) { + if (node.operator === SyntaxKind.MinusToken) { + return getFreshTypeOfLiteralType(getLiteralType(-(node.operand).text)); + } + else if (node.operator === SyntaxKind.PlusToken) { + return getFreshTypeOfLiteralType(getLiteralType(+(node.operand).text)); + } } switch (node.operator) { case SyntaxKind.PlusToken: @@ -18419,15 +18506,6 @@ namespace ts { return forEachChild(n, containsSuperCall); } - function markThisReferencesAsErrors(n: Node): void { - if (n.kind === SyntaxKind.ThisKeyword) { - error(n, Diagnostics.this_cannot_be_referenced_in_current_location); - } - else if (n.kind !== SyntaxKind.FunctionExpression && n.kind !== SyntaxKind.FunctionDeclaration) { - forEachChild(n, markThisReferencesAsErrors); - } - } - function isInstancePropertyWithInitializer(n: Node): boolean { return n.kind === SyntaxKind.PropertyDeclaration && !(getModifierFlags(n) & ModifierFlags.Static) && @@ -18578,7 +18656,17 @@ namespace ts { forEach(node.typeArguments, checkSourceElement); if (produceDiagnostics) { const symbol = getNodeLinks(node).resolvedSymbol; - const typeParameters = symbol.flags & SymbolFlags.TypeAlias ? getSymbolLinks(symbol).typeParameters : (type).target.localTypeParameters; + if (!symbol) { + // There is no resolved symbol cached if the type resolved to a builtin + // via JSDoc type reference resolution (eg, Boolean became boolean), none + // of which are generic when they have no associated symbol + error(node, Diagnostics.Type_0_is_not_generic, typeToString(type)); + return; + } + let typeParameters = symbol.flags & SymbolFlags.TypeAlias && getSymbolLinks(symbol).typeParameters; + if (!typeParameters && getObjectFlags(type) & ObjectFlags.Reference) { + typeParameters = (type).target.localTypeParameters; + } checkTypeArgumentConstraints(typeParameters, node.typeArguments); } } @@ -18643,6 +18731,8 @@ namespace ts { } function checkIndexedAccessType(node: IndexedAccessTypeNode) { + checkSourceElement(node.objectType); + checkSourceElement(node.indexType); checkIndexedAccessIndexType(getTypeFromIndexedAccessTypeNode(node), node); } diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 554b46dcdfe..e82e70dc764 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -1446,14 +1446,10 @@ namespace ts { } } else { - // If no includes were specified, exclude common package folders and the outDir - const specs = includeSpecs ? [] : ["node_modules", "bower_components", "jspm_packages"]; - const outDir = raw["compilerOptions"] && raw["compilerOptions"]["outDir"]; if (outDir) { - specs.push(outDir); + excludeSpecs = [outDir]; } - excludeSpecs = specs; } if (fileNames === undefined && includeSpecs === undefined) { @@ -2015,23 +2011,13 @@ namespace ts { } function validateSpecs(specs: ReadonlyArray, errors: Push, allowTrailingRecursion: boolean, jsonSourceFile: JsonSourceFile, specKey: string) { - const validSpecs: string[] = []; - for (const spec of specs) { - if (!allowTrailingRecursion && invalidTrailingRecursionPattern.test(spec)) { - errors.push(createDiagnostic(Diagnostics.File_specification_cannot_end_in_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, spec)); + return specs.filter(spec => { + const diag = specToDiagnostic(spec, allowTrailingRecursion); + if (diag !== undefined) { + errors.push(createDiagnostic(diag, spec)); } - else if (invalidMultipleRecursionPatterns.test(spec)) { - errors.push(createDiagnostic(Diagnostics.File_specification_cannot_contain_multiple_recursive_directory_wildcards_Asterisk_Asterisk_Colon_0, spec)); - } - else if (invalidDotDotAfterRecursiveWildcardPattern.test(spec)) { - errors.push(createDiagnostic(Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, spec)); - } - else { - validSpecs.push(spec); - } - } - - return validSpecs; + return diag === undefined; + }); function createDiagnostic(message: DiagnosticMessage, spec: string): Diagnostic { if (jsonSourceFile && jsonSourceFile.jsonObject) { @@ -2049,6 +2035,18 @@ namespace ts { } } + function specToDiagnostic(spec: string, allowTrailingRecursion: boolean): ts.DiagnosticMessage | undefined { + if (!allowTrailingRecursion && invalidTrailingRecursionPattern.test(spec)) { + return Diagnostics.File_specification_cannot_end_in_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0; + } + else if (invalidMultipleRecursionPatterns.test(spec)) { + return Diagnostics.File_specification_cannot_contain_multiple_recursive_directory_wildcards_Asterisk_Asterisk_Colon_0; + } + else if (invalidDotDotAfterRecursiveWildcardPattern.test(spec)) { + return Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0; + } + } + /** * Gets directories in a set of include patterns that should be watched for changes. */ diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 728fb433c04..d5c8396d06d 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -11,20 +11,6 @@ namespace ts { /* @internal */ namespace ts { - /** - * Ternary values are defined such that - * x & y is False if either x or y is False. - * x & y is Maybe if either x or y is Maybe, but neither x or y is False. - * x & y is True if both x and y are True. - * x | y is False if both x and y are False. - * x | y is Maybe if either x or y is Maybe, but neither x or y is True. - * x | y is True if either x or y is True. - */ - export const enum Ternary { - False = 0, - Maybe = 1, - True = -1 - } // More efficient to create a collator once and use its `compare` than to call `a.localeCompare(b)` many times. export const collator: { compare(a: string, b: string): number } = typeof Intl === "object" && typeof Intl.Collator === "function" ? new Intl.Collator(/*locales*/ undefined, { usage: "sort", sensitivity: "accent" }) : undefined; @@ -375,11 +361,11 @@ namespace ts { return false; } - export function filterMutate(array: T[], f: (x: T) => boolean): void { + export function filterMutate(array: T[], f: (x: T, i: number, array: T[]) => boolean): void { let outIndex = 0; - for (const item of array) { - if (f(item)) { - array[outIndex] = item; + for (let i = 0; i < array.length; i++) { + if (f(array[i], i, array)) { + array[outIndex] = array[i]; outIndex++; } } @@ -1304,12 +1290,12 @@ namespace ts { export function createFileDiagnostic(file: SourceFile, start: number, length: number, message: DiagnosticMessage): Diagnostic { const end = start + length; - Debug.assert(start >= 0, "start must be non-negative, is " + start); - Debug.assert(length >= 0, "length must be non-negative, is " + length); + Debug.assertGreaterThanOrEqual(start, 0); + Debug.assertGreaterThanOrEqual(length, 0); if (file) { - Debug.assert(start <= file.text.length, `start must be within the bounds of the file. ${start} > ${file.text.length}`); - Debug.assert(end <= file.text.length, `end must be the bounds of the file. ${end} > ${file.text.length}`); + Debug.assertLessThanOrEqual(start, file.text.length); + Debug.assertLessThanOrEqual(end, file.text.length); } let text = getLocaleSpecificMessage(message); @@ -1889,14 +1875,54 @@ namespace ts { const reservedCharacterPattern = /[^\w\s\/]/g; const wildcardCharCodes = [CharacterCodes.asterisk, CharacterCodes.question]; - /** - * Matches any single directory segment unless it is the last segment and a .min.js file - * Breakdown: - * [^./] # matches everything up to the first . character (excluding directory seperators) - * (\\.(?!min\\.js$))? # matches . characters but not if they are part of the .min.js file extension - */ - const singleAsteriskRegexFragmentFiles = "([^./]|(\\.(?!min\\.js$))?)*"; - const singleAsteriskRegexFragmentOther = "[^/]*"; + /* @internal */ + export const commonPackageFolders: ReadonlyArray = ["node_modules", "bower_components", "jspm_packages"]; + + const implicitExcludePathRegexPattern = `(?!(${commonPackageFolders.join("|")})(/|$))`; + + interface WildcardMatcher { + singleAsteriskRegexFragment: string; + doubleAsteriskRegexFragment: string; + replaceWildcardCharacter: (match: string) => string; + } + + const filesMatcher: WildcardMatcher = { + /** + * Matches any single directory segment unless it is the last segment and a .min.js file + * Breakdown: + * [^./] # matches everything up to the first . character (excluding directory seperators) + * (\\.(?!min\\.js$))? # matches . characters but not if they are part of the .min.js file extension + */ + singleAsteriskRegexFragment: "([^./]|(\\.(?!min\\.js$))?)*", + /** + * Regex for the ** wildcard. Matches any number of subdirectories. When used for including + * files or directories, does not match subdirectories that start with a . character + */ + doubleAsteriskRegexFragment: `(/${implicitExcludePathRegexPattern}[^/.][^/]*)*?`, + replaceWildcardCharacter: match => replaceWildcardCharacter(match, filesMatcher.singleAsteriskRegexFragment) + }; + + const directoriesMatcher: WildcardMatcher = { + singleAsteriskRegexFragment: "[^/]*", + /** + * Regex for the ** wildcard. Matches any number of subdirectories. When used for including + * files or directories, does not match subdirectories that start with a . character + */ + doubleAsteriskRegexFragment: `(/${implicitExcludePathRegexPattern}[^/.][^/]*)*?`, + replaceWildcardCharacter: match => replaceWildcardCharacter(match, directoriesMatcher.singleAsteriskRegexFragment) + }; + + const excludeMatcher: WildcardMatcher = { + singleAsteriskRegexFragment: "[^/]*", + doubleAsteriskRegexFragment: "(/.+?)?", + replaceWildcardCharacter: match => replaceWildcardCharacter(match, excludeMatcher.singleAsteriskRegexFragment) + }; + + const wildcardMatchers = { + files: filesMatcher, + directories: directoriesMatcher, + exclude: excludeMatcher + }; export function getRegularExpressionForWildcard(specs: ReadonlyArray, basePath: string, usage: "files" | "directories" | "exclude"): string | undefined { const patterns = getRegularExpressionsForWildcards(specs, basePath, usage); @@ -1915,17 +1941,8 @@ namespace ts { return undefined; } - const replaceWildcardCharacter = usage === "files" ? replaceWildCardCharacterFiles : replaceWildCardCharacterOther; - const singleAsteriskRegexFragment = usage === "files" ? singleAsteriskRegexFragmentFiles : singleAsteriskRegexFragmentOther; - - /** - * Regex for the ** wildcard. Matches any number of subdirectories. When used for including - * files or directories, does not match subdirectories that start with a . character - */ - const doubleAsteriskRegexFragment = usage === "exclude" ? "(/.+?)?" : "(/[^/.][^/]*)*?"; - return flatMap(specs, spec => - spec && getSubPatternFromSpec(spec, basePath, usage, singleAsteriskRegexFragment, doubleAsteriskRegexFragment, replaceWildcardCharacter)); + spec && getSubPatternFromSpec(spec, basePath, usage, wildcardMatchers[usage])); } /** @@ -1936,7 +1953,7 @@ namespace ts { return !/[.*?]/.test(lastPathComponent); } - function getSubPatternFromSpec(spec: string, basePath: string, usage: "files" | "directories" | "exclude", singleAsteriskRegexFragment: string, doubleAsteriskRegexFragment: string, replaceWildcardCharacter: (match: string) => string): string | undefined { + function getSubPatternFromSpec(spec: string, basePath: string, usage: "files" | "directories" | "exclude", { singleAsteriskRegexFragment, doubleAsteriskRegexFragment, replaceWildcardCharacter }: WildcardMatcher): string | undefined { let subpattern = ""; let hasRecursiveDirectoryWildcard = false; let hasWrittenComponent = false; @@ -1975,20 +1992,36 @@ namespace ts { } if (usage !== "exclude") { + let componentPattern = ""; // The * and ? wildcards should not match directories or files that start with . if they // appear first in a component. Dotted directories and files can be included explicitly // like so: **/.*/.* if (component.charCodeAt(0) === CharacterCodes.asterisk) { - subpattern += "([^./]" + singleAsteriskRegexFragment + ")?"; + componentPattern += "([^./]" + singleAsteriskRegexFragment + ")?"; component = component.substr(1); } else if (component.charCodeAt(0) === CharacterCodes.question) { - subpattern += "[^./]"; + componentPattern += "[^./]"; component = component.substr(1); } - } - subpattern += component.replace(reservedCharacterPattern, replaceWildcardCharacter); + componentPattern += component.replace(reservedCharacterPattern, replaceWildcardCharacter); + + // Patterns should not include subfolders like node_modules unless they are + // explicitly included as part of the path. + // + // As an optimization, if the component pattern is the same as the component, + // then there definitely were no wildcard characters and we do not need to + // add the exclusion pattern. + if (componentPattern !== component) { + subpattern += implicitExcludePathRegexPattern; + } + + subpattern += componentPattern; + } + else { + subpattern += component.replace(reservedCharacterPattern, replaceWildcardCharacter); + } } hasWrittenComponent = true; @@ -2002,14 +2035,6 @@ namespace ts { return subpattern; } - function replaceWildCardCharacterFiles(match: string) { - return replaceWildcardCharacter(match, singleAsteriskRegexFragmentFiles); - } - - function replaceWildCardCharacterOther(match: string) { - return replaceWildcardCharacter(match, singleAsteriskRegexFragmentOther); - } - function replaceWildcardCharacter(match: string, singleAsteriskRegexFragment: string) { return match === "*" ? singleAsteriskRegexFragment : match === "?" ? "[^/]" : "\\" + match; } @@ -2364,15 +2389,40 @@ namespace ts { return currentAssertionLevel >= level; } - export function assert(expression: boolean, message?: string, verboseDebugInfo?: () => string, stackCrawlMark?: Function): void { + export function assert(expression: boolean, message?: string, verboseDebugInfo?: string | (() => string), stackCrawlMark?: Function): void { if (!expression) { if (verboseDebugInfo) { - message += "\r\nVerbose Debug Information: " + verboseDebugInfo(); + message += "\r\nVerbose Debug Information: " + (typeof verboseDebugInfo === "string" ? verboseDebugInfo : verboseDebugInfo()); } fail(message ? "False expression: " + message : "False expression.", stackCrawlMark || assert); } } + export function assertEqual(a: T, b: T, msg?: string, msg2?: string): void { + if (a !== b) { + const message = msg ? msg2 ? `${msg} ${msg2}` : msg : ""; + fail(`Expected ${a} === ${b}. ${message}`); + } + } + + export function assertLessThan(a: number, b: number, msg?: string): void { + if (a >= b) { + fail(`Expected ${a} < ${b}. ${msg || ""}`); + } + } + + export function assertLessThanOrEqual(a: number, b: number): void { + if (a > b) { + fail(`Expected ${a} <= ${b}`); + } + } + + export function assertGreaterThanOrEqual(a: number, b: number): void { + if (a < b) { + fail(`Expected ${a} >= ${b}`); + } + } + export function fail(message?: string, stackCrawlMark?: Function): void { debugger; const e = new Error(message ? `Debug Failure. ${message}` : "Debug Failure."); diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 3b4b0ddf667..03164c54ffd 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1908,6 +1908,10 @@ "category": "Error", "code": 2559 }, + "Value of type '{0}' has no properties in common with type '{1}'. Did you mean to call it?": { + "category": "Error", + "code": 2560 + }, "JSX element attributes type '{0}' may not be a union type.": { "category": "Error", "code": 2600 diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index de08b209d3e..4f07331a98e 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1346,7 +1346,9 @@ namespace ts { emitExpression(node.left); increaseIndentIf(indentBeforeOperator, isCommaOperator ? " " : undefined); + emitLeadingCommentsOfPosition(node.operatorToken.pos); writeTokenNode(node.operatorToken); + emitTrailingCommentsOfPosition(node.operatorToken.end); increaseIndentIf(indentAfterOperator, " "); emitExpression(node.right); decreaseIndentIf(indentBeforeOperator, indentAfterOperator); diff --git a/src/compiler/moduleNameResolver.ts b/src/compiler/moduleNameResolver.ts index 513c741ba09..be867db9743 100644 --- a/src/compiler/moduleNameResolver.ts +++ b/src/compiler/moduleNameResolver.ts @@ -678,7 +678,7 @@ namespace ts { const { resolvedModule, failedLookupLocations } = nodeModuleNameResolverWorker(moduleName, initialDir, { moduleResolution: ts.ModuleResolutionKind.NodeJs, allowJs: true }, host, /*cache*/ undefined, /*jsOnly*/ true); if (!resolvedModule) { - throw new Error(`Could not resolve JS module ${moduleName} starting at ${initialDir}. Looked in: ${failedLookupLocations.join(", ")}`); + throw new Error(`Could not resolve JS module '${moduleName}' starting at '${initialDir}'. Looked in: ${failedLookupLocations.join(", ")}`); } return resolvedModule.resolvedFileName; } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 34670cd2834..49b682c7302 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -2061,7 +2061,7 @@ namespace ts { return fragment; } - function parseLiteralLikeNode(kind: SyntaxKind): LiteralLikeNode { + function parseLiteralLikeNode(kind: SyntaxKind): LiteralExpression | LiteralLikeNode { const node = createNode(kind); const text = scanner.getTokenValue(); node.text = text; @@ -2611,11 +2611,31 @@ namespace ts { return token() === SyntaxKind.DotToken ? undefined : node; } - function parseLiteralTypeNode(): LiteralTypeNode { - const node = createNode(SyntaxKind.LiteralType); - node.literal = parseSimpleUnaryExpression(); - finishNode(node); - return node; + function parseLiteralTypeNode(negative?: boolean): LiteralTypeNode { + const node = createNode(SyntaxKind.LiteralType) as LiteralTypeNode; + let unaryMinusExpression: PrefixUnaryExpression; + if (negative) { + unaryMinusExpression = createNode(SyntaxKind.PrefixUnaryExpression) as PrefixUnaryExpression; + unaryMinusExpression.operator = SyntaxKind.MinusToken; + nextToken(); + } + let expression: UnaryExpression; + switch (token()) { + case SyntaxKind.StringLiteral: + case SyntaxKind.NumericLiteral: + expression = parseLiteralLikeNode(token()) as LiteralExpression; + break; + case SyntaxKind.TrueKeyword: + case SyntaxKind.FalseKeyword: + expression = parseTokenNode(); + } + if (negative) { + unaryMinusExpression.operand = expression; + finishNode(unaryMinusExpression); + expression = unaryMinusExpression; + } + node.literal = expression; + return finishNode(node); } function nextTokenIsNumericLiteral() { @@ -2650,7 +2670,7 @@ namespace ts { case SyntaxKind.FalseKeyword: return parseLiteralTypeNode(); case SyntaxKind.MinusToken: - return lookAhead(nextTokenIsNumericLiteral) ? parseLiteralTypeNode() : parseTypeReference(); + return lookAhead(nextTokenIsNumericLiteral) ? parseLiteralTypeNode(/*negative*/ true) : parseTypeReference(); case SyntaxKind.VoidKeyword: case SyntaxKind.NullKeyword: return parseTokenNode(); @@ -2700,6 +2720,7 @@ namespace ts { case SyntaxKind.TrueKeyword: case SyntaxKind.FalseKeyword: case SyntaxKind.ObjectKeyword: + case SyntaxKind.AsteriskToken: return true; case SyntaxKind.MinusToken: return lookAhead(nextTokenIsNumericLiteral); diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 9c6d4bb795d..89cfb074bff 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -4,6 +4,18 @@ declare function setTimeout(handler: (...args: any[]) => void, timeout: number): declare function clearTimeout(handle: any): void; namespace ts { + /** + * Set a high stack trace limit to provide more information in case of an error. + * Called for command-line and server use cases. + * Not called if TypeScript is used as a library. + */ + /* @internal */ + export function setStackTraceLimit() { + if ((Error as any).stackTraceLimit < 100) { // Also tests that we won't set the property if it doesn't exist. + (Error as any).stackTraceLimit = 100; + } + } + export enum FileWatcherEventKind { Created, Changed, diff --git a/src/compiler/transformers/generators.ts b/src/compiler/transformers/generators.ts index 41edf24fe9e..5e2016ab590 100644 --- a/src/compiler/transformers/generators.ts +++ b/src/compiler/transformers/generators.ts @@ -2448,7 +2448,7 @@ namespace ts { * @param location An optional source map location for the statement. */ function createInlineBreak(label: Label, location?: TextRange): ReturnStatement { - Debug.assert(label > 0, `Invalid label: ${label}`); + Debug.assertLessThan(0, label, "Invalid label"); return setTextRange( createReturn( createArrayLiteral([ diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 8cc2e5c5ef6..db25afe45b6 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -665,6 +665,8 @@ namespace ts { } } +ts.setStackTraceLimit(); + if (ts.Debug.isDebugging) { ts.Debug.enableDebugInfo(); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 09f1b5cfcc5..0eb7f03e3ec 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3067,6 +3067,7 @@ namespace ts { hasReportedStatementInAmbientContext?: boolean; // Cache boolean if we report statements in ambient context jsxFlags?: JsxFlags; // flags for knowing what kind of element/attributes we're dealing with resolvedJsxElementAttributesType?: Type; // resolved element attributes type of a JSX openinglike element + resolvedJsxElementAllAttributesType?: Type; // resolved all element attributes type of a JSX openinglike element hasSuperCall?: boolean; // recorded result when we try to find super-call. We only try to find one if this flag is undefined, indicating that we haven't made an attempt. superCall?: ExpressionStatement; // Cached first super-call found in the constructor. Used in checking whether super is called before this-accessing switchTypes?: Type[]; // Cached array of switch case expression types @@ -3425,11 +3426,29 @@ namespace ts { AnyDefault = 1 << 2, // Infer anyType for no inferences (otherwise emptyObjectType) } + /** + * Ternary values are defined such that + * x & y is False if either x or y is False. + * x & y is Maybe if either x or y is Maybe, but neither x or y is False. + * x & y is True if both x and y are True. + * x | y is False if both x and y are False. + * x | y is Maybe if either x or y is Maybe, but neither x or y is True. + * x | y is True if either x or y is True. + */ + export const enum Ternary { + False = 0, + Maybe = 1, + True = -1 + } + + export type TypeComparer = (s: Type, t: Type, reportErrors?: boolean) => Ternary; + /* @internal */ export interface InferenceContext extends TypeMapper { signature: Signature; // Generic signature for which inferences are made inferences: InferenceInfo[]; // Inferences made for each type parameter flags: InferenceFlags; // Inference flags + compareTypes: TypeComparer; // Type comparer function } /* @internal */ diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 24e0d59318b..76d242e8b08 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -922,21 +922,11 @@ namespace ts { } export function getContainingFunction(node: Node): FunctionLike { - while (true) { - node = node.parent; - if (!node || isFunctionLike(node)) { - return node; - } - } + return findAncestor(node.parent, isFunctionLike); } export function getContainingClass(node: Node): ClassLikeDeclaration { - while (true) { - node = node.parent; - if (!node || isClassLike(node)) { - return node; - } - } + return findAncestor(node.parent, isClassLike); } export function getThisContainer(node: Node, includeArrowFunctions: boolean): Node { diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 55a8f3ebb4d..7122f55cae2 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -420,7 +420,7 @@ namespace Utils { const maxHarnessFrames = 1; - export function filterStack(error: Error, stackTraceLimit: number = Infinity) { + export function filterStack(error: Error, stackTraceLimit = Infinity) { const stack = (error).stack; if (stack) { const lines = stack.split(/\r\n?|\n/g); diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index c604b224656..af5a998df99 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -681,11 +681,11 @@ namespace Harness.LanguageService { } info(message: string): void { - return this.host.log(message); + this.host.log(message); } - msg(message: string) { - return this.host.log(message); + err(message: string): void { + this.host.log(message); } loggingEnabled() { @@ -700,17 +700,12 @@ namespace Harness.LanguageService { return false; } - - endGroup(): void { - } + group() { throw ts.notImplemented(); } perftrc(message: string): void { return this.host.log(message); } - startGroup(): void { - } - setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): any { return setTimeout(callback, ms, args); } @@ -795,7 +790,7 @@ namespace Harness.LanguageService { default: return { module: undefined, - error: "Could not resolve module" + error: new Error("Could not resolve module") }; } diff --git a/src/harness/unittests/cachingInServerLSHost.ts b/src/harness/unittests/cachingInServerLSHost.ts index eb2907e89de..92c10c1ff6d 100644 --- a/src/harness/unittests/cachingInServerLSHost.ts +++ b/src/harness/unittests/cachingInServerLSHost.ts @@ -52,21 +52,9 @@ namespace ts { } function createProject(rootFile: string, serverHost: server.ServerHost): { project: server.Project, rootScriptInfo: server.ScriptInfo } { - const logger: server.Logger = { - close: noop, - hasLevel: () => false, - loggingEnabled: () => false, - perftrc: noop, - info: noop, - startGroup: noop, - endGroup: noop, - msg: noop, - getLogFileName: (): string => undefined - }; - const svcOpts: server.ProjectServiceOptions = { host: serverHost, - logger, + logger: projectSystem.nullLogger, cancellationToken: { isCancellationRequested: () => false }, useSingleInferredProject: false, typingsInstaller: undefined diff --git a/src/harness/unittests/matchFiles.ts b/src/harness/unittests/matchFiles.ts index e0454671930..9e2a5883754 100644 --- a/src/harness/unittests/matchFiles.ts +++ b/src/harness/unittests/matchFiles.ts @@ -73,6 +73,7 @@ namespace ts { "c:/dev/a.d.ts", "c:/dev/a.js", "c:/dev/b.ts", + "c:/dev/x/a.ts", "c:/dev/node_modules/a.ts", "c:/dev/bower_components/a.ts", "c:/dev/jspm_packages/a.ts" @@ -141,7 +142,8 @@ namespace ts { errors: [], fileNames: [ "c:/dev/a.ts", - "c:/dev/b.ts" + "c:/dev/b.ts", + "c:/dev/x/a.ts" ], wildcardDirectories: { "c:/dev": ts.WatchDirectoryFlags.Recursive @@ -462,7 +464,6 @@ namespace ts { }; validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath); }); - it("same named declarations are excluded", () => { const json = { include: [ @@ -651,71 +652,127 @@ namespace ts { }; validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath); }); - it("with common package folders and no exclusions", () => { - const json = { - include: [ - "**/a.ts" - ] - }; - const expected: ts.ParsedCommandLine = { - options: {}, - errors: [], - fileNames: [ - "c:/dev/a.ts", - "c:/dev/bower_components/a.ts", - "c:/dev/jspm_packages/a.ts", - "c:/dev/node_modules/a.ts" - ], - wildcardDirectories: { - "c:/dev": ts.WatchDirectoryFlags.Recursive - }, - }; - validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath); - }); - it("with common package folders and exclusions", () => { - const json = { - include: [ - "**/a.ts" - ], - exclude: [ - "a.ts" - ] - }; - const expected: ts.ParsedCommandLine = { - options: {}, - errors: [], - fileNames: [ - "c:/dev/bower_components/a.ts", - "c:/dev/jspm_packages/a.ts", - "c:/dev/node_modules/a.ts" - ], - wildcardDirectories: { - "c:/dev": ts.WatchDirectoryFlags.Recursive - }, - }; - validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath); - }); - it("with common package folders and empty exclude", () => { - const json = { - include: [ - "**/a.ts" - ], - exclude: [] - }; - const expected: ts.ParsedCommandLine = { - options: {}, - errors: [], - fileNames: [ - "c:/dev/a.ts", - "c:/dev/bower_components/a.ts", - "c:/dev/jspm_packages/a.ts", - "c:/dev/node_modules/a.ts" - ], - wildcardDirectories: { - "c:/dev": ts.WatchDirectoryFlags.Recursive - }, - }; - validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath); + describe("with common package folders", () => { + it("and no exclusions", () => { + const json = { + include: [ + "**/a.ts" + ] + }; + const expected: ts.ParsedCommandLine = { + options: {}, + errors: [], + fileNames: [ + "c:/dev/a.ts", + "c:/dev/x/a.ts" + ], + wildcardDirectories: { + "c:/dev": ts.WatchDirectoryFlags.Recursive + }, + }; + validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath); + }); + it("and exclusions", () => { + const json = { + include: [ + "**/?.ts" + ], + exclude: [ + "a.ts" + ] + }; + const expected: ts.ParsedCommandLine = { + options: {}, + errors: [], + fileNames: [ + "c:/dev/b.ts", + "c:/dev/x/a.ts" + ], + wildcardDirectories: { + "c:/dev": ts.WatchDirectoryFlags.Recursive + }, + }; + validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath); + }); + it("and empty exclude", () => { + const json = { + include: [ + "**/a.ts" + ], + exclude: [] + }; + const expected: ts.ParsedCommandLine = { + options: {}, + errors: [], + fileNames: [ + "c:/dev/a.ts", + "c:/dev/x/a.ts" + ], + wildcardDirectories: { + "c:/dev": ts.WatchDirectoryFlags.Recursive + }, + }; + validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath); + }); + it("and explicit recursive include", () => { + const json = { + include: [ + "**/a.ts", + "**/node_modules/a.ts" + ] + }; + const expected: ts.ParsedCommandLine = { + options: {}, + errors: [], + fileNames: [ + "c:/dev/a.ts", + "c:/dev/x/a.ts", + "c:/dev/node_modules/a.ts" + ], + wildcardDirectories: { + "c:/dev": ts.WatchDirectoryFlags.Recursive + }, + }; + validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath); + }); + it("and wildcard include", () => { + const json = { + include: [ + "*/a.ts" + ] + }; + const expected: ts.ParsedCommandLine = { + options: {}, + errors: [], + fileNames: [ + "c:/dev/x/a.ts" + ], + wildcardDirectories: { + "c:/dev": ts.WatchDirectoryFlags.Recursive + }, + }; + validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath); + }); + it("and explicit wildcard include", () => { + const json = { + include: [ + "*/a.ts", + "node_modules/a.ts" + ] + }; + const expected: ts.ParsedCommandLine = { + options: {}, + errors: [], + fileNames: [ + "c:/dev/x/a.ts", + "c:/dev/node_modules/a.ts" + ], + wildcardDirectories: { + "c:/dev": ts.WatchDirectoryFlags.Recursive + }, + }; + validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath); + }); }); it("exclude .js files when allowJs=false", () => { const json = { @@ -1066,6 +1123,7 @@ namespace ts { }; validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath); }); + describe("with trailing recursive directory", () => { it("in includes", () => { const json = { @@ -1264,6 +1322,7 @@ namespace ts { }); }); }); + describe("with files or folders that begin with a .", () => { it("that are not explicitly included", () => { const json = { diff --git a/src/harness/unittests/session.ts b/src/harness/unittests/session.ts index 862ebee4b03..1ce5792a81b 100644 --- a/src/harness/unittests/session.ts +++ b/src/harness/unittests/session.ts @@ -28,18 +28,6 @@ namespace ts.server { createHash: Harness.LanguageService.mockHash, }; - const mockLogger: Logger = { - close: noop, - hasLevel(): boolean { return false; }, - loggingEnabled(): boolean { return false; }, - perftrc: noop, - info: noop, - startGroup: noop, - endGroup: noop, - msg: noop, - getLogFileName: (): string => undefined - }; - class TestSession extends Session { getProjectService() { return this.projectService; @@ -58,7 +46,7 @@ namespace ts.server { typingsInstaller: undefined, byteLength: Utils.byteLength, hrtime: process.hrtime, - logger: mockLogger, + logger: projectSystem.nullLogger, canUseEvents: true }; return new TestSession(opts); @@ -408,7 +396,7 @@ namespace ts.server { typingsInstaller: undefined, byteLength: Utils.byteLength, hrtime: process.hrtime, - logger: mockLogger, + logger: projectSystem.nullLogger, canUseEvents: true }); this.addProtocolHandler(this.customHandler, () => { @@ -475,7 +463,7 @@ namespace ts.server { typingsInstaller: undefined, byteLength: Utils.byteLength, hrtime: process.hrtime, - logger: mockLogger, + logger: projectSystem.nullLogger, canUseEvents: true }); this.addProtocolHandler("echo", (req: protocol.Request) => ({ diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index f11900aea11..6c60160740e 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -34,18 +34,17 @@ namespace ts.projectSystem { } export const nullLogger: server.Logger = { - close: () => void 0, - hasLevel: () => void 0, + close: noop, + hasLevel: () => false, loggingEnabled: () => false, - perftrc: () => void 0, - info: () => void 0, - startGroup: () => void 0, - endGroup: () => void 0, - msg: () => void 0, + perftrc: noop, + info: noop, + err: noop, + group: noop, getLogFileName: (): string => undefined }; - export const { content: libFileContent } = Harness.getDefaultLibraryFile(Harness.IO); + const { content: libFileContent } = Harness.getDefaultLibraryFile(Harness.IO); export const libFile: FileOrFolder = { path: "/a/lib/lib.d.ts", content: libFileContent @@ -118,7 +117,7 @@ namespace ts.projectSystem { return JSON.stringify({ dependencies }); } - export function getExecutingFilePathFromLibFile(): string { + function getExecutingFilePathFromLibFile(): string { return combinePaths(getDirectoryPath(libFile.path), "tsc.js"); } @@ -130,7 +129,7 @@ namespace ts.projectSystem { return map(fileNames, toExternalFile); } - export class TestServerEventManager { + class TestServerEventManager { public events: server.ProjectServiceEvent[] = []; handler: server.ProjectServiceEventHandler = (event: server.ProjectServiceEvent) => { @@ -143,7 +142,7 @@ namespace ts.projectSystem { } } - export interface TestServerHostCreationParameters { + interface TestServerHostCreationParameters { useCaseSensitiveFileNames?: boolean; executingFilePath?: string; currentDirectory?: string; @@ -205,7 +204,7 @@ namespace ts.projectSystem { return new TestSession(opts); } - export interface CreateProjectServiceParameters { + interface CreateProjectServiceParameters { cancellationToken?: HostCancellationToken; logger?: server.Logger; useSingleInferredProject?: boolean; @@ -253,15 +252,15 @@ namespace ts.projectSystem { entries: FSEntry[]; } - export function isFolder(s: FSEntry): s is Folder { + function isFolder(s: FSEntry): s is Folder { return isArray((s).entries); } - export function isFile(s: FSEntry): s is File { + function isFile(s: FSEntry): s is File { return typeof (s).content === "string"; } - export function addFolder(fullPath: string, toPath: (s: string) => Path, fs: Map): Folder { + function addFolder(fullPath: string, toPath: (s: string) => Path, fs: Map): Folder { const path = toPath(fullPath); if (fs.has(path)) { Debug.assert(isFolder(fs.get(path))); @@ -279,29 +278,29 @@ namespace ts.projectSystem { return entry; } - export function checkMapKeys(caption: string, map: Map, expectedKeys: string[]) { + function checkMapKeys(caption: string, map: Map, expectedKeys: string[]) { assert.equal(map.size, expectedKeys.length, `${caption}: incorrect size of map`); for (const name of expectedKeys) { assert.isTrue(map.has(name), `${caption} is expected to contain ${name}, actual keys: ${arrayFrom(map.keys())}`); } } - export function checkFileNames(caption: string, actualFileNames: string[], expectedFileNames: string[]) { + function checkFileNames(caption: string, actualFileNames: string[], expectedFileNames: string[]) { assert.equal(actualFileNames.length, expectedFileNames.length, `${caption}: incorrect actual number of files, expected ${JSON.stringify(expectedFileNames)}, got ${actualFileNames}`); for (const f of expectedFileNames) { assert.isTrue(contains(actualFileNames, f), `${caption}: expected to find ${f} in ${JSON.stringify(actualFileNames)}`); } } - export function checkNumberOfConfiguredProjects(projectService: server.ProjectService, expected: number) { + function checkNumberOfConfiguredProjects(projectService: server.ProjectService, expected: number) { assert.equal(projectService.configuredProjects.length, expected, `expected ${expected} configured project(s)`); } - export function checkNumberOfExternalProjects(projectService: server.ProjectService, expected: number) { + function checkNumberOfExternalProjects(projectService: server.ProjectService, expected: number) { assert.equal(projectService.externalProjects.length, expected, `expected ${expected} external project(s)`); } - export function checkNumberOfInferredProjects(projectService: server.ProjectService, expected: number) { + function checkNumberOfInferredProjects(projectService: server.ProjectService, expected: number) { assert.equal(projectService.inferredProjects.length, expected, `expected ${expected} inferred project(s)`); } @@ -315,7 +314,7 @@ namespace ts.projectSystem { checkMapKeys("watchedFiles", host.watchedFiles, expectedFiles); } - export function checkWatchedDirectories(host: TestServerHost, expectedDirectories: string[]) { + function checkWatchedDirectories(host: TestServerHost, expectedDirectories: string[]) { checkMapKeys("watchedDirectories", host.watchedDirectories, expectedDirectories); } @@ -323,11 +322,11 @@ namespace ts.projectSystem { checkFileNames(`${server.ProjectKind[project.projectKind]} project, actual files`, project.getFileNames(), expectedFiles); } - export function checkProjectRootFiles(project: server.Project, expectedFiles: string[]) { + function checkProjectRootFiles(project: server.Project, expectedFiles: string[]) { checkFileNames(`${server.ProjectKind[project.projectKind]} project, rootFileNames`, project.getRootFiles(), expectedFiles); } - export class Callbacks { + class Callbacks { private map: TimeOutCallback[] = []; private nextId = 1; @@ -363,7 +362,7 @@ namespace ts.projectSystem { } } - export type TimeOutCallback = () => any; + type TimeOutCallback = () => any; export class TestServerHost implements server.ServerHost { args: string[] = []; @@ -2332,6 +2331,50 @@ namespace ts.projectSystem { const navbar = projectService.externalProjects[0].getLanguageService(/*ensureSynchronized*/ false).getNavigationBarItems(f1.path); assert.equal(navbar[0].spans[0].length, f1.content.length); }); + + it("deleting config file opened from the external project works", () => { + const site = { + path: "/user/someuser/project/js/site.js", + content: "" + }; + const configFile = { + path: "/user/someuser/project/tsconfig.json", + content: "{}" + }; + const projectFileName = "/user/someuser/project/WebApplication6.csproj"; + const host = createServerHost([libFile, site, configFile]); + const projectService = createProjectService(host); + + const externalProject: protocol.ExternalProject = { + projectFileName, + rootFiles: [toExternalFile(site.path), toExternalFile(configFile.path)], + options: { allowJs: false }, + typeAcquisition: { "include": [] } + }; + + projectService.openExternalProjects([externalProject]); + + let knownProjects = projectService.synchronizeProjectList([]); + checkNumberOfProjects(projectService, { configuredProjects: 1, externalProjects: 0, inferredProjects: 0 }); + + const configProject = projectService.configuredProjects[0]; + checkProjectActualFiles(configProject, [libFile.path]); + + const diagnostics = configProject.getAllProjectErrors(); + assert.equal(diagnostics[0].code, Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code); + + host.reloadFS([libFile, site]); + host.triggerFileWatcherCallback(configFile.path, FileWatcherEventKind.Deleted); + + knownProjects = projectService.synchronizeProjectList(map(knownProjects, proj => proj.info)); + checkNumberOfProjects(projectService, { configuredProjects: 0, externalProjects: 0, inferredProjects: 0 }); + + externalProject.rootFiles.length = 1; + projectService.openExternalProjects([externalProject]); + + checkNumberOfProjects(projectService, { configuredProjects: 0, externalProjects: 1, inferredProjects: 0 }); + checkProjectActualFiles(projectService.externalProjects[0], [site.path, libFile.path]); + }); }); describe("Proper errors", () => { diff --git a/src/lib/es2015.symbol.wellknown.d.ts b/src/lib/es2015.symbol.wellknown.d.ts index 578cf0acbc2..b7c2610e652 100644 --- a/src/lib/es2015.symbol.wellknown.d.ts +++ b/src/lib/es2015.symbol.wellknown.d.ts @@ -110,7 +110,7 @@ interface Map { readonly [Symbol.toStringTag]: "Map"; } -interface WeakMap{ +interface WeakMap { readonly [Symbol.toStringTag]: "WeakMap"; } diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 4ffdf5d6c5d..554ac337920 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -326,8 +326,8 @@ namespace ts.server { typingsInstaller: ITypingsInstaller; eventHandler?: ProjectServiceEventHandler; throttleWaitMilliseconds?: number; - globalPlugins?: string[]; - pluginProbeLocations?: string[]; + globalPlugins?: ReadonlyArray; + pluginProbeLocations?: ReadonlyArray; allowLocalPluginLoads?: boolean; } @@ -928,26 +928,24 @@ namespace ts.server { return; } - this.logger.startGroup(); + this.logger.group(info => { + let counter = 0; + counter = printProjects(this.externalProjects, info, counter); + counter = printProjects(this.configuredProjects, info, counter); + printProjects(this.inferredProjects, info, counter); - let counter = 0; - counter = printProjects(this.logger, this.externalProjects, counter); - counter = printProjects(this.logger, this.configuredProjects, counter); - counter = printProjects(this.logger, this.inferredProjects, counter); + info("Open files: "); + for (const rootFile of this.openFiles) { + info(`\t${rootFile.fileName}`); + } + }); - this.logger.info("Open files: "); - for (const rootFile of this.openFiles) { - this.logger.info(`\t${rootFile.fileName}`); - } - - this.logger.endGroup(); - - function printProjects(logger: Logger, projects: Project[], counter: number) { + function printProjects(projects: Project[], info: (msg: string) => void, counter: number): number { for (const project of projects) { project.updateGraph(); - logger.info(`Project '${project.getProjectName()}' (${ProjectKind[project.projectKind]}) ${counter}`); - logger.info(project.filesToString()); - logger.info("-----------------------------------------------"); + info(`Project '${project.getProjectName()}' (${ProjectKind[project.projectKind]}) ${counter}`); + info(project.filesToString()); + info("-----------------------------------------------"); counter++; } return counter; diff --git a/src/server/project.ts b/src/server/project.ts index 106089fad97..97c8eb706f3 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -170,7 +170,8 @@ namespace ts.server { log(`Loading ${moduleName} from ${initialDir} (resolved to ${resolvedPath})`); const result = host.require(resolvedPath, moduleName); if (result.error) { - log(`Failed to load module: ${JSON.stringify(result.error)}`); + const err = result.error.stack || result.error.message || JSON.stringify(result.error); + log(`Failed to load module '${moduleName}': ${err}`); return undefined; } return result.module; @@ -362,7 +363,7 @@ namespace ts.server { return map(this.program.getSourceFiles(), sourceFile => { const scriptInfo = this.projectService.getScriptInfoForPath(sourceFile.path); if (!scriptInfo) { - Debug.assert(false, `scriptInfo for a file '${sourceFile.fileName}' is missing.`); + Debug.fail(`scriptInfo for a file '${sourceFile.fileName}' is missing.`); } return scriptInfo; }); @@ -494,7 +495,7 @@ namespace ts.server { this.projectStateVersion++; } - private extractUnresolvedImportsFromSourceFile(file: SourceFile, result: string[]) { + private extractUnresolvedImportsFromSourceFile(file: SourceFile, result: Push) { const cached = this.cachedUnresolvedImportsPerFile.get(file.path); if (cached) { // found cached result - use it and return @@ -554,7 +555,7 @@ namespace ts.server { for (const sourceFile of this.program.getSourceFiles()) { this.extractUnresolvedImportsFromSourceFile(sourceFile, result); } - this.lastCachedUnresolvedImportsList = toSortedArray(result); + this.lastCachedUnresolvedImportsList = toDeduplicatedSortedArray(result); } unresolvedImports = this.lastCachedUnresolvedImportsList; @@ -936,9 +937,9 @@ namespace ts.server { export class ConfiguredProject extends Project { private typeAcquisition: TypeAcquisition; private projectFileWatcher: FileWatcher; - private directoryWatcher: FileWatcher; - private directoriesWatchedForWildcards: Map; - private typeRootsWatchers: FileWatcher[]; + private directoryWatcher: FileWatcher | undefined; + private directoriesWatchedForWildcards: Map | undefined; + private typeRootsWatchers: FileWatcher[] | undefined; readonly canonicalConfigFilePath: NormalizedPath; private plugins: PluginModule[] = []; @@ -1134,10 +1135,12 @@ namespace ts.server { this.typeRootsWatchers = undefined; } - this.directoriesWatchedForWildcards.forEach(watcher => { - watcher.close(); - }); - this.directoriesWatchedForWildcards = undefined; + if (this.directoriesWatchedForWildcards) { + this.directoriesWatchedForWildcards.forEach(watcher => { + watcher.close(); + }); + this.directoriesWatchedForWildcards = undefined; + } this.stopWatchingDirectory(); } diff --git a/src/server/server.ts b/src/server/server.ts index 6600b63dcb1..47a858c0efd 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -15,8 +15,8 @@ namespace ts.server { typingSafeListLocation: string; npmLocation: string | undefined; telemetryEnabled: boolean; - globalPlugins: string[]; - pluginProbeLocations: string[]; + globalPlugins: ReadonlyArray; + pluginProbeLocations: ReadonlyArray; allowLocalPluginLoads: boolean; } @@ -116,8 +116,6 @@ namespace ts.server { birthtime: Date; } - type RequireResult = { module: {}, error: undefined } | { module: undefined, error: {} }; - const readline: { createInterface(options: ReadLineOptions): NodeJS.EventEmitter; } = require("readline"); @@ -141,8 +139,6 @@ namespace ts.server { class Logger implements server.Logger { private fd = -1; private seq = 0; - private inGroup = false; - private firstInGroup = true; constructor(private readonly logFilename: string, private readonly traceToConsole: boolean, @@ -172,22 +168,24 @@ namespace ts.server { } perftrc(s: string) { - this.msg(s, Msg.Perf); + this.msg(s, "Perf"); } info(s: string) { - this.msg(s, Msg.Info); + this.msg(s, "Info"); } - startGroup() { - this.inGroup = true; - this.firstInGroup = true; + err(s: string) { + this.msg(s, "Err"); } - endGroup() { - this.inGroup = false; + group(logGroupEntries: (log: (msg: string) => void) => void) { + let firstInGroup = false; + logGroupEntries(s => { + this.msg(s, "Info", /*inGroup*/ true, firstInGroup); + firstInGroup = false; + }); this.seq++; - this.firstInGroup = true; } loggingEnabled() { @@ -198,26 +196,32 @@ namespace ts.server { return this.loggingEnabled() && this.level >= level; } - msg(s: string, type: Msg.Types = Msg.Err) { - if (this.fd >= 0 || this.traceToConsole) { - s = `[${nowString()}] ${s}\n`; + private msg(s: string, type: string, inGroup = false, firstInGroup = false) { + if (!this.canWrite) return; + + s = `[${nowString()}] ${s}\n`; + if (!inGroup || firstInGroup) { const prefix = Logger.padStringRight(type + " " + this.seq.toString(), " "); - if (this.firstInGroup) { - s = prefix + s; - this.firstInGroup = false; - } - if (!this.inGroup) { - this.seq++; - this.firstInGroup = true; - } - if (this.fd >= 0) { - const buf = new Buffer(s); - // tslint:disable-next-line no-null-keyword - fs.writeSync(this.fd, buf, 0, buf.length, /*position*/ null); - } - if (this.traceToConsole) { - console.warn(s); - } + s = prefix + s; + } + this.write(s); + if (!inGroup) { + this.seq++; + } + } + + private get canWrite() { + return this.fd >= 0 || this.traceToConsole; + } + + private write(s: string) { + if (this.fd >= 0) { + const buf = new Buffer(s); + // tslint:disable-next-line no-null-keyword + fs.writeSync(this.fd, buf, 0, buf.length, /*position*/ null); + } + if (this.traceToConsole) { + console.warn(s); } } } @@ -757,11 +761,21 @@ namespace ts.server { validateLocaleAndSetLanguage(localeStr, sys); } + setStackTraceLimit(); + const typingSafeListLocation = findArgument(Arguments.TypingSafeListLocation); const npmLocation = findArgument(Arguments.NpmLocation); - const globalPlugins = (findArgument("--globalPlugins") || "").split(","); - const pluginProbeLocations = (findArgument("--pluginProbeLocations") || "").split(","); + function parseStringArray(argName: string): ReadonlyArray { + const arg = findArgument(argName); + if (arg === undefined) { + return emptyArray; + } + return arg.split(",").filter(name => name !== ""); + } + + const globalPlugins = parseStringArray("--globalPlugins"); + const pluginProbeLocations = parseStringArray("--pluginProbeLocations"); const allowLocalPluginLoads = hasArgument("--allowLocalPluginLoads"); const useSingleInferredProject = hasArgument("--useSingleInferredProject"); diff --git a/src/server/session.ts b/src/server/session.ts index 074ba4d6ca1..3c9c1b714de 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -163,26 +163,22 @@ namespace ts.server { * Scheduling is done via instance of NextStep. If on current step subsequent step was not scheduled - operation is assumed to be completed. */ class MultistepOperation implements NextStep { - private requestId: number; + private requestId: number | undefined; private timerHandle: any; - private immediateId: any; - private completed = true; + private immediateId: number | undefined; constructor(private readonly operationHost: MultistepOperationHost) {} public startNew(action: (next: NextStep) => void) { this.complete(); this.requestId = this.operationHost.getCurrentRequestId(); - this.completed = false; this.executeAction(action); } private complete() { - if (!this.completed) { - if (this.requestId) { - this.operationHost.sendRequestCompletedEvent(this.requestId); - } - this.completed = true; + if (this.requestId !== undefined) { + this.operationHost.sendRequestCompletedEvent(this.requestId); + this.requestId = undefined; } this.setTimerHandle(undefined); this.setImmediateId(undefined); @@ -259,8 +255,8 @@ namespace ts.server { eventHandler?: ProjectServiceEventHandler; throttleWaitMilliseconds?: number; - globalPlugins?: string[]; - pluginProbeLocations?: string[]; + globalPlugins?: ReadonlyArray; + pluginProbeLocations?: ReadonlyArray; allowLocalPluginLoads?: boolean; } @@ -337,7 +333,7 @@ namespace ts.server { case ContextEvent: const { project, fileName } = event.data; this.projectService.logger.info(`got context event, updating diagnostics for ${fileName}`); - this.errorCheck.startNew(next => this.updateErrorCheck(next, [{ fileName, project }], this.changeSeq, (n) => n === this.changeSeq, 100)); + this.errorCheck.startNew(next => this.updateErrorCheck(next, [{ fileName, project }], 100)); break; case ConfigFileDiagEvent: const { triggerFile, configFileName, diagnostics } = event.data; @@ -370,7 +366,7 @@ namespace ts.server { msg += "\n" + (err).stack; } } - this.logger.msg(msg, Msg.Err); + this.logger.err(msg); } public send(msg: protocol.Message) { @@ -453,22 +449,23 @@ namespace ts.server { } } - private updateProjectStructure(seq: number, matchSeq: (seq: number) => boolean, ms = 1500) { + private updateProjectStructure() { + const ms = 1500; + const seq = this.changeSeq; this.host.setTimeout(() => { - if (matchSeq(seq)) { + if (this.changeSeq === seq) { this.projectService.refreshInferredProjects(); } }, ms); } - private updateErrorCheck(next: NextStep, checkList: PendingErrorCheck[], seq: number, matchSeq: (seq: number) => boolean, ms = 1500, followMs = 200, requireOpen = true) { - if (followMs > ms) { - followMs = ms; - } + private updateErrorCheck(next: NextStep, checkList: PendingErrorCheck[], ms: number, requireOpen = true) { + const seq = this.changeSeq; + const followMs = Math.min(ms, 200); let index = 0; const checkOne = () => { - if (matchSeq(seq)) { + if (this.changeSeq === seq) { const checkSpec = checkList[index]; index++; if (checkSpec.project.containsFile(checkSpec.fileName, requireOpen)) { @@ -483,7 +480,7 @@ namespace ts.server { } }; - if ((checkList.length > index) && (matchSeq(seq))) { + if (checkList.length > index && this.changeSeq === seq) { next.delay(ms, checkOne); } } @@ -1262,14 +1259,14 @@ namespace ts.server { } private getDiagnostics(next: NextStep, delay: number, fileNames: string[]): void { - const checkList = mapDefined(fileNames, uncheckedFileName => { + const checkList = mapDefined(fileNames, uncheckedFileName => { const fileName = toNormalizedPath(uncheckedFileName); const project = this.projectService.getDefaultProjectForFile(fileName, /*refreshInferredProjects*/ true); return project && { fileName, project }; }); if (checkList.length > 0) { - this.updateErrorCheck(next, checkList, this.changeSeq, (n) => n === this.changeSeq, delay); + this.updateErrorCheck(next, checkList, delay); } } @@ -1283,7 +1280,7 @@ namespace ts.server { scriptInfo.editContent(start, end, args.insertString); this.changeSeq++; } - this.updateProjectStructure(this.changeSeq, n => n === this.changeSeq); + this.updateProjectStructure(); } } @@ -1638,7 +1635,7 @@ namespace ts.server { const checkList = fileNamesInProject.map(fileName => ({ fileName, project })); // Project level error analysis runs on background files too, therefore // doesn't require the file to be opened - this.updateErrorCheck(next, checkList, this.changeSeq, (n) => n === this.changeSeq, delay, 200, /*requireOpen*/ false); + this.updateErrorCheck(next, checkList, delay, /*requireOpen*/ false); } } @@ -1949,7 +1946,7 @@ namespace ts.server { return this.executeWithRequestId(request.seq, () => handler(request)); } else { - this.logger.msg(`Unrecognized JSON command: ${JSON.stringify(request)}`, Msg.Err); + this.logger.err(`Unrecognized JSON command: ${JSON.stringify(request)}`); this.output(undefined, CommandNames.Unknown, request.seq, `Unrecognized JSON command: ${request.command}`); return { responseRequired: false }; } diff --git a/src/server/types.ts b/src/server/types.ts index 07b94fe827e..4fc4356a4a9 100644 --- a/src/server/types.ts +++ b/src/server/types.ts @@ -9,7 +9,7 @@ declare namespace ts.server { data: any; } - type RequireResult = { module: {}, error: undefined } | { module: undefined, error: {} }; + type RequireResult = { module: {}, error: undefined } | { module: undefined, error: { stack?: string, message?: string } }; export interface ServerHost extends System { setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): any; clearTimeout(timeoutId: any): void; diff --git a/src/server/utilities.ts b/src/server/utilities.ts index fc88f11408a..0d4bc101ff6 100644 --- a/src/server/utilities.ts +++ b/src/server/utilities.ts @@ -17,22 +17,11 @@ namespace ts.server { loggingEnabled(): boolean; perftrc(s: string): void; info(s: string): void; - startGroup(): void; - endGroup(): void; - msg(s: string, type?: Msg.Types): void; + err(s: string): void; + group(logGroupEntries: (log: (msg: string) => void) => void): void; getLogFileName(): string; } - export namespace Msg { - export type Err = "Err"; - export const Err: Err = "Err"; - export type Info = "Info"; - export const Info: Info = "Info"; - export type Perf = "Perf"; - export const Perf: Perf = "Perf"; - export type Types = Err | Info | Perf; - } - function getProjectRootPath(project: Project): Path { switch (project.projectKind) { case ProjectKind.Configured: @@ -262,6 +251,15 @@ namespace ts.server { return arr as SortedArray; } + export function toDeduplicatedSortedArray(arr: string[]): SortedArray { + arr.sort(); + filterMutate(arr, isNonDuplicateInSortedArray); + return arr as SortedArray; + } + function isNonDuplicateInSortedArray(value: T, index: number, array: T[]) { + return index === 0 || value !== array[index - 1]; + } + export function enumerateInsertsAndDeletes(newItems: SortedReadonlyArray, oldItems: SortedReadonlyArray, inserted: (newItem: T) => void, deleted: (oldItem: T) => void, compare?: Comparer) { compare = compare || compareValues; let newIndex = 0; diff --git a/src/services/classifier.ts b/src/services/classifier.ts index dc5d99bc490..4552d8bf985 100644 --- a/src/services/classifier.ts +++ b/src/services/classifier.ts @@ -260,11 +260,11 @@ namespace ts { templateStack.pop(); } else { - Debug.assert(token === SyntaxKind.TemplateMiddle, "Should have been a template middle. Was " + token); + Debug.assertEqual(token, SyntaxKind.TemplateMiddle, "Should have been a template middle."); } } else { - Debug.assert(lastTemplateStackToken === SyntaxKind.OpenBraceToken, "Should have been an open brace. Was: " + token); + Debug.assertEqual(lastTemplateStackToken, SyntaxKind.OpenBraceToken, "Should have been an open brace"); templateStack.pop(); } } diff --git a/src/services/completions.ts b/src/services/completions.ts index b49567ef217..fde07aa78f6 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -1000,7 +1000,7 @@ namespace ts.Completions { const typeForObject = typeChecker.getTypeAtLocation(objectLikeContainer); if (!typeForObject) return false; // In a binding pattern, get only known properties. Everywhere else we will get all possible properties. - typeMembers = typeChecker.getPropertiesOfType(typeForObject); + typeMembers = typeChecker.getPropertiesOfType(typeForObject).filter((symbol) => !(getDeclarationModifierFlagsFromSymbol(symbol) & ModifierFlags.NonPublicAccessibilityModifier)); existingMembers = (objectLikeContainer).elements; } } diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts index 15bbf5041d3..2daf8d9d284 100644 --- a/src/services/formatting/rules.ts +++ b/src/services/formatting/rules.ts @@ -195,6 +195,7 @@ namespace ts.formatting { // Insert space after opening and before closing nonempty parenthesis public SpaceAfterOpenParen: Rule; public SpaceBeforeCloseParen: Rule; + public SpaceBetweenOpenParens: Rule; public NoSpaceBetweenParens: Rule; public NoSpaceAfterOpenParen: Rule; public NoSpaceBeforeCloseParen: Rule; @@ -457,6 +458,7 @@ namespace ts.formatting { // Insert space after opening and before closing nonempty parenthesis this.SpaceAfterOpenParen = new Rule(RuleDescriptor.create3(SyntaxKind.OpenParenToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Space)); this.SpaceBeforeCloseParen = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Space)); + this.SpaceBetweenOpenParens = new Rule(RuleDescriptor.create1(SyntaxKind.OpenParenToken, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Space)); this.NoSpaceBetweenParens = new Rule(RuleDescriptor.create1(SyntaxKind.OpenParenToken, SyntaxKind.CloseParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete)); this.NoSpaceAfterOpenParen = new Rule(RuleDescriptor.create3(SyntaxKind.OpenParenToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete)); this.NoSpaceBeforeCloseParen = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete)); @@ -544,7 +546,7 @@ namespace ts.formatting { this.SpaceAfterComma, this.NoSpaceAfterComma, this.SpaceAfterAnonymousFunctionKeyword, this.NoSpaceAfterAnonymousFunctionKeyword, this.SpaceAfterKeywordInControl, this.NoSpaceAfterKeywordInControl, - this.SpaceAfterOpenParen, this.SpaceBeforeCloseParen, this.NoSpaceBetweenParens, this.NoSpaceAfterOpenParen, this.NoSpaceBeforeCloseParen, + this.SpaceAfterOpenParen, this.SpaceBeforeCloseParen, this.SpaceBetweenOpenParens, this.NoSpaceBetweenParens, this.NoSpaceAfterOpenParen, this.NoSpaceBeforeCloseParen, this.SpaceAfterOpenBracket, this.SpaceBeforeCloseBracket, this.NoSpaceBetweenBrackets, this.NoSpaceAfterOpenBracket, this.NoSpaceBeforeCloseBracket, this.SpaceAfterOpenBrace, this.SpaceBeforeCloseBrace, this.NoSpaceBetweenEmptyBraceBrackets, this.NoSpaceAfterOpenBrace, this.NoSpaceBeforeCloseBrace, this.SpaceAfterTemplateHeadAndMiddle, this.SpaceBeforeTemplateMiddleAndTail, this.NoSpaceAfterTemplateHeadAndMiddle, this.NoSpaceBeforeTemplateMiddleAndTail, diff --git a/src/services/jsTyping.ts b/src/services/jsTyping.ts index 4de7bb3191d..5e7b7d424f8 100644 --- a/src/services/jsTyping.ts +++ b/src/services/jsTyping.ts @@ -35,7 +35,7 @@ namespace ts.JsTyping { "crypto", "stream", "util", "assert", "tty", "domain", "constants", "process", "v8", "timers", "console"]; - const nodeCoreModules = arrayToMap(nodeCoreModuleList, x => x); + const nodeCoreModules = arrayToSet(nodeCoreModuleList); /** * A map of loose file names to library names that we are confident require typings diff --git a/src/services/services.ts b/src/services/services.ts index 6a171ad9166..874c2feb107 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1258,7 +1258,7 @@ namespace ts { // We do not support the scenario where a host can modify a registered // file's script kind, i.e. in one project some file is treated as ".ts" // and in another as ".js" - Debug.assert(hostFileInformation.scriptKind === oldSourceFile.scriptKind, "Registered script kind (" + oldSourceFile.scriptKind + ") should match new script kind (" + hostFileInformation.scriptKind + ") for file: " + path); + Debug.assertEqual(hostFileInformation.scriptKind, oldSourceFile.scriptKind, "Registered script kind should match new script kind.", path); return documentRegistry.updateDocumentWithKey(fileName, path, newSettings, documentRegistryBucketKey, hostFileInformation.scriptSnapshot, hostFileInformation.version, hostFileInformation.scriptKind); } diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts index f008c829116..2976b0d28ee 100644 --- a/src/services/signatureHelp.ts +++ b/src/services/signatureHelp.ts @@ -136,7 +136,9 @@ namespace ts.SignatureHelp { const kind = invocation.typeArguments && invocation.typeArguments.pos === list.pos ? ArgumentListKind.TypeArguments : ArgumentListKind.CallArguments; const argumentCount = getArgumentCount(list); - Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount, `argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`); + if (argumentIndex !== 0) { + Debug.assertLessThan(argumentIndex, argumentCount); + } const argumentsSpan = getApplicableSpanForArguments(list, sourceFile); return { kind, invocation, argumentsSpan, argumentIndex, argumentCount }; } @@ -270,7 +272,9 @@ namespace ts.SignatureHelp { ? 1 : (tagExpression.template).templateSpans.length + 1; - Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount, `argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`); + if (argumentIndex !== 0) { + Debug.assertLessThan(argumentIndex, argumentCount); + } return { kind: ArgumentListKind.TaggedTemplateArguments, invocation: tagExpression, @@ -402,7 +406,9 @@ namespace ts.SignatureHelp { }; }); - Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount, `argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`); + if (argumentIndex !== 0) { + Debug.assertLessThan(argumentIndex, argumentCount); + } const selectedItemIndex = candidates.indexOf(resolvedSignature); Debug.assert(selectedItemIndex !== -1); // If candidates is non-empty it should always include bestSignature. We check for an empty candidates before calling this function. diff --git a/src/services/transpile.ts b/src/services/transpile.ts index 561c188c6cd..79a69b886d9 100644 --- a/src/services/transpile.ts +++ b/src/services/transpile.ts @@ -78,11 +78,11 @@ namespace ts { getSourceFile: (fileName) => fileName === normalizePath(inputFileName) ? sourceFile : undefined, writeFile: (name, text) => { if (fileExtensionIs(name, ".map")) { - Debug.assert(sourceMapText === undefined, `Unexpected multiple source map outputs for the file '${name}'`); + Debug.assertEqual(sourceMapText, undefined, "Unexpected multiple source map outputs, file:", name); sourceMapText = text; } else { - Debug.assert(outputText === undefined, `Unexpected multiple outputs for the file: '${name}'`); + Debug.assertEqual(outputText, undefined, "Unexpected multiple outputs, file:", name); outputText = text; } }, diff --git a/tests/baselines/reference/anyIndexedAccessArrayNoException.errors.txt b/tests/baselines/reference/anyIndexedAccessArrayNoException.errors.txt index e207468122b..01c5bd6b2e2 100644 --- a/tests/baselines/reference/anyIndexedAccessArrayNoException.errors.txt +++ b/tests/baselines/reference/anyIndexedAccessArrayNoException.errors.txt @@ -1,8 +1,11 @@ +tests/cases/compiler/anyIndexedAccessArrayNoException.ts(1,12): error TS1122: A tuple type element list cannot be empty. tests/cases/compiler/anyIndexedAccessArrayNoException.ts(1,12): error TS2538: Type '[]' cannot be used as an index type. -==== tests/cases/compiler/anyIndexedAccessArrayNoException.ts (1 errors) ==== +==== tests/cases/compiler/anyIndexedAccessArrayNoException.ts (2 errors) ==== var x: any[[]]; ~~ +!!! error TS1122: A tuple type element list cannot be empty. + ~~ !!! error TS2538: Type '[]' cannot be used as an index type. \ No newline at end of file diff --git a/tests/baselines/reference/checkTypePredicateForRedundantProperties.errors.txt b/tests/baselines/reference/checkTypePredicateForRedundantProperties.errors.txt new file mode 100644 index 00000000000..a5cb9b0a098 --- /dev/null +++ b/tests/baselines/reference/checkTypePredicateForRedundantProperties.errors.txt @@ -0,0 +1,13 @@ +tests/cases/compiler/checkTypePredicateForRedundantProperties.ts(1,35): error TS2300: Duplicate identifier 'a'. +tests/cases/compiler/checkTypePredicateForRedundantProperties.ts(1,46): error TS2300: Duplicate identifier 'a'. + + +==== tests/cases/compiler/checkTypePredicateForRedundantProperties.ts (2 errors) ==== + function addProp2(x: any): x is { a: string; a: string; } { + ~ +!!! error TS2300: Duplicate identifier 'a'. + ~ +!!! error TS2300: Duplicate identifier 'a'. + return true; + } + \ No newline at end of file diff --git a/tests/baselines/reference/checkTypePredicateForRedundantProperties.js b/tests/baselines/reference/checkTypePredicateForRedundantProperties.js new file mode 100644 index 00000000000..8f7be2bfbbc --- /dev/null +++ b/tests/baselines/reference/checkTypePredicateForRedundantProperties.js @@ -0,0 +1,10 @@ +//// [checkTypePredicateForRedundantProperties.ts] +function addProp2(x: any): x is { a: string; a: string; } { + return true; +} + + +//// [checkTypePredicateForRedundantProperties.js] +function addProp2(x) { + return true; +} diff --git a/tests/baselines/reference/commentOnBinaryOperator1.js b/tests/baselines/reference/commentOnBinaryOperator1.js new file mode 100644 index 00000000000..daad6f5bdde --- /dev/null +++ b/tests/baselines/reference/commentOnBinaryOperator1.js @@ -0,0 +1,25 @@ +//// [commentOnBinaryOperator1.ts] +var a = 'some' + // comment + + 'text'; + +var b = 'some' + /* comment */ + + 'text'; + +var c = 'some' + /* comment */ + + /*comment1*/ + 'text'; + +//// [commentOnBinaryOperator1.js] +var a = 'some' + // comment + + 'text'; +var b = 'some' + /* comment */ + + 'text'; +var c = 'some' + /* comment */ + +/*comment1*/ + 'text'; diff --git a/tests/baselines/reference/commentOnBinaryOperator1.symbols b/tests/baselines/reference/commentOnBinaryOperator1.symbols new file mode 100644 index 00000000000..db92e4e4fc5 --- /dev/null +++ b/tests/baselines/reference/commentOnBinaryOperator1.symbols @@ -0,0 +1,19 @@ +=== tests/cases/compiler/commentOnBinaryOperator1.ts === +var a = 'some' +>a : Symbol(a, Decl(commentOnBinaryOperator1.ts, 0, 3)) + + // comment + + 'text'; + +var b = 'some' +>b : Symbol(b, Decl(commentOnBinaryOperator1.ts, 4, 3)) + + /* comment */ + + 'text'; + +var c = 'some' +>c : Symbol(c, Decl(commentOnBinaryOperator1.ts, 8, 3)) + + /* comment */ + + /*comment1*/ + 'text'; diff --git a/tests/baselines/reference/commentOnBinaryOperator1.types b/tests/baselines/reference/commentOnBinaryOperator1.types new file mode 100644 index 00000000000..59724d42f51 --- /dev/null +++ b/tests/baselines/reference/commentOnBinaryOperator1.types @@ -0,0 +1,29 @@ +=== tests/cases/compiler/commentOnBinaryOperator1.ts === +var a = 'some' +>a : string +>'some' // comment + 'text' : string +>'some' : "some" + + // comment + + 'text'; +>'text' : "text" + +var b = 'some' +>b : string +>'some' /* comment */ + 'text' : string +>'some' : "some" + + /* comment */ + + 'text'; +>'text' : "text" + +var c = 'some' +>c : string +>'some' /* comment */ + /*comment1*/ 'text' : string +>'some' : "some" + + /* comment */ + + /*comment1*/ + 'text'; +>'text' : "text" + diff --git a/tests/baselines/reference/commentOnBinaryOperator2.js b/tests/baselines/reference/commentOnBinaryOperator2.js new file mode 100644 index 00000000000..5d87ddccbef --- /dev/null +++ b/tests/baselines/reference/commentOnBinaryOperator2.js @@ -0,0 +1,22 @@ +//// [commentOnBinaryOperator2.ts] +var a = 'some' + // comment + + 'text'; + +var b = 'some' + /* comment */ + + 'text'; + +var c = 'some' + /* comment */ + + /*comment1*/ + 'text'; + +//// [commentOnBinaryOperator2.js] +var a = 'some' + + 'text'; +var b = 'some' + + 'text'; +var c = 'some' + + + 'text'; diff --git a/tests/baselines/reference/commentOnBinaryOperator2.symbols b/tests/baselines/reference/commentOnBinaryOperator2.symbols new file mode 100644 index 00000000000..10a0e94dd36 --- /dev/null +++ b/tests/baselines/reference/commentOnBinaryOperator2.symbols @@ -0,0 +1,19 @@ +=== tests/cases/compiler/commentOnBinaryOperator2.ts === +var a = 'some' +>a : Symbol(a, Decl(commentOnBinaryOperator2.ts, 0, 3)) + + // comment + + 'text'; + +var b = 'some' +>b : Symbol(b, Decl(commentOnBinaryOperator2.ts, 4, 3)) + + /* comment */ + + 'text'; + +var c = 'some' +>c : Symbol(c, Decl(commentOnBinaryOperator2.ts, 8, 3)) + + /* comment */ + + /*comment1*/ + 'text'; diff --git a/tests/baselines/reference/commentOnBinaryOperator2.types b/tests/baselines/reference/commentOnBinaryOperator2.types new file mode 100644 index 00000000000..411c8c69b8a --- /dev/null +++ b/tests/baselines/reference/commentOnBinaryOperator2.types @@ -0,0 +1,29 @@ +=== tests/cases/compiler/commentOnBinaryOperator2.ts === +var a = 'some' +>a : string +>'some' // comment + 'text' : string +>'some' : "some" + + // comment + + 'text'; +>'text' : "text" + +var b = 'some' +>b : string +>'some' /* comment */ + 'text' : string +>'some' : "some" + + /* comment */ + + 'text'; +>'text' : "text" + +var c = 'some' +>c : string +>'some' /* comment */ + /*comment1*/ 'text' : string +>'some' : "some" + + /* comment */ + + /*comment1*/ + 'text'; +>'text' : "text" + diff --git a/tests/baselines/reference/commentsArgumentsOfCallExpression2.js b/tests/baselines/reference/commentsArgumentsOfCallExpression2.js index a7065410ff4..e05256b86b6 100644 --- a/tests/baselines/reference/commentsArgumentsOfCallExpression2.js +++ b/tests/baselines/reference/commentsArgumentsOfCallExpression2.js @@ -14,7 +14,7 @@ foo( function foo(/*c1*/ x, /*d1*/ y, /*e1*/ w) { } var a, b; foo(/*c2*/ 1, /*d2*/ 1 + 2, /*e1*/ a + b); -foo(/*c3*/ function () { }, /*d2*/ function () { }, /*e2*/ a + b); +foo(/*c3*/ function () { }, /*d2*/ function () { }, /*e2*/ a +/*e3*/ b); foo(/*c3*/ function () { }, /*d3*/ function () { }, /*e3*/ (a + b)); foo( /*c4*/ function () { }, diff --git a/tests/baselines/reference/deferredLookupTypeResolution.js b/tests/baselines/reference/deferredLookupTypeResolution.js new file mode 100644 index 00000000000..5f8edd63e57 --- /dev/null +++ b/tests/baselines/reference/deferredLookupTypeResolution.js @@ -0,0 +1,64 @@ +//// [deferredLookupTypeResolution.ts] +// Repro from #17456 + +type StringContains = ( + { [K in S]: 'true' } & + { [key: string]: 'false' } + )[L] + +type ObjectHasKey = StringContains + +type First = ObjectHasKey; // Should be deferred + +type T1 = ObjectHasKey<{ a: string }, 'a'>; // 'true' +type T2 = ObjectHasKey<{ a: string }, 'b'>; // 'false' + +// Verify that mapped type isn't eagerly resolved in type-to-string operation + +declare function f1(a: A, b: B): { [P in A | B]: any }; + +function f2(a: A) { + return f1(a, 'x'); +} + +function f3(x: 'a' | 'b') { + return f2(x); +} + + +//// [deferredLookupTypeResolution.js] +"use strict"; +// Repro from #17456 +function f2(a) { + return f1(a, 'x'); +} +function f3(x) { + return f2(x); +} + + +//// [deferredLookupTypeResolution.d.ts] +declare type StringContains = ({ + [K in S]: 'true'; +} & { + [key: string]: 'false'; +})[L]; +declare type ObjectHasKey = StringContains; +declare type First = ObjectHasKey; +declare type T1 = ObjectHasKey<{ + a: string; +}, 'a'>; +declare type T2 = ObjectHasKey<{ + a: string; +}, 'b'>; +declare function f1(a: A, b: B): { + [P in A | B]: any; +}; +declare function f2(a: A): { + [P in A | "x"]: any; +}; +declare function f3(x: 'a' | 'b'): { + a: any; + b: any; + x: any; +}; diff --git a/tests/baselines/reference/deferredLookupTypeResolution.symbols b/tests/baselines/reference/deferredLookupTypeResolution.symbols new file mode 100644 index 00000000000..022dc3cc2f4 --- /dev/null +++ b/tests/baselines/reference/deferredLookupTypeResolution.symbols @@ -0,0 +1,76 @@ +=== tests/cases/compiler/deferredLookupTypeResolution.ts === +// Repro from #17456 + +type StringContains = ( +>StringContains : Symbol(StringContains, Decl(deferredLookupTypeResolution.ts, 0, 0)) +>S : Symbol(S, Decl(deferredLookupTypeResolution.ts, 2, 20)) +>L : Symbol(L, Decl(deferredLookupTypeResolution.ts, 2, 37)) + + { [K in S]: 'true' } & +>K : Symbol(K, Decl(deferredLookupTypeResolution.ts, 3, 7)) +>S : Symbol(S, Decl(deferredLookupTypeResolution.ts, 2, 20)) + + { [key: string]: 'false' } +>key : Symbol(key, Decl(deferredLookupTypeResolution.ts, 4, 7)) + + )[L] +>L : Symbol(L, Decl(deferredLookupTypeResolution.ts, 2, 37)) + +type ObjectHasKey = StringContains +>ObjectHasKey : Symbol(ObjectHasKey, Decl(deferredLookupTypeResolution.ts, 5, 6)) +>O : Symbol(O, Decl(deferredLookupTypeResolution.ts, 7, 18)) +>L : Symbol(L, Decl(deferredLookupTypeResolution.ts, 7, 20)) +>StringContains : Symbol(StringContains, Decl(deferredLookupTypeResolution.ts, 0, 0)) +>O : Symbol(O, Decl(deferredLookupTypeResolution.ts, 7, 18)) +>L : Symbol(L, Decl(deferredLookupTypeResolution.ts, 7, 20)) + +type First = ObjectHasKey; // Should be deferred +>First : Symbol(First, Decl(deferredLookupTypeResolution.ts, 7, 67)) +>T : Symbol(T, Decl(deferredLookupTypeResolution.ts, 9, 11)) +>ObjectHasKey : Symbol(ObjectHasKey, Decl(deferredLookupTypeResolution.ts, 5, 6)) +>T : Symbol(T, Decl(deferredLookupTypeResolution.ts, 9, 11)) + +type T1 = ObjectHasKey<{ a: string }, 'a'>; // 'true' +>T1 : Symbol(T1, Decl(deferredLookupTypeResolution.ts, 9, 37)) +>ObjectHasKey : Symbol(ObjectHasKey, Decl(deferredLookupTypeResolution.ts, 5, 6)) +>a : Symbol(a, Decl(deferredLookupTypeResolution.ts, 11, 24)) + +type T2 = ObjectHasKey<{ a: string }, 'b'>; // 'false' +>T2 : Symbol(T2, Decl(deferredLookupTypeResolution.ts, 11, 43)) +>ObjectHasKey : Symbol(ObjectHasKey, Decl(deferredLookupTypeResolution.ts, 5, 6)) +>a : Symbol(a, Decl(deferredLookupTypeResolution.ts, 12, 24)) + +// Verify that mapped type isn't eagerly resolved in type-to-string operation + +declare function f1(a: A, b: B): { [P in A | B]: any }; +>f1 : Symbol(f1, Decl(deferredLookupTypeResolution.ts, 12, 43)) +>A : Symbol(A, Decl(deferredLookupTypeResolution.ts, 16, 20)) +>B : Symbol(B, Decl(deferredLookupTypeResolution.ts, 16, 37)) +>a : Symbol(a, Decl(deferredLookupTypeResolution.ts, 16, 56)) +>A : Symbol(A, Decl(deferredLookupTypeResolution.ts, 16, 20)) +>b : Symbol(b, Decl(deferredLookupTypeResolution.ts, 16, 61)) +>B : Symbol(B, Decl(deferredLookupTypeResolution.ts, 16, 37)) +>P : Symbol(P, Decl(deferredLookupTypeResolution.ts, 16, 72)) +>A : Symbol(A, Decl(deferredLookupTypeResolution.ts, 16, 20)) +>B : Symbol(B, Decl(deferredLookupTypeResolution.ts, 16, 37)) + +function f2(a: A) { +>f2 : Symbol(f2, Decl(deferredLookupTypeResolution.ts, 16, 91)) +>A : Symbol(A, Decl(deferredLookupTypeResolution.ts, 18, 12)) +>a : Symbol(a, Decl(deferredLookupTypeResolution.ts, 18, 30)) +>A : Symbol(A, Decl(deferredLookupTypeResolution.ts, 18, 12)) + + return f1(a, 'x'); +>f1 : Symbol(f1, Decl(deferredLookupTypeResolution.ts, 12, 43)) +>a : Symbol(a, Decl(deferredLookupTypeResolution.ts, 18, 30)) +} + +function f3(x: 'a' | 'b') { +>f3 : Symbol(f3, Decl(deferredLookupTypeResolution.ts, 20, 1)) +>x : Symbol(x, Decl(deferredLookupTypeResolution.ts, 22, 12)) + + return f2(x); +>f2 : Symbol(f2, Decl(deferredLookupTypeResolution.ts, 16, 91)) +>x : Symbol(x, Decl(deferredLookupTypeResolution.ts, 22, 12)) +} + diff --git a/tests/baselines/reference/deferredLookupTypeResolution.types b/tests/baselines/reference/deferredLookupTypeResolution.types new file mode 100644 index 00000000000..d9486d30b07 --- /dev/null +++ b/tests/baselines/reference/deferredLookupTypeResolution.types @@ -0,0 +1,79 @@ +=== tests/cases/compiler/deferredLookupTypeResolution.ts === +// Repro from #17456 + +type StringContains = ( +>StringContains : ({ [K in S]: "true"; } & { [key: string]: "false"; })[L] +>S : S +>L : L + + { [K in S]: 'true' } & +>K : K +>S : S + + { [key: string]: 'false' } +>key : string + + )[L] +>L : L + +type ObjectHasKey = StringContains +>ObjectHasKey : ({ [K in S]: "true"; } & { [key: string]: "false"; })[L] +>O : O +>L : L +>StringContains : ({ [K in S]: "true"; } & { [key: string]: "false"; })[L] +>O : O +>L : L + +type First = ObjectHasKey; // Should be deferred +>First : ({ [K in S]: "true"; } & { [key: string]: "false"; })["0"] +>T : T +>ObjectHasKey : ({ [K in S]: "true"; } & { [key: string]: "false"; })[L] +>T : T + +type T1 = ObjectHasKey<{ a: string }, 'a'>; // 'true' +>T1 : "true" +>ObjectHasKey : ({ [K in S]: "true"; } & { [key: string]: "false"; })[L] +>a : string + +type T2 = ObjectHasKey<{ a: string }, 'b'>; // 'false' +>T2 : "false" +>ObjectHasKey : ({ [K in S]: "true"; } & { [key: string]: "false"; })[L] +>a : string + +// Verify that mapped type isn't eagerly resolved in type-to-string operation + +declare function f1(a: A, b: B): { [P in A | B]: any }; +>f1 : (a: A, b: B) => { [P in A | B]: any; } +>A : A +>B : B +>a : A +>A : A +>b : B +>B : B +>P : P +>A : A +>B : B + +function f2(a: A) { +>f2 : (a: A) => { [P in A | B]: any; } +>A : A +>a : A +>A : A + + return f1(a, 'x'); +>f1(a, 'x') : { [P in A | B]: any; } +>f1 : (a: A, b: B) => { [P in A | B]: any; } +>a : A +>'x' : "x" +} + +function f3(x: 'a' | 'b') { +>f3 : (x: "a" | "b") => { a: any; b: any; x: any; } +>x : "a" | "b" + + return f2(x); +>f2(x) : { a: any; b: any; x: any; } +>f2 : (a: A) => { [P in A | B]: any; } +>x : "a" | "b" +} + diff --git a/tests/baselines/reference/deferredLookupTypeResolution2.errors.txt b/tests/baselines/reference/deferredLookupTypeResolution2.errors.txt new file mode 100644 index 00000000000..f6bbe72f1a6 --- /dev/null +++ b/tests/baselines/reference/deferredLookupTypeResolution2.errors.txt @@ -0,0 +1,31 @@ +tests/cases/compiler/deferredLookupTypeResolution2.ts(14,13): error TS2536: Type '({ [K in S]: "true"; } & { [key: string]: "false"; })["1"]' cannot be used to index type '{ true: "true"; }'. +tests/cases/compiler/deferredLookupTypeResolution2.ts(19,21): error TS2536: Type '({ true: "otherwise"; } & { [k: string]: "true"; })[({ [K in S]: "true"; } & { [key: string]: "false"; })["1"]]' cannot be used to index type '{ true: "true"; }'. + + +==== tests/cases/compiler/deferredLookupTypeResolution2.ts (2 errors) ==== + // Repro from #17456 + + type StringContains = ({ [K in S]: 'true' } & { [key: string]: 'false'})[L]; + + type ObjectHasKey = StringContains; + + type A = ObjectHasKey; + + type B = ObjectHasKey<[string, number], '1'>; // "true" + type C = ObjectHasKey<[string, number], '2'>; // "false" + type D = A<[string]>; // "true" + + // Error, "false" not handled + type E = { true: 'true' }[ObjectHasKey]; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2536: Type '({ [K in S]: "true"; } & { [key: string]: "false"; })["1"]' cannot be used to index type '{ true: "true"; }'. + + type Juxtapose = ({ true: 'otherwise' } & { [k: string]: 'true' })[ObjectHasKey]; + + // Error, "otherwise" is missing + type DeepError = { true: 'true' }[Juxtapose]; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2536: Type '({ true: "otherwise"; } & { [k: string]: "true"; })[({ [K in S]: "true"; } & { [key: string]: "false"; })["1"]]' cannot be used to index type '{ true: "true"; }'. + + type DeepOK = { true: 'true', otherwise: 'false' }[Juxtapose]; + \ No newline at end of file diff --git a/tests/baselines/reference/deferredLookupTypeResolution2.js b/tests/baselines/reference/deferredLookupTypeResolution2.js new file mode 100644 index 00000000000..97289f47f9c --- /dev/null +++ b/tests/baselines/reference/deferredLookupTypeResolution2.js @@ -0,0 +1,55 @@ +//// [deferredLookupTypeResolution2.ts] +// Repro from #17456 + +type StringContains = ({ [K in S]: 'true' } & { [key: string]: 'false'})[L]; + +type ObjectHasKey = StringContains; + +type A = ObjectHasKey; + +type B = ObjectHasKey<[string, number], '1'>; // "true" +type C = ObjectHasKey<[string, number], '2'>; // "false" +type D = A<[string]>; // "true" + +// Error, "false" not handled +type E = { true: 'true' }[ObjectHasKey]; + +type Juxtapose = ({ true: 'otherwise' } & { [k: string]: 'true' })[ObjectHasKey]; + +// Error, "otherwise" is missing +type DeepError = { true: 'true' }[Juxtapose]; + +type DeepOK = { true: 'true', otherwise: 'false' }[Juxtapose]; + + +//// [deferredLookupTypeResolution2.js] +"use strict"; +// Repro from #17456 + + +//// [deferredLookupTypeResolution2.d.ts] +declare type StringContains = ({ + [K in S]: 'true'; +} & { + [key: string]: 'false'; +})[L]; +declare type ObjectHasKey = StringContains; +declare type A = ObjectHasKey; +declare type B = ObjectHasKey<[string, number], '1'>; +declare type C = ObjectHasKey<[string, number], '2'>; +declare type D = A<[string]>; +declare type E = { + true: 'true'; +}[ObjectHasKey]; +declare type Juxtapose = ({ + true: 'otherwise'; +} & { + [k: string]: 'true'; +})[ObjectHasKey]; +declare type DeepError = { + true: 'true'; +}[Juxtapose]; +declare type DeepOK = { + true: 'true'; + otherwise: 'false'; +}[Juxtapose]; diff --git a/tests/baselines/reference/doNotInferUnrelatedTypes.js b/tests/baselines/reference/doNotInferUnrelatedTypes.js new file mode 100644 index 00000000000..cff708cec89 --- /dev/null +++ b/tests/baselines/reference/doNotInferUnrelatedTypes.js @@ -0,0 +1,11 @@ +//// [doNotInferUnrelatedTypes.ts] +// #16709 +declare function dearray(ara: ReadonlyArray): T; +type LiteralType = "foo" | "bar"; +declare var alt: Array; + +let foo: LiteralType = dearray(alt); + + +//// [doNotInferUnrelatedTypes.js] +var foo = dearray(alt); diff --git a/tests/baselines/reference/doNotInferUnrelatedTypes.symbols b/tests/baselines/reference/doNotInferUnrelatedTypes.symbols new file mode 100644 index 00000000000..ce7351cf78f --- /dev/null +++ b/tests/baselines/reference/doNotInferUnrelatedTypes.symbols @@ -0,0 +1,24 @@ +=== tests/cases/compiler/doNotInferUnrelatedTypes.ts === +// #16709 +declare function dearray(ara: ReadonlyArray): T; +>dearray : Symbol(dearray, Decl(doNotInferUnrelatedTypes.ts, 0, 0)) +>T : Symbol(T, Decl(doNotInferUnrelatedTypes.ts, 1, 25)) +>ara : Symbol(ara, Decl(doNotInferUnrelatedTypes.ts, 1, 28)) +>ReadonlyArray : Symbol(ReadonlyArray, Decl(lib.d.ts, --, --)) +>T : Symbol(T, Decl(doNotInferUnrelatedTypes.ts, 1, 25)) +>T : Symbol(T, Decl(doNotInferUnrelatedTypes.ts, 1, 25)) + +type LiteralType = "foo" | "bar"; +>LiteralType : Symbol(LiteralType, Decl(doNotInferUnrelatedTypes.ts, 1, 54)) + +declare var alt: Array; +>alt : Symbol(alt, Decl(doNotInferUnrelatedTypes.ts, 3, 11)) +>Array : Symbol(Array, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>LiteralType : Symbol(LiteralType, Decl(doNotInferUnrelatedTypes.ts, 1, 54)) + +let foo: LiteralType = dearray(alt); +>foo : Symbol(foo, Decl(doNotInferUnrelatedTypes.ts, 5, 3)) +>LiteralType : Symbol(LiteralType, Decl(doNotInferUnrelatedTypes.ts, 1, 54)) +>dearray : Symbol(dearray, Decl(doNotInferUnrelatedTypes.ts, 0, 0)) +>alt : Symbol(alt, Decl(doNotInferUnrelatedTypes.ts, 3, 11)) + diff --git a/tests/baselines/reference/doNotInferUnrelatedTypes.types b/tests/baselines/reference/doNotInferUnrelatedTypes.types new file mode 100644 index 00000000000..5068c04ab1b --- /dev/null +++ b/tests/baselines/reference/doNotInferUnrelatedTypes.types @@ -0,0 +1,25 @@ +=== tests/cases/compiler/doNotInferUnrelatedTypes.ts === +// #16709 +declare function dearray(ara: ReadonlyArray): T; +>dearray : (ara: ReadonlyArray) => T +>T : T +>ara : ReadonlyArray +>ReadonlyArray : ReadonlyArray +>T : T +>T : T + +type LiteralType = "foo" | "bar"; +>LiteralType : LiteralType + +declare var alt: Array; +>alt : LiteralType[] +>Array : T[] +>LiteralType : LiteralType + +let foo: LiteralType = dearray(alt); +>foo : LiteralType +>LiteralType : LiteralType +>dearray(alt) : LiteralType +>dearray : (ara: ReadonlyArray) => T +>alt : LiteralType[] + diff --git a/tests/baselines/reference/emitExponentiationOperator3.types b/tests/baselines/reference/emitExponentiationOperator3.types index 2fe95d4db8f..666d46dca39 100644 --- a/tests/baselines/reference/emitExponentiationOperator3.types +++ b/tests/baselines/reference/emitExponentiationOperator3.types @@ -113,32 +113,32 @@ var temp = 10; (+3) ** temp++; >(+3) ** temp++ : number ->(+3) : number ->+3 : number +>(+3) : 3 +>+3 : 3 >3 : 3 >temp++ : number >temp : number (+3) ** temp--; >(+3) ** temp-- : number ->(+3) : number ->+3 : number +>(+3) : 3 +>+3 : 3 >3 : 3 >temp-- : number >temp : number (+3) ** ++temp; >(+3) ** ++temp : number ->(+3) : number ->+3 : number +>(+3) : 3 +>+3 : 3 >3 : 3 >++temp : number >temp : number (+3) ** --temp; >(+3) ** --temp : number ->(+3) : number ->+3 : number +>(+3) : 3 +>+3 : 3 >3 : 3 >--temp : number >temp : number @@ -185,8 +185,8 @@ var temp = 10; (+3) ** temp++ ** 2; >(+3) ** temp++ ** 2 : number ->(+3) : number ->+3 : number +>(+3) : 3 +>+3 : 3 >3 : 3 >temp++ ** 2 : number >temp++ : number @@ -195,8 +195,8 @@ var temp = 10; (+3) ** temp-- ** 2; >(+3) ** temp-- ** 2 : number ->(+3) : number ->+3 : number +>(+3) : 3 +>+3 : 3 >3 : 3 >temp-- ** 2 : number >temp-- : number @@ -205,8 +205,8 @@ var temp = 10; (+3) ** ++temp ** 2; >(+3) ** ++temp ** 2 : number ->(+3) : number ->+3 : number +>(+3) : 3 +>+3 : 3 >3 : 3 >++temp ** 2 : number >++temp : number @@ -215,8 +215,8 @@ var temp = 10; (+3) ** --temp ** 2; >(+3) ** --temp ** 2 : number ->(+3) : number ->+3 : number +>(+3) : 3 +>+3 : 3 >3 : 3 >--temp ** 2 : number >--temp : number diff --git a/tests/baselines/reference/enumClassification.types b/tests/baselines/reference/enumClassification.types index d0581c7fcae..73c8041a6b7 100644 --- a/tests/baselines/reference/enumClassification.types +++ b/tests/baselines/reference/enumClassification.types @@ -131,7 +131,7 @@ enum E11 { A = +0, >A : E11 ->+0 : number +>+0 : 0 >0 : 0 B, diff --git a/tests/baselines/reference/expressionTypeNodeShouldError.errors.txt b/tests/baselines/reference/expressionTypeNodeShouldError.errors.txt new file mode 100644 index 00000000000..86a592779ca --- /dev/null +++ b/tests/baselines/reference/expressionTypeNodeShouldError.errors.txt @@ -0,0 +1,90 @@ +tests/cases/compiler/base.d.ts(1,23): error TS1005: ',' expected. +tests/cases/compiler/base.d.ts(1,34): error TS1005: '=' expected. +tests/cases/compiler/boolean.ts(7,23): error TS1005: ',' expected. +tests/cases/compiler/boolean.ts(7,24): error TS1134: Variable declaration expected. +tests/cases/compiler/boolean.ts(11,16): error TS2304: Cannot find name 'document'. +tests/cases/compiler/boolean.ts(12,22): error TS1005: ';' expected. +tests/cases/compiler/number.ts(7,26): error TS1005: ',' expected. +tests/cases/compiler/number.ts(7,27): error TS1134: Variable declaration expected. +tests/cases/compiler/number.ts(11,16): error TS2304: Cannot find name 'document'. +tests/cases/compiler/number.ts(12,20): error TS1005: ';' expected. +tests/cases/compiler/string.ts(7,20): error TS1005: ',' expected. +tests/cases/compiler/string.ts(7,21): error TS1134: Variable declaration expected. +tests/cases/compiler/string.ts(11,15): error TS2304: Cannot find name 'document'. +tests/cases/compiler/string.ts(12,19): error TS1005: ';' expected. + + +==== tests/cases/compiler/base.d.ts (2 errors) ==== + declare const x: "foo".charCodeAt(0); + ~ +!!! error TS1005: ',' expected. + ~ +!!! error TS1005: '=' expected. + +==== tests/cases/compiler/string.ts (4 errors) ==== + interface String { + typeof(x: T): T; + } + + class C { + foo() { + const x: "".typeof(this.foo); + ~ +!!! error TS1005: ',' expected. + ~~~~~~ +!!! error TS1134: Variable declaration expected. + } + } + + const nodes = document.getElementsByTagName("li"); + ~~~~~~~~ +!!! error TS2304: Cannot find name 'document'. + type ItemType = "".typeof(nodes.item(0)); + ~ +!!! error TS1005: ';' expected. + +==== tests/cases/compiler/number.ts (4 errors) ==== + interface Number { + typeof(x: T): T; + } + + class C2 { + foo() { + const x: 3.141592.typeof(this.foo); + ~ +!!! error TS1005: ',' expected. + ~~~~~~ +!!! error TS1134: Variable declaration expected. + } + } + + const nodes2 = document.getElementsByTagName("li"); + ~~~~~~~~ +!!! error TS2304: Cannot find name 'document'. + type ItemType2 = 4..typeof(nodes.item(0)); + ~ +!!! error TS1005: ';' expected. + +==== tests/cases/compiler/boolean.ts (4 errors) ==== + interface Boolean { + typeof(x: T): T; + } + + class C3 { + foo() { + const x: false.typeof(this.foo); + ~ +!!! error TS1005: ',' expected. + ~~~~~~ +!!! error TS1134: Variable declaration expected. + } + } + + const nodes3 = document.getElementsByTagName("li"); + ~~~~~~~~ +!!! error TS2304: Cannot find name 'document'. + type ItemType3 = true.typeof(nodes.item(0)); + ~ +!!! error TS1005: ';' expected. + + \ No newline at end of file diff --git a/tests/baselines/reference/expressionTypeNodeShouldError.js b/tests/baselines/reference/expressionTypeNodeShouldError.js new file mode 100644 index 00000000000..80f9f82145a --- /dev/null +++ b/tests/baselines/reference/expressionTypeNodeShouldError.js @@ -0,0 +1,85 @@ +//// [tests/cases/compiler/expressionTypeNodeShouldError.ts] //// + +//// [base.d.ts] +declare const x: "foo".charCodeAt(0); + +//// [string.ts] +interface String { + typeof(x: T): T; +} + +class C { + foo() { + const x: "".typeof(this.foo); + } +} + +const nodes = document.getElementsByTagName("li"); +type ItemType = "".typeof(nodes.item(0)); + +//// [number.ts] +interface Number { + typeof(x: T): T; +} + +class C2 { + foo() { + const x: 3.141592.typeof(this.foo); + } +} + +const nodes2 = document.getElementsByTagName("li"); +type ItemType2 = 4..typeof(nodes.item(0)); + +//// [boolean.ts] +interface Boolean { + typeof(x: T): T; +} + +class C3 { + foo() { + const x: false.typeof(this.foo); + } +} + +const nodes3 = document.getElementsByTagName("li"); +type ItemType3 = true.typeof(nodes.item(0)); + + + +//// [string.js] +var C = (function () { + function C() { + } + C.prototype.foo = function () { + var x; + typeof (this.foo); + }; + return C; +}()); +var nodes = document.getElementsByTagName("li"); +typeof (nodes.item(0)); +//// [number.js] +var C2 = (function () { + function C2() { + } + C2.prototype.foo = function () { + var x; + typeof (this.foo); + }; + return C2; +}()); +var nodes2 = document.getElementsByTagName("li"); +typeof (nodes.item(0)); +//// [boolean.js] +var C3 = (function () { + function C3() { + } + C3.prototype.foo = function () { + var x; + typeof (this.foo); + }; + return C3; +}()); +var nodes3 = document.getElementsByTagName("li"); +typeof (nodes.item(0)); diff --git a/tests/baselines/reference/indexSignatureAndMappedType.errors.txt b/tests/baselines/reference/indexSignatureAndMappedType.errors.txt new file mode 100644 index 00000000000..623fcd11bd0 --- /dev/null +++ b/tests/baselines/reference/indexSignatureAndMappedType.errors.txt @@ -0,0 +1,47 @@ +tests/cases/compiler/indexSignatureAndMappedType.ts(6,5): error TS2322: Type '{ [key: string]: T; }' is not assignable to type 'Record'. +tests/cases/compiler/indexSignatureAndMappedType.ts(15,5): error TS2322: Type 'Record' is not assignable to type '{ [key: string]: T; }'. + Type 'U' is not assignable to type 'T'. +tests/cases/compiler/indexSignatureAndMappedType.ts(16,5): error TS2322: Type '{ [key: string]: T; }' is not assignable to type 'Record'. + + +==== tests/cases/compiler/indexSignatureAndMappedType.ts (3 errors) ==== + // A mapped type { [P in K]: X }, where K is a generic type, is related to + // { [key: string]: Y } if X is related to Y. + + function f1(x: { [key: string]: T }, y: Record) { + x = y; + y = x; // Error + ~ +!!! error TS2322: Type '{ [key: string]: T; }' is not assignable to type 'Record'. + } + + function f2(x: { [key: string]: T }, y: Record) { + x = y; + y = x; + } + + function f3(x: { [key: string]: T }, y: Record) { + x = y; // Error + ~ +!!! error TS2322: Type 'Record' is not assignable to type '{ [key: string]: T; }'. +!!! error TS2322: Type 'U' is not assignable to type 'T'. + y = x; // Error + ~ +!!! error TS2322: Type '{ [key: string]: T; }' is not assignable to type 'Record'. + } + + // Repro from #14548 + + type Dictionary = { + [key: string]: string; + }; + + interface IBaseEntity { + name: string; + properties: Dictionary; + } + + interface IEntity extends IBaseEntity { + properties: Record; + } + \ No newline at end of file diff --git a/tests/baselines/reference/indexSignatureAndMappedType.js b/tests/baselines/reference/indexSignatureAndMappedType.js new file mode 100644 index 00000000000..be58286fb46 --- /dev/null +++ b/tests/baselines/reference/indexSignatureAndMappedType.js @@ -0,0 +1,73 @@ +//// [indexSignatureAndMappedType.ts] +// A mapped type { [P in K]: X }, where K is a generic type, is related to +// { [key: string]: Y } if X is related to Y. + +function f1(x: { [key: string]: T }, y: Record) { + x = y; + y = x; // Error +} + +function f2(x: { [key: string]: T }, y: Record) { + x = y; + y = x; +} + +function f3(x: { [key: string]: T }, y: Record) { + x = y; // Error + y = x; // Error +} + +// Repro from #14548 + +type Dictionary = { + [key: string]: string; +}; + +interface IBaseEntity { + name: string; + properties: Dictionary; +} + +interface IEntity extends IBaseEntity { + properties: Record; +} + + +//// [indexSignatureAndMappedType.js] +"use strict"; +// A mapped type { [P in K]: X }, where K is a generic type, is related to +// { [key: string]: Y } if X is related to Y. +function f1(x, y) { + x = y; + y = x; // Error +} +function f2(x, y) { + x = y; + y = x; +} +function f3(x, y) { + x = y; // Error + y = x; // Error +} + + +//// [indexSignatureAndMappedType.d.ts] +declare function f1(x: { + [key: string]: T; +}, y: Record): void; +declare function f2(x: { + [key: string]: T; +}, y: Record): void; +declare function f3(x: { + [key: string]: T; +}, y: Record): void; +declare type Dictionary = { + [key: string]: string; +}; +interface IBaseEntity { + name: string; + properties: Dictionary; +} +interface IEntity extends IBaseEntity { + properties: Record; +} diff --git a/tests/baselines/reference/jsdocTypeGenericInstantiationAttempt.symbols b/tests/baselines/reference/jsdocTypeGenericInstantiationAttempt.symbols new file mode 100644 index 00000000000..1b17b09fce2 --- /dev/null +++ b/tests/baselines/reference/jsdocTypeGenericInstantiationAttempt.symbols @@ -0,0 +1,12 @@ +=== tests/cases/compiler/index.js === +/** + * @param {Array<*>} list + */ +function thing(list) { +>thing : Symbol(thing, Decl(index.js, 0, 0)) +>list : Symbol(list, Decl(index.js, 3, 15)) + + return list; +>list : Symbol(list, Decl(index.js, 3, 15)) +} + diff --git a/tests/baselines/reference/jsdocTypeGenericInstantiationAttempt.types b/tests/baselines/reference/jsdocTypeGenericInstantiationAttempt.types new file mode 100644 index 00000000000..50231c9d868 --- /dev/null +++ b/tests/baselines/reference/jsdocTypeGenericInstantiationAttempt.types @@ -0,0 +1,12 @@ +=== tests/cases/compiler/index.js === +/** + * @param {Array<*>} list + */ +function thing(list) { +>thing : (list: any[]) => any[] +>list : any[] + + return list; +>list : any[] +} + diff --git a/tests/baselines/reference/jsdocTypeNongenericInstantiationAttempt.errors.txt b/tests/baselines/reference/jsdocTypeNongenericInstantiationAttempt.errors.txt new file mode 100644 index 00000000000..645eca539f4 --- /dev/null +++ b/tests/baselines/reference/jsdocTypeNongenericInstantiationAttempt.errors.txt @@ -0,0 +1,97 @@ +tests/cases/compiler/index.js(2,19): error TS2315: Type 'boolean' is not generic. +tests/cases/compiler/index2.js(2,19): error TS2315: Type 'void' is not generic. +tests/cases/compiler/index3.js(2,19): error TS2315: Type 'undefined' is not generic. +tests/cases/compiler/index4.js(2,19): error TS2315: Type 'Function' is not generic. +tests/cases/compiler/index5.js(2,19): error TS2315: Type 'string' is not generic. +tests/cases/compiler/index6.js(2,19): error TS2315: Type 'number' is not generic. +tests/cases/compiler/index7.js(2,19): error TS2315: Type 'any' is not generic. +tests/cases/compiler/index8.js(4,12): error TS2304: Cannot find name 'fn'. +tests/cases/compiler/index8.js(4,15): error TS2304: Cannot find name 'T'. + + +==== tests/cases/compiler/index.js (1 errors) ==== + /** + * @param {(m: Boolean) => string} somebody + ~~~~~~~~~~ +!!! error TS2315: Type 'boolean' is not generic. + */ + function sayHello(somebody) { + return 'Hello ' + somebody; + } + +==== tests/cases/compiler/index2.js (1 errors) ==== + /** + * @param {(m: Void) => string} somebody + ~~~~~~~ +!!! error TS2315: Type 'void' is not generic. + */ + function sayHello2(somebody) { + return 'Hello ' + somebody; + } + + +==== tests/cases/compiler/index3.js (1 errors) ==== + /** + * @param {(m: Undefined) => string} somebody + ~~~~~~~~~~~~ +!!! error TS2315: Type 'undefined' is not generic. + */ + function sayHello3(somebody) { + return 'Hello ' + somebody; + } + + +==== tests/cases/compiler/index4.js (1 errors) ==== + /** + * @param {(m: Function) => string} somebody + ~~~~~~~~~~~ +!!! error TS2315: Type 'Function' is not generic. + */ + function sayHello4(somebody) { + return 'Hello ' + somebody; + } + + +==== tests/cases/compiler/index5.js (1 errors) ==== + /** + * @param {(m: String) => string} somebody + ~~~~~~~~~ +!!! error TS2315: Type 'string' is not generic. + */ + function sayHello5(somebody) { + return 'Hello ' + somebody; + } + + +==== tests/cases/compiler/index6.js (1 errors) ==== + /** + * @param {(m: Number) => string} somebody + ~~~~~~~~~ +!!! error TS2315: Type 'number' is not generic. + */ + function sayHello6(somebody) { + return 'Hello ' + somebody; + } + + +==== tests/cases/compiler/index7.js (1 errors) ==== + /** + * @param {(m: Object) => string} somebody + ~~~~~~~~~ +!!! error TS2315: Type 'any' is not generic. + */ + function sayHello7(somebody) { + return 'Hello ' + somebody; + } + +==== tests/cases/compiler/index8.js (2 errors) ==== + function fn() {} + + /** + * @param {fn} somebody + ~~ +!!! error TS2304: Cannot find name 'fn'. + ~ +!!! error TS2304: Cannot find name 'T'. + */ + function sayHello8(somebody) { } \ No newline at end of file diff --git a/tests/baselines/reference/jsdocTypeTagCast.js b/tests/baselines/reference/jsdocTypeTagCast.js index c2df50cd6dd..ffe59d0138d 100644 --- a/tests/baselines/reference/jsdocTypeTagCast.js +++ b/tests/baselines/reference/jsdocTypeTagCast.js @@ -97,7 +97,7 @@ var a; /** @type {string} */ var s; var a = ("" + 4); -var s = "" + (4); +var s = "" +/** @type {*} */ (4); var SomeBase = (function () { function SomeBase() { this.p = 42; @@ -128,19 +128,19 @@ var someBase = new SomeBase(); var someDerived = new SomeDerived(); var someOther = new SomeOther(); var someFakeClass = new SomeFakeClass(); -someBase = (someDerived); -someBase = (someBase); -someBase = (someOther); // Error -someDerived = (someDerived); -someDerived = (someBase); -someDerived = (someOther); // Error -someOther = (someDerived); // Error -someOther = (someBase); // Error -someOther = (someOther); +someBase =/** @type {SomeBase} */ (someDerived); +someBase =/** @type {SomeBase} */ (someBase); +someBase =/** @type {SomeBase} */ (someOther); // Error +someDerived =/** @type {SomeDerived} */ (someDerived); +someDerived =/** @type {SomeDerived} */ (someBase); +someDerived =/** @type {SomeDerived} */ (someOther); // Error +someOther =/** @type {SomeOther} */ (someDerived); // Error +someOther =/** @type {SomeOther} */ (someBase); // Error +someOther =/** @type {SomeOther} */ (someOther); someFakeClass = someBase; someFakeClass = someDerived; someBase = someFakeClass; // Error -someBase = (someFakeClass); +someBase =/** @type {SomeBase} */ (someFakeClass); // Type assertion cannot be a type-predicate type /** @type {number | string} */ var numOrStr; diff --git a/tests/baselines/reference/keyofAndIndexedAccessErrors.errors.txt b/tests/baselines/reference/keyofAndIndexedAccessErrors.errors.txt index 1e88e2abc43..0634c3419e0 100644 --- a/tests/baselines/reference/keyofAndIndexedAccessErrors.errors.txt +++ b/tests/baselines/reference/keyofAndIndexedAccessErrors.errors.txt @@ -15,6 +15,7 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(35,21): error tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(36,21): error TS2538: Type 'boolean' cannot be used as an index type. tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(41,31): error TS2538: Type 'boolean' cannot be used as an index type. tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(46,16): error TS2538: Type 'boolean' cannot be used as an index type. +tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(49,12): error TS1122: A tuple type element list cannot be empty. tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(63,33): error TS2345: Argument of type '"size"' is not assignable to parameter of type '"name" | "width" | "height" | "visible"'. tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(64,33): error TS2345: Argument of type '"name" | "size"' is not assignable to parameter of type '"name" | "width" | "height" | "visible"'. Type '"size"' is not assignable to type '"name" | "width" | "height" | "visible"'. @@ -28,7 +29,7 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(76,5): error tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(77,5): error TS2322: Type 'keyof (T & U)' is not assignable to type 'keyof (T | U)'. -==== tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts (24 errors) ==== +==== tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts (25 errors) ==== class Shape { name: string; width: number; @@ -112,6 +113,8 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(77,5): error type T60 = {}["toString"]; type T61 = []["toString"]; + ~~ +!!! error TS1122: A tuple type element list cannot be empty. declare let cond: boolean; diff --git a/tests/baselines/reference/mappedTypeErrors2.errors.txt b/tests/baselines/reference/mappedTypeErrors2.errors.txt new file mode 100644 index 00000000000..18bc7cc7239 --- /dev/null +++ b/tests/baselines/reference/mappedTypeErrors2.errors.txt @@ -0,0 +1,35 @@ +tests/cases/conformance/types/mapped/mappedTypeErrors2.ts(9,30): error TS2536: Type 'K' cannot be used to index type 'T1'. +tests/cases/conformance/types/mapped/mappedTypeErrors2.ts(13,30): error TS2536: Type 'K' cannot be used to index type 'T3'. +tests/cases/conformance/types/mapped/mappedTypeErrors2.ts(15,47): error TS2536: Type 'S' cannot be used to index type 'AB'. +tests/cases/conformance/types/mapped/mappedTypeErrors2.ts(17,49): error TS2536: Type 'L' cannot be used to index type '{ [key in AB[S]]: true; }'. + + +==== tests/cases/conformance/types/mapped/mappedTypeErrors2.ts (4 errors) ==== + // Repros from #17238 + + type AB = { + a: 'a' + b: 'a' + }; + + type T1 = { [key in AB[K]]: true }; + type T2 = T1[K]; // Error + ~~~~~~~~ +!!! error TS2536: Type 'K' cannot be used to index type 'T1'. + + type R = AB[keyof AB]; // "a" + type T3 = { [key in R]: true }; + type T4 = T3[K] // Error + ~~~~~ +!!! error TS2536: Type 'K' cannot be used to index type 'T3'. + + type T5 = {[key in AB[S]]: true}[S]; // Error + ~~~~~ +!!! error TS2536: Type 'S' cannot be used to index type 'AB'. + + type T6 = {[key in AB[S]]: true}[L]; // Error + ~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2536: Type 'L' cannot be used to index type '{ [key in AB[S]]: true; }'. + + type T7 = {[key in AB[S]]: true}[L]; + \ No newline at end of file diff --git a/tests/baselines/reference/mappedTypeErrors2.js b/tests/baselines/reference/mappedTypeErrors2.js new file mode 100644 index 00000000000..d9fc6b0bc72 --- /dev/null +++ b/tests/baselines/reference/mappedTypeErrors2.js @@ -0,0 +1,49 @@ +//// [mappedTypeErrors2.ts] +// Repros from #17238 + +type AB = { + a: 'a' + b: 'a' +}; + +type T1 = { [key in AB[K]]: true }; +type T2 = T1[K]; // Error + +type R = AB[keyof AB]; // "a" +type T3 = { [key in R]: true }; +type T4 = T3[K] // Error + +type T5 = {[key in AB[S]]: true}[S]; // Error + +type T6 = {[key in AB[S]]: true}[L]; // Error + +type T7 = {[key in AB[S]]: true}[L]; + + +//// [mappedTypeErrors2.js] +// Repros from #17238 + + +//// [mappedTypeErrors2.d.ts] +declare type AB = { + a: 'a'; + b: 'a'; +}; +declare type T1 = { + [key in AB[K]]: true; +}; +declare type T2 = T1[K]; +declare type R = AB[keyof AB]; +declare type T3 = { + [key in R]: true; +}; +declare type T4 = T3[K]; +declare type T5 = { + [key in AB[S]]: true; +}[S]; +declare type T6 = { + [key in AB[S]]: true; +}[L]; +declare type T7 = { + [key in AB[S]]: true; +}[L]; diff --git a/tests/baselines/reference/noUnusedLocals_selfReference.errors.txt b/tests/baselines/reference/noUnusedLocals_selfReference.errors.txt new file mode 100644 index 00000000000..af40081e71c --- /dev/null +++ b/tests/baselines/reference/noUnusedLocals_selfReference.errors.txt @@ -0,0 +1,28 @@ +tests/cases/compiler/noUnusedLocals_selfReference.ts(3,10): error TS6133: 'f' is declared but never used. +tests/cases/compiler/noUnusedLocals_selfReference.ts(4,7): error TS6133: 'C' is declared but never used. +tests/cases/compiler/noUnusedLocals_selfReference.ts(7,6): error TS6133: 'E' is declared but never used. + + +==== tests/cases/compiler/noUnusedLocals_selfReference.ts (3 errors) ==== + export {}; // Make this a module scope, so these are local variables. + + function f() { f; } + ~ +!!! error TS6133: 'f' is declared but never used. + class C { + ~ +!!! error TS6133: 'C' is declared but never used. + m() { C; } + } + enum E { A = 0, B = E.A } + ~ +!!! error TS6133: 'E' is declared but never used. + + // Does not detect mutual recursion. + function g() { D; } + class D { m() { g; } } + + // Does not work on private methods. + class P { private m() { this.m; } } + P; + \ No newline at end of file diff --git a/tests/baselines/reference/noUnusedLocals_selfReference.js b/tests/baselines/reference/noUnusedLocals_selfReference.js new file mode 100644 index 00000000000..74a39923d57 --- /dev/null +++ b/tests/baselines/reference/noUnusedLocals_selfReference.js @@ -0,0 +1,49 @@ +//// [noUnusedLocals_selfReference.ts] +export {}; // Make this a module scope, so these are local variables. + +function f() { f; } +class C { + m() { C; } +} +enum E { A = 0, B = E.A } + +// Does not detect mutual recursion. +function g() { D; } +class D { m() { g; } } + +// Does not work on private methods. +class P { private m() { this.m; } } +P; + + +//// [noUnusedLocals_selfReference.js] +"use strict"; +exports.__esModule = true; +function f() { f; } +var C = (function () { + function C() { + } + C.prototype.m = function () { C; }; + return C; +}()); +var E; +(function (E) { + E[E["A"] = 0] = "A"; + E[E["B"] = 0] = "B"; +})(E || (E = {})); +// Does not detect mutual recursion. +function g() { D; } +var D = (function () { + function D() { + } + D.prototype.m = function () { g; }; + return D; +}()); +// Does not work on private methods. +var P = (function () { + function P() { + } + P.prototype.m = function () { this.m; }; + return P; +}()); +P; diff --git a/tests/baselines/reference/parser15.4.4.14-9-2.js b/tests/baselines/reference/parser15.4.4.14-9-2.js index 0f533cd9a26..e24da870d3e 100644 --- a/tests/baselines/reference/parser15.4.4.14-9-2.js +++ b/tests/baselines/reference/parser15.4.4.14-9-2.js @@ -41,9 +41,9 @@ function testcase() { var one = 1; var _float = -(4 / 3); var a = new Array(false, undefined, null, "0", obj, -1.3333333333333, "str", -0, true, +0, one, 1, 0, false, _float, -(4 / 3)); - if (a.indexOf(-(4 / 3)) === 14 && - a.indexOf(0) === 7 && - a.indexOf(-0) === 7 && + if (a.indexOf(-(4 / 3)) === 14 &&// a[14]=_float===-(4/3) + a.indexOf(0) === 7 &&// a[7] = +0, 0===+0 + a.indexOf(-0) === 7 &&// a[7] = +0, -0===+0 a.indexOf(1) === 10) { return true; } diff --git a/tests/baselines/reference/parserGreaterThanTokenAmbiguity10.js b/tests/baselines/reference/parserGreaterThanTokenAmbiguity10.js index 862722b1a73..7ff9e380dcf 100644 --- a/tests/baselines/reference/parserGreaterThanTokenAmbiguity10.js +++ b/tests/baselines/reference/parserGreaterThanTokenAmbiguity10.js @@ -6,5 +6,6 @@ //// [parserGreaterThanTokenAmbiguity10.js] 1 - >>> + // before + >>>// after 2; diff --git a/tests/baselines/reference/parserGreaterThanTokenAmbiguity15.js b/tests/baselines/reference/parserGreaterThanTokenAmbiguity15.js index b6f905e12e7..03e6211ae15 100644 --- a/tests/baselines/reference/parserGreaterThanTokenAmbiguity15.js +++ b/tests/baselines/reference/parserGreaterThanTokenAmbiguity15.js @@ -6,5 +6,6 @@ //// [parserGreaterThanTokenAmbiguity15.js] 1 - >>= + // before + >>=// after 2; diff --git a/tests/baselines/reference/parserGreaterThanTokenAmbiguity20.js b/tests/baselines/reference/parserGreaterThanTokenAmbiguity20.js index 01d1d6401f2..ba5e380043d 100644 --- a/tests/baselines/reference/parserGreaterThanTokenAmbiguity20.js +++ b/tests/baselines/reference/parserGreaterThanTokenAmbiguity20.js @@ -6,5 +6,6 @@ //// [parserGreaterThanTokenAmbiguity20.js] 1 - >>>= + // Before + >>>=// after 2; diff --git a/tests/baselines/reference/parserGreaterThanTokenAmbiguity5.js b/tests/baselines/reference/parserGreaterThanTokenAmbiguity5.js index c65b76f504a..e240746caa4 100644 --- a/tests/baselines/reference/parserGreaterThanTokenAmbiguity5.js +++ b/tests/baselines/reference/parserGreaterThanTokenAmbiguity5.js @@ -6,5 +6,6 @@ //// [parserGreaterThanTokenAmbiguity5.js] 1 - >> + // before + >>// after 2; diff --git a/tests/baselines/reference/prefixedNumberLiteralAssignToNumberLiteralType.js b/tests/baselines/reference/prefixedNumberLiteralAssignToNumberLiteralType.js new file mode 100644 index 00000000000..63f1f42d101 --- /dev/null +++ b/tests/baselines/reference/prefixedNumberLiteralAssignToNumberLiteralType.js @@ -0,0 +1,8 @@ +//// [prefixedNumberLiteralAssignToNumberLiteralType.ts] +let x: 1 = +1; + +let y: -1 = -1; + +//// [prefixedNumberLiteralAssignToNumberLiteralType.js] +var x = +1; +var y = -1; diff --git a/tests/baselines/reference/prefixedNumberLiteralAssignToNumberLiteralType.symbols b/tests/baselines/reference/prefixedNumberLiteralAssignToNumberLiteralType.symbols new file mode 100644 index 00000000000..4ab5d371e8a --- /dev/null +++ b/tests/baselines/reference/prefixedNumberLiteralAssignToNumberLiteralType.symbols @@ -0,0 +1,7 @@ +=== tests/cases/compiler/prefixedNumberLiteralAssignToNumberLiteralType.ts === +let x: 1 = +1; +>x : Symbol(x, Decl(prefixedNumberLiteralAssignToNumberLiteralType.ts, 0, 3)) + +let y: -1 = -1; +>y : Symbol(y, Decl(prefixedNumberLiteralAssignToNumberLiteralType.ts, 2, 3)) + diff --git a/tests/baselines/reference/prefixedNumberLiteralAssignToNumberLiteralType.types b/tests/baselines/reference/prefixedNumberLiteralAssignToNumberLiteralType.types new file mode 100644 index 00000000000..93bd5cc6274 --- /dev/null +++ b/tests/baselines/reference/prefixedNumberLiteralAssignToNumberLiteralType.types @@ -0,0 +1,13 @@ +=== tests/cases/compiler/prefixedNumberLiteralAssignToNumberLiteralType.ts === +let x: 1 = +1; +>x : 1 +>+1 : 1 +>1 : 1 + +let y: -1 = -1; +>y : -1 +>-1 : -1 +>1 : 1 +>-1 : -1 +>1 : 1 + diff --git a/tests/baselines/reference/project/nodeModulesMaxDepthExceeded/amd/nodeModulesMaxDepthExceeded.errors.txt b/tests/baselines/reference/project/nodeModulesMaxDepthExceeded/amd/nodeModulesMaxDepthExceeded.errors.txt index a1b170ce665..bd2dd6231f3 100644 --- a/tests/baselines/reference/project/nodeModulesMaxDepthExceeded/amd/nodeModulesMaxDepthExceeded.errors.txt +++ b/tests/baselines/reference/project/nodeModulesMaxDepthExceeded/amd/nodeModulesMaxDepthExceeded.errors.txt @@ -9,7 +9,7 @@ maxDepthExceeded/root.ts(4,4): error TS2540: Cannot assign to 'rel' because it i "maxNodeModuleJsDepth": 1, // Note: Module m1 is already included as a root file "outDir": "built" }, - "include": ["**/*"], + "include": ["**/*", "node_modules/**/*"], "exclude": ["node_modules/m2/**/*"] } diff --git a/tests/baselines/reference/project/nodeModulesMaxDepthExceeded/node/nodeModulesMaxDepthExceeded.errors.txt b/tests/baselines/reference/project/nodeModulesMaxDepthExceeded/node/nodeModulesMaxDepthExceeded.errors.txt index a1b170ce665..bd2dd6231f3 100644 --- a/tests/baselines/reference/project/nodeModulesMaxDepthExceeded/node/nodeModulesMaxDepthExceeded.errors.txt +++ b/tests/baselines/reference/project/nodeModulesMaxDepthExceeded/node/nodeModulesMaxDepthExceeded.errors.txt @@ -9,7 +9,7 @@ maxDepthExceeded/root.ts(4,4): error TS2540: Cannot assign to 'rel' because it i "maxNodeModuleJsDepth": 1, // Note: Module m1 is already included as a root file "outDir": "built" }, - "include": ["**/*"], + "include": ["**/*", "node_modules/**/*"], "exclude": ["node_modules/m2/**/*"] } diff --git a/tests/baselines/reference/signatureInstantiationWithRecursiveConstraints.js b/tests/baselines/reference/signatureInstantiationWithRecursiveConstraints.js new file mode 100644 index 00000000000..0be7fa1444d --- /dev/null +++ b/tests/baselines/reference/signatureInstantiationWithRecursiveConstraints.js @@ -0,0 +1,30 @@ +//// [signatureInstantiationWithRecursiveConstraints.ts] +// Repro from #17148 + +class Foo { + myFunc(arg: T) {} +} + +class Bar { + myFunc(arg: T) {} +} + +const myVar: Foo = new Bar(); + + +//// [signatureInstantiationWithRecursiveConstraints.js] +"use strict"; +// Repro from #17148 +var Foo = (function () { + function Foo() { + } + Foo.prototype.myFunc = function (arg) { }; + return Foo; +}()); +var Bar = (function () { + function Bar() { + } + Bar.prototype.myFunc = function (arg) { }; + return Bar; +}()); +var myVar = new Bar(); diff --git a/tests/baselines/reference/signatureInstantiationWithRecursiveConstraints.symbols b/tests/baselines/reference/signatureInstantiationWithRecursiveConstraints.symbols new file mode 100644 index 00000000000..ebc1b625d9e --- /dev/null +++ b/tests/baselines/reference/signatureInstantiationWithRecursiveConstraints.symbols @@ -0,0 +1,30 @@ +=== tests/cases/compiler/signatureInstantiationWithRecursiveConstraints.ts === +// Repro from #17148 + +class Foo { +>Foo : Symbol(Foo, Decl(signatureInstantiationWithRecursiveConstraints.ts, 0, 0)) + + myFunc(arg: T) {} +>myFunc : Symbol(Foo.myFunc, Decl(signatureInstantiationWithRecursiveConstraints.ts, 2, 11)) +>T : Symbol(T, Decl(signatureInstantiationWithRecursiveConstraints.ts, 3, 9)) +>Foo : Symbol(Foo, Decl(signatureInstantiationWithRecursiveConstraints.ts, 0, 0)) +>arg : Symbol(arg, Decl(signatureInstantiationWithRecursiveConstraints.ts, 3, 24)) +>T : Symbol(T, Decl(signatureInstantiationWithRecursiveConstraints.ts, 3, 9)) +} + +class Bar { +>Bar : Symbol(Bar, Decl(signatureInstantiationWithRecursiveConstraints.ts, 4, 1)) + + myFunc(arg: T) {} +>myFunc : Symbol(Bar.myFunc, Decl(signatureInstantiationWithRecursiveConstraints.ts, 6, 11)) +>T : Symbol(T, Decl(signatureInstantiationWithRecursiveConstraints.ts, 7, 9)) +>Bar : Symbol(Bar, Decl(signatureInstantiationWithRecursiveConstraints.ts, 4, 1)) +>arg : Symbol(arg, Decl(signatureInstantiationWithRecursiveConstraints.ts, 7, 24)) +>T : Symbol(T, Decl(signatureInstantiationWithRecursiveConstraints.ts, 7, 9)) +} + +const myVar: Foo = new Bar(); +>myVar : Symbol(myVar, Decl(signatureInstantiationWithRecursiveConstraints.ts, 10, 5)) +>Foo : Symbol(Foo, Decl(signatureInstantiationWithRecursiveConstraints.ts, 0, 0)) +>Bar : Symbol(Bar, Decl(signatureInstantiationWithRecursiveConstraints.ts, 4, 1)) + diff --git a/tests/baselines/reference/signatureInstantiationWithRecursiveConstraints.types b/tests/baselines/reference/signatureInstantiationWithRecursiveConstraints.types new file mode 100644 index 00000000000..2368835be08 --- /dev/null +++ b/tests/baselines/reference/signatureInstantiationWithRecursiveConstraints.types @@ -0,0 +1,31 @@ +=== tests/cases/compiler/signatureInstantiationWithRecursiveConstraints.ts === +// Repro from #17148 + +class Foo { +>Foo : Foo + + myFunc(arg: T) {} +>myFunc : (arg: T) => void +>T : T +>Foo : Foo +>arg : T +>T : T +} + +class Bar { +>Bar : Bar + + myFunc(arg: T) {} +>myFunc : (arg: T) => void +>T : T +>Bar : Bar +>arg : T +>T : T +} + +const myVar: Foo = new Bar(); +>myVar : Foo +>Foo : Foo +>new Bar() : Bar +>Bar : typeof Bar + diff --git a/tests/baselines/reference/typeGuardsInConditionalExpression.js b/tests/baselines/reference/typeGuardsInConditionalExpression.js index 9aade91612a..8be83a887b1 100644 --- a/tests/baselines/reference/typeGuardsInConditionalExpression.js +++ b/tests/baselines/reference/typeGuardsInConditionalExpression.js @@ -138,7 +138,7 @@ function foo8(x) { var b; return typeof x === "string" ? x === "hello" - : ((b = x) && + : ((b = x) &&// number | boolean (typeof x === "boolean" ? x // boolean : x == 10)); // boolean diff --git a/tests/baselines/reference/unaryPlus.types b/tests/baselines/reference/unaryPlus.types index 8f92b51a159..9bd741f2ad1 100644 --- a/tests/baselines/reference/unaryPlus.types +++ b/tests/baselines/reference/unaryPlus.types @@ -2,7 +2,7 @@ // allowed per spec var a = +1; >a : number ->+1 : number +>+1 : 1 >1 : 1 var b = +(""); diff --git a/tests/baselines/reference/unusedLocalsAndParametersTypeAliases2.errors.txt b/tests/baselines/reference/unusedLocalsAndParametersTypeAliases2.errors.txt index 528ca9f9e73..e5c6f476480 100644 --- a/tests/baselines/reference/unusedLocalsAndParametersTypeAliases2.errors.txt +++ b/tests/baselines/reference/unusedLocalsAndParametersTypeAliases2.errors.txt @@ -1,8 +1,9 @@ tests/cases/compiler/unusedLocalsAndParametersTypeAliases2.ts(2,6): error TS6133: 'handler1' is declared but never used. +tests/cases/compiler/unusedLocalsAndParametersTypeAliases2.ts(5,10): error TS6133: 'foo' is declared but never used. tests/cases/compiler/unusedLocalsAndParametersTypeAliases2.ts(6,10): error TS6133: 'handler2' is declared but never used. -==== tests/cases/compiler/unusedLocalsAndParametersTypeAliases2.ts (2 errors) ==== +==== tests/cases/compiler/unusedLocalsAndParametersTypeAliases2.ts (3 errors) ==== // unused type handler1 = () => void; ~~~~~~~~ @@ -10,6 +11,8 @@ tests/cases/compiler/unusedLocalsAndParametersTypeAliases2.ts(6,10): error TS613 function foo() { + ~~~ +!!! error TS6133: 'foo' is declared but never used. type handler2 = () => void; ~~~~~~~~ !!! error TS6133: 'handler2' is declared but never used. diff --git a/tests/baselines/reference/weakType.errors.txt b/tests/baselines/reference/weakType.errors.txt index 441ef70ac16..ffc1d237593 100644 --- a/tests/baselines/reference/weakType.errors.txt +++ b/tests/baselines/reference/weakType.errors.txt @@ -1,14 +1,17 @@ -tests/cases/compiler/weakType.ts(16,13): error TS2559: Type '12' has no properties in common with type 'Settings'. -tests/cases/compiler/weakType.ts(17,13): error TS2559: Type '"completely wrong"' has no properties in common with type 'Settings'. -tests/cases/compiler/weakType.ts(18,13): error TS2559: Type 'false' has no properties in common with type 'Settings'. -tests/cases/compiler/weakType.ts(35,18): error TS2559: Type '{ error?: number; }' has no properties in common with type 'ChangeOptions'. -tests/cases/compiler/weakType.ts(60,5): error TS2322: Type '{ properties: { wrong: string; }; }' is not assignable to type 'Weak & Spoiler'. +tests/cases/compiler/weakType.ts(15,13): error TS2560: Value of type '() => { timeout: number; }' has no properties in common with type 'Settings'. Did you mean to call it? +tests/cases/compiler/weakType.ts(16,13): error TS2560: Value of type '() => { timeout: number; }' has no properties in common with type 'Settings'. Did you mean to call it? +tests/cases/compiler/weakType.ts(17,13): error TS2560: Value of type 'CtorOnly' has no properties in common with type 'Settings'. Did you mean to call it? +tests/cases/compiler/weakType.ts(18,13): error TS2559: Type '12' has no properties in common with type 'Settings'. +tests/cases/compiler/weakType.ts(19,13): error TS2559: Type '"completely wrong"' has no properties in common with type 'Settings'. +tests/cases/compiler/weakType.ts(20,13): error TS2559: Type 'false' has no properties in common with type 'Settings'. +tests/cases/compiler/weakType.ts(37,18): error TS2559: Type '{ error?: number; }' has no properties in common with type 'ChangeOptions'. +tests/cases/compiler/weakType.ts(62,5): error TS2322: Type '{ properties: { wrong: string; }; }' is not assignable to type 'Weak & Spoiler'. Type '{ properties: { wrong: string; }; }' is not assignable to type 'Weak'. Types of property 'properties' are incompatible. Type '{ wrong: string; }' has no properties in common with type '{ b?: number; }'. -==== tests/cases/compiler/weakType.ts (5 errors) ==== +==== tests/cases/compiler/weakType.ts (8 errors) ==== interface Settings { timeout?: number; onError?(): void; @@ -17,13 +20,21 @@ tests/cases/compiler/weakType.ts(60,5): error TS2322: Type '{ properties: { wron function getDefaultSettings() { return { timeout: 1000 }; } + interface CtorOnly { + new(s: string): { timeout: 1000 } + } function doSomething(settings: Settings) { /* ... */ } // forgot to call `getDefaultSettings` - // but it is not caught because we don't check for call signatures doSomething(getDefaultSettings); - // same for arrow expressions: - doSomething(() => { }); + ~~~~~~~~~~~~~~~~~~ +!!! error TS2560: Value of type '() => { timeout: number; }' has no properties in common with type 'Settings'. Did you mean to call it? + doSomething(() => ({ timeout: 1000 })); + ~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2560: Value of type '() => { timeout: number; }' has no properties in common with type 'Settings'. Did you mean to call it? + doSomething(null as CtorOnly); + ~~~~~~~~~~~~~~~~ +!!! error TS2560: Value of type 'CtorOnly' has no properties in common with type 'Settings'. Did you mean to call it? doSomething(12); ~~ !!! error TS2559: Type '12' has no properties in common with type 'Settings'. @@ -82,4 +93,5 @@ tests/cases/compiler/weakType.ts(60,5): error TS2322: Type '{ properties: { wron !!! error TS2322: Type '{ properties: { wrong: string; }; }' is not assignable to type 'Weak'. !!! error TS2322: Types of property 'properties' are incompatible. !!! error TS2322: Type '{ wrong: string; }' has no properties in common with type '{ b?: number; }'. + \ No newline at end of file diff --git a/tests/baselines/reference/weakType.js b/tests/baselines/reference/weakType.js index 5637271ccec..2a1dc4ca0e4 100644 --- a/tests/baselines/reference/weakType.js +++ b/tests/baselines/reference/weakType.js @@ -7,13 +7,15 @@ interface Settings { function getDefaultSettings() { return { timeout: 1000 }; } +interface CtorOnly { + new(s: string): { timeout: 1000 } +} function doSomething(settings: Settings) { /* ... */ } // forgot to call `getDefaultSettings` -// but it is not caught because we don't check for call signatures doSomething(getDefaultSettings); -// same for arrow expressions: -doSomething(() => { }); +doSomething(() => ({ timeout: 1000 })); +doSomething(null as CtorOnly); doSomething(12); doSomething('completely wrong'); doSomething(false); @@ -59,6 +61,7 @@ declare let unknown: { } } let weak: Weak & Spoiler = unknown + //// [weakType.js] @@ -67,10 +70,9 @@ function getDefaultSettings() { } function doSomething(settings) { } // forgot to call `getDefaultSettings` -// but it is not caught because we don't check for call signatures doSomething(getDefaultSettings); -// same for arrow expressions: -doSomething(function () { }); +doSomething(function () { return ({ timeout: 1000 }); }); +doSomething(null); doSomething(12); doSomething('completely wrong'); doSomething(false); diff --git a/tests/cases/compiler/checkTypePredicateForRedundantProperties.ts b/tests/cases/compiler/checkTypePredicateForRedundantProperties.ts new file mode 100644 index 00000000000..35222f1e9db --- /dev/null +++ b/tests/cases/compiler/checkTypePredicateForRedundantProperties.ts @@ -0,0 +1,3 @@ +function addProp2(x: any): x is { a: string; a: string; } { + return true; +} diff --git a/tests/cases/compiler/commentOnBinaryOperator1.ts b/tests/cases/compiler/commentOnBinaryOperator1.ts new file mode 100644 index 00000000000..29de3410c32 --- /dev/null +++ b/tests/cases/compiler/commentOnBinaryOperator1.ts @@ -0,0 +1,12 @@ +var a = 'some' + // comment + + 'text'; + +var b = 'some' + /* comment */ + + 'text'; + +var c = 'some' + /* comment */ + + /*comment1*/ + 'text'; \ No newline at end of file diff --git a/tests/cases/compiler/commentOnBinaryOperator2.ts b/tests/cases/compiler/commentOnBinaryOperator2.ts new file mode 100644 index 00000000000..023655e16c0 --- /dev/null +++ b/tests/cases/compiler/commentOnBinaryOperator2.ts @@ -0,0 +1,13 @@ +// @removeComments: true +var a = 'some' + // comment + + 'text'; + +var b = 'some' + /* comment */ + + 'text'; + +var c = 'some' + /* comment */ + + /*comment1*/ + 'text'; \ No newline at end of file diff --git a/tests/cases/compiler/deferredLookupTypeResolution.ts b/tests/cases/compiler/deferredLookupTypeResolution.ts new file mode 100644 index 00000000000..6c9853266ad --- /dev/null +++ b/tests/cases/compiler/deferredLookupTypeResolution.ts @@ -0,0 +1,28 @@ +// @strict: true +// @declaration: true + +// Repro from #17456 + +type StringContains = ( + { [K in S]: 'true' } & + { [key: string]: 'false' } + )[L] + +type ObjectHasKey = StringContains + +type First = ObjectHasKey; // Should be deferred + +type T1 = ObjectHasKey<{ a: string }, 'a'>; // 'true' +type T2 = ObjectHasKey<{ a: string }, 'b'>; // 'false' + +// Verify that mapped type isn't eagerly resolved in type-to-string operation + +declare function f1(a: A, b: B): { [P in A | B]: any }; + +function f2(a: A) { + return f1(a, 'x'); +} + +function f3(x: 'a' | 'b') { + return f2(x); +} diff --git a/tests/cases/compiler/deferredLookupTypeResolution2.ts b/tests/cases/compiler/deferredLookupTypeResolution2.ts new file mode 100644 index 00000000000..4aa18c092ba --- /dev/null +++ b/tests/cases/compiler/deferredLookupTypeResolution2.ts @@ -0,0 +1,24 @@ +// @strict: true +// @declaration: true + +// Repro from #17456 + +type StringContains = ({ [K in S]: 'true' } & { [key: string]: 'false'})[L]; + +type ObjectHasKey = StringContains; + +type A = ObjectHasKey; + +type B = ObjectHasKey<[string, number], '1'>; // "true" +type C = ObjectHasKey<[string, number], '2'>; // "false" +type D = A<[string]>; // "true" + +// Error, "false" not handled +type E = { true: 'true' }[ObjectHasKey]; + +type Juxtapose = ({ true: 'otherwise' } & { [k: string]: 'true' })[ObjectHasKey]; + +// Error, "otherwise" is missing +type DeepError = { true: 'true' }[Juxtapose]; + +type DeepOK = { true: 'true', otherwise: 'false' }[Juxtapose]; diff --git a/tests/cases/compiler/doNotInferUnrelatedTypes.ts b/tests/cases/compiler/doNotInferUnrelatedTypes.ts new file mode 100644 index 00000000000..08a7d793430 --- /dev/null +++ b/tests/cases/compiler/doNotInferUnrelatedTypes.ts @@ -0,0 +1,6 @@ +// #16709 +declare function dearray(ara: ReadonlyArray): T; +type LiteralType = "foo" | "bar"; +declare var alt: Array; + +let foo: LiteralType = dearray(alt); diff --git a/tests/cases/compiler/expressionTypeNodeShouldError.ts b/tests/cases/compiler/expressionTypeNodeShouldError.ts new file mode 100644 index 00000000000..0f6463f454b --- /dev/null +++ b/tests/cases/compiler/expressionTypeNodeShouldError.ts @@ -0,0 +1,45 @@ +// @Filename: base.d.ts +declare const x: "foo".charCodeAt(0); + +// @filename: string.ts +interface String { + typeof(x: T): T; +} + +class C { + foo() { + const x: "".typeof(this.foo); + } +} + +const nodes = document.getElementsByTagName("li"); +type ItemType = "".typeof(nodes.item(0)); + +// @filename: number.ts +interface Number { + typeof(x: T): T; +} + +class C2 { + foo() { + const x: 3.141592.typeof(this.foo); + } +} + +const nodes2 = document.getElementsByTagName("li"); +type ItemType2 = 4..typeof(nodes.item(0)); + +// @filename: boolean.ts +interface Boolean { + typeof(x: T): T; +} + +class C3 { + foo() { + const x: false.typeof(this.foo); + } +} + +const nodes3 = document.getElementsByTagName("li"); +type ItemType3 = true.typeof(nodes.item(0)); + diff --git a/tests/cases/compiler/indexSignatureAndMappedType.ts b/tests/cases/compiler/indexSignatureAndMappedType.ts new file mode 100644 index 00000000000..b5f9e8a0030 --- /dev/null +++ b/tests/cases/compiler/indexSignatureAndMappedType.ts @@ -0,0 +1,35 @@ +// @strict: true +// @declaration: true + +// A mapped type { [P in K]: X }, where K is a generic type, is related to +// { [key: string]: Y } if X is related to Y. + +function f1(x: { [key: string]: T }, y: Record) { + x = y; + y = x; // Error +} + +function f2(x: { [key: string]: T }, y: Record) { + x = y; + y = x; +} + +function f3(x: { [key: string]: T }, y: Record) { + x = y; // Error + y = x; // Error +} + +// Repro from #14548 + +type Dictionary = { + [key: string]: string; +}; + +interface IBaseEntity { + name: string; + properties: Dictionary; +} + +interface IEntity extends IBaseEntity { + properties: Record; +} diff --git a/tests/cases/compiler/jsdocTypeGenericInstantiationAttempt.ts b/tests/cases/compiler/jsdocTypeGenericInstantiationAttempt.ts new file mode 100644 index 00000000000..f7a694f6124 --- /dev/null +++ b/tests/cases/compiler/jsdocTypeGenericInstantiationAttempt.ts @@ -0,0 +1,10 @@ +// @allowJs: true +// @noEmit: true +// @checkJs: true +// @filename: index.js +/** + * @param {Array<*>} list + */ +function thing(list) { + return list; +} diff --git a/tests/cases/compiler/jsdocTypeNongenericInstantiationAttempt.ts b/tests/cases/compiler/jsdocTypeNongenericInstantiationAttempt.ts new file mode 100644 index 00000000000..bf05c7f6de7 --- /dev/null +++ b/tests/cases/compiler/jsdocTypeNongenericInstantiationAttempt.ts @@ -0,0 +1,71 @@ +// @allowJs: true +// @noEmit: true +// @checkJs: true +// @filename: index.js +/** + * @param {(m: Boolean) => string} somebody + */ +function sayHello(somebody) { + return 'Hello ' + somebody; +} + +// @filename: index2.js +/** + * @param {(m: Void) => string} somebody + */ +function sayHello2(somebody) { + return 'Hello ' + somebody; +} + + +// @filename: index3.js +/** + * @param {(m: Undefined) => string} somebody + */ +function sayHello3(somebody) { + return 'Hello ' + somebody; +} + + +// @filename: index4.js +/** + * @param {(m: Function) => string} somebody + */ +function sayHello4(somebody) { + return 'Hello ' + somebody; +} + + +// @filename: index5.js +/** + * @param {(m: String) => string} somebody + */ +function sayHello5(somebody) { + return 'Hello ' + somebody; +} + + +// @filename: index6.js +/** + * @param {(m: Number) => string} somebody + */ +function sayHello6(somebody) { + return 'Hello ' + somebody; +} + + +// @filename: index7.js +/** + * @param {(m: Object) => string} somebody + */ +function sayHello7(somebody) { + return 'Hello ' + somebody; +} + +// @filename: index8.js +function fn() {} + +/** + * @param {fn} somebody + */ +function sayHello8(somebody) { } \ No newline at end of file diff --git a/tests/cases/compiler/noUnusedLocals_selfReference.ts b/tests/cases/compiler/noUnusedLocals_selfReference.ts new file mode 100644 index 00000000000..8eb528743c0 --- /dev/null +++ b/tests/cases/compiler/noUnusedLocals_selfReference.ts @@ -0,0 +1,17 @@ +// @noUnusedLocals: true + +export {}; // Make this a module scope, so these are local variables. + +function f() { f; } +class C { + m() { C; } +} +enum E { A = 0, B = E.A } + +// Does not detect mutual recursion. +function g() { D; } +class D { m() { g; } } + +// Does not work on private methods. +class P { private m() { this.m; } } +P; diff --git a/tests/cases/compiler/prefixedNumberLiteralAssignToNumberLiteralType.ts b/tests/cases/compiler/prefixedNumberLiteralAssignToNumberLiteralType.ts new file mode 100644 index 00000000000..6c2afbba2bd --- /dev/null +++ b/tests/cases/compiler/prefixedNumberLiteralAssignToNumberLiteralType.ts @@ -0,0 +1,3 @@ +let x: 1 = +1; + +let y: -1 = -1; \ No newline at end of file diff --git a/tests/cases/compiler/signatureInstantiationWithRecursiveConstraints.ts b/tests/cases/compiler/signatureInstantiationWithRecursiveConstraints.ts new file mode 100644 index 00000000000..4f25446aad8 --- /dev/null +++ b/tests/cases/compiler/signatureInstantiationWithRecursiveConstraints.ts @@ -0,0 +1,13 @@ +// @strict: true + +// Repro from #17148 + +class Foo { + myFunc(arg: T) {} +} + +class Bar { + myFunc(arg: T) {} +} + +const myVar: Foo = new Bar(); diff --git a/tests/cases/compiler/weakType.ts b/tests/cases/compiler/weakType.ts index ffe51205e53..08c9d95e672 100644 --- a/tests/cases/compiler/weakType.ts +++ b/tests/cases/compiler/weakType.ts @@ -6,13 +6,15 @@ interface Settings { function getDefaultSettings() { return { timeout: 1000 }; } +interface CtorOnly { + new(s: string): { timeout: 1000 } +} function doSomething(settings: Settings) { /* ... */ } // forgot to call `getDefaultSettings` -// but it is not caught because we don't check for call signatures doSomething(getDefaultSettings); -// same for arrow expressions: -doSomething(() => { }); +doSomething(() => ({ timeout: 1000 })); +doSomething(null as CtorOnly); doSomething(12); doSomething('completely wrong'); doSomething(false); @@ -58,3 +60,4 @@ declare let unknown: { } } let weak: Weak & Spoiler = unknown + diff --git a/tests/cases/conformance/types/mapped/mappedTypeErrors2.ts b/tests/cases/conformance/types/mapped/mappedTypeErrors2.ts new file mode 100644 index 00000000000..100a93eb159 --- /dev/null +++ b/tests/cases/conformance/types/mapped/mappedTypeErrors2.ts @@ -0,0 +1,22 @@ +// @strictNullChecks: true +// @declaration: true + +// Repros from #17238 + +type AB = { + a: 'a' + b: 'a' +}; + +type T1 = { [key in AB[K]]: true }; +type T2 = T1[K]; // Error + +type R = AB[keyof AB]; // "a" +type T3 = { [key in R]: true }; +type T4 = T3[K] // Error + +type T5 = {[key in AB[S]]: true}[S]; // Error + +type T6 = {[key in AB[S]]: true}[L]; // Error + +type T7 = {[key in AB[S]]: true}[L]; diff --git a/tests/cases/fourslash/completionListInObjectBindingPattern14.ts b/tests/cases/fourslash/completionListInObjectBindingPattern14.ts new file mode 100644 index 00000000000..425813a5543 --- /dev/null +++ b/tests/cases/fourslash/completionListInObjectBindingPattern14.ts @@ -0,0 +1,9 @@ +/// + +////const { b/**/ } = new class { +//// private ab; +//// protected bc; +////} + +goTo.marker(); +verify.completionListIsEmpty(); diff --git a/tests/cases/fourslash/formattingSpaceBetweenParent.ts b/tests/cases/fourslash/formattingSpaceBetweenParent.ts new file mode 100644 index 00000000000..60ec632f59c --- /dev/null +++ b/tests/cases/fourslash/formattingSpaceBetweenParent.ts @@ -0,0 +1,14 @@ +/// + +/////*1*/foo(() => 1); +/////*2*/foo(1); +/////*3*/if((true)){} + +format.setOption("InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis", true); +format.document(); +goTo.marker("1"); +verify.currentLineContentIs("foo( () => 1 );"); +goTo.marker("2"); +verify.currentLineContentIs("foo( 1 );"); +goTo.marker("3"); +verify.currentLineContentIs("if ( ( true ) ) { }"); diff --git a/tests/cases/projects/NodeModulesSearch/maxDepthExceeded/tsconfig.json b/tests/cases/projects/NodeModulesSearch/maxDepthExceeded/tsconfig.json index 52633bb5a98..b2ee28482ba 100644 --- a/tests/cases/projects/NodeModulesSearch/maxDepthExceeded/tsconfig.json +++ b/tests/cases/projects/NodeModulesSearch/maxDepthExceeded/tsconfig.json @@ -4,6 +4,6 @@ "maxNodeModuleJsDepth": 1, // Note: Module m1 is already included as a root file "outDir": "built" }, - "include": ["**/*"], + "include": ["**/*", "node_modules/**/*"], "exclude": ["node_modules/m2/**/*"] } diff --git a/tests/webTestServer.ts b/tests/webTestServer.ts index abfd71a8fff..20228f9a741 100644 --- a/tests/webTestServer.ts +++ b/tests/webTestServer.ts @@ -125,22 +125,6 @@ function dir(dirPath: string, spec?: string, options?: any) { } } -// fs.rmdirSync won't delete directories with files in it -function deleteFolderRecursive(dirPath: string) { - if (fs.existsSync(dirPath)) { - fs.readdirSync(dirPath).forEach((file) => { - const curPath = path.join(dirPath, file); - if (fs.statSync(curPath).isDirectory()) { // recurse - deleteFolderRecursive(curPath); - } - else { // delete file - fs.unlinkSync(curPath); - } - }); - fs.rmdirSync(dirPath); - } -}; - function writeFile(path: string, data: any) { ensureDirectoriesExist(getDirectoryPath(path)); fs.writeFileSync(path, data); diff --git a/tslint.json b/tslint.json index bcd4dfa2223..de60ad7683a 100644 --- a/tslint.json +++ b/tslint.json @@ -7,6 +7,7 @@ "check-space" ], "curly":[true, "ignore-same-line"], + "debug-assert": true, "indent": [true, "spaces" ],