diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7913520bfdb..10400a3853a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5788,8 +5788,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 { @@ -5901,6 +5900,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; @@ -5972,11 +5975,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; } } @@ -7604,26 +7614,73 @@ namespace ts { 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) { @@ -8017,7 +8074,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; } @@ -8458,7 +8515,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; @@ -8468,7 +8525,7 @@ namespace ts { } if (source.typeParameters) { - source = instantiateSignatureInContextOf(source, target); + source = instantiateSignatureInContextOf(source, target, /*contextualMapper*/ undefined, compareTypes); } let result = Ternary.True; @@ -9614,6 +9671,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) { @@ -10216,13 +10278,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 { @@ -10325,6 +10388,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; @@ -10518,10 +10594,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) { @@ -10653,7 +10733,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; } } @@ -15054,8 +15134,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); diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 728fb433c04..262564813e9 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; 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/parser.ts b/src/compiler/parser.ts index a7d749ee206..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(); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 09f1b5cfcc5..3b57608abc5 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3425,11 +3425,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/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/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/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/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/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/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/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/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/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();