From 396e7d5a9d45f549492af6740c346d1d47f901fc Mon Sep 17 00:00:00 2001 From: rubenpieters Date: Wed, 4 Sep 2019 15:33:17 +0200 Subject: [PATCH 1/2] Add binding of return statement, to check performance impact --- src/compiler/binder.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index e09fa73e3c234..de6987a729909 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -2440,6 +2440,11 @@ namespace ts { bindCallExpression(node); } break; + case SyntaxKind.ReturnStatement: + if (currentFlow) { + node.flowNode = currentFlow; + } + break; // Members of classes, interfaces, and modules case SyntaxKind.ClassExpression: From feb523501936575a6cac59e558fcd4331df515fc Mon Sep 17 00:00:00 2001 From: rubenpieters Date: Wed, 13 Nov 2019 16:21:05 +0100 Subject: [PATCH 2/2] implementation sketch of dependent-type-like functions --- src/compiler/checker.ts | 265 +++++++++++++--- .../reference/depTypeLikeFun.errors.txt | 208 ++++++++++++ tests/baselines/reference/depTypeLikeFun.js | 253 +++++++++++++++ .../reference/depTypeLikeFun.symbols | 286 +++++++++++++++++ .../baselines/reference/depTypeLikeFun.types | 299 ++++++++++++++++++ tests/cases/compiler/depTypeLikeFun.ts | 131 ++++++++ 6 files changed, 1403 insertions(+), 39 deletions(-) create mode 100644 tests/baselines/reference/depTypeLikeFun.errors.txt create mode 100644 tests/baselines/reference/depTypeLikeFun.js create mode 100644 tests/baselines/reference/depTypeLikeFun.symbols create mode 100644 tests/baselines/reference/depTypeLikeFun.types create mode 100644 tests/cases/compiler/depTypeLikeFun.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bb65f5f3782b2..0647bc97095a5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -12086,8 +12086,8 @@ namespace ts { return !!(type.flags & TypeFlags.TypeParameter && (type).isThisType); } - function getSimplifiedType(type: Type, writing: boolean): Type { - return type.flags & TypeFlags.IndexedAccess ? getSimplifiedIndexedAccessType(type, writing) : + function getSimplifiedType(type: Type, writing: boolean, getTypeMapper?: () => TypeParameterContext): Type { + return type.flags & TypeFlags.IndexedAccess ? getSimplifiedIndexedAccessType(type, writing, getTypeMapper) : type.flags & TypeFlags.Conditional ? getSimplifiedConditionalType(type, writing) : type; } @@ -12114,16 +12114,33 @@ namespace ts { // Transform an indexed access to a simpler form, if possible. Return the simpler form, or return // the type itself if no transformation is possible. The writing flag indicates that the type is // the target of an assignment. - function getSimplifiedIndexedAccessType(type: IndexedAccessType, writing: boolean): Type { + function getSimplifiedIndexedAccessType(type: IndexedAccessType, writing: boolean, getTypeMapper?: () => TypeParameterContext): Type { + // We recursively simplify the object type as it may in turn be an indexed access type. For example, with + // '{ [P in T]: { [Q in U]: number } }[T][U]' we want to first simplify the inner indexed access type. + const objectType = getSimplifiedType(type.objectType, writing, getTypeMapper); + const indexType = getSimplifiedType(type.indexType, writing, getTypeMapper); + + // instantiate with type mapper, when: + // 1. index type is a type parameter + // 2. index type is able to index the given object type + // 3. object type is concrete + if ( + getTypeMapper !== undefined && + indexType.flags & TypeFlags.TypeParameter && + isTypeAssignableTo(indexType, getIndexType(objectType, /*stringsOnly*/ false)) && + objectType.flags & TypeFlags.Object + ) { + const typeMapper = getTypeMapper(); + if (typeMapper !== undefined) { + return instantiateType(type, typeMapper); + } + } + const cache = writing ? "simplifiedForWriting" : "simplifiedForReading"; if (type[cache]) { return type[cache] === circularConstraintType ? type : type[cache]!; } type[cache] = circularConstraintType; - // We recursively simplify the object type as it may in turn be an indexed access type. For example, with - // '{ [P in T]: { [Q in U]: number } }[T][U]' we want to first simplify the inner indexed access type. - const objectType = getSimplifiedType(type.objectType, writing); - const indexType = getSimplifiedType(type.indexType, writing); // T[A | B] -> T[A] | T[B] (reading) // T[A | B] -> T[A] & T[B] (writing) const distributedOverIndex = distributeObjectOverIndexType(objectType, indexType, writing); @@ -13557,8 +13574,8 @@ namespace ts { * Like `checkTypeAssignableTo`, but if it would issue an error, instead performs structural comparisons of the types using the given expression node to * attempt to issue more specific errors on, for example, specific object literal properties or tuple members. */ - function checkTypeAssignableToAndOptionallyElaborate(source: Type, target: Type, errorNode: Node | undefined, expr: Expression | undefined, headMessage?: DiagnosticMessage, containingMessageChain?: () => DiagnosticMessageChain | undefined): boolean { - return checkTypeRelatedToAndOptionallyElaborate(source, target, assignableRelation, errorNode, expr, headMessage, containingMessageChain, /*errorOutputContainer*/ undefined); + function checkTypeAssignableToAndOptionallyElaborate(source: Type, target: Type, errorNode: Node | undefined, expr: Expression | undefined, headMessage?: DiagnosticMessage, containingMessageChain?: () => DiagnosticMessageChain | undefined, getTypeMapper?: () => TypeParameterContext): boolean { + return checkTypeRelatedToAndOptionallyElaborate(source, target, assignableRelation, errorNode, expr, headMessage, containingMessageChain, /*errorOutputContainer*/ undefined, getTypeMapper); } function checkTypeRelatedToAndOptionallyElaborate( @@ -13569,11 +13586,12 @@ namespace ts { expr: Expression | undefined, headMessage: DiagnosticMessage | undefined, containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, - errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } | undefined + errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } | undefined, + getTypeMapper?: () => TypeParameterContext, ): boolean { - if (isTypeRelatedTo(source, target, relation)) return true; + if (isTypeRelatedTo(source, target, relation, getTypeMapper)) return true; if (!errorNode || !elaborateError(expr, source, target, relation, headMessage, containingMessageChain, errorOutputContainer)) { - return checkTypeRelatedTo(source, target, relation, errorNode, headMessage, containingMessageChain, errorOutputContainer); + return checkTypeRelatedTo(source, target, relation, errorNode, headMessage, containingMessageChain, errorOutputContainer, getTypeMapper); } return false; } @@ -14285,7 +14303,7 @@ namespace ts { return false; } - function isTypeRelatedTo(source: Type, target: Type, relation: Map) { + function isTypeRelatedTo(source: Type, target: Type, relation: Map, getTypeMapper?: () => TypeParameterContext) { if (isFreshLiteralType(source)) { source = (source).regularType; } @@ -14304,7 +14322,7 @@ namespace ts { } } if (source.flags & TypeFlags.StructuredOrInstantiable || target.flags & TypeFlags.StructuredOrInstantiable) { - return checkTypeRelatedTo(source, target, relation, /*errorNode*/ undefined); + return checkTypeRelatedTo(source, target, relation, /*errorNode*/ undefined, /*headMessage*/ undefined, /*containingMessageChain*/ undefined, /*errorOutputContainer*/ undefined, getTypeMapper); } return false; } @@ -14313,11 +14331,11 @@ namespace ts { return getObjectFlags(source) & ObjectFlags.JsxAttributes && !isUnhyphenatedJsxName(sourceProp.escapedName); } - function getNormalizedType(type: Type, writing: boolean): Type { + function getNormalizedType(type: Type, writing: boolean, getTypeMapper?: () => TypeParameterContext): Type { return isFreshLiteralType(type) ? (type).regularType : getObjectFlags(type) & ObjectFlags.Reference && (type).node ? createTypeReference((type).target, getTypeArguments(type)) : type.flags & TypeFlags.Substitution ? writing ? (type).typeVariable : (type).substitute : - type.flags & TypeFlags.Simplifiable ? getSimplifiedType(type, writing) : + type.flags & TypeFlags.Simplifiable ? getSimplifiedType(type, writing, getTypeMapper) : type; } @@ -14340,6 +14358,7 @@ namespace ts { headMessage?: DiagnosticMessage, containingMessageChain?: () => DiagnosticMessageChain | undefined, errorOutputContainer?: { errors?: Diagnostic[], skipLogging?: boolean }, + getTypeMapper?: () => TypeParameterContext, ): boolean { let errorInfo: DiagnosticMessageChain | undefined; let relatedInfo: [DiagnosticRelatedInformation, ...DiagnosticRelatedInformation[]] | undefined; @@ -14356,7 +14375,7 @@ namespace ts { Debug.assert(relation !== identityRelation || !errorNode, "no error reporting in identity checking"); - const result = isRelatedTo(source, target, /*reportErrors*/ !!errorNode, headMessage); + const result = isRelatedTo(source, target, /*reportErrors*/ !!errorNode, headMessage, /*isApparentIntersectionConstituent*/ undefined, getTypeMapper); if (incompatibleStack.length) { reportIncompatibleStack(); } @@ -14380,7 +14399,7 @@ namespace ts { if (headMessage && errorNode && !result && source.symbol) { const links = getSymbolLinks(source.symbol); if (links.originatingImport && !isImportCall(links.originatingImport)) { - const helpfulRetry = checkTypeRelatedTo(getTypeOfSymbol(links.target!), target, relation, /*errorNode*/ undefined); + const helpfulRetry = checkTypeRelatedTo(getTypeOfSymbol(links.target!), target, relation, /*errorNode*/ undefined, /*headMessage*/ undefined, /*containingMessageChain*/ undefined, /*errorOutputContainer*/ undefined, getTypeMapper); if (helpfulRetry) { // Likely an incorrect import. Issue a helpful diagnostic to produce a quickfix to change the import const diag = createDiagnosticForNode(links.originatingImport, Diagnostics.Type_originates_at_this_import_A_namespace_style_import_cannot_be_called_or_constructed_and_will_cause_a_failure_at_runtime_Consider_using_a_default_import_or_import_require_here_instead); @@ -14626,13 +14645,13 @@ namespace ts { * * Ternary.Maybe if they are related with assumptions of other relationships, or * * Ternary.False if they are not related. */ - function isRelatedTo(originalSource: Type, originalTarget: Type, reportErrors = false, headMessage?: DiagnosticMessage, isApparentIntersectionConstituent?: boolean): Ternary { + function isRelatedTo(originalSource: Type, originalTarget: Type, reportErrors = false, headMessage?: DiagnosticMessage, isApparentIntersectionConstituent?: boolean, getTypeMapper?: () => TypeParameterContext): Ternary { // Normalize the source and target types: Turn fresh literal types into regular literal types, // turn deferred type references into regular type references, simplify indexed access and // conditional types, and resolve substitution types to either the substitution (on the source // side) or the type variable (on the target side). - let source = getNormalizedType(originalSource, /*writing*/ false); - let target = getNormalizedType(originalTarget, /*writing*/ true); + let source = getNormalizedType(originalSource, /*writing*/ false, getTypeMapper); + let target = getNormalizedType(originalTarget, /*writing*/ true, getTypeMapper); // Try to see if we're relating something like `Foo` -> `Bar | null | undefined`. // If so, reporting the `null` and `undefined` in the type is hardly useful. @@ -14734,7 +14753,7 @@ namespace ts { result = someTypeRelatedToType(source, target, /*reportErrors*/ false); } if (!result && (source.flags & TypeFlags.StructuredOrInstantiable || target.flags & TypeFlags.StructuredOrInstantiable)) { - if (result = recursiveTypeRelatedTo(source, target, reportErrors, isIntersectionConstituent)) { + if (result = recursiveTypeRelatedTo(source, target, reportErrors, isIntersectionConstituent, getTypeMapper)) { resetErrorInfo(saveErrorInfo); } } @@ -15134,7 +15153,7 @@ namespace ts { // Third, check if both types are part of deeply nested chains of generic type instantiations and if so assume the types are // equal and infinitely expanding. Fourth, if we have reached a depth of 100 nested comparisons, assume we have runaway recursion // and issue an error. Otherwise, actually compare the structure of the two types. - function recursiveTypeRelatedTo(source: Type, target: Type, reportErrors: boolean, isIntersectionConstituent: boolean): Ternary { + function recursiveTypeRelatedTo(source: Type, target: Type, reportErrors: boolean, isIntersectionConstituent: boolean, getTypeMapper?: () => TypeParameterContext): Ternary { if (overflow) { return Ternary.False; } @@ -15194,7 +15213,7 @@ namespace ts { return originalHandler!(onlyUnreliable); }; } - const result = expandingFlags !== ExpandingFlags.Both ? structuredTypeRelatedTo(source, target, reportErrors, isIntersectionConstituent) : Ternary.Maybe; + const result = expandingFlags !== ExpandingFlags.Both ? structuredTypeRelatedTo(source, target, reportErrors, isIntersectionConstituent, getTypeMapper) : Ternary.Maybe; if (outofbandVarianceMarkerHandler) { outofbandVarianceMarkerHandler = originalHandler; } @@ -15218,7 +15237,7 @@ namespace ts { return result; } - function structuredTypeRelatedTo(source: Type, target: Type, reportErrors: boolean, isIntersectionConstituent: boolean): Ternary { + function structuredTypeRelatedTo(source: Type, target: Type, reportErrors: boolean, isIntersectionConstituent: boolean, getTypeMapper?: () => TypeParameterContext): Ternary { const flags = source.flags & target.flags; if (relation === identityRelation && !(flags & TypeFlags.Object)) { if (flags & TypeFlags.Index) { @@ -15487,7 +15506,7 @@ namespace ts { if (source.flags & (TypeFlags.Object | TypeFlags.Intersection) && target.flags & TypeFlags.Object) { // Report structural errors only if we haven't reported any errors yet const reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo.errorInfo && !sourceIsPrimitive; - result = propertiesRelatedTo(source, target, reportStructuralErrors, /*excludedProperties*/ undefined, isIntersectionConstituent); + result = propertiesRelatedTo(source, target, reportStructuralErrors, /*excludedProperties*/ undefined, isIntersectionConstituent, getTypeMapper); if (result) { result &= signaturesRelatedTo(source, target, SignatureKind.Call, reportStructuralErrors); if (result) { @@ -15671,7 +15690,7 @@ namespace ts { // Compare the remaining non-discriminant properties of each match. let result = Ternary.True; for (const type of matchingTypes) { - result &= propertiesRelatedTo(source, type, /*reportErrors*/ false, excludedProperties, /*isIntersectionConstituent*/ false); + result &= propertiesRelatedTo(source, type, /*reportErrors*/ false, excludedProperties, /*isIntersectionConstituent*/ false, getTypeMapper); if (result) { result &= signaturesRelatedTo(source, type, SignatureKind.Call, /*reportStructuralErrors*/ false); if (result) { @@ -15707,7 +15726,7 @@ namespace ts { return result || properties; } - function isPropertySymbolTypeRelated(sourceProp: Symbol, targetProp: Symbol, getTypeOfSourceProperty: (sym: Symbol) => Type, reportErrors: boolean, isIntersectionConstituent: boolean): Ternary { + function isPropertySymbolTypeRelated(sourceProp: Symbol, targetProp: Symbol, getTypeOfSourceProperty: (sym: Symbol) => Type, reportErrors: boolean, isIntersectionConstituent: boolean, getTypeMapper?: () => TypeParameterContext): Ternary { const targetIsOptional = strictNullChecks && !!(getCheckFlags(targetProp) & CheckFlags.Partial); const source = getTypeOfSourceProperty(sourceProp); if (getCheckFlags(targetProp) & CheckFlags.DeferredType && !getSymbolLinks(targetProp).type) { @@ -15719,11 +15738,11 @@ namespace ts { let result = unionParent ? Ternary.False : Ternary.True; const targetTypes = links.deferralConstituents!; for (const targetType of targetTypes) { - const related = isRelatedTo(source, targetType, /*reportErrors*/ false, /*headMessage*/ undefined, /*isIntersectionConstituent*/ !unionParent); + const related = isRelatedTo(source, targetType, /*reportErrors*/ false, /*headMessage*/ undefined, /*isIntersectionConstituent*/ !unionParent, getTypeMapper); if (!unionParent) { if (!related) { // Can't assign to a target individually - have to fallback to assigning to the _whole_ intersection (which forces normalization) - return isRelatedTo(source, addOptionality(getTypeOfSymbol(targetProp), targetIsOptional), reportErrors); + return isRelatedTo(source, addOptionality(getTypeOfSymbol(targetProp), targetIsOptional), reportErrors, /*headMessage*/ undefined, /*isApparentIntersectionConstituent*/ undefined, getTypeMapper); } result &= related; } @@ -15734,23 +15753,23 @@ namespace ts { } } if (unionParent && !result && targetIsOptional) { - result = isRelatedTo(source, undefinedType); + result = isRelatedTo(source, undefinedType, /*reportErrors*/ undefined, /*headMessage*/ undefined, /*isApparentIntersectionConstituent*/ undefined, getTypeMapper); } if (unionParent && !result && reportErrors) { // The easiest way to get the right errors here is to un-defer (which may be costly) // If it turns out this is too costly too often, we can replicate the error handling logic within // typeRelatedToSomeType without the discriminatable type branch (as that requires a manifest union // type on which to hand discriminable properties, which we are expressly trying to avoid here) - return isRelatedTo(source, addOptionality(getTypeOfSymbol(targetProp), targetIsOptional), reportErrors); + return isRelatedTo(source, addOptionality(getTypeOfSymbol(targetProp), targetIsOptional), reportErrors, /*headMessage*/ undefined, /*isApparentIntersectionConstituent*/ undefined, getTypeMapper); } return result; } else { - return isRelatedTo(source, addOptionality(getTypeOfSymbol(targetProp), targetIsOptional), reportErrors, /*headMessage*/ undefined, isIntersectionConstituent); + return isRelatedTo(source, addOptionality(getTypeOfSymbol(targetProp), targetIsOptional), reportErrors, /*headMessage*/ undefined, isIntersectionConstituent, getTypeMapper); } } - function propertyRelatedTo(source: Type, target: Type, sourceProp: Symbol, targetProp: Symbol, getTypeOfSourceProperty: (sym: Symbol) => Type, reportErrors: boolean, isIntersectionConstituent: boolean): Ternary { + function propertyRelatedTo(source: Type, target: Type, sourceProp: Symbol, targetProp: Symbol, getTypeOfSourceProperty: (sym: Symbol) => Type, reportErrors: boolean, isIntersectionConstituent: boolean, getTypeMapper?: () => TypeParameterContext): Ternary { const sourcePropFlags = getDeclarationModifierFlagsFromSymbol(sourceProp); const targetPropFlags = getDeclarationModifierFlagsFromSymbol(targetProp); if (sourcePropFlags & ModifierFlags.Private || targetPropFlags & ModifierFlags.Private) { @@ -15792,7 +15811,7 @@ namespace ts { return Ternary.False; } // If the target comes from a partial union prop, allow `undefined` in the target type - const related = isPropertySymbolTypeRelated(sourceProp, targetProp, getTypeOfSourceProperty, reportErrors, isIntersectionConstituent); + const related = isPropertySymbolTypeRelated(sourceProp, targetProp, getTypeOfSourceProperty, reportErrors, isIntersectionConstituent, getTypeMapper); if (!related) { if (reportErrors) { reportIncompatibleError(Diagnostics.Types_of_property_0_are_incompatible, symbolToString(targetProp)); @@ -15817,7 +15836,7 @@ namespace ts { return related; } - function propertiesRelatedTo(source: Type, target: Type, reportErrors: boolean, excludedProperties: UnderscoreEscapedMap | undefined, isIntersectionConstituent: boolean): Ternary { + function propertiesRelatedTo(source: Type, target: Type, reportErrors: boolean, excludedProperties: UnderscoreEscapedMap | undefined, isIntersectionConstituent: boolean, getTypeMapper?: () => TypeParameterContext): Ternary { if (relation === identityRelation) { return propertiesIdenticalTo(source, target, excludedProperties); } @@ -15907,7 +15926,7 @@ namespace ts { if (!(targetProp.flags & SymbolFlags.Prototype) && (!numericNamesOnly || isNumericLiteralName(name) || name === "length")) { const sourceProp = getPropertyOfType(source, name); if (sourceProp && sourceProp !== targetProp) { - const related = propertyRelatedTo(source, target, sourceProp, targetProp, getTypeOfSymbol, reportErrors, isIntersectionConstituent); + const related = propertyRelatedTo(source, target, sourceProp, targetProp, getTypeOfSymbol, reportErrors, isIntersectionConstituent, getTypeMapper); if (!related) { return Ternary.False; } @@ -16177,6 +16196,174 @@ namespace ts { } } + type TypeParameterContext = TypeMapper | undefined; + + // utilize cached context type mapper, otherwise construct it + function createConstructTypeMapper(node: Node): () => TypeParameterContext { + // cache the context + let mapper: TypeParameterContext; + return () => { + if (mapper !== undefined) { + return mapper; + } + else { + return constructTypeMapper(node); + } + }; + } + + // construct the context type mapper + function constructTypeMapper(node: Node): TypeMapper { + const func = getContainingFunction(node); + const flowNode = node.flowNode; + if (func === undefined || flowNode === undefined) { + return identityMapper; + } + const signature = getSignatureFromDeclaration(func); + const typeParameters = signature.typeParameters; + if (typeParameters !== undefined) { + return lowerBoundTypeMapper(flowNode, typeParameters); + } + else { + return identityMapper; + } + } + + // construct type mapper which replaces the type parameters by their lower bounds, if it is not bot + function lowerBoundTypeMapper(flow: FlowNode, typeParams: readonly TypeParameter[]): TypeMapper { + const lits = getCheckedWithLiterals(flow, typeParams); + const newTypeParams: TypeParameter[] = []; + const lowerBounds: Type[] = []; + for (let i = 0; i < typeParams.length; i++) { + const lb = findLowerBound(typeParams[i], lits[i]); + if (lb !== "bot") { + newTypeParams.push(typeParams[i]); + lowerBounds.push(lb); + } + } + return createTypeMapper(newTypeParams, lowerBounds); + } + + // returns the lower bound of the type, given the checked with literals + // a lower bound of 'bot' means that the type parameter has no lower bound + function findLowerBound(typeParam: TypeParameter, checkedWithLiterals: { t: Type[], f: Type[] }): Type | "bot" { + // return bot if no relevant comparisons were found + if (checkedWithLiterals.t.length === 0 && checkedWithLiterals.f.length === 0) { + return "bot"; + } + const constraint = getConstraintOfTypeParameter(typeParam); + // constraint must be defined as a union + if (constraint === undefined || ! (constraint.flags & TypeFlags.Union)) { + return "bot"; + } + // constraint must be a union of singleton types + const unionConstraint = constraint; + const unionComponents = unionConstraint.types; + if (! unionComponents.reduce((acc, t) => acc && isSingletonType(t), true)) { + return "bot"; + } + + const unionT = getUnionType(checkedWithLiterals.t); + const unionF = getUnionType(checkedWithLiterals.f); + // keep only literals from true branch checks + const literalsInT = checkedWithLiterals.t.length === 0 ? + unionConstraint : + filterType(unionConstraint, (t: Type) => { + return isTypeSubtypeOf(t, unionT); + }); + // remove literals from false branch checks + const union = filterType(literalsInT, (t: Type) => { + return ! isTypeSubtypeOf(t, unionF); + }); + return union; + } + + function isSingletonType(type: Type): boolean { + return type.flags & TypeFlags.EnumLiteral ? true : + type.flags & TypeFlags.StringLiteral ? true : + type.flags & TypeFlags.NumberLiteral ? true : + type.flags & TypeFlags.BigIntLiteral ? true : + type.flags & TypeFlags.BooleanLiteral ? true : + false; + } + + // traverses flow graph to obtain all checked-with string literals + // for each of the requested type parameters + // key 't' contains all checked-with literals in true branches + // key 'f' contains all checked-with literals in false branches + // returns an entry in the list for each of the given type parameters + function getCheckedWithLiterals(flow: FlowNode, typeParams: readonly TypeParameter[]): { t: Type[], f: Type[] }[] { + if (typeParams.length === 0) { + return []; + } + let result: { t: Type[], f: Type[] }[] = []; + for (let i = 0; i <= typeParams.length; i++) { + result.push({ t: [], f: [] }); + } + + if (flow.flags & FlowFlags.Condition) { + const flowCond = flow; + + // fetch results of antecedent statement recursively + const antecedentChecks = getCheckedWithLiterals(flowCond.antecedent, typeParams); + result = antecedentChecks; + + const expr = flowCond.node; + const trueBranch = !!(flowCond.flags & FlowFlags.TrueCondition); + if (expr.kind === SyntaxKind.BinaryExpression) { + const binExpr = expr; + const opKind = binExpr.operatorToken.kind; + if (opKind === SyntaxKind.EqualsEqualsEqualsToken) { + const left = getReferenceCandidate(binExpr.left); + const right = getReferenceCandidate(binExpr.right); + + let i = 0; + for (const typeParam of typeParams) { + if ( + isStringLiteralLike(left) && + exprAnnotatedWithTypeParam(left, typeParam) + ) { + const litVal = (left).text; + result[i][trueBranch ? "t" : "f"].push(getLiteralType(litVal)); + } + if ( + isStringLiteralLike(right) && + exprAnnotatedWithTypeParam(left, typeParam) + ) { + const litVal = (right).text; + result[i][trueBranch ? "t" : "f"].push(getLiteralType(litVal)); + } + i++; + } + } + } + return result; + } + else if (flow.flags & FlowFlags.Assignment) { + return getCheckedWithLiterals((flow).antecedent, typeParams); + } + else if (flow.flags & FlowFlags.ArrayMutation) { + return getCheckedWithLiterals((flow).antecedent, typeParams); + } + // TODO: add other cases + else { + return result; + } + } + + // check whether the annotated type of an expression is exactly the given type parameter + function exprAnnotatedWithTypeParam(expr: Expression, typeParam: TypeParameter): boolean { + switch (expr.kind) { + case SyntaxKind.Identifier: + return getTypeOfSymbol(getResolvedSymbol(expr)) === typeParam; + case SyntaxKind.NonNullExpression: + case SyntaxKind.ParenthesizedExpression: + return exprAnnotatedWithTypeParam((expr).expression, typeParam); + default: + return false; + } + } + function discriminateTypeByDiscriminableItems(target: UnionType, discriminators: [() => Type, __String][], related: (source: Type, target: Type) => boolean | Ternary): Type | undefined; function discriminateTypeByDiscriminableItems(target: UnionType, discriminators: [() => Type, __String][], related: (source: Type, target: Type) => boolean | Ternary, defaultValue: Type): Type; function discriminateTypeByDiscriminableItems(target: UnionType, discriminators: [() => Type, __String][], related: (source: Type, target: Type) => boolean | Ternary, defaultValue?: Type) { @@ -31241,7 +31428,7 @@ namespace ts { } } else if (func.kind === SyntaxKind.Constructor) { - if (node.expression && !checkTypeAssignableToAndOptionallyElaborate(exprType, returnType, node, node.expression)) { + if (node.expression && !checkTypeAssignableToAndOptionallyElaborate(exprType, returnType, node, node.expression, /*headMessage*/ undefined, /*containingMessageChain*/ undefined, createConstructTypeMapper(node))) { error(node, Diagnostics.Return_type_of_constructor_signature_must_be_assignable_to_the_instance_type_of_the_class); } } @@ -31254,7 +31441,7 @@ namespace ts { // If the function has a return type, but promisedType is // undefined, an error will be reported in checkAsyncFunctionReturnType // so we don't need to report one here. - checkTypeAssignableToAndOptionallyElaborate(unwrappedExprType, unwrappedReturnType, node, node.expression); + checkTypeAssignableToAndOptionallyElaborate(unwrappedExprType, unwrappedReturnType, node, node.expression, /*headMessage*/ undefined, /*containingMessageChain*/ undefined, createConstructTypeMapper(node)); } } } diff --git a/tests/baselines/reference/depTypeLikeFun.errors.txt b/tests/baselines/reference/depTypeLikeFun.errors.txt new file mode 100644 index 0000000000000..e0dc9ad5eb639 --- /dev/null +++ b/tests/baselines/reference/depTypeLikeFun.errors.txt @@ -0,0 +1,208 @@ +tests/cases/compiler/depTypeLikeFun.ts(30,9): error TS2322: Type 'true' is not assignable to type 'number'. +tests/cases/compiler/depTypeLikeFun.ts(33,9): error TS2322: Type '1' is not assignable to type 'boolean'. +tests/cases/compiler/depTypeLikeFun.ts(55,9): error TS2322: Type '1' is not assignable to type 'F[X]'. + Type '1' is not assignable to type 'never'. +tests/cases/compiler/depTypeLikeFun.ts(58,9): error TS2322: Type 'true' is not assignable to type 'F[X]'. + Type 'true' is not assignable to type 'never'. +tests/cases/compiler/depTypeLikeFun.ts(65,15): error TS2322: Type 'F[T]' is not assignable to type 'number'. + Type 'number | boolean' is not assignable to type 'number'. + Type 'false' is not assignable to type 'number'. +tests/cases/compiler/depTypeLikeFun.ts(78,9): error TS2322: Type '2' is not assignable to type 'F[T]'. + Type '2' is not assignable to type 'never'. +tests/cases/compiler/depTypeLikeFun.ts(91,11): error TS2322: Type '1' is not assignable to type 'F[X]'. + Type '1' is not assignable to type 'never'. +tests/cases/compiler/depTypeLikeFun.ts(94,11): error TS2322: Type 'true' is not assignable to type 'F[X]'. + Type 'true' is not assignable to type 'never'. +tests/cases/compiler/depTypeLikeFun.ts(112,7): error TS2322: Type '{ a: true; b: 1; }' is not assignable to type 'Complex'. + Types of property 'a' are incompatible. + Type 'true' is not assignable to type 'number'. +tests/cases/compiler/depTypeLikeFun.ts(115,7): error TS2322: Type '{ a: 1; b: true; }' is not assignable to type 'Complex'. + Types of property 'a' are incompatible. + Type '1' is not assignable to type 'boolean'. +tests/cases/compiler/depTypeLikeFun.ts(122,5): error TS2322: Type '(ft: number) => 1' is not assignable to type '(ft: F[T]) => F[T]'. + Types of parameters 'ft' and 'ft' are incompatible. + Type 'F[T]' is not assignable to type 'number'. + Type 'number | boolean' is not assignable to type 'number'. + Type 'false' is not assignable to type 'number'. +tests/cases/compiler/depTypeLikeFun.ts(127,5): error TS2322: Type '(ft: F[T]) => true' is not assignable to type '(ft: F[T]) => F[T]'. + Type 'true' is not assignable to type 'F[T]'. + Type 'true' is not assignable to type 'never'. + + +==== tests/cases/compiler/depTypeLikeFun.ts (12 errors) ==== + type F = { + t: number, + f: boolean, + } + + type G = { + a: number, + b: boolean, + c: string, + } + + type Complex = { + a: { t: number, f: boolean }[X], + b: { t: boolean, f: number }[X], + } + + function f1(x: X): F[X] { + if (x === "t") { + // no error + return 1; + } else { + // no error + return true; + } + } + + function f2(x: X): F[X] { + if (x === "t") { + // error + return true; + ~~~~~~~~~~~~ +!!! error TS2322: Type 'true' is not assignable to type 'number'. + } else { + // error + return 1; + ~~~~~~~~~ +!!! error TS2322: Type '1' is not assignable to type 'boolean'. + } + } + + function f3(x: X): G[X] { + if (x === "a") { + // no error + return 1; + } else { + if (x === "b") { + // no error + return true; + } else { + // no error + return "z"; + } + } + } + + function f4(x: X, y: Y): F[X] { + if (y === "t") { + // error + return 1; + ~~~~~~~~~ +!!! error TS2322: Type '1' is not assignable to type 'F[X]'. +!!! error TS2322: Type '1' is not assignable to type 'never'. + } else { + // error + return true; + ~~~~~~~~~~~~ +!!! error TS2322: Type 'true' is not assignable to type 'F[X]'. +!!! error TS2322: Type 'true' is not assignable to type 'never'. + } + } + + function f5(str: T, ft: F[T]): F[T] { + if (str === "t") { + // error + const n: number = ft; + ~ +!!! error TS2322: Type 'F[T]' is not assignable to type 'number'. +!!! error TS2322: Type 'number | boolean' is not assignable to type 'number'. +!!! error TS2322: Type 'false' is not assignable to type 'number'. + // no error + return 1; + } else { + // no error + return true; + } + } + + declare var obj: F; + function f6(str: T, str2: T): F[T] { + if (str === "t") { + // error + obj[str2] = 2; + ~~~~~~~~~ +!!! error TS2322: Type '2' is not assignable to type 'F[T]'. +!!! error TS2322: Type '2' is not assignable to type 'never'. + // no error + return 1; + } else { + // no error + return true; + } + } + + class C7 { + f7(x: X): F[X] { + if (x === "t") { + // error + return 1; + ~~~~~~~~~ +!!! error TS2322: Type '1' is not assignable to type 'F[X]'. +!!! error TS2322: Type '1' is not assignable to type 'never'. + } else { + // error + return true; + ~~~~~~~~~~~~ +!!! error TS2322: Type 'true' is not assignable to type 'F[X]'. +!!! error TS2322: Type 'true' is not assignable to type 'never'. + } + } + } + + function f8(x: X): Complex { + if (x === "t") { + // no error + return { a: 1, b: true }; + } else { + // no error + return { a: true, b: 1 }; + } + } + + function f9(x: X): Complex { + if (x === "t") { + // error + return { a: true, b: 1 }; + ~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type '{ a: true; b: 1; }' is not assignable to type 'Complex'. +!!! error TS2322: Types of property 'a' are incompatible. +!!! error TS2322: Type 'true' is not assignable to type 'number'. + } else { + // error + return { a: 1, b: true }; + ~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type '{ a: 1; b: true; }' is not assignable to type 'Complex'. +!!! error TS2322: Types of property 'a' are incompatible. +!!! error TS2322: Type '1' is not assignable to type 'boolean'. + } + } + + function f10(str: T): (ft: F[T]) => F[T] { + if (str === "t") { + // error + return (ft: number) => { + ~~~~~~~~~~~~~~~~~~~~~~~~ + return 1; + ~~~~~~~~~~~~~~~~~ + }; + ~~~~~~ +!!! error TS2322: Type '(ft: number) => 1' is not assignable to type '(ft: F[T]) => F[T]'. +!!! error TS2322: Types of parameters 'ft' and 'ft' are incompatible. +!!! error TS2322: Type 'F[T]' is not assignable to type 'number'. +!!! error TS2322: Type 'number | boolean' is not assignable to type 'number'. +!!! error TS2322: Type 'false' is not assignable to type 'number'. + } else { + // error + return (ft: F[T]) => { + ~~~~~~~~~~~~~~~~~~~~~~ + return true; + ~~~~~~~~~~~~~~~~~~~~ + }; + ~~~~~~ +!!! error TS2322: Type '(ft: F[T]) => true' is not assignable to type '(ft: F[T]) => F[T]'. +!!! error TS2322: Type 'true' is not assignable to type 'F[T]'. +!!! error TS2322: Type 'true' is not assignable to type 'never'. + } + } \ No newline at end of file diff --git a/tests/baselines/reference/depTypeLikeFun.js b/tests/baselines/reference/depTypeLikeFun.js new file mode 100644 index 0000000000000..c4ee26f49ba78 --- /dev/null +++ b/tests/baselines/reference/depTypeLikeFun.js @@ -0,0 +1,253 @@ +//// [depTypeLikeFun.ts] +type F = { + t: number, + f: boolean, +} + +type G = { + a: number, + b: boolean, + c: string, +} + +type Complex = { + a: { t: number, f: boolean }[X], + b: { t: boolean, f: number }[X], +} + +function f1(x: X): F[X] { + if (x === "t") { + // no error + return 1; + } else { + // no error + return true; + } +} + +function f2(x: X): F[X] { + if (x === "t") { + // error + return true; + } else { + // error + return 1; + } +} + +function f3(x: X): G[X] { + if (x === "a") { + // no error + return 1; + } else { + if (x === "b") { + // no error + return true; + } else { + // no error + return "z"; + } + } +} + +function f4(x: X, y: Y): F[X] { + if (y === "t") { + // error + return 1; + } else { + // error + return true; + } +} + +function f5(str: T, ft: F[T]): F[T] { + if (str === "t") { + // error + const n: number = ft; + // no error + return 1; + } else { + // no error + return true; + } +} + +declare var obj: F; +function f6(str: T, str2: T): F[T] { + if (str === "t") { + // error + obj[str2] = 2; + // no error + return 1; + } else { + // no error + return true; + } +} + +class C7 { + f7(x: X): F[X] { + if (x === "t") { + // error + return 1; + } else { + // error + return true; + } + } +} + +function f8(x: X): Complex { + if (x === "t") { + // no error + return { a: 1, b: true }; + } else { + // no error + return { a: true, b: 1 }; + } +} + +function f9(x: X): Complex { + if (x === "t") { + // error + return { a: true, b: 1 }; + } else { + // error + return { a: 1, b: true }; + } +} + +function f10(str: T): (ft: F[T]) => F[T] { + if (str === "t") { + // error + return (ft: number) => { + return 1; + }; + } else { + // error + return (ft: F[T]) => { + return true; + }; + } +} + +//// [depTypeLikeFun.js] +function f1(x) { + if (x === "t") { + // no error + return 1; + } + else { + // no error + return true; + } +} +function f2(x) { + if (x === "t") { + // error + return true; + } + else { + // error + return 1; + } +} +function f3(x) { + if (x === "a") { + // no error + return 1; + } + else { + if (x === "b") { + // no error + return true; + } + else { + // no error + return "z"; + } + } +} +function f4(x, y) { + if (y === "t") { + // error + return 1; + } + else { + // error + return true; + } +} +function f5(str, ft) { + if (str === "t") { + // error + var n = ft; + // no error + return 1; + } + else { + // no error + return true; + } +} +function f6(str, str2) { + if (str === "t") { + // error + obj[str2] = 2; + // no error + return 1; + } + else { + // no error + return true; + } +} +var C7 = /** @class */ (function () { + function C7() { + } + C7.prototype.f7 = function (x) { + if (x === "t") { + // error + return 1; + } + else { + // error + return true; + } + }; + return C7; +}()); +function f8(x) { + if (x === "t") { + // no error + return { a: 1, b: true }; + } + else { + // no error + return { a: true, b: 1 }; + } +} +function f9(x) { + if (x === "t") { + // error + return { a: true, b: 1 }; + } + else { + // error + return { a: 1, b: true }; + } +} +function f10(str) { + if (str === "t") { + // error + return function (ft) { + return 1; + }; + } + else { + // error + return function (ft) { + return true; + }; + } +} diff --git a/tests/baselines/reference/depTypeLikeFun.symbols b/tests/baselines/reference/depTypeLikeFun.symbols new file mode 100644 index 0000000000000..89d03655ee013 --- /dev/null +++ b/tests/baselines/reference/depTypeLikeFun.symbols @@ -0,0 +1,286 @@ +=== tests/cases/compiler/depTypeLikeFun.ts === +type F = { +>F : Symbol(F, Decl(depTypeLikeFun.ts, 0, 0)) + + t: number, +>t : Symbol(t, Decl(depTypeLikeFun.ts, 0, 10)) + + f: boolean, +>f : Symbol(f, Decl(depTypeLikeFun.ts, 1, 12)) +} + +type G = { +>G : Symbol(G, Decl(depTypeLikeFun.ts, 3, 1)) + + a: number, +>a : Symbol(a, Decl(depTypeLikeFun.ts, 5, 10)) + + b: boolean, +>b : Symbol(b, Decl(depTypeLikeFun.ts, 6, 12)) + + c: string, +>c : Symbol(c, Decl(depTypeLikeFun.ts, 7, 13)) +} + +type Complex = { +>Complex : Symbol(Complex, Decl(depTypeLikeFun.ts, 9, 1)) +>X : Symbol(X, Decl(depTypeLikeFun.ts, 11, 13)) + + a: { t: number, f: boolean }[X], +>a : Symbol(a, Decl(depTypeLikeFun.ts, 11, 37)) +>t : Symbol(t, Decl(depTypeLikeFun.ts, 12, 8)) +>f : Symbol(f, Decl(depTypeLikeFun.ts, 12, 19)) +>X : Symbol(X, Decl(depTypeLikeFun.ts, 11, 13)) + + b: { t: boolean, f: number }[X], +>b : Symbol(b, Decl(depTypeLikeFun.ts, 12, 36)) +>t : Symbol(t, Decl(depTypeLikeFun.ts, 13, 8)) +>f : Symbol(f, Decl(depTypeLikeFun.ts, 13, 20)) +>X : Symbol(X, Decl(depTypeLikeFun.ts, 11, 13)) +} + +function f1(x: X): F[X] { +>f1 : Symbol(f1, Decl(depTypeLikeFun.ts, 14, 1)) +>X : Symbol(X, Decl(depTypeLikeFun.ts, 16, 12)) +>x : Symbol(x, Decl(depTypeLikeFun.ts, 16, 33)) +>X : Symbol(X, Decl(depTypeLikeFun.ts, 16, 12)) +>F : Symbol(F, Decl(depTypeLikeFun.ts, 0, 0)) +>X : Symbol(X, Decl(depTypeLikeFun.ts, 16, 12)) + + if (x === "t") { +>x : Symbol(x, Decl(depTypeLikeFun.ts, 16, 33)) + + // no error + return 1; + } else { + // no error + return true; + } +} + +function f2(x: X): F[X] { +>f2 : Symbol(f2, Decl(depTypeLikeFun.ts, 24, 1)) +>X : Symbol(X, Decl(depTypeLikeFun.ts, 26, 12)) +>x : Symbol(x, Decl(depTypeLikeFun.ts, 26, 33)) +>X : Symbol(X, Decl(depTypeLikeFun.ts, 26, 12)) +>F : Symbol(F, Decl(depTypeLikeFun.ts, 0, 0)) +>X : Symbol(X, Decl(depTypeLikeFun.ts, 26, 12)) + + if (x === "t") { +>x : Symbol(x, Decl(depTypeLikeFun.ts, 26, 33)) + + // error + return true; + } else { + // error + return 1; + } +} + +function f3(x: X): G[X] { +>f3 : Symbol(f3, Decl(depTypeLikeFun.ts, 34, 1)) +>X : Symbol(X, Decl(depTypeLikeFun.ts, 36, 12)) +>x : Symbol(x, Decl(depTypeLikeFun.ts, 36, 39)) +>X : Symbol(X, Decl(depTypeLikeFun.ts, 36, 12)) +>G : Symbol(G, Decl(depTypeLikeFun.ts, 3, 1)) +>X : Symbol(X, Decl(depTypeLikeFun.ts, 36, 12)) + + if (x === "a") { +>x : Symbol(x, Decl(depTypeLikeFun.ts, 36, 39)) + + // no error + return 1; + } else { + if (x === "b") { +>x : Symbol(x, Decl(depTypeLikeFun.ts, 36, 39)) + + // no error + return true; + } else { + // no error + return "z"; + } + } +} + +function f4(x: X, y: Y): F[X] { +>f4 : Symbol(f4, Decl(depTypeLikeFun.ts, 49, 1)) +>X : Symbol(X, Decl(depTypeLikeFun.ts, 51, 12)) +>Y : Symbol(Y, Decl(depTypeLikeFun.ts, 51, 32)) +>x : Symbol(x, Decl(depTypeLikeFun.ts, 51, 54)) +>X : Symbol(X, Decl(depTypeLikeFun.ts, 51, 12)) +>y : Symbol(y, Decl(depTypeLikeFun.ts, 51, 59)) +>Y : Symbol(Y, Decl(depTypeLikeFun.ts, 51, 32)) +>F : Symbol(F, Decl(depTypeLikeFun.ts, 0, 0)) +>X : Symbol(X, Decl(depTypeLikeFun.ts, 51, 12)) + + if (y === "t") { +>y : Symbol(y, Decl(depTypeLikeFun.ts, 51, 59)) + + // error + return 1; + } else { + // error + return true; + } +} + +function f5(str: T, ft: F[T]): F[T] { +>f5 : Symbol(f5, Decl(depTypeLikeFun.ts, 59, 1)) +>T : Symbol(T, Decl(depTypeLikeFun.ts, 61, 12)) +>str : Symbol(str, Decl(depTypeLikeFun.ts, 61, 33)) +>T : Symbol(T, Decl(depTypeLikeFun.ts, 61, 12)) +>ft : Symbol(ft, Decl(depTypeLikeFun.ts, 61, 40)) +>F : Symbol(F, Decl(depTypeLikeFun.ts, 0, 0)) +>T : Symbol(T, Decl(depTypeLikeFun.ts, 61, 12)) +>F : Symbol(F, Decl(depTypeLikeFun.ts, 0, 0)) +>T : Symbol(T, Decl(depTypeLikeFun.ts, 61, 12)) + + if (str === "t") { +>str : Symbol(str, Decl(depTypeLikeFun.ts, 61, 33)) + + // error + const n: number = ft; +>n : Symbol(n, Decl(depTypeLikeFun.ts, 64, 13)) +>ft : Symbol(ft, Decl(depTypeLikeFun.ts, 61, 40)) + + // no error + return 1; + } else { + // no error + return true; + } +} + +declare var obj: F; +>obj : Symbol(obj, Decl(depTypeLikeFun.ts, 73, 11)) +>F : Symbol(F, Decl(depTypeLikeFun.ts, 0, 0)) + +function f6(str: T, str2: T): F[T] { +>f6 : Symbol(f6, Decl(depTypeLikeFun.ts, 73, 19)) +>T : Symbol(T, Decl(depTypeLikeFun.ts, 74, 12)) +>str : Symbol(str, Decl(depTypeLikeFun.ts, 74, 33)) +>T : Symbol(T, Decl(depTypeLikeFun.ts, 74, 12)) +>str2 : Symbol(str2, Decl(depTypeLikeFun.ts, 74, 40)) +>T : Symbol(T, Decl(depTypeLikeFun.ts, 74, 12)) +>F : Symbol(F, Decl(depTypeLikeFun.ts, 0, 0)) +>T : Symbol(T, Decl(depTypeLikeFun.ts, 74, 12)) + + if (str === "t") { +>str : Symbol(str, Decl(depTypeLikeFun.ts, 74, 33)) + + // error + obj[str2] = 2; +>obj : Symbol(obj, Decl(depTypeLikeFun.ts, 73, 11)) +>str2 : Symbol(str2, Decl(depTypeLikeFun.ts, 74, 40)) + + // no error + return 1; + } else { + // no error + return true; + } +} + +class C7 { +>C7 : Symbol(C7, Decl(depTypeLikeFun.ts, 84, 1)) +>X : Symbol(X, Decl(depTypeLikeFun.ts, 86, 9)) + + f7(x: X): F[X] { +>f7 : Symbol(C7.f7, Decl(depTypeLikeFun.ts, 86, 32)) +>x : Symbol(x, Decl(depTypeLikeFun.ts, 87, 5)) +>X : Symbol(X, Decl(depTypeLikeFun.ts, 86, 9)) +>F : Symbol(F, Decl(depTypeLikeFun.ts, 0, 0)) +>X : Symbol(X, Decl(depTypeLikeFun.ts, 86, 9)) + + if (x === "t") { +>x : Symbol(x, Decl(depTypeLikeFun.ts, 87, 5)) + + // error + return 1; + } else { + // error + return true; + } + } +} + +function f8(x: X): Complex { +>f8 : Symbol(f8, Decl(depTypeLikeFun.ts, 96, 1)) +>X : Symbol(X, Decl(depTypeLikeFun.ts, 98, 12)) +>x : Symbol(x, Decl(depTypeLikeFun.ts, 98, 33)) +>X : Symbol(X, Decl(depTypeLikeFun.ts, 98, 12)) +>Complex : Symbol(Complex, Decl(depTypeLikeFun.ts, 9, 1)) +>X : Symbol(X, Decl(depTypeLikeFun.ts, 98, 12)) + + if (x === "t") { +>x : Symbol(x, Decl(depTypeLikeFun.ts, 98, 33)) + + // no error + return { a: 1, b: true }; +>a : Symbol(a, Decl(depTypeLikeFun.ts, 101, 16)) +>b : Symbol(b, Decl(depTypeLikeFun.ts, 101, 22)) + + } else { + // no error + return { a: true, b: 1 }; +>a : Symbol(a, Decl(depTypeLikeFun.ts, 104, 16)) +>b : Symbol(b, Decl(depTypeLikeFun.ts, 104, 25)) + } +} + +function f9(x: X): Complex { +>f9 : Symbol(f9, Decl(depTypeLikeFun.ts, 106, 1)) +>X : Symbol(X, Decl(depTypeLikeFun.ts, 108, 12)) +>x : Symbol(x, Decl(depTypeLikeFun.ts, 108, 33)) +>X : Symbol(X, Decl(depTypeLikeFun.ts, 108, 12)) +>Complex : Symbol(Complex, Decl(depTypeLikeFun.ts, 9, 1)) +>X : Symbol(X, Decl(depTypeLikeFun.ts, 108, 12)) + + if (x === "t") { +>x : Symbol(x, Decl(depTypeLikeFun.ts, 108, 33)) + + // error + return { a: true, b: 1 }; +>a : Symbol(a, Decl(depTypeLikeFun.ts, 111, 14)) +>b : Symbol(b, Decl(depTypeLikeFun.ts, 111, 23)) + + } else { + // error + return { a: 1, b: true }; +>a : Symbol(a, Decl(depTypeLikeFun.ts, 114, 14)) +>b : Symbol(b, Decl(depTypeLikeFun.ts, 114, 20)) + } +} + +function f10(str: T): (ft: F[T]) => F[T] { +>f10 : Symbol(f10, Decl(depTypeLikeFun.ts, 116, 1)) +>T : Symbol(T, Decl(depTypeLikeFun.ts, 118, 13)) +>str : Symbol(str, Decl(depTypeLikeFun.ts, 118, 34)) +>T : Symbol(T, Decl(depTypeLikeFun.ts, 118, 13)) +>ft : Symbol(ft, Decl(depTypeLikeFun.ts, 118, 44)) +>F : Symbol(F, Decl(depTypeLikeFun.ts, 0, 0)) +>T : Symbol(T, Decl(depTypeLikeFun.ts, 118, 13)) +>F : Symbol(F, Decl(depTypeLikeFun.ts, 0, 0)) +>T : Symbol(T, Decl(depTypeLikeFun.ts, 118, 13)) + + if (str === "t") { +>str : Symbol(str, Decl(depTypeLikeFun.ts, 118, 34)) + + // error + return (ft: number) => { +>ft : Symbol(ft, Decl(depTypeLikeFun.ts, 121, 12)) + + return 1; + }; + } else { + // error + return (ft: F[T]) => { +>ft : Symbol(ft, Decl(depTypeLikeFun.ts, 126, 12)) +>F : Symbol(F, Decl(depTypeLikeFun.ts, 0, 0)) +>T : Symbol(T, Decl(depTypeLikeFun.ts, 118, 13)) + + return true; + }; + } +} diff --git a/tests/baselines/reference/depTypeLikeFun.types b/tests/baselines/reference/depTypeLikeFun.types new file mode 100644 index 0000000000000..8143e5738c9b5 --- /dev/null +++ b/tests/baselines/reference/depTypeLikeFun.types @@ -0,0 +1,299 @@ +=== tests/cases/compiler/depTypeLikeFun.ts === +type F = { +>F : F + + t: number, +>t : number + + f: boolean, +>f : boolean +} + +type G = { +>G : G + + a: number, +>a : number + + b: boolean, +>b : boolean + + c: string, +>c : string +} + +type Complex = { +>Complex : Complex + + a: { t: number, f: boolean }[X], +>a : { t: number; f: boolean; }[X] +>t : number +>f : boolean + + b: { t: boolean, f: number }[X], +>b : { t: boolean; f: number; }[X] +>t : boolean +>f : number +} + +function f1(x: X): F[X] { +>f1 : (x: X) => F[X] +>x : X + + if (x === "t") { +>x === "t" : boolean +>x : X +>"t" : "t" + + // no error + return 1; +>1 : 1 + + } else { + // no error + return true; +>true : true + } +} + +function f2(x: X): F[X] { +>f2 : (x: X) => F[X] +>x : X + + if (x === "t") { +>x === "t" : boolean +>x : X +>"t" : "t" + + // error + return true; +>true : true + + } else { + // error + return 1; +>1 : 1 + } +} + +function f3(x: X): G[X] { +>f3 : (x: X) => G[X] +>x : X + + if (x === "a") { +>x === "a" : boolean +>x : X +>"a" : "a" + + // no error + return 1; +>1 : 1 + + } else { + if (x === "b") { +>x === "b" : boolean +>x : X +>"b" : "b" + + // no error + return true; +>true : true + + } else { + // no error + return "z"; +>"z" : "z" + } + } +} + +function f4(x: X, y: Y): F[X] { +>f4 : (x: X, y: Y) => F[X] +>x : X +>y : Y + + if (y === "t") { +>y === "t" : boolean +>y : Y +>"t" : "t" + + // error + return 1; +>1 : 1 + + } else { + // error + return true; +>true : true + } +} + +function f5(str: T, ft: F[T]): F[T] { +>f5 : (str: T, ft: F[T]) => F[T] +>str : T +>ft : F[T] + + if (str === "t") { +>str === "t" : boolean +>str : T +>"t" : "t" + + // error + const n: number = ft; +>n : number +>ft : F[T] + + // no error + return 1; +>1 : 1 + + } else { + // no error + return true; +>true : true + } +} + +declare var obj: F; +>obj : F + +function f6(str: T, str2: T): F[T] { +>f6 : (str: T, str2: T) => F[T] +>str : T +>str2 : T + + if (str === "t") { +>str === "t" : boolean +>str : T +>"t" : "t" + + // error + obj[str2] = 2; +>obj[str2] = 2 : 2 +>obj[str2] : F[T] +>obj : F +>str2 : T +>2 : 2 + + // no error + return 1; +>1 : 1 + + } else { + // no error + return true; +>true : true + } +} + +class C7 { +>C7 : C7 + + f7(x: X): F[X] { +>f7 : (x: X) => F[X] +>x : X + + if (x === "t") { +>x === "t" : boolean +>x : X +>"t" : "t" + + // error + return 1; +>1 : 1 + + } else { + // error + return true; +>true : true + } + } +} + +function f8(x: X): Complex { +>f8 : (x: X) => Complex +>x : X + + if (x === "t") { +>x === "t" : boolean +>x : X +>"t" : "t" + + // no error + return { a: 1, b: true }; +>{ a: 1, b: true } : { a: 1; b: true; } +>a : 1 +>1 : 1 +>b : true +>true : true + + } else { + // no error + return { a: true, b: 1 }; +>{ a: true, b: 1 } : { a: true; b: 1; } +>a : true +>true : true +>b : 1 +>1 : 1 + } +} + +function f9(x: X): Complex { +>f9 : (x: X) => Complex +>x : X + + if (x === "t") { +>x === "t" : boolean +>x : X +>"t" : "t" + + // error + return { a: true, b: 1 }; +>{ a: true, b: 1 } : { a: true; b: 1; } +>a : true +>true : true +>b : 1 +>1 : 1 + + } else { + // error + return { a: 1, b: true }; +>{ a: 1, b: true } : { a: 1; b: true; } +>a : 1 +>1 : 1 +>b : true +>true : true + } +} + +function f10(str: T): (ft: F[T]) => F[T] { +>f10 : (str: T) => (ft: F[T]) => F[T] +>str : T +>ft : F[T] + + if (str === "t") { +>str === "t" : boolean +>str : T +>"t" : "t" + + // error + return (ft: number) => { +>(ft: number) => { return 1; } : (ft: number) => 1 +>ft : number + + return 1; +>1 : 1 + + }; + } else { + // error + return (ft: F[T]) => { +>(ft: F[T]) => { return true; } : (ft: F[T]) => true +>ft : F[T] + + return true; +>true : true + + }; + } +} diff --git a/tests/cases/compiler/depTypeLikeFun.ts b/tests/cases/compiler/depTypeLikeFun.ts new file mode 100644 index 0000000000000..3c9f73dddf66c --- /dev/null +++ b/tests/cases/compiler/depTypeLikeFun.ts @@ -0,0 +1,131 @@ +type F = { + t: number, + f: boolean, +} + +type G = { + a: number, + b: boolean, + c: string, +} + +type Complex = { + a: { t: number, f: boolean }[X], + b: { t: boolean, f: number }[X], +} + +function f1(x: X): F[X] { + if (x === "t") { + // no error + return 1; + } else { + // no error + return true; + } +} + +function f2(x: X): F[X] { + if (x === "t") { + // error + return true; + } else { + // error + return 1; + } +} + +function f3(x: X): G[X] { + if (x === "a") { + // no error + return 1; + } else { + if (x === "b") { + // no error + return true; + } else { + // no error + return "z"; + } + } +} + +function f4(x: X, y: Y): F[X] { + if (y === "t") { + // error + return 1; + } else { + // error + return true; + } +} + +function f5(str: T, ft: F[T]): F[T] { + if (str === "t") { + // error + const n: number = ft; + // no error + return 1; + } else { + // no error + return true; + } +} + +declare var obj: F; +function f6(str: T, str2: T): F[T] { + if (str === "t") { + // error + obj[str2] = 2; + // no error + return 1; + } else { + // no error + return true; + } +} + +class C7 { + f7(x: X): F[X] { + if (x === "t") { + // error + return 1; + } else { + // error + return true; + } + } +} + +function f8(x: X): Complex { + if (x === "t") { + // no error + return { a: 1, b: true }; + } else { + // no error + return { a: true, b: 1 }; + } +} + +function f9(x: X): Complex { + if (x === "t") { + // error + return { a: true, b: 1 }; + } else { + // error + return { a: 1, b: true }; + } +} + +function f10(str: T): (ft: F[T]) => F[T] { + if (str === "t") { + // error + return (ft: number) => { + return 1; + }; + } else { + // error + return (ft: F[T]) => { + return true; + }; + } +} \ No newline at end of file