Narrow generic conditional and indexed access return types when checking return statements (#56941)

This commit is contained in:
Gabriela Araujo Britto 2024-11-05 18:18:24 -08:00 коммит произвёл GitHub
Родитель 5e2e32120b
Коммит 30979c2651
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
41 изменённых файлов: 11878 добавлений и 44 удалений

Просмотреть файл

@ -543,6 +543,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
var preSwitchCaseFlow: FlowNode | undefined;
var activeLabelList: ActiveLabel | undefined;
var hasExplicitReturn: boolean;
var inReturnPosition: boolean;
var hasFlowEffects: boolean;
// state used for emit helpers
@ -622,6 +623,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
currentExceptionTarget = undefined;
activeLabelList = undefined;
hasExplicitReturn = false;
inReturnPosition = false;
hasFlowEffects = false;
inAssignmentPattern = false;
emitFlags = NodeFlags.None;
@ -967,7 +969,9 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
const saveContainer = container;
const saveThisParentContainer = thisParentContainer;
const savedBlockScopeContainer = blockScopeContainer;
const savedInReturnPosition = inReturnPosition;
if (node.kind === SyntaxKind.ArrowFunction && node.body.kind !== SyntaxKind.Block) inReturnPosition = true;
// Depending on what kind of node this is, we may have to adjust the current container
// and block-container. If the current node is a container, then it is automatically
// considered the current block-container as well. Also, for containers that we know
@ -1071,6 +1075,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
bindChildren(node);
}
inReturnPosition = savedInReturnPosition;
container = saveContainer;
thisParentContainer = saveThisParentContainer;
blockScopeContainer = savedBlockScopeContainer;
@ -1571,7 +1576,10 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
}
function bindReturnOrThrow(node: ReturnStatement | ThrowStatement): void {
const savedInReturnPosition = inReturnPosition;
inReturnPosition = true;
bind(node.expression);
inReturnPosition = savedInReturnPosition;
if (node.kind === SyntaxKind.ReturnStatement) {
hasExplicitReturn = true;
if (currentReturnTarget) {
@ -2016,10 +2024,16 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
hasFlowEffects = false;
bindCondition(node.condition, trueLabel, falseLabel);
currentFlow = finishFlowLabel(trueLabel);
if (inReturnPosition) {
node.flowNodeWhenTrue = currentFlow;
}
bind(node.questionToken);
bind(node.whenTrue);
addAntecedent(postExpressionLabel, currentFlow);
currentFlow = finishFlowLabel(falseLabel);
if (inReturnPosition) {
node.flowNodeWhenFalse = currentFlow;
}
bind(node.colonToken);
bind(node.whenFalse);
addAntecedent(postExpressionLabel, currentFlow);

Просмотреть файл

@ -513,6 +513,7 @@ import {
isCompoundAssignment,
isComputedNonLiteralName,
isComputedPropertyName,
isConditionalExpression,
isConditionalTypeNode,
isConstAssertion,
isConstructorDeclaration,
@ -2369,6 +2370,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
[".jsx", ".jsx"],
[".json", ".json"],
];
var narrowableReturnTypeCache = new Map<string, boolean>();
/* eslint-enable no-var */
initializeTypeChecker();
@ -16580,14 +16583,18 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return !!(type.flags & TypeFlags.Substitution && (type as SubstitutionType).constraint.flags & TypeFlags.Unknown);
}
function getSubstitutionType(baseType: Type, constraint: Type) {
return constraint.flags & TypeFlags.AnyOrUnknown || constraint === baseType || baseType.flags & TypeFlags.Any ?
baseType :
getOrCreateSubstitutionType(baseType, constraint);
function isNarrowingSubstitutionType(type: Type): boolean {
return !!(type.flags & TypeFlags.Substitution && (type as SubstitutionType).objectFlags & ObjectFlags.IsNarrowingType);
}
function getOrCreateSubstitutionType(baseType: Type, constraint: Type) {
const id = `${getTypeId(baseType)}>${getTypeId(constraint)}`;
function getSubstitutionType(baseType: Type, constraint: Type, isNarrowed?: boolean) {
return constraint.flags & TypeFlags.AnyOrUnknown || constraint === baseType || baseType.flags & TypeFlags.Any ?
baseType :
getOrCreateSubstitutionType(baseType, constraint, isNarrowed);
}
function getOrCreateSubstitutionType(baseType: Type, constraint: Type, isNarrowed?: boolean) {
const id = `${getTypeId(baseType)}>${getTypeId(constraint)}${isNarrowed ? ">N" : ""}`;
const cached = substitutionTypes.get(id);
if (cached) {
return cached;
@ -16595,6 +16602,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const result = createType(TypeFlags.Substitution) as SubstitutionType;
result.baseType = baseType;
result.constraint = constraint;
if (isNarrowed) {
result.objectFlags |= ObjectFlags.IsNarrowingType;
}
substitutionTypes.set(id, result);
return result;
}
@ -17655,7 +17665,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// types are known not to circularly reference themselves (as is the case with union types created by
// expression constructs such as array literals and the || and ?: operators). Named types can
// circularly reference themselves and therefore cannot be subtype reduced during their declaration.
// For example, "type Item = string | (() => Item" is a named type that circularly references itself.
// For example, "type Item = string | (() => Item)" is a named type that circularly references itself.
function getUnionType(types: readonly Type[], unionReduction: UnionReduction = UnionReduction.Literal, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[], origin?: Type): Type {
if (types.length === 0) {
return neverType;
@ -19126,7 +19136,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return isGenericType(type) || checkTuples && isTupleType(type) && some(getElementTypes(type), isGenericType);
}
function getConditionalType(root: ConditionalRoot, mapper: TypeMapper | undefined, forConstraint: boolean, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]): Type {
function getConditionalType(
root: ConditionalRoot,
mapper: TypeMapper | undefined,
forConstraint: boolean,
aliasSymbol?: Symbol,
aliasTypeArguments?: readonly Type[],
forNarrowing?: boolean,
): Type {
let result;
let extraTypes: Type[] | undefined;
let tailCount = 0;
@ -19148,6 +19165,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
if (checkType === wildcardType || extendsType === wildcardType) {
return wildcardType;
}
const effectiveCheckType = forNarrowing && isNarrowingSubstitutionType(checkType)
? (checkType as SubstitutionType).constraint
: checkType;
const checkTypeNode = skipTypeParentheses(root.node.checkType);
const extendsTypeNode = skipTypeParentheses(root.node.extendsType);
// When the check and extends types are simple tuple types of the same arity, we defer resolution of the
@ -19155,7 +19175,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// types can be written `[X] extends [Y] ? ...` and be deferred similarly to `X extends Y ? ...`.
const checkTuples = isSimpleTupleType(checkTypeNode) && isSimpleTupleType(extendsTypeNode) &&
length((checkTypeNode as TupleTypeNode).elements) === length((extendsTypeNode as TupleTypeNode).elements);
const checkTypeDeferred = isDeferredType(checkType, checkTuples);
const checkTypeDeferred = isDeferredType(effectiveCheckType, checkTuples);
let combinedMapper: TypeMapper | undefined;
if (root.inferTypeParameters) {
// When we're looking at making an inference for an infer type, when we get its constraint, it'll automagically be
@ -19191,17 +19211,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const inferredExtendsType = combinedMapper ? instantiateType(root.extendsType, combinedMapper) : extendsType;
// We attempt to resolve the conditional type only when the check and extends types are non-generic
if (!checkTypeDeferred && !isDeferredType(inferredExtendsType, checkTuples)) {
// Return falseType for a definitely false extends check. We check an instantiations of the two
// Return falseType for a definitely false extends check. We check an instantiation of the two
// types with type parameters mapped to the wildcard type, the most permissive instantiations
// possible (the wildcard type is assignable to and from all types). If those are not related,
// then no instantiations will be and we can just return the false branch type.
if (!(inferredExtendsType.flags & TypeFlags.AnyOrUnknown) && (checkType.flags & TypeFlags.Any || !isTypeAssignableTo(getPermissiveInstantiation(checkType), getPermissiveInstantiation(inferredExtendsType)))) {
if (!(inferredExtendsType.flags & TypeFlags.AnyOrUnknown) && (effectiveCheckType.flags & TypeFlags.Any || !isTypeAssignableTo(getPermissiveInstantiation(effectiveCheckType), getPermissiveInstantiation(inferredExtendsType)))) {
// Return union of trueType and falseType for 'any' since it matches anything. Furthermore, for a
// distributive conditional type applied to the constraint of a type variable, include trueType if
// there are possible values of the check type that are also possible values of the extends type.
// We use a reverse assignability check as it is less expensive than the comparable relationship
// and avoids false positives of a non-empty intersection check.
if (checkType.flags & TypeFlags.Any || forConstraint && !(inferredExtendsType.flags & TypeFlags.Never) && someType(getPermissiveInstantiation(inferredExtendsType), t => isTypeAssignableTo(t, getPermissiveInstantiation(checkType)))) {
if (effectiveCheckType.flags & TypeFlags.Any || forConstraint && !(inferredExtendsType.flags & TypeFlags.Never) && someType(getPermissiveInstantiation(inferredExtendsType), t => isTypeAssignableTo(t, getPermissiveInstantiation(effectiveCheckType)))) {
(extraTypes || (extraTypes = [])).push(instantiateType(getTypeFromTypeNode(root.node.trueType), combinedMapper || mapper));
}
// If falseType is an immediately nested conditional type that isn't distributive or has an
@ -19225,7 +19245,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// that has no constraint. This ensures that, for example, the type
// type Foo<T extends { x: any }> = T extends { x: string } ? string : number
// doesn't immediately resolve to 'string' instead of being deferred.
if (inferredExtendsType.flags & TypeFlags.AnyOrUnknown || isTypeAssignableTo(getRestrictiveInstantiation(checkType), getRestrictiveInstantiation(inferredExtendsType))) {
if (inferredExtendsType.flags & TypeFlags.AnyOrUnknown || isTypeAssignableTo(getRestrictiveInstantiation(effectiveCheckType), getRestrictiveInstantiation(inferredExtendsType))) {
const trueType = getTypeFromTypeNode(root.node.trueType);
const trueMapper = combinedMapper || mapper;
if (canTailRecurse(trueType, trueMapper)) {
@ -20351,13 +20371,38 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
if (!result) {
const newMapper = createTypeMapper(root.outerTypeParameters, typeArguments);
const checkType = root.checkType;
const distributionType = root.isDistributive ? getReducedType(getMappedType(checkType, newMapper)) : undefined;
let distributionType = root.isDistributive ? getReducedType(getMappedType(checkType, newMapper)) : undefined;
let narrowingBaseType: Type | undefined;
const forNarrowing = distributionType && isNarrowingSubstitutionType(distributionType) && isNarrowableConditionalType(type, mapper);
if (forNarrowing) {
narrowingBaseType = (distributionType as SubstitutionType).baseType;
distributionType = getReducedType((distributionType as SubstitutionType).constraint);
}
// Distributive conditional types are distributed over union types. For example, when the
// distributive conditional type T extends U ? X : Y is instantiated with A | B for T, the
// result is (A extends U ? X : Y) | (B extends U ? X : Y).
result = distributionType && checkType !== distributionType && distributionType.flags & (TypeFlags.Union | TypeFlags.Never) ?
mapTypeWithAlias(distributionType, t => getConditionalType(root, prependTypeMapping(checkType, t, newMapper), forConstraint), aliasSymbol, aliasTypeArguments) :
getConditionalType(root, newMapper, forConstraint, aliasSymbol, aliasTypeArguments);
if (distributionType && checkType !== distributionType && distributionType.flags & (TypeFlags.Union | TypeFlags.Never)) {
if (narrowingBaseType) {
result = mapTypeToIntersection(
distributionType,
(t: Type) =>
getConditionalType(
root,
prependTypeMapping(checkType, getSubstitutionType(narrowingBaseType, t, /*isNarrowed*/ true), newMapper),
forConstraint,
/*aliasSymbol*/ undefined,
/*aliasTypeArguments*/ undefined,
forNarrowing,
),
);
}
else {
result = mapTypeWithAlias(distributionType, (t: Type) => getConditionalType(root, prependTypeMapping(checkType, t, newMapper), forConstraint), aliasSymbol, aliasTypeArguments);
}
}
else {
result = getConditionalType(root, newMapper, forConstraint, aliasSymbol, aliasTypeArguments, forNarrowing);
}
root.instantiations!.set(id, result);
}
return result;
@ -20439,7 +20484,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return getIndexedAccessType(instantiateType((type as IndexedAccessType).objectType, mapper), instantiateType((type as IndexedAccessType).indexType, mapper), (type as IndexedAccessType).accessFlags, /*accessNode*/ undefined, newAliasSymbol, newAliasTypeArguments);
}
if (flags & TypeFlags.Conditional) {
return getConditionalTypeInstantiation(type as ConditionalType, combineTypeMappers((type as ConditionalType).mapper, mapper), /*forConstraint*/ false, aliasSymbol, aliasTypeArguments);
return getConditionalTypeInstantiation(
type as ConditionalType,
combineTypeMappers((type as ConditionalType).mapper, mapper),
/*forConstraint*/ false,
aliasSymbol,
aliasTypeArguments,
);
}
if (flags & TypeFlags.Substitution) {
const newBaseType = instantiateType((type as SubstitutionType).baseType, mapper);
@ -21657,7 +21708,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
if (type.flags & TypeFlags.Intersection && shouldNormalizeIntersection(type as IntersectionType)) {
// Normalization handles cases like
// Partial<T>[K] & ({} | null) ==>
// Partial<T>[K] & {} | Partial<T>[K} & null ==>
// Partial<T>[K] & {} | Partial<T>[K] & null ==>
// (T[K] | undefined) & {} | (T[K] | undefined) & null ==>
// T[K] & {} | undefined & {} | T[K] & null | undefined & null ==>
// T[K] & {} | T[K] & null
@ -21672,10 +21723,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
function shouldNormalizeIntersection(type: IntersectionType) {
let hasInstantiable = false;
let hasNullableOrEmpty = false;
let hasSubstitution = false;
for (const t of type.types) {
hasInstantiable ||= !!(t.flags & TypeFlags.Instantiable);
hasNullableOrEmpty ||= !!(t.flags & TypeFlags.Nullable) || isEmptyAnonymousObjectType(t);
if (hasInstantiable && hasNullableOrEmpty) return true;
hasSubstitution ||= isNarrowingSubstitutionType(t); // This avoids displaying error messages with types like `T & T` when narrowing a return type
if (hasInstantiable && hasNullableOrEmpty || hasSubstitution) return true;
}
return false;
}
@ -27859,6 +27912,23 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return changed ? mappedTypes && getUnionType(mappedTypes, noReductions ? UnionReduction.None : UnionReduction.Literal) : type;
}
/**
* Similar to {@link mapType}, but creates an intersection with the result of mapping over a union type.
*/
function mapTypeToIntersection(type: Type, mapper: (t: Type) => Type): Type {
if (type.flags & TypeFlags.Never) {
return type;
}
if (!(type.flags & TypeFlags.Union)) {
return mapper(type);
}
const origin = (type as UnionType).origin;
const types = origin && origin.flags & TypeFlags.Union ? (origin as UnionType).types : (type as UnionType).types;
const mappedTypes = types.map(t => t.flags & TypeFlags.Union ? mapTypeToIntersection(t, mapper) : mapper(t));
return getIntersectionType(mappedTypes);
}
function mapTypeWithAlias(type: Type, mapper: (t: Type) => Type, aliasSymbol: Symbol | undefined, aliasTypeArguments: readonly Type[] | undefined) {
return type.flags & TypeFlags.Union && aliasSymbol ?
getUnionType(map((type as UnionType).types, mapper), UnionReduction.Literal, aliasSymbol, aliasTypeArguments) :
@ -29743,7 +29813,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return contextualType && !isGenericType(contextualType);
}
function getNarrowableTypeForReference(type: Type, reference: Node, checkMode?: CheckMode) {
function getNarrowableTypeForReference(type: Type, reference: Node, checkMode?: CheckMode, forReturnTypeNarrowing?: boolean) {
if (isNoInferType(type)) {
type = (type as SubstitutionType).baseType;
}
@ -29756,7 +29826,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// 'string | undefined' to give control flow analysis the opportunity to narrow to type 'string'.
const substituteConstraints = !(checkMode && checkMode & CheckMode.Inferential) &&
someType(type, isGenericTypeWithUnionConstraint) &&
(isConstraintPosition(type, reference) || hasContextualTypeWithNoGenericTypes(reference, checkMode));
(forReturnTypeNarrowing || isConstraintPosition(type, reference) || hasContextualTypeWithNoGenericTypes(reference, checkMode));
return substituteConstraints ? mapType(type, getBaseConstraintOrType) : type;
}
@ -31282,9 +31352,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
function getContextualTypeForReturnExpression(node: Expression, contextFlags: ContextFlags | undefined): Type | undefined {
const func = getContainingFunction(node);
if (func) {
const functionFlags = getFunctionFlags(func);
const links = getNodeLinks(node);
if (links.contextualReturnType) {
if (functionFlags & FunctionFlags.Async) {
return getUnionType([links.contextualReturnType, createPromiseLikeType(links.contextualReturnType)]);
}
return links.contextualReturnType;
}
let contextualReturnType = getContextualReturnType(func, contextFlags);
if (contextualReturnType) {
const functionFlags = getFunctionFlags(func);
if (functionFlags & FunctionFlags.Generator) { // Generator or AsyncGenerator function
const isAsyncGenerator = (functionFlags & FunctionFlags.Async) !== 0;
if (contextualReturnType.flags & TypeFlags.Union) {
@ -32035,6 +32112,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
if (index >= 0) {
return contextualTypes[index];
}
const links = getNodeLinks(node);
if (links.contextualReturnType) {
if (node.flags & NodeFlags.AwaitContext) {
return getUnionType([links.contextualReturnType, createPromiseLikeType(links.contextualReturnType)]);
}
return links.contextualReturnType;
}
const { parent } = node;
switch (parent.kind) {
case SyntaxKind.VariableDeclaration:
@ -38902,14 +38986,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const exprType = checkExpression(node.body);
const returnOrPromisedType = returnType && unwrapReturnType(returnType, functionFlags);
if (returnOrPromisedType) {
const effectiveCheckNode = getEffectiveCheckNode(node.body);
if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Async) { // Async function
const awaitedType = checkAwaitedType(exprType, /*withAlias*/ false, effectiveCheckNode, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
checkTypeAssignableToAndOptionallyElaborate(awaitedType, returnOrPromisedType, effectiveCheckNode, effectiveCheckNode);
}
else { // Normal function
checkTypeAssignableToAndOptionallyElaborate(exprType, returnOrPromisedType, effectiveCheckNode, effectiveCheckNode);
}
checkReturnExpression(node, returnOrPromisedType, node.body, node.body, exprType);
}
}
}
@ -45631,7 +45708,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const signature = getSignatureFromDeclaration(container);
const returnType = getReturnTypeOfSignature(signature);
const functionFlags = getFunctionFlags(container);
if (strictNullChecks || node.expression || returnType.flags & TypeFlags.Never) {
const exprType = node.expression ? checkExpressionCached(node.expression) : undefinedType;
if (container.kind === SyntaxKind.SetAccessor) {
@ -45640,21 +45716,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
}
else if (container.kind === SyntaxKind.Constructor) {
const exprType = node.expression ? checkExpressionCached(node.expression) : undefinedType;
if (node.expression && !checkTypeAssignableToAndOptionallyElaborate(exprType, returnType, node, node.expression)) {
error(node, Diagnostics.Return_type_of_constructor_signature_must_be_assignable_to_the_instance_type_of_the_class);
}
}
else if (getReturnTypeFromAnnotation(container)) {
const unwrappedReturnType = unwrapReturnType(returnType, functionFlags) ?? returnType;
const unwrappedExprType = functionFlags & FunctionFlags.Async
? checkAwaitedType(exprType, /*withAlias*/ false, node, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member)
: exprType;
if (unwrappedReturnType) {
// 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);
}
const unwrappedReturnType = unwrapReturnType(returnType, getFunctionFlags(container)) ?? returnType;
checkReturnExpression(container, unwrappedReturnType, node, node.expression, exprType);
}
}
else if (container.kind !== SyntaxKind.Constructor && compilerOptions.noImplicitReturns && !isUnwrappedReturnTypeUndefinedVoidOrAny(container, returnType)) {
@ -45663,6 +45732,306 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
}
// When checking an arrow expression such as `(x) => exp`, then `node` is the expression `exp`.
// Otherwise, `node` is a return statement.
function checkReturnExpression(
container: SignatureDeclaration,
unwrappedReturnType: Type,
node: ReturnStatement | Expression,
expr: Expression | undefined,
exprType: Type,
inConditionalExpression = false,
): void {
const excludeJSDocTypeAssertions = isInJSFile(node);
const functionFlags = getFunctionFlags(container);
if (expr) {
const unwrappedExpr = skipParentheses(expr, excludeJSDocTypeAssertions);
if (isConditionalExpression(unwrappedExpr)) {
checkReturnExpression(container, unwrappedReturnType, node, unwrappedExpr.whenTrue, checkExpression(unwrappedExpr.whenTrue), /*inConditionalExpression*/ true);
checkReturnExpression(container, unwrappedReturnType, node, unwrappedExpr.whenFalse, checkExpression(unwrappedExpr.whenFalse), /*inConditionalExpression*/ true);
return;
}
}
const inReturnStatement = node.kind === SyntaxKind.ReturnStatement;
const unwrappedExprType = functionFlags & FunctionFlags.Async
? checkAwaitedType(
exprType,
/*withAlias*/ false,
node,
Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member,
)
: exprType;
const effectiveExpr = expr && getEffectiveCheckNode(expr); // The effective expression for diagnostics purposes.
const errorNode = inReturnStatement && !inConditionalExpression ? node : effectiveExpr;
// If the return type is not narrowable, we simply check if the return expression type is assignable to the return type.
if (!(unwrappedReturnType.flags & (TypeFlags.IndexedAccess | TypeFlags.Conditional)) || !couldContainTypeVariables(unwrappedReturnType)) {
checkTypeAssignableToAndOptionallyElaborate(unwrappedExprType, unwrappedReturnType, errorNode, effectiveExpr);
return;
}
// If type of return expression is assignable to original return type, we don't need to narrow the return type.
if (checkTypeAssignableTo(unwrappedExprType, unwrappedReturnType, /*errorNode*/ undefined)) {
return;
}
// There are two cases for obtaining a position in the control-flow graph on which references will be analyzed:
// - When the return expression is defined, and it is one of the two branches of a conditional expression, then the position is the expression itself:
// `function foo(...) {
// return cond ? |expr| : ...
// }`
// - When the return expression is undefined, or it is defined and it is not one of the branches of a conditional expression, then the position is the return statement itself:
// `function foo(...) {
// |return expr;|
// }`
// or
// `function foo(...) {
// |return;|
// }`
let narrowPosition: Node = node;
let narrowFlowNode = inReturnStatement && (node as ReturnStatement).flowNode;
if (expr && isConditionalExpression(expr.parent)) {
narrowFlowNode = expr.parent.whenTrue === expr ? expr.parent.flowNodeWhenTrue : expr.parent.flowNodeWhenFalse;
narrowPosition = expr;
}
if (!narrowFlowNode) {
checkTypeAssignableToAndOptionallyElaborate(unwrappedExprType, unwrappedReturnType, errorNode, effectiveExpr);
return;
}
const allTypeParameters = appendTypeParameters(getOuterTypeParameters(container, /*includeThisTypes*/ false), getEffectiveTypeParameterDeclarations(container as DeclarationWithTypeParameters));
const narrowableTypeParameters = allTypeParameters && getNarrowableTypeParameters(allTypeParameters);
if (
!narrowableTypeParameters ||
!narrowableTypeParameters.length ||
!isNarrowableReturnType(unwrappedReturnType as ConditionalType | IndexedAccessType)
) {
checkTypeAssignableToAndOptionallyElaborate(unwrappedExprType, unwrappedReturnType, errorNode, effectiveExpr);
return;
}
const narrowedTypeParameters: TypeParameter[] = [];
const narrowedTypes: Type[] = [];
for (const [typeParam, symbol, reference] of narrowableTypeParameters) {
const narrowReference = factory.cloneNode(reference); // Construct a reference that can be narrowed.
// Don't reuse the original reference's node id,
// because that could cause us to get a type that was cached for the original reference.
narrowReference.id = undefined;
// Set the symbol of the synthetic reference.
// This allows us to get the type of the reference at a location where the reference is possibly shadowed.
getNodeLinks(narrowReference).resolvedSymbol = symbol;
setParent(narrowReference, narrowPosition.parent);
narrowReference.flowNode = narrowFlowNode;
const initialType = getNarrowableTypeForReference(typeParam, narrowReference, /*checkMode*/ undefined, /*forReturnTypeNarrowing*/ true);
if (initialType === typeParam) {
continue;
}
const flowType = getFlowTypeOfReference(narrowReference, initialType);
const exprType = getTypeFromFlowType(flowType);
// If attempting to narrow the expression type did not produce a narrower type,
// then discard this type parameter from narrowing.
if (
exprType.flags & TypeFlags.AnyOrUnknown
|| isErrorType(exprType)
|| exprType === typeParam
|| exprType === mapType(typeParam, getBaseConstraintOrType)
) {
continue;
}
const narrowedType = getSubstitutionType(typeParam, exprType, /*isNarrowed*/ true);
narrowedTypeParameters.push(typeParam);
narrowedTypes.push(narrowedType);
}
const narrowMapper = createTypeMapper(narrowedTypeParameters, narrowedTypes);
const narrowedReturnType = instantiateType(
unwrappedReturnType,
narrowMapper,
);
if (expr) {
const links = getNodeLinks(expr);
if (!links.contextualReturnType) {
links.contextualReturnType = narrowedReturnType;
}
}
const narrowedExprType = expr ? checkExpression(expr) : undefinedType;
const narrowedUnwrappedExprType = functionFlags & FunctionFlags.Async
? checkAwaitedType(
narrowedExprType,
/*withAlias*/ false,
node,
Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member,
)
: narrowedExprType;
checkTypeAssignableToAndOptionallyElaborate(narrowedUnwrappedExprType, narrowedReturnType, errorNode, effectiveExpr);
}
/**
* Narrowable type parameters are type parameters that:
* (1) have a union type constraint;
* (2) are used as the type of a single parameter in the function, and nothing else
*/
function getNarrowableTypeParameters(candidates: TypeParameter[]): [TypeParameter, Symbol, Identifier][] {
const narrowableParams: [TypeParameter, Symbol, Identifier][] = [];
for (const typeParam of candidates) {
const constraint = getConstraintOfTypeParameter(typeParam);
if (!constraint || !(constraint.flags & TypeFlags.Union)) continue;
if (typeParam.symbol && typeParam.symbol.declarations && typeParam.symbol.declarations.length === 1) {
const declaration = typeParam.symbol.declarations[0];
const container = isJSDocTemplateTag(declaration.parent) ? getJSDocHost(declaration.parent) : declaration.parent;
if (!isFunctionLike(container)) continue;
let reference: Identifier | undefined;
let hasInvalidReference = false;
for (const paramDecl of container.parameters) {
const typeNode = getEffectiveTypeAnnotationNode(paramDecl);
if (!typeNode) continue;
if (isTypeParameterReferenced(typeParam, typeNode)) {
let candidateReference;
if (
isTypeReferenceNode(typeNode) &&
isReferenceToTypeParameter(typeParam, typeNode) &&
(candidateReference = getValidParameterReference(paramDecl, constraint))
) {
// Type parameter has more than one valid reference.
if (reference) {
hasInvalidReference = true;
break;
}
reference = candidateReference;
}
else { // Type parameter has invalid reference.
hasInvalidReference = true;
break;
}
}
}
if (!hasInvalidReference && reference) {
const symbol = getResolvedSymbol(reference);
if (symbol !== unknownSymbol) narrowableParams.push([typeParam, symbol, reference]);
}
}
}
return narrowableParams;
// For a parameter of declared type `T` to be a valid reference for narrowing, it must satisfy:
// - the parameter name is an identifier
// - if the parameter is optional, then `T`'s constraint must allow for undefined
function getValidParameterReference(paramDecl: ParameterDeclaration, constraint: Type): Identifier | undefined {
if (!isIdentifier(paramDecl.name)) return;
const isOptional = !!paramDecl.questionToken || isJSDocOptionalParameter(paramDecl);
if (isOptional && !containsUndefinedType(constraint)) return;
return paramDecl.name;
}
function isReferenceToTypeParameter(typeParam: TypeParameter, node: TypeReferenceNode) {
return getTypeFromTypeReference(node) === typeParam;
}
function isTypeParameterReferenced(typeParam: TypeParameter, node: TypeNode) {
return isReferenced(node);
function isReferenced(node: Node): boolean {
if (isTypeReferenceNode(node)) {
return isReferenceToTypeParameter(typeParam, node);
}
if (isTypeQueryNode(node)) {
return isTypeParameterPossiblyReferenced(typeParam, node);
}
return !!forEachChild(node, isReferenced);
}
}
}
function isNarrowableReturnType(returnType: IndexedAccessType | ConditionalType): boolean {
return isConditionalType(returnType)
? isNarrowableConditionalType(returnType)
: !!(returnType.indexType.flags & TypeFlags.TypeParameter);
}
function isNarrowableConditionalType(type: ConditionalType, mapper?: TypeMapper): boolean {
const typeArguments = mapper && map(type.root.outerTypeParameters, t => {
const mapped = getMappedType(t, mapper);
if (isNarrowingSubstitutionType(mapped)) {
return (mapped as SubstitutionType).baseType;
}
return mapped;
});
const id = `${type.id}:${getTypeListId(typeArguments)}`;
let result = narrowableReturnTypeCache.get(id);
if (result === undefined) {
const nonNarrowingMapper = type.root.outerTypeParameters
&& typeArguments
&& createTypeMapper(type.root.outerTypeParameters, typeArguments);
const instantiatedType = instantiateType(type, nonNarrowingMapper);
result = isConditionalType(instantiatedType) && isNarrowableConditionalTypeWorker(instantiatedType);
narrowableReturnTypeCache.set(id, result);
}
return result;
}
// A narrowable conditional type is one that has the following shape:
// `T extends A ? TrueBranch<T> : FalseBranch<T>`, in other words:
// (0) The conditional type is distributive;
// (1) The conditional type has no `infer` type parameters;
// (2) The conditional type's check type is a narrowable type parameter (i.e. a type parameter with a union constraint);
// (3) The extends type `A` is a type or a union of types belonging to the union constraint of the type parameter;
// (4) `TrueBranch<T>` and `FalseBranch<T>` must be valid, recursively.
// In particular, the false-most branch of the conditional type must be `never`.
function isNarrowableConditionalTypeWorker(type: ConditionalType): boolean {
// (0)
if (!type.root.isDistributive) {
return false;
}
// (1)
if (type.root.inferTypeParameters) {
return false;
}
// (2)
if (!(type.checkType.flags & TypeFlags.TypeParameter)) {
return false;
}
// (2)
const constraintType = getConstraintOfTypeParameter(type.checkType as TypeParameter);
if (!constraintType || !(constraintType.flags & TypeFlags.Union)) {
return false;
}
// (3)
if (
!everyType(type.extendsType, extendsType =>
some(
(constraintType as UnionType).types,
constraintType => isTypeIdenticalTo(constraintType, extendsType),
))
) {
return false;
}
// (4)
const trueType = getTrueTypeFromConditionalType(type);
const isValidTrueType = isConditionalType(trueType)
? isNarrowableConditionalType(trueType)
: true;
if (!isValidTrueType) return false;
const falseType = getFalseTypeFromConditionalType(type);
const isValidFalseType = isConditionalType(falseType)
? isNarrowableConditionalType(falseType)
: falseType === neverType;
return isValidFalseType;
}
function isConditionalType(type: Type): type is ConditionalType {
return !!(type.flags & TypeFlags.Conditional);
}
function checkWithStatement(node: WithStatement) {
// Grammar checking for withStatement
if (!checkGrammarStatementInAmbientContext(node)) {

Просмотреть файл

@ -3481,6 +3481,8 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode
propagateChildFlags(node.whenTrue) |
propagateChildFlags(node.colonToken) |
propagateChildFlags(node.whenFalse);
node.flowNodeWhenFalse = undefined;
node.flowNodeWhenTrue = undefined;
return node;
}

Просмотреть файл

@ -2735,6 +2735,10 @@ export interface ConditionalExpression extends Expression {
readonly whenTrue: Expression;
readonly colonToken: ColonToken;
readonly whenFalse: Expression;
/** @internal*/
flowNodeWhenTrue: FlowNode | undefined;
/** @internal */
flowNodeWhenFalse: FlowNode | undefined;
}
export type FunctionBody = Block;
@ -6240,6 +6244,7 @@ export interface NodeLinks {
decoratorSignature?: Signature; // Signature for decorator as if invoked by the runtime.
spreadIndices?: { first: number | undefined, last: number | undefined }; // Indices of first and last spread elements in array literal
parameterInitializerContainsUndefined?: boolean; // True if this is a parameter declaration whose type annotation contains "undefined".
contextualReturnType?: Type; // If the node is a return statement's expression, then this is the contextual return type.
fakeScopeForSignatureDeclaration?: "params" | "typeParams"; // If present, this is a fake scope injected into an enclosing declaration chain.
assertionExpressionType?: Type; // Cached type of the expression of a type assertion
potentialThisCollisions?: Node[];
@ -6506,6 +6511,8 @@ export const enum ObjectFlags {
IsGenericIndexType = 1 << 23, // Union or intersection contains generic index type
/** @internal */
IsGenericType = IsGenericObjectType | IsGenericIndexType,
/** @internal */
IsNarrowingType = 1 << 24, // Substitution type that comes from type narrowing
// Flags that require TypeFlags.Union
/** @internal */
@ -6905,12 +6912,16 @@ export interface StringMappingType extends InstantiableType {
}
// Type parameter substitution (TypeFlags.Substitution)
// Substitution types are created for type parameters or indexed access types that occur in the
// - Substitution types are created for type parameters or indexed access types that occur in the
// true branch of a conditional type. For example, in 'T extends string ? Foo<T> : Bar<T>', the
// reference to T in Foo<T> is resolved as a substitution type that substitutes 'string & T' for T.
// Thus, if Foo has a 'string' constraint on its type parameter, T will satisfy it.
// Substitution type are also created for NoInfer<T> types. Those are represented as substitution
// - Substitution types are also created for NoInfer<T> types. Those are represented as substitution
// types where the constraint is type 'unknown' (which is never generated for the case above).
// - Substitution types are also created for return type narrowing:
// if a type parameter `T` is linked to a parameter `x` and `x`'s narrowed type is `S`,
// we represent that with a substitution type with base `T` and constraint `S`.
// The resulting substitution type has `ObjectFlags.IsNarrowedType` set.
export interface SubstitutionType extends InstantiableType {
objectFlags: ObjectFlags;
baseType: Type; // Target type

Просмотреть файл

@ -0,0 +1,13 @@
//// [tests/cases/compiler/arrowExpressionJs.ts] ////
=== mytest.js ===
/**
* @template T
* @param {T|undefined} value value or not
* @returns {T} result value
*/
const cloneObjectGood = value => /** @type {T} */({ ...value });
>cloneObjectGood : Symbol(cloneObjectGood, Decl(mytest.js, 5, 5))
>value : Symbol(value, Decl(mytest.js, 5, 23))
>value : Symbol(value, Decl(mytest.js, 5, 23))

Просмотреть файл

@ -0,0 +1,22 @@
//// [tests/cases/compiler/arrowExpressionJs.ts] ////
=== mytest.js ===
/**
* @template T
* @param {T|undefined} value value or not
* @returns {T} result value
*/
const cloneObjectGood = value => /** @type {T} */({ ...value });
>cloneObjectGood : <T>(value: T | undefined) => T
> : ^ ^^ ^^ ^^^^^
>value => /** @type {T} */({ ...value }) : <T>(value: T | undefined) => T
> : ^ ^^ ^^ ^^^^^
>value : T | undefined
> : ^^^^^^^^^^^^^
>({ ...value }) : T
> : ^
>{ ...value } : {}
> : ^^
>value : T | undefined
> : ^^^^^^^^^^^^^

Просмотреть файл

@ -0,0 +1,645 @@
dependentReturnType1.ts(11,9): error TS2322: Type 'number' is not assignable to type 'A[T]'.
Type 'number' is not assignable to type 'string'.
dependentReturnType1.ts(26,9): error TS2322: Type '""' is not assignable to type 'C[T]'.
Type '""' is not assignable to type 'never'.
dependentReturnType1.ts(35,9): error TS2322: Type '""' is not assignable to type 'never'.
dependentReturnType1.ts(69,9): error TS2322: Type '{ a: "a"; b: "b"; c: "c"; d: "d"; e: "e"; f: "f"; }' is not assignable to type 'T extends 1 ? One : T extends 2 ? Two : T extends 3 ? Three : Four'.
dependentReturnType1.ts(71,5): error TS2322: Type '{ a: "a"; b: "b"; c: "c"; d: "d"; e: "e"; f: "f"; g: "g"; }' is not assignable to type 'T extends 1 ? One : T extends 2 ? Two : T extends 3 ? Three : Four'.
dependentReturnType1.ts(80,22): error TS2353: Object literal may only specify known properties, and 'b' does not exist in type 'Three & Four'.
dependentReturnType1.ts(96,9): error TS2322: Type 'LeftOut' is not assignable to type 'Arg extends LeftIn ? LeftOut : Arg extends RightIn ? RightOut : never'.
dependentReturnType1.ts(98,9): error TS2322: Type 'RightOut' is not assignable to type 'Arg extends LeftIn ? LeftOut : Arg extends RightIn ? RightOut : never'.
dependentReturnType1.ts(115,9): error TS2322: Type 'number' is not assignable to type 'T extends Dog ? number : string'.
dependentReturnType1.ts(117,5): error TS2322: Type 'string' is not assignable to type 'T extends Dog ? number : string'.
dependentReturnType1.ts(152,13): error TS2322: Type 'string' is not assignable to type 'T extends string ? this : T extends undefined ? string : never'.
dependentReturnType1.ts(154,9): error TS2322: Type 'this' is not assignable to type 'T extends string ? this : T extends undefined ? string : never'.
Type 'Unnamed' is not assignable to type 'T extends string ? this : T extends undefined ? string : never'.
dependentReturnType1.ts(169,13): error TS2322: Type 'this' is not assignable to type 'string'.
Type 'Unnamed' is not assignable to type 'string'.
dependentReturnType1.ts(172,9): error TS2322: Type 'T & {}' is not assignable to type 'this'.
'this' could be instantiated with an arbitrary type which could be unrelated to 'T & {}'.
dependentReturnType1.ts(206,24): error TS2322: Type 'string' is not assignable to type 'number'.
dependentReturnType1.ts(206,28): error TS2322: Type 'number' is not assignable to type 'string'.
dependentReturnType1.ts(243,9): error TS2322: Type '""' is not assignable to type 'T extends 1 | 2 ? T extends 1 ? string : T extends 2 ? boolean : never : T extends 3 ? number : never'.
dependentReturnType1.ts(245,9): error TS2322: Type 'true' is not assignable to type 'T extends 1 | 2 ? T extends 1 ? string : T extends 2 ? boolean : never : T extends 3 ? number : never'.
dependentReturnType1.ts(247,5): error TS2322: Type '3' is not assignable to type 'T extends 1 | 2 ? T extends 1 ? string : T extends 2 ? boolean : never : T extends 3 ? number : never'.
dependentReturnType1.ts(275,9): error TS2322: Type '1' is not assignable to type 'HelperCond<{ x: U; y: V; }, { x: string; y: true; }, 1, { x: number; y: false; }, 2>'.
dependentReturnType1.ts(278,9): error TS2322: Type '2' is not assignable to type 'HelperCond<{ x: U; y: V; }, { x: string; y: true; }, 1, { x: number; y: false; }, 2>'.
dependentReturnType1.ts(280,5): error TS2322: Type '0' is not assignable to type 'HelperCond<{ x: U; y: V; }, { x: string; y: true; }, 1, { x: number; y: false; }, 2>'.
dependentReturnType1.ts(302,9): error TS2322: Type 'string' is not assignable to type 'string[]'.
dependentReturnType1.ts(311,9): error TS2322: Type 'undefined' is not assignable to type 'T extends {} ? void : T extends undefined ? number : never'.
dependentReturnType1.ts(313,5): error TS2322: Type 'number' is not assignable to type 'T extends {} ? void : T extends undefined ? number : never'.
dependentReturnType1.ts(334,9): error TS2322: Type '1' is not assignable to type '4'.
dependentReturnType1.ts(367,13): error TS2322: Type 'number' is not assignable to type 'T extends 1 ? number : T extends 2 ? string : never'.
dependentReturnType1.ts(369,9): error TS2322: Type 'string' is not assignable to type 'T extends 1 ? number : T extends 2 ? string : never'.
dependentReturnType1.ts(392,9): error TS2322: Type '2' is not assignable to type 'T extends true ? 1 : T extends false ? 2 : never'.
dependentReturnType1.ts(402,13): error TS2322: Type 'number' is not assignable to type 'string'.
dependentReturnType1.ts(412,9): error TS2322: Type 'true' is not assignable to type 'T extends [infer R] ? R : T extends number ? boolean : never'.
dependentReturnType1.ts(414,5): error TS2322: Type '""' is not assignable to type 'T extends [infer R] ? R : T extends number ? boolean : never'.
dependentReturnType1.ts(439,15): error TS2322: Type '1' is not assignable to type 'T extends true ? 1 : T extends false ? 2 : never'.
dependentReturnType1.ts(441,11): error TS2322: Type '2' is not assignable to type 'T extends true ? 1 : T extends false ? 2 : never'.
dependentReturnType1.ts(470,13): error TS2322: Type 'R' is not assignable to type 'ConditionalReturnType<T, R, EOp>'.
dependentReturnType1.ts(472,13): error TS2322: Type 'R' is not assignable to type 'ConditionalReturnType<T, R, EOp>'.
dependentReturnType1.ts(474,13): error TS2322: Type 'T' is not assignable to type 'ConditionalReturnType<T, R, EOp>'.
dependentReturnType1.ts(488,9): error TS2322: Type 'R' is not assignable to type 'ConditionalReturnType<T, R, EOp>'.
dependentReturnType1.ts(514,5): error TS2322: Type '1' is not assignable to type 'never'.
==== dependentReturnType1.ts (39 errors) ====
interface A {
1: number;
2: string;
}
function f1<T extends 1 | 2>(x: T): A[T] {
if (x === 1) {
return 0; // Ok
}
else {
return 1; // Error
~~~~~~
!!! error TS2322: Type 'number' is not assignable to type 'A[T]'.
!!! error TS2322: Type 'number' is not assignable to type 'string'.
}
}
interface C {
1: number;
2: string;
3: boolean;
}
function f2<T extends 1 | 2 | 3>(x: T): C[T] {
if (x === 1) {
return 0; // Ok
}
else {
return ""; // Error, returned expression needs to have type string & boolean (= never)
~~~~~~
!!! error TS2322: Type '""' is not assignable to type 'C[T]'.
!!! error TS2322: Type '""' is not assignable to type 'never'.
}
}
function f3<T extends 1 | 2 | 3>(x: T): T extends 1 ? number : T extends 2 ? string : T extends 3 ? boolean : never {
if (x === 1) {
return 0; // Ok
}
else {
return ""; // Error, returned expression needs to have type string & boolean (= never)
~~~~~~
!!! error TS2322: Type '""' is not assignable to type 'never'.
}
}
interface One {
a: "a";
b: "b";
c: "c";
d: "d";
}
interface Two {
a: "a";
b: "b";
e: "e";
f: "f";
}
interface Three {
a: "a";
c: "c";
e: "e";
g: "g";
}
interface Four {
a: "a";
d: "d";
f: "f";
g: "g";
}
// Badly written conditional return type, will not trigger narrowing
function f10<T extends 1 | 2 | 3 | 4>(x: T): T extends 1 ? One : T extends 2 ? Two : T extends 3 ? Three : Four {
if (x === 1 || x === 2) {
return { a: "a", b: "b", c: "c", d: "d", e: "e", f: "f" }; // Error
~~~~~~
!!! error TS2322: Type '{ a: "a"; b: "b"; c: "c"; d: "d"; e: "e"; f: "f"; }' is not assignable to type 'T extends 1 ? One : T extends 2 ? Two : T extends 3 ? Three : Four'.
}
return { a: "a", b: "b", c: "c", d: "d", e: "e", f: "f", g: "g" }; // Error
~~~~~~
!!! error TS2322: Type '{ a: "a"; b: "b"; c: "c"; d: "d"; e: "e"; f: "f"; g: "g"; }' is not assignable to type 'T extends 1 ? One : T extends 2 ? Two : T extends 3 ? Three : Four'.
}
// Well written conditional
function f101<T extends 1 | 2 | 3 | 4>(x: T): T extends 1 ? One : T extends 2 ? Two : T extends 3 ? Three : T extends 4 ? Four : never {
if (x === 1 || x === 2) {
return { a: "a", b: "b", c: "c", d: "d", e: "e", f: "f" }; // Ok
}
// Excess property becomes a problem with the change,
// because we now check assignability to a narrower type...
return { a: "a", b: "b", c: "c", d: "d", e: "e", f: "f", g: "g" }; // EPC Error
~
!!! error TS2353: Object literal may only specify known properties, and 'b' does not exist in type 'Three & Four'.
}
// This will not work for several reasons:
// - first because the constraint of type parameter `Arg` is generic,
// so attempting to narrow the type of `arg` in the `if` would result in type `Arg & LeftIn`,
// which when substituted in the conditional return type, would not further resolve that conditional type
// - second because the `else` branch would never work because we don't narrow the type of `arg` to `Arg & RightIn`
function conditionalProducingIf<LeftIn, RightIn, LeftOut, RightOut, Arg extends LeftIn | RightIn>(
arg: Arg,
cond: (arg: LeftIn | RightIn) => arg is LeftIn,
produceLeftOut: (arg: LeftIn) => LeftOut,
produceRightOut: (arg: RightIn) => RightOut):
Arg extends LeftIn ? LeftOut : Arg extends RightIn ? RightOut : never
{
if (cond(arg)) {
return produceLeftOut(arg);
~~~~~~
!!! error TS2322: Type 'LeftOut' is not assignable to type 'Arg extends LeftIn ? LeftOut : Arg extends RightIn ? RightOut : never'.
} else {
return produceRightOut(arg as RightIn);
~~~~~~
!!! error TS2322: Type 'RightOut' is not assignable to type 'Arg extends LeftIn ? LeftOut : Arg extends RightIn ? RightOut : never'.
}
}
interface Animal {
name: string;
}
interface Dog extends Animal {
bark: () => string;
}
// This would be unsafe to narrow.
declare function isDog(x: Animal): x is Dog;
declare function doggy(x: Dog): number;
function f12<T extends Animal>(x: T): T extends Dog ? number : string {
if (isDog(x)) { // `x` has type `T & Dog` here
return doggy(x);
~~~~~~
!!! error TS2322: Type 'number' is not assignable to type 'T extends Dog ? number : string'.
}
return ""; // Error: Should not work because we can't express "not a Dog" in the type system
~~~~~~
!!! error TS2322: Type 'string' is not assignable to type 'T extends Dog ? number : string'.
}
// Cannot narrow `keyof` too eagerly or something like the below breaks
function f<Entry extends { [index: string]: number | boolean }, EntryId extends keyof Entry>(entry: EntryId): Entry[EntryId] {
const entries = {} as Entry;
return entries[entry];
}
// Works the same as before
declare function takeA(val: 'A'): void;
export function bounceAndTakeIfA<AB extends 'A' | 'B'>(value: AB): AB {
if (value === 'A') {
takeA(value);
takeAB(value);
return value;
}
return value;
function takeAB(val: AB): void {}
}
// Works the same as before
export function bbb<AB extends "a" | "b">(value: AB): "a" {
if (value === "a") {
return value;
}
return "a";
}
class Unnamed {
root!: { name: string };
// Error: No narrowing because parameter is optional but `T` doesn't allow for undefined
name<T extends string>(name?: T): T extends string ? this : T extends undefined ? string : never {
if (typeof name === 'undefined') {
return this.root.name;
~~~~~~
!!! error TS2322: Type 'string' is not assignable to type 'T extends string ? this : T extends undefined ? string : never'.
}
return this;
~~~~~~
!!! error TS2322: Type 'this' is not assignable to type 'T extends string ? this : T extends undefined ? string : never'.
!!! error TS2322: Type 'Unnamed' is not assignable to type 'T extends string ? this : T extends undefined ? string : never'.
}
// Good conditional
name2<T extends string | undefined>(name?: T): T extends string ? this : T extends undefined ? string : never {
if (typeof name === 'undefined') {
return this.root.name; // Ok
}
this.root.name = name;
return this; // Ok
}
// Good conditional, wrong return expressions
name3<T extends string | undefined>(name?: T): T extends string ? this : T extends undefined ? string : never {
if (typeof name === 'undefined') {
return this; // Error
~~~~~~
!!! error TS2322: Type 'this' is not assignable to type 'string'.
!!! error TS2322: Type 'Unnamed' is not assignable to type 'string'.
}
this.root.name = name;
return name; // Error
~~~~~~
!!! error TS2322: Type 'T & {}' is not assignable to type 'this'.
!!! error TS2322: 'this' could be instantiated with an arbitrary type which could be unrelated to 'T & {}'.
}
}
// Conditional expressions
interface Aa {
1: number;
2: string;
3: boolean;
}
function trivialConditional<T extends 1 | 2 | 3>(x: T): Aa[T] {
if (x !== 1) {
return x === 2 ? "" : true;
}
else {
return 0;
}
}
function conditional<T extends boolean>(x: T):
T extends true ? 1 : T extends false ? 2 : never {
return x ? 1 : 2; // Ok
}
function contextualConditional<T extends "a" | "b">(
x: T
): T extends "a" ? "a" : T extends "b" ? number : never {
return x === "a" ? x : parseInt(x); // Ok
}
function conditionalWithError<T extends "a" | "b">(
x: T
): T extends "a" ? number : T extends "b" ? string : never {
return x === "a" ? x : parseInt(x); // Error
~
!!! error TS2322: Type 'string' is not assignable to type 'number'.
~~~~~~~~~~~
!!! error TS2322: Type 'number' is not assignable to type 'string'.
}
// Multiple indexed type reductions
interface BB {
"a": number;
[y: number]: string;
}
interface AA<T extends keyof BB> {
"c": BB[T];
"d": boolean,
}
function reduction<T extends keyof BB, U extends "c" | "d">(x: T, y: U): AA<T>[U] {
if (y === "c" && x === "a") {
// AA<T>[U='c'] -> BB[T]
// BB[T='a'] -> number
return 0; // Ok
}
return undefined as never;
}
// Substitution types are not narrowed
function subsCond<T extends 1 | 2 | 3>(
x: T,
): T extends 1 | 2
? T extends 1
? string
: T extends 2
? boolean
: never
: T extends 3
? number
: never {
if (x === 1) {
return "";
~~~~~~
!!! error TS2322: Type '""' is not assignable to type 'T extends 1 | 2 ? T extends 1 ? string : T extends 2 ? boolean : never : T extends 3 ? number : never'.
} else if (x == 2) {
return true;
~~~~~~
!!! error TS2322: Type 'true' is not assignable to type 'T extends 1 | 2 ? T extends 1 ? string : T extends 2 ? boolean : never : T extends 3 ? number : never'.
}
return 3;
~~~~~~
!!! error TS2322: Type '3' is not assignable to type 'T extends 1 | 2 ? T extends 1 ? string : T extends 2 ? boolean : never : T extends 3 ? number : never'.
}
// Unsafe: check types overlap
declare function q(x: object): x is { b: number };
function foo<T extends { a: string } | { b: number }>(
x: T,
): T extends { a: string } ? number : T extends { b: number } ? string : never {
if (q(x)) {
x.b;
return "";
}
x.a;
return 1;
}
let y = { a: "", b: 1 }
const r = foo<{ a: string }>(y); // type says number but actually string
type HelperCond<T, A, R1, B, R2> = T extends A ? R1 : T extends B ? R2 : never;
// We don't narrow the return type because the conditionals are not distributive
function foo2<U extends string | number, V extends boolean>(x: U, y: V):
HelperCond<{ x: U, y: V },
{ x: string, y: true }, 1,
{ x: number, y: false }, 2> {
if (typeof x === "string" && y === true) {
return 1; // Error
~~~~~~
!!! error TS2322: Type '1' is not assignable to type 'HelperCond<{ x: U; y: V; }, { x: string; y: true; }, 1, { x: number; y: false; }, 2>'.
}
if (typeof x === "number" && y === false) {
return 2; // Error
~~~~~~
!!! error TS2322: Type '2' is not assignable to type 'HelperCond<{ x: U; y: V; }, { x: string; y: true; }, 1, { x: number; y: false; }, 2>'.
}
return 0; // Error
~~~~~~
!!! error TS2322: Type '0' is not assignable to type 'HelperCond<{ x: U; y: V; }, { x: string; y: true; }, 1, { x: number; y: false; }, 2>'.
}
// From https://github.com/microsoft/TypeScript/issues/24929#issue-332087943
declare function isString(s: unknown): s is string;
// capitalize a string or each element of an array of strings
function capitalize<T extends string | string[]>(
input: T
): T extends string[] ? string[] : T extends string ? string : never {
if (isString(input)) {
return input[0].toUpperCase() + input.slice(1); // Ok
} else {
return input.map(elt => capitalize(elt)); // Ok
}
}
function badCapitalize<T extends string | string[]>(
input: T
): T extends string[] ? string[] : T extends string ? string : never {
if (isString(input)) {
return input[0].toUpperCase() + input.slice(1); // Ok
} else {
return input[0].toUpperCase() + input.slice(1); // Bad, error
~~~~~~
!!! error TS2322: Type 'string' is not assignable to type 'string[]'.
}
}
// No narrowing because conditional's extends type is different from type parameter constraint types
function voidRet<T extends { a: string } | undefined>(
x: T
): T extends {} ? void : T extends undefined ? number : never {
if (x) {
return;
~~~~~~
!!! error TS2322: Type 'undefined' is not assignable to type 'T extends {} ? void : T extends undefined ? number : never'.
}
return 1;
~~~~~~
!!! error TS2322: Type 'number' is not assignable to type 'T extends {} ? void : T extends undefined ? number : never'.
}
// Multiple type parameters at once
function woo<T extends string | number, U extends string | number>(
x: T,
y: U,
): T extends string
? U extends string
? 1
: U extends number
? 2
: never
: T extends number
? U extends number
? 3
: U extends string
? 4
: never
: never {
if (typeof x === "number" && typeof y === "string") {
return 1; // Good error
~~~~~~
!!! error TS2322: Type '1' is not assignable to type '4'.
}
return undefined as any;
}
function ttt<T extends string | number, U extends string | number>(
x: T,
y: U,
): T extends string
? U extends string
? 1
: U extends number
? 2
: never
: T extends number
? U extends number
? 3
: U extends string
? 4
: never
: never {
if (typeof x === "number" && typeof y === "string") {
return 4; // Ok
}
return undefined as any;
}
// Shadowing of the narrowed reference
function shadowing<T extends 1 | 2>(x: T): T extends 1 ? number : T extends 2 ? string : never {
if (true) {
let x: number = Math.random() ? 1 : 2;
if (x === 1) {
return 1; // Error
~~~~~~
!!! error TS2322: Type 'number' is not assignable to type 'T extends 1 ? number : T extends 2 ? string : never'.
}
return ""; // Error
~~~~~~
!!! error TS2322: Type 'string' is not assignable to type 'T extends 1 ? number : T extends 2 ? string : never'.
}
}
function noShadowing<T extends 1 | 2>(x: T): T extends 1 ? number : T extends 2 ? string : never {
if (true) {
if (x === 1) {
return 1; // Ok
}
return ""; // Ok
}
}
// If the narrowing reference is out of scope, we simply won't narrow its type
declare let someX: boolean;
function scope2<T extends boolean>(a: T): T extends true ? 1 : T extends false ? 2 : never {
if ((true)) {
const someX = a;
if (someX) { // We narrow `someX` and the return type here
return 1;
}
}
if (!someX) { // This is a different `someX`, so we don't narrow here
return 2;
~~~~~~
!!! error TS2322: Type '2' is not assignable to type 'T extends true ? 1 : T extends false ? 2 : never'.
}
return undefined as any;
}
function moreShadowing<T extends 1 | 2>(x: T): T extends 1 ? number : T extends 2 ? string : never {
if (x === 2) {
let x: number = Math.random() ? 1 : 2;
if (x === 1) {
return 1; // Error
~~~~~~
!!! error TS2322: Type 'number' is not assignable to type 'string'.
}
return ""; // Ok
}
return 0; // Ok
}
// This would be unsafe to narrow due to `infer` type.
function withInfer<T extends [string] | number>(x: T): T extends [infer R] ? R : T extends number ? boolean : never {
if (typeof x === "number") {
return true;
~~~~~~
!!! error TS2322: Type 'true' is not assignable to type 'T extends [infer R] ? R : T extends number ? boolean : never'.
}
return "";
~~~~~~
!!! error TS2322: Type '""' is not assignable to type 'T extends [infer R] ? R : T extends number ? boolean : never'.
}
const withInferResult = withInfer(["a"] as const); // The type says it returns `"a"`, but the function actually returns `""`.
// Ok
async function abool<T extends true | false>(x: T): Promise<T extends true ? 1 : T extends false ? 2 : never> {
if (x) {
return 1;
}
return 2;
}
// Ok
function* bbool<T extends true | false>(x: T): Generator<number, T extends true ? 1 : T extends false ? 2 : never, unknown> {
yield 3;
if (x) {
return 1;
}
return 2;
}
// We don't do the same type of narrowing for `yield` statements
function* cbool<T extends true | false>(x: T): Generator<T extends true ? 1 : T extends false ? 2 : never, number, unknown> {
if (x) {
yield 1;
~
!!! error TS2322: Type '1' is not assignable to type 'T extends true ? 1 : T extends false ? 2 : never'.
}
yield 2;
~
!!! error TS2322: Type '2' is not assignable to type 'T extends true ? 1 : T extends false ? 2 : never'.
return 0;
}
// From #33912
abstract class Operation<T, R> {
abstract perform(t: T): R;
}
type ConditionalReturnType<T, R, EOp extends Operation<T, R> | undefined> =
EOp extends Operation<T, R> ? R : EOp extends undefined ? T | R : never;
class ConditionalOperation<
T,
R,
EOp extends Operation<T, R> | undefined,
> extends Operation<T, ConditionalReturnType<T, R, EOp>> {
constructor(
private predicate: (value: T) => boolean,
private thenOp: Operation<T, R>,
private elseOp?: EOp,
) {
super();
}
// We won't try to narrow the return type because `T` is declared on the class and we don't analyze this case.
perform(t: T): ConditionalReturnType<T, R, EOp> {
if (this.predicate(t)) {
return this.thenOp.perform(t); // Bad: this is assignable to all of the branches of the conditional, but we still can't return it
~~~~~~
!!! error TS2322: Type 'R' is not assignable to type 'ConditionalReturnType<T, R, EOp>'.
} else if (typeof this.elseOp !== "undefined") {
return this.elseOp.perform(t); // Would be ok
~~~~~~
!!! error TS2322: Type 'R' is not assignable to type 'ConditionalReturnType<T, R, EOp>'.
} else {
return t; // Would be ok
~~~~~~
!!! error TS2322: Type 'T' is not assignable to type 'ConditionalReturnType<T, R, EOp>'.
}
}
}
// Like the version above, we will not attempt to narrow because there's more than one reference to `T`,
// because `T` shows up in the type of `predicate`.
function perform<T, R, EOp extends Operation<T, R> | undefined>(
t: T,
predicate: (value: T) => boolean,
thenOp: Operation<T, R>,
elseOp?: EOp,
): ConditionalReturnType<T, R, EOp> {
if (predicate(t)) {
return thenOp.perform(t); // Bad: this is assignable to all of the branches of the conditional, but we still can't return it
~~~~~~
!!! error TS2322: Type 'R' is not assignable to type 'ConditionalReturnType<T, R, EOp>'.
} else if (elseOp !== undefined) {
return elseOp.perform(t); // Would be ok
} else {
return t; // Would be ok
}
}
// Return conditional expressions with parentheses
function returnStuff1<T extends boolean>(x: T ): T extends true ? 1 : T extends false ? 2 : never {
return (x ? (1) : 2);
}
function returnStuff2<T extends 1 | 2 | "a">(x: T ):
T extends 1 ? "one" : T extends 2 ? "two" : T extends "a" ? 0 : never {
return (typeof x === "string" ? 0 : (x === 1 ? ("one") : "two"));
}
// If the conditional type's input is `never`, then it resolves to `never`:
function neverOk<T extends boolean>(x: T): T extends true ? 1 : T extends false ? 2 : never {
if (x === true) {
return 1;
}
if (x === false) {
return 2;
}
return 1;
~~~~~~
!!! error TS2322: Type '1' is not assignable to type 'never'.
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,314 @@
file.js(155,13): error TS2322: Type 'undefined' is not assignable to type 'HelperCond<I, string, T | undefined, RegExp, SettingComposedValue<T>[]>'.
file.js(168,16): error TS2536: Type 'I' cannot be used to index type '{ [s: string]: any; }'.
file.js(185,9): error TS2322: Type 'Record<string, MyObj[]>' is not assignable to type 'HelperCond<T, string, MyObj[], undefined, Record<string, MyObj[]>>'.
==== file.js (3 errors) ====
// Adapted from ts-error-deltas repos
/**
* @template T
* @template A
* @template R1
* @template B
* @template R2
* @typedef {T extends A ? R1 : T extends B ? R2 : never} HelperCond
*/
/**
* @typedef IMessage
* @property {string} [html]
* @property {Object[]} [tokens]
*/
class NewKatex {
/**
* @param {string} s
* @returns {string}
*/
render(s) {
return "";
}
/**
* @template {string | IMessage} T
* @param {T} message
* @returns {T extends string ? string : T extends IMessage ? IMessage : never}
*/
renderMessage(message) {
if (typeof message === 'string') {
return this.render(message); // Ok
}
if (!message.html?.trim()) {
return message; // Ok
}
if (!message.tokens) {
message.tokens = [];
}
message.html = this.render(message.html);
return message; // Ok
}
}
/**
* @template {true | false} T
* @param {{ dollarSyntax: boolean; parenthesisSyntax: boolean; }} options
* @param {T} _isMessage
* @returns {T extends true ? (message: IMessage) => IMessage : T extends false ? (message: string) => string : never}
*/
function createKatexMessageRendering(options, _isMessage) {
const instance = new NewKatex();
if (_isMessage) {
return (/** @type {IMessage} */ message) => instance.renderMessage(message); // Ok
}
return (/** @type {string} */ message) => instance.renderMessage(message); // Ok
}
// File: Rocket.Chat/apps/meteor/app/settings/lib/settings.ts
/**
* @typedef {Record<any, any>} MyObj
*/
/**
* @typedef {MyObj} SettingValue
*/
/**
* @template {SettingValue} T
* @typedef {Object} SettingComposedValue
* @property {string} key
* @property {SettingValue} value
*/
/**
* @callback SettingCallback
* @param {string} key
* @param {SettingValue} value
* @param {boolean} [initialLoad]
* @returns {void}
*/
/** @type {{ settings: { [s: string]: any } }} */
const Meteor = /** @type {any} */ (undefined);
/** @type {{ isRegExp(x: unknown): x is RegExp; }} */
const _ = /** @type {any} */ (undefined);
/**
* @param {RegExp} x
* @returns {void}
*/
function takesRegExp(x) {
return /** @type {any} */ undefined;
}
/**
* @param {string} x
* @returns {void}
*/
function takesString(x) {
return /** @type {any} */ undefined;
}
/**
* @class NewSettingsBase
*/
class NewSettingsBase {
/**
* @template {SettingCallback | undefined} C
* @template {string | RegExp} I
* @template {SettingValue} T
* @param {I} _id
* @param {C} [callback]
* @returns {HelperCond<C, SettingCallback, void, undefined, HelperCond<I, string, T | undefined, RegExp, SettingComposedValue<T>[]>>}
*/
newGet(_id, callback) {
if (callback !== undefined) {
if (!Meteor.settings) {
return; // Ok
}
if (_id === '*') {
return Object.keys(Meteor.settings).forEach((key) => {
const value = Meteor.settings[key];
callback(key, value);
});
}
if (_.isRegExp(_id) && Meteor.settings) {
return Object.keys(Meteor.settings).forEach((key) => {
if (!_id.test(key)) {
return;
}
const value = Meteor.settings[key];
callback(key, value);
});
}
if (typeof _id === 'string') {
const value = Meteor.settings[_id];
if (value != null) {
callback(_id, Meteor.settings[_id]);
}
return; // Ok
}
return; // Ok, needed for exhaustiveness check
}
if (!Meteor.settings) {
return undefined; // Error
~~~~~~
!!! error TS2322: Type 'undefined' is not assignable to type 'HelperCond<I, string, T | undefined, RegExp, SettingComposedValue<T>[]>'.
}
if (_.isRegExp(_id)) {
return Object.keys(Meteor.settings).reduce((/** @type {SettingComposedValue<T>[]} */ items, key) => {
const value = Meteor.settings[key];
if (_id.test(key)) {
items.push({ key, value });
}
return items;
}, []); // Ok
}
return Meteor.settings?.[_id]; // Error
~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2536: Type 'I' cannot be used to index type '{ [s: string]: any; }'.
}
}
// File: Rocket.Chat/apps/meteor/app/ui-utils/client/lib/messageBox.ts
/**
* @typedef {MyObj} MessageBoxAction
*/
/**
* @template {string | undefined} T
* @param {T} group
* @returns {HelperCond<T, string, MessageBoxAction[], undefined, Record<string, MessageBoxAction[]>>}
*/
function getWithBug(group) {
if (!group) {
return /** @type {Record<string, MessageBoxAction[]>} */({}); // Error
~~~~~~
!!! error TS2322: Type 'Record<string, MyObj[]>' is not assignable to type 'HelperCond<T, string, MyObj[], undefined, Record<string, MyObj[]>>'.
}
return /** @type {MessageBoxAction[]} */([]); // Ok
}
/**
* @template {string | undefined} T
* @param {T} group
* @returns {HelperCond<T, string, MessageBoxAction[], undefined, Record<string, MessageBoxAction[]>>}
*/
function getWithoutBug(group) {
if (group === undefined) {
return /** @type {Record<string, MessageBoxAction[]>} */({}); // Ok
}
return /** @type {MessageBoxAction[]} */([]); // Ok
}
// File: Rocket.Chat/apps/meteor/ee/server/lib/engagementDashboard/date.ts
/**
* @param {string} x
* @returns {Date}
*/
function mapDateForAPI(x) {
return /** @type {any} */ (undefined);
}
/**
* @template {string | undefined} T
* @param {string} start
* @param {T} [end]
* @returns {HelperCond<T, string, { start: Date, end: Date }, undefined, { start: Date, end: undefined }>}
*/
function transformDatesForAPI(start, end) {
return end !== undefined ?
{
start: mapDateForAPI(start),
end: mapDateForAPI(end),
} :
{
start: mapDateForAPI(start),
end: undefined
};
}
// File: Rocket.Chat/packages/agenda/src/Agenda.ts
/**
* @typedef {MyObj} RepeatOptions
*/
/**
* @typedef {MyObj} Job
*/
/**
* @typedef {Object} IJob
* @property {MyObj} data
*/
class NewAgenda {
/**
* @param {string | number} interval
* @param {string} name
* @param {IJob['data']} data
* @param {RepeatOptions} options
* @returns {Promise<Job>}
*/
async _createIntervalJob(interval, name, data, options) {
return /** @type {any} */ (undefined);
}
/**
* @param {string | number} interval
* @param {string[]} names
* @param {IJob['data']} data
* @param {RepeatOptions} options
* @returns {Promise<Job[]> | undefined}
*/
_createIntervalJobs(interval, names, data, options) {
return undefined;
}
/**
* @template {string | string[]} T
* @param {string | number} interval
* @param {T} name
* @param {IJob['data']} data
* @param {RepeatOptions} options
* @returns {Promise<HelperCond<T, string, Job, string[], Job[] | undefined>>}
*/
async newEvery(interval, name, data, options) {
if (typeof name === 'string') {
return this._createIntervalJob(interval, name, data, options); // Ok
}
if (Array.isArray(name)) {
return this._createIntervalJobs(interval, name, data, options); // Ok
}
throw new Error('Unexpected error: Invalid job name(s)');
}
}
// File: angular/packages/common/src/pipes/case_conversion_pipes.ts
/**
* @template {string | null | undefined} T
* @param {T} value
* @returns {HelperCond<T, string, string, null | undefined, null>}
*/
function transform1(value) {
if (value == null) return null; // Ok
if (typeof value !== 'string') {
throw new Error();
}
return value.toLowerCase(); // Ok
}

Просмотреть файл

@ -0,0 +1,594 @@
//// [tests/cases/compiler/dependentReturnType2.ts] ////
=== file.js ===
// Adapted from ts-error-deltas repos
/**
* @template T
* @template A
* @template R1
* @template B
* @template R2
* @typedef {T extends A ? R1 : T extends B ? R2 : never} HelperCond
*/
/**
* @typedef IMessage
* @property {string} [html]
* @property {Object[]} [tokens]
*/
class NewKatex {
>NewKatex : Symbol(NewKatex, Decl(file.js, 0, 0))
/**
* @param {string} s
* @returns {string}
*/
render(s) {
>render : Symbol(NewKatex.render, Decl(file.js, 17, 16))
>s : Symbol(s, Decl(file.js, 22, 11))
return "";
}
/**
* @template {string | IMessage} T
* @param {T} message
* @returns {T extends string ? string : T extends IMessage ? IMessage : never}
*/
renderMessage(message) {
>renderMessage : Symbol(NewKatex.renderMessage, Decl(file.js, 24, 5))
>message : Symbol(message, Decl(file.js, 31, 18))
if (typeof message === 'string') {
>message : Symbol(message, Decl(file.js, 31, 18))
return this.render(message); // Ok
>this.render : Symbol(NewKatex.render, Decl(file.js, 17, 16))
>this : Symbol(NewKatex, Decl(file.js, 0, 0))
>render : Symbol(NewKatex.render, Decl(file.js, 17, 16))
>message : Symbol(message, Decl(file.js, 31, 18))
}
if (!message.html?.trim()) {
>message.html?.trim : Symbol(String.trim, Decl(lib.es5.d.ts, --, --))
>message.html : Symbol(html, Decl(file.js, 13, 3))
>message : Symbol(message, Decl(file.js, 31, 18))
>html : Symbol(html, Decl(file.js, 13, 3))
>trim : Symbol(String.trim, Decl(lib.es5.d.ts, --, --))
return message; // Ok
>message : Symbol(message, Decl(file.js, 31, 18))
}
if (!message.tokens) {
>message.tokens : Symbol(tokens, Decl(file.js, 14, 3))
>message : Symbol(message, Decl(file.js, 31, 18))
>tokens : Symbol(tokens, Decl(file.js, 14, 3))
message.tokens = [];
>message.tokens : Symbol(tokens, Decl(file.js, 14, 3))
>message : Symbol(message, Decl(file.js, 31, 18))
>tokens : Symbol(tokens, Decl(file.js, 14, 3))
}
message.html = this.render(message.html);
>message.html : Symbol(html, Decl(file.js, 13, 3))
>message : Symbol(message, Decl(file.js, 31, 18))
>html : Symbol(html, Decl(file.js, 13, 3))
>this.render : Symbol(NewKatex.render, Decl(file.js, 17, 16))
>this : Symbol(NewKatex, Decl(file.js, 0, 0))
>render : Symbol(NewKatex.render, Decl(file.js, 17, 16))
>message.html : Symbol(html, Decl(file.js, 13, 3))
>message : Symbol(message, Decl(file.js, 31, 18))
>html : Symbol(html, Decl(file.js, 13, 3))
return message; // Ok
>message : Symbol(message, Decl(file.js, 31, 18))
}
}
/**
* @template {true | false} T
* @param {{ dollarSyntax: boolean; parenthesisSyntax: boolean; }} options
* @param {T} _isMessage
* @returns {T extends true ? (message: IMessage) => IMessage : T extends false ? (message: string) => string : never}
*/
function createKatexMessageRendering(options, _isMessage) {
>createKatexMessageRendering : Symbol(createKatexMessageRendering, Decl(file.js, 47, 1))
>options : Symbol(options, Decl(file.js, 55, 37))
>_isMessage : Symbol(_isMessage, Decl(file.js, 55, 45))
const instance = new NewKatex();
>instance : Symbol(instance, Decl(file.js, 56, 9))
>NewKatex : Symbol(NewKatex, Decl(file.js, 0, 0))
if (_isMessage) {
>_isMessage : Symbol(_isMessage, Decl(file.js, 55, 45))
return (/** @type {IMessage} */ message) => instance.renderMessage(message); // Ok
>message : Symbol(message, Decl(file.js, 58, 16))
>instance.renderMessage : Symbol(NewKatex.renderMessage, Decl(file.js, 24, 5))
>instance : Symbol(instance, Decl(file.js, 56, 9))
>renderMessage : Symbol(NewKatex.renderMessage, Decl(file.js, 24, 5))
>message : Symbol(message, Decl(file.js, 58, 16))
}
return (/** @type {string} */ message) => instance.renderMessage(message); // Ok
>message : Symbol(message, Decl(file.js, 60, 12))
>instance.renderMessage : Symbol(NewKatex.renderMessage, Decl(file.js, 24, 5))
>instance : Symbol(instance, Decl(file.js, 56, 9))
>renderMessage : Symbol(NewKatex.renderMessage, Decl(file.js, 24, 5))
>message : Symbol(message, Decl(file.js, 60, 12))
}
// File: Rocket.Chat/apps/meteor/app/settings/lib/settings.ts
/**
* @typedef {Record<any, any>} MyObj
*/
/**
* @typedef {MyObj} SettingValue
*/
/**
* @template {SettingValue} T
* @typedef {Object} SettingComposedValue
* @property {string} key
* @property {SettingValue} value
*/
/**
* @callback SettingCallback
* @param {string} key
* @param {SettingValue} value
* @param {boolean} [initialLoad]
* @returns {void}
*/
/** @type {{ settings: { [s: string]: any } }} */
const Meteor = /** @type {any} */ (undefined);
>Meteor : Symbol(Meteor, Decl(file.js, 90, 5))
>undefined : Symbol(undefined)
/** @type {{ isRegExp(x: unknown): x is RegExp; }} */
const _ = /** @type {any} */ (undefined);
>_ : Symbol(_, Decl(file.js, 92, 5))
>undefined : Symbol(undefined)
/**
* @param {RegExp} x
* @returns {void}
*/
function takesRegExp(x) {
>takesRegExp : Symbol(takesRegExp, Decl(file.js, 92, 41))
>x : Symbol(x, Decl(file.js, 98, 21))
return /** @type {any} */ undefined;
>undefined : Symbol(undefined)
}
/**
* @param {string} x
* @returns {void}
*/
function takesString(x) {
>takesString : Symbol(takesString, Decl(file.js, 100, 1))
>x : Symbol(x, Decl(file.js, 105, 21))
return /** @type {any} */ undefined;
>undefined : Symbol(undefined)
}
/**
* @class NewSettingsBase
*/
class NewSettingsBase {
>NewSettingsBase : Symbol(NewSettingsBase, Decl(file.js, 107, 1))
/**
* @template {SettingCallback | undefined} C
* @template {string | RegExp} I
* @template {SettingValue} T
* @param {I} _id
* @param {C} [callback]
* @returns {HelperCond<C, SettingCallback, void, undefined, HelperCond<I, string, T | undefined, RegExp, SettingComposedValue<T>[]>>}
*/
newGet(_id, callback) {
>newGet : Symbol(NewSettingsBase.newGet, Decl(file.js, 112, 23))
>_id : Symbol(_id, Decl(file.js, 121, 11))
>callback : Symbol(callback, Decl(file.js, 121, 15))
if (callback !== undefined) {
>callback : Symbol(callback, Decl(file.js, 121, 15))
>undefined : Symbol(undefined)
if (!Meteor.settings) {
>Meteor.settings : Symbol(settings, Decl(file.js, 89, 12))
>Meteor : Symbol(Meteor, Decl(file.js, 90, 5))
>settings : Symbol(settings, Decl(file.js, 89, 12))
return; // Ok
}
if (_id === '*') {
>_id : Symbol(_id, Decl(file.js, 121, 11))
return Object.keys(Meteor.settings).forEach((key) => {
>Object.keys(Meteor.settings).forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --))
>Object.keys : Symbol(ObjectConstructor.keys, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --))
>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>keys : Symbol(ObjectConstructor.keys, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --))
>Meteor.settings : Symbol(settings, Decl(file.js, 89, 12))
>Meteor : Symbol(Meteor, Decl(file.js, 90, 5))
>settings : Symbol(settings, Decl(file.js, 89, 12))
>forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --))
>key : Symbol(key, Decl(file.js, 127, 61))
const value = Meteor.settings[key];
>value : Symbol(value, Decl(file.js, 128, 25))
>Meteor.settings : Symbol(settings, Decl(file.js, 89, 12))
>Meteor : Symbol(Meteor, Decl(file.js, 90, 5))
>settings : Symbol(settings, Decl(file.js, 89, 12))
>key : Symbol(key, Decl(file.js, 127, 61))
callback(key, value);
>callback : Symbol(callback, Decl(file.js, 121, 15))
>key : Symbol(key, Decl(file.js, 127, 61))
>value : Symbol(value, Decl(file.js, 128, 25))
});
}
if (_.isRegExp(_id) && Meteor.settings) {
>_.isRegExp : Symbol(isRegExp, Decl(file.js, 91, 12))
>_ : Symbol(_, Decl(file.js, 92, 5))
>isRegExp : Symbol(isRegExp, Decl(file.js, 91, 12))
>_id : Symbol(_id, Decl(file.js, 121, 11))
>Meteor.settings : Symbol(settings, Decl(file.js, 89, 12))
>Meteor : Symbol(Meteor, Decl(file.js, 90, 5))
>settings : Symbol(settings, Decl(file.js, 89, 12))
return Object.keys(Meteor.settings).forEach((key) => {
>Object.keys(Meteor.settings).forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --))
>Object.keys : Symbol(ObjectConstructor.keys, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --))
>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>keys : Symbol(ObjectConstructor.keys, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --))
>Meteor.settings : Symbol(settings, Decl(file.js, 89, 12))
>Meteor : Symbol(Meteor, Decl(file.js, 90, 5))
>settings : Symbol(settings, Decl(file.js, 89, 12))
>forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --))
>key : Symbol(key, Decl(file.js, 133, 61))
if (!_id.test(key)) {
>_id.test : Symbol(RegExp.test, Decl(lib.es5.d.ts, --, --))
>_id : Symbol(_id, Decl(file.js, 121, 11))
>test : Symbol(RegExp.test, Decl(lib.es5.d.ts, --, --))
>key : Symbol(key, Decl(file.js, 133, 61))
return;
}
const value = Meteor.settings[key];
>value : Symbol(value, Decl(file.js, 137, 25))
>Meteor.settings : Symbol(settings, Decl(file.js, 89, 12))
>Meteor : Symbol(Meteor, Decl(file.js, 90, 5))
>settings : Symbol(settings, Decl(file.js, 89, 12))
>key : Symbol(key, Decl(file.js, 133, 61))
callback(key, value);
>callback : Symbol(callback, Decl(file.js, 121, 15))
>key : Symbol(key, Decl(file.js, 133, 61))
>value : Symbol(value, Decl(file.js, 137, 25))
});
}
if (typeof _id === 'string') {
>_id : Symbol(_id, Decl(file.js, 121, 11))
const value = Meteor.settings[_id];
>value : Symbol(value, Decl(file.js, 143, 21))
>Meteor.settings : Symbol(settings, Decl(file.js, 89, 12))
>Meteor : Symbol(Meteor, Decl(file.js, 90, 5))
>settings : Symbol(settings, Decl(file.js, 89, 12))
>_id : Symbol(_id, Decl(file.js, 121, 11))
if (value != null) {
>value : Symbol(value, Decl(file.js, 143, 21))
callback(_id, Meteor.settings[_id]);
>callback : Symbol(callback, Decl(file.js, 121, 15))
>_id : Symbol(_id, Decl(file.js, 121, 11))
>Meteor.settings : Symbol(settings, Decl(file.js, 89, 12))
>Meteor : Symbol(Meteor, Decl(file.js, 90, 5))
>settings : Symbol(settings, Decl(file.js, 89, 12))
>_id : Symbol(_id, Decl(file.js, 121, 11))
}
return; // Ok
}
return; // Ok, needed for exhaustiveness check
}
if (!Meteor.settings) {
>Meteor.settings : Symbol(settings, Decl(file.js, 89, 12))
>Meteor : Symbol(Meteor, Decl(file.js, 90, 5))
>settings : Symbol(settings, Decl(file.js, 89, 12))
return undefined; // Error
>undefined : Symbol(undefined)
}
if (_.isRegExp(_id)) {
>_.isRegExp : Symbol(isRegExp, Decl(file.js, 91, 12))
>_ : Symbol(_, Decl(file.js, 92, 5))
>isRegExp : Symbol(isRegExp, Decl(file.js, 91, 12))
>_id : Symbol(_id, Decl(file.js, 121, 11))
return Object.keys(Meteor.settings).reduce((/** @type {SettingComposedValue<T>[]} */ items, key) => {
>Object.keys(Meteor.settings).reduce : Symbol(Array.reduce, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>Object.keys : Symbol(ObjectConstructor.keys, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --))
>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>keys : Symbol(ObjectConstructor.keys, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --))
>Meteor.settings : Symbol(settings, Decl(file.js, 89, 12))
>Meteor : Symbol(Meteor, Decl(file.js, 90, 5))
>settings : Symbol(settings, Decl(file.js, 89, 12))
>reduce : Symbol(Array.reduce, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>items : Symbol(items, Decl(file.js, 158, 56))
>key : Symbol(key, Decl(file.js, 158, 103))
const value = Meteor.settings[key];
>value : Symbol(value, Decl(file.js, 159, 21))
>Meteor.settings : Symbol(settings, Decl(file.js, 89, 12))
>Meteor : Symbol(Meteor, Decl(file.js, 90, 5))
>settings : Symbol(settings, Decl(file.js, 89, 12))
>key : Symbol(key, Decl(file.js, 158, 103))
if (_id.test(key)) {
>_id.test : Symbol(RegExp.test, Decl(lib.es5.d.ts, --, --))
>_id : Symbol(_id, Decl(file.js, 121, 11))
>test : Symbol(RegExp.test, Decl(lib.es5.d.ts, --, --))
>key : Symbol(key, Decl(file.js, 158, 103))
items.push({ key, value });
>items.push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --))
>items : Symbol(items, Decl(file.js, 158, 56))
>push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --))
>key : Symbol(key, Decl(file.js, 161, 32))
>value : Symbol(value, Decl(file.js, 161, 37))
}
return items;
>items : Symbol(items, Decl(file.js, 158, 56))
}, []); // Ok
}
return Meteor.settings?.[_id]; // Error
>Meteor.settings : Symbol(settings, Decl(file.js, 89, 12))
>Meteor : Symbol(Meteor, Decl(file.js, 90, 5))
>settings : Symbol(settings, Decl(file.js, 89, 12))
>_id : Symbol(_id, Decl(file.js, 121, 11))
}
}
// File: Rocket.Chat/apps/meteor/app/ui-utils/client/lib/messageBox.ts
/**
* @typedef {MyObj} MessageBoxAction
*/
/**
* @template {string | undefined} T
* @param {T} group
* @returns {HelperCond<T, string, MessageBoxAction[], undefined, Record<string, MessageBoxAction[]>>}
*/
function getWithBug(group) {
>getWithBug : Symbol(getWithBug, Decl(file.js, 169, 1))
>group : Symbol(group, Decl(file.js, 182, 20))
if (!group) {
>group : Symbol(group, Decl(file.js, 182, 20))
return /** @type {Record<string, MessageBoxAction[]>} */({}); // Error
}
return /** @type {MessageBoxAction[]} */([]); // Ok
}
/**
* @template {string | undefined} T
* @param {T} group
* @returns {HelperCond<T, string, MessageBoxAction[], undefined, Record<string, MessageBoxAction[]>>}
*/
function getWithoutBug(group) {
>getWithoutBug : Symbol(getWithoutBug, Decl(file.js, 187, 1))
>group : Symbol(group, Decl(file.js, 194, 23))
if (group === undefined) {
>group : Symbol(group, Decl(file.js, 194, 23))
>undefined : Symbol(undefined)
return /** @type {Record<string, MessageBoxAction[]>} */({}); // Ok
}
return /** @type {MessageBoxAction[]} */([]); // Ok
}
// File: Rocket.Chat/apps/meteor/ee/server/lib/engagementDashboard/date.ts
/**
* @param {string} x
* @returns {Date}
*/
function mapDateForAPI(x) {
>mapDateForAPI : Symbol(mapDateForAPI, Decl(file.js, 199, 1))
>x : Symbol(x, Decl(file.js, 207, 23))
return /** @type {any} */ (undefined);
>undefined : Symbol(undefined)
}
/**
* @template {string | undefined} T
* @param {string} start
* @param {T} [end]
* @returns {HelperCond<T, string, { start: Date, end: Date }, undefined, { start: Date, end: undefined }>}
*/
function transformDatesForAPI(start, end) {
>transformDatesForAPI : Symbol(transformDatesForAPI, Decl(file.js, 209, 1))
>start : Symbol(start, Decl(file.js, 217, 30))
>end : Symbol(end, Decl(file.js, 217, 36))
return end !== undefined ?
>end : Symbol(end, Decl(file.js, 217, 36))
>undefined : Symbol(undefined)
{
start: mapDateForAPI(start),
>start : Symbol(start, Decl(file.js, 219, 9))
>mapDateForAPI : Symbol(mapDateForAPI, Decl(file.js, 199, 1))
>start : Symbol(start, Decl(file.js, 217, 30))
end: mapDateForAPI(end),
>end : Symbol(end, Decl(file.js, 220, 40))
>mapDateForAPI : Symbol(mapDateForAPI, Decl(file.js, 199, 1))
>end : Symbol(end, Decl(file.js, 217, 36))
} :
{
start: mapDateForAPI(start),
>start : Symbol(start, Decl(file.js, 223, 9))
>mapDateForAPI : Symbol(mapDateForAPI, Decl(file.js, 199, 1))
>start : Symbol(start, Decl(file.js, 217, 30))
end: undefined
>end : Symbol(end, Decl(file.js, 224, 40))
>undefined : Symbol(undefined)
};
}
// File: Rocket.Chat/packages/agenda/src/Agenda.ts
/**
* @typedef {MyObj} RepeatOptions
*/
/**
* @typedef {MyObj} Job
*/
/**
* @typedef {Object} IJob
* @property {MyObj} data
*/
class NewAgenda {
>NewAgenda : Symbol(NewAgenda, Decl(file.js, 227, 1))
/**
* @param {string | number} interval
* @param {string} name
* @param {IJob['data']} data
* @param {RepeatOptions} options
* @returns {Promise<Job>}
*/
async _createIntervalJob(interval, name, data, options) {
>_createIntervalJob : Symbol(NewAgenda._createIntervalJob, Decl(file.js, 243, 17))
>interval : Symbol(interval, Decl(file.js, 251, 29))
>name : Symbol(name, Decl(file.js, 251, 38))
>data : Symbol(data, Decl(file.js, 251, 44))
>options : Symbol(options, Decl(file.js, 251, 50))
return /** @type {any} */ (undefined);
>undefined : Symbol(undefined)
}
/**
* @param {string | number} interval
* @param {string[]} names
* @param {IJob['data']} data
* @param {RepeatOptions} options
* @returns {Promise<Job[]> | undefined}
*/
_createIntervalJobs(interval, names, data, options) {
>_createIntervalJobs : Symbol(NewAgenda._createIntervalJobs, Decl(file.js, 253, 5))
>interval : Symbol(interval, Decl(file.js, 262, 24))
>names : Symbol(names, Decl(file.js, 262, 33))
>data : Symbol(data, Decl(file.js, 262, 40))
>options : Symbol(options, Decl(file.js, 262, 46))
return undefined;
>undefined : Symbol(undefined)
}
/**
* @template {string | string[]} T
* @param {string | number} interval
* @param {T} name
* @param {IJob['data']} data
* @param {RepeatOptions} options
* @returns {Promise<HelperCond<T, string, Job, string[], Job[] | undefined>>}
*/
async newEvery(interval, name, data, options) {
>newEvery : Symbol(NewAgenda.newEvery, Decl(file.js, 264, 5))
>interval : Symbol(interval, Decl(file.js, 274, 19))
>name : Symbol(name, Decl(file.js, 274, 28))
>data : Symbol(data, Decl(file.js, 274, 34))
>options : Symbol(options, Decl(file.js, 274, 40))
if (typeof name === 'string') {
>name : Symbol(name, Decl(file.js, 274, 28))
return this._createIntervalJob(interval, name, data, options); // Ok
>this._createIntervalJob : Symbol(NewAgenda._createIntervalJob, Decl(file.js, 243, 17))
>this : Symbol(NewAgenda, Decl(file.js, 227, 1))
>_createIntervalJob : Symbol(NewAgenda._createIntervalJob, Decl(file.js, 243, 17))
>interval : Symbol(interval, Decl(file.js, 274, 19))
>name : Symbol(name, Decl(file.js, 274, 28))
>data : Symbol(data, Decl(file.js, 274, 34))
>options : Symbol(options, Decl(file.js, 274, 40))
}
if (Array.isArray(name)) {
>Array.isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --))
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 4 more)
>isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --))
>name : Symbol(name, Decl(file.js, 274, 28))
return this._createIntervalJobs(interval, name, data, options); // Ok
>this._createIntervalJobs : Symbol(NewAgenda._createIntervalJobs, Decl(file.js, 253, 5))
>this : Symbol(NewAgenda, Decl(file.js, 227, 1))
>_createIntervalJobs : Symbol(NewAgenda._createIntervalJobs, Decl(file.js, 253, 5))
>interval : Symbol(interval, Decl(file.js, 274, 19))
>name : Symbol(name, Decl(file.js, 274, 28))
>data : Symbol(data, Decl(file.js, 274, 34))
>options : Symbol(options, Decl(file.js, 274, 40))
}
throw new Error('Unexpected error: Invalid job name(s)');
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2022.error.d.ts, --, --))
}
}
// File: angular/packages/common/src/pipes/case_conversion_pipes.ts
/**
* @template {string | null | undefined} T
* @param {T} value
* @returns {HelperCond<T, string, string, null | undefined, null>}
*/
function transform1(value) {
>transform1 : Symbol(transform1, Decl(file.js, 285, 1))
>value : Symbol(value, Decl(file.js, 294, 20))
if (value == null) return null; // Ok
>value : Symbol(value, Decl(file.js, 294, 20))
if (typeof value !== 'string') {
>value : Symbol(value, Decl(file.js, 294, 20))
throw new Error();
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2022.error.d.ts, --, --))
}
return value.toLowerCase(); // Ok
>value.toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --))
>value : Symbol(value, Decl(file.js, 294, 20))
>toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --))
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,224 @@
dependentReturnType3.ts(114,13): error TS2322: Type 'undefined' is not assignable to type 'HelperCond<I, string, T | undefined, RegExp, SettingComposedValue<T>[]>'.
dependentReturnType3.ts(130,16): error TS2536: Type 'I' cannot be used to index type '{ [s: string]: any; }'.
dependentReturnType3.ts(141,9): error TS2322: Type 'Record<string, object[]>' is not assignable to type 'HelperCond<T, string, object[], undefined, Record<string, object[]>>'.
==== dependentReturnType3.ts (3 errors) ====
// Adapted from ts-error-deltas repos
type HelperCond<T, A, R1, B, R2> =
T extends A
? R1
: T extends B
? R2
: never;
// File: Rocket.Chat/apps/meteor/app/katex/client/index.ts
interface IMessage {
html?: string;
tokens?: {}[];
}
class NewKatex {
render(s: string): string {
return "";
}
renderMessage<T extends string | IMessage>(message: T):
T extends string
? string
: T extends IMessage
? IMessage
: never {
if (typeof message === 'string') {
return this.render(message); // Ok
}
if (!message.html?.trim()) {
return message; // Ok
}
if (!message.tokens) {
message.tokens = [];
}
message.html = this.render(message.html);
return message; // Ok
}
}
export function createKatexMessageRendering<T extends true | false>(
options: {
dollarSyntax: boolean;
parenthesisSyntax: boolean;
},
_isMessage: T,
): T extends true
? (message: IMessage) => IMessage
: T extends false
? (message: string) => string
: never {
const instance = new NewKatex();
if (_isMessage) {
return (message: IMessage): IMessage => instance.renderMessage(message); // Ok
}
return (message: string): string => instance.renderMessage(message); // Ok
}
// File: Rocket.Chat/apps/meteor/app/settings/lib/settings.ts
type SettingComposedValue<T extends SettingValue = SettingValue> = { key: string; value: T };
type SettingCallback = (key: string, value: SettingValue, initialLoad?: boolean) => void;
type SettingValue = object;
declare const Meteor: { settings: { [s: string]: any } };
declare const _: { isRegExp(x: unknown): x is RegExp; };
declare function takesRegExp(x: RegExp): void;
declare function takesString(x: string): void;
class NewSettingsBase {
public newGet<C extends SettingCallback | undefined, I extends string | RegExp, T extends SettingValue = SettingValue>(
_id: I,
callback?: C,
): HelperCond<C,
SettingCallback, void,
undefined, HelperCond<I,
string, T | undefined,
RegExp, SettingComposedValue<T>[]>> {
if (callback !== undefined) {
if (!Meteor.settings) {
return; // Ok
}
if (_id === '*') {
return Object.keys(Meteor.settings).forEach((key) => { // Ok
const value = Meteor.settings[key];
callback(key, value);
});
}
if (_.isRegExp(_id) && Meteor.settings) {
return Object.keys(Meteor.settings).forEach((key) => { // Ok
if (!_id.test(key)) {
return;
}
const value = Meteor.settings[key];
callback(key, value);
});
}
if (typeof _id === 'string') {
const value = Meteor.settings[_id];
if (value != null) {
callback(_id, Meteor.settings[_id]);
}
return; // Ok
}
return; // Ok, needed for exhaustiveness check
}
if (!Meteor.settings) { // Wrong: we don't know that _id is string here, cannot return undefined
return undefined; // Error
~~~~~~
!!! error TS2322: Type 'undefined' is not assignable to type 'HelperCond<I, string, T | undefined, RegExp, SettingComposedValue<T>[]>'.
}
if (_.isRegExp(_id)) {
return Object.keys(Meteor.settings).reduce((items: SettingComposedValue<T>[], key) => {
const value = Meteor.settings[key];
if (_id.test(key)) {
items.push({
key,
value,
});
}
return items;
}, []); // Ok
}
return Meteor.settings?.[_id]; // Error
~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2536: Type 'I' cannot be used to index type '{ [s: string]: any; }'.
// The indexing currently doesn't work because it doesn't use the narrowed type of `_id`.
}
}
// File: Rocket.Chat/apps/meteor/app/ui-utils/client/lib/messageBox.ts
type MessageBoxAction = object;
function getWithBug<T extends string | undefined>(group: T):
HelperCond<T, string, MessageBoxAction[], undefined, Record<string, MessageBoxAction[]>> {
if (!group) {
return {} as Record<string, MessageBoxAction[]>; // Error, could fall into this branch when group is empty string
~~~~~~
!!! error TS2322: Type 'Record<string, object[]>' is not assignable to type 'HelperCond<T, string, object[], undefined, Record<string, object[]>>'.
}
return [] as MessageBoxAction[]; // Ok
}
function getWithoutBug<T extends string | undefined>(group: T):
HelperCond<T, string, MessageBoxAction[], undefined, Record<string, MessageBoxAction[]>> {
if (group === undefined) {
return {} as Record<string, MessageBoxAction[]>; // Ok
}
return [] as MessageBoxAction[]; // Ok
}
// File: Rocket.Chat/apps/meteor/ee/server/lib/engagementDashboard/date.ts
declare function mapDateForAPI(x: string): Date;
export function transformDatesForAPI<T extends string | undefined>(
start: string,
end?: T
): HelperCond<T, string, { start: Date, end: Date }, undefined, { start: Date, end: undefined }> {
return end !== undefined ? // Ok
{
start: mapDateForAPI(start),
end: mapDateForAPI(end),
} :
{
start: mapDateForAPI(start),
end: undefined
};
}
// File: Rocket.Chat/packages/agenda/src/Agenda.ts
type RepeatOptions = object;
type Job = object;
type IJob = { data: object };
class NewAgenda {
public async _createIntervalJob(interval: string | number, name: string, data: IJob['data'], options: RepeatOptions): Promise<Job> { return undefined as any; }
private _createIntervalJobs(
interval: string | number,
names: string[],
data: IJob['data'],
options: RepeatOptions,
): Promise<Job[]> | undefined { return undefined as any; }
public async newEvery<T extends string | string[]>(
interval: string | number,
name: T,
data: IJob['data'],
options: RepeatOptions): Promise<HelperCond<T, string, Job, string[], Job[] | undefined>> {
if (typeof name === 'string') {
return this._createIntervalJob(interval, name, data, options); // Ok
}
if (Array.isArray(name)) {
return this._createIntervalJobs(interval, name, data, options); // Ok
// Possible bug in original: createIntervalJobs can return undefined, but the original overload did not acount for that.
}
throw new Error('Unexpected error: Invalid job name(s)');
}
}
// File: angular/packages/common/src/pipes/case_conversion_pipes.ts
function transform1<T extends string | null | undefined>(value: T): HelperCond<T, string, string, null | undefined, null> {
if (value == null) return null; // Ok
if (typeof value !== 'string') {
throw new Error();
}
return value.toLowerCase(); // Ok
}

Просмотреть файл

@ -0,0 +1,679 @@
//// [tests/cases/compiler/dependentReturnType3.ts] ////
=== dependentReturnType3.ts ===
// Adapted from ts-error-deltas repos
type HelperCond<T, A, R1, B, R2> =
>HelperCond : Symbol(HelperCond, Decl(dependentReturnType3.ts, 0, 0))
>T : Symbol(T, Decl(dependentReturnType3.ts, 2, 16))
>A : Symbol(A, Decl(dependentReturnType3.ts, 2, 18))
>R1 : Symbol(R1, Decl(dependentReturnType3.ts, 2, 21))
>B : Symbol(B, Decl(dependentReturnType3.ts, 2, 25))
>R2 : Symbol(R2, Decl(dependentReturnType3.ts, 2, 28))
T extends A
>T : Symbol(T, Decl(dependentReturnType3.ts, 2, 16))
>A : Symbol(A, Decl(dependentReturnType3.ts, 2, 18))
? R1
>R1 : Symbol(R1, Decl(dependentReturnType3.ts, 2, 21))
: T extends B
>T : Symbol(T, Decl(dependentReturnType3.ts, 2, 16))
>B : Symbol(B, Decl(dependentReturnType3.ts, 2, 25))
? R2
>R2 : Symbol(R2, Decl(dependentReturnType3.ts, 2, 28))
: never;
// File: Rocket.Chat/apps/meteor/app/katex/client/index.ts
interface IMessage {
>IMessage : Symbol(IMessage, Decl(dependentReturnType3.ts, 7, 20))
html?: string;
>html : Symbol(IMessage.html, Decl(dependentReturnType3.ts, 11, 20))
tokens?: {}[];
>tokens : Symbol(IMessage.tokens, Decl(dependentReturnType3.ts, 12, 18))
}
class NewKatex {
>NewKatex : Symbol(NewKatex, Decl(dependentReturnType3.ts, 14, 1))
render(s: string): string {
>render : Symbol(NewKatex.render, Decl(dependentReturnType3.ts, 16, 16))
>s : Symbol(s, Decl(dependentReturnType3.ts, 17, 11))
return "";
}
renderMessage<T extends string | IMessage>(message: T):
>renderMessage : Symbol(NewKatex.renderMessage, Decl(dependentReturnType3.ts, 19, 5))
>T : Symbol(T, Decl(dependentReturnType3.ts, 21, 18))
>IMessage : Symbol(IMessage, Decl(dependentReturnType3.ts, 7, 20))
>message : Symbol(message, Decl(dependentReturnType3.ts, 21, 47))
>T : Symbol(T, Decl(dependentReturnType3.ts, 21, 18))
T extends string
>T : Symbol(T, Decl(dependentReturnType3.ts, 21, 18))
? string
: T extends IMessage
>T : Symbol(T, Decl(dependentReturnType3.ts, 21, 18))
>IMessage : Symbol(IMessage, Decl(dependentReturnType3.ts, 7, 20))
? IMessage
>IMessage : Symbol(IMessage, Decl(dependentReturnType3.ts, 7, 20))
: never {
if (typeof message === 'string') {
>message : Symbol(message, Decl(dependentReturnType3.ts, 21, 47))
return this.render(message); // Ok
>this.render : Symbol(NewKatex.render, Decl(dependentReturnType3.ts, 16, 16))
>this : Symbol(NewKatex, Decl(dependentReturnType3.ts, 14, 1))
>render : Symbol(NewKatex.render, Decl(dependentReturnType3.ts, 16, 16))
>message : Symbol(message, Decl(dependentReturnType3.ts, 21, 47))
}
if (!message.html?.trim()) {
>message.html?.trim : Symbol(String.trim, Decl(lib.es5.d.ts, --, --))
>message.html : Symbol(IMessage.html, Decl(dependentReturnType3.ts, 11, 20))
>message : Symbol(message, Decl(dependentReturnType3.ts, 21, 47))
>html : Symbol(IMessage.html, Decl(dependentReturnType3.ts, 11, 20))
>trim : Symbol(String.trim, Decl(lib.es5.d.ts, --, --))
return message; // Ok
>message : Symbol(message, Decl(dependentReturnType3.ts, 21, 47))
}
if (!message.tokens) {
>message.tokens : Symbol(IMessage.tokens, Decl(dependentReturnType3.ts, 12, 18))
>message : Symbol(message, Decl(dependentReturnType3.ts, 21, 47))
>tokens : Symbol(IMessage.tokens, Decl(dependentReturnType3.ts, 12, 18))
message.tokens = [];
>message.tokens : Symbol(IMessage.tokens, Decl(dependentReturnType3.ts, 12, 18))
>message : Symbol(message, Decl(dependentReturnType3.ts, 21, 47))
>tokens : Symbol(IMessage.tokens, Decl(dependentReturnType3.ts, 12, 18))
}
message.html = this.render(message.html);
>message.html : Symbol(IMessage.html, Decl(dependentReturnType3.ts, 11, 20))
>message : Symbol(message, Decl(dependentReturnType3.ts, 21, 47))
>html : Symbol(IMessage.html, Decl(dependentReturnType3.ts, 11, 20))
>this.render : Symbol(NewKatex.render, Decl(dependentReturnType3.ts, 16, 16))
>this : Symbol(NewKatex, Decl(dependentReturnType3.ts, 14, 1))
>render : Symbol(NewKatex.render, Decl(dependentReturnType3.ts, 16, 16))
>message.html : Symbol(IMessage.html, Decl(dependentReturnType3.ts, 11, 20))
>message : Symbol(message, Decl(dependentReturnType3.ts, 21, 47))
>html : Symbol(IMessage.html, Decl(dependentReturnType3.ts, 11, 20))
return message; // Ok
>message : Symbol(message, Decl(dependentReturnType3.ts, 21, 47))
}
}
export function createKatexMessageRendering<T extends true | false>(
>createKatexMessageRendering : Symbol(createKatexMessageRendering, Decl(dependentReturnType3.ts, 42, 1))
>T : Symbol(T, Decl(dependentReturnType3.ts, 44, 44))
options: {
>options : Symbol(options, Decl(dependentReturnType3.ts, 44, 68))
dollarSyntax: boolean;
>dollarSyntax : Symbol(dollarSyntax, Decl(dependentReturnType3.ts, 45, 14))
parenthesisSyntax: boolean;
>parenthesisSyntax : Symbol(parenthesisSyntax, Decl(dependentReturnType3.ts, 46, 30))
},
_isMessage: T,
>_isMessage : Symbol(_isMessage, Decl(dependentReturnType3.ts, 48, 6))
>T : Symbol(T, Decl(dependentReturnType3.ts, 44, 44))
): T extends true
>T : Symbol(T, Decl(dependentReturnType3.ts, 44, 44))
? (message: IMessage) => IMessage
>message : Symbol(message, Decl(dependentReturnType3.ts, 51, 7))
>IMessage : Symbol(IMessage, Decl(dependentReturnType3.ts, 7, 20))
>IMessage : Symbol(IMessage, Decl(dependentReturnType3.ts, 7, 20))
: T extends false
>T : Symbol(T, Decl(dependentReturnType3.ts, 44, 44))
? (message: string) => string
>message : Symbol(message, Decl(dependentReturnType3.ts, 53, 9))
: never {
const instance = new NewKatex();
>instance : Symbol(instance, Decl(dependentReturnType3.ts, 55, 9))
>NewKatex : Symbol(NewKatex, Decl(dependentReturnType3.ts, 14, 1))
if (_isMessage) {
>_isMessage : Symbol(_isMessage, Decl(dependentReturnType3.ts, 48, 6))
return (message: IMessage): IMessage => instance.renderMessage(message); // Ok
>message : Symbol(message, Decl(dependentReturnType3.ts, 57, 16))
>IMessage : Symbol(IMessage, Decl(dependentReturnType3.ts, 7, 20))
>IMessage : Symbol(IMessage, Decl(dependentReturnType3.ts, 7, 20))
>instance.renderMessage : Symbol(NewKatex.renderMessage, Decl(dependentReturnType3.ts, 19, 5))
>instance : Symbol(instance, Decl(dependentReturnType3.ts, 55, 9))
>renderMessage : Symbol(NewKatex.renderMessage, Decl(dependentReturnType3.ts, 19, 5))
>message : Symbol(message, Decl(dependentReturnType3.ts, 57, 16))
}
return (message: string): string => instance.renderMessage(message); // Ok
>message : Symbol(message, Decl(dependentReturnType3.ts, 59, 12))
>instance.renderMessage : Symbol(NewKatex.renderMessage, Decl(dependentReturnType3.ts, 19, 5))
>instance : Symbol(instance, Decl(dependentReturnType3.ts, 55, 9))
>renderMessage : Symbol(NewKatex.renderMessage, Decl(dependentReturnType3.ts, 19, 5))
>message : Symbol(message, Decl(dependentReturnType3.ts, 59, 12))
}
// File: Rocket.Chat/apps/meteor/app/settings/lib/settings.ts
type SettingComposedValue<T extends SettingValue = SettingValue> = { key: string; value: T };
>SettingComposedValue : Symbol(SettingComposedValue, Decl(dependentReturnType3.ts, 60, 1))
>T : Symbol(T, Decl(dependentReturnType3.ts, 63, 26))
>SettingValue : Symbol(SettingValue, Decl(dependentReturnType3.ts, 64, 89))
>SettingValue : Symbol(SettingValue, Decl(dependentReturnType3.ts, 64, 89))
>key : Symbol(key, Decl(dependentReturnType3.ts, 63, 68))
>value : Symbol(value, Decl(dependentReturnType3.ts, 63, 81))
>T : Symbol(T, Decl(dependentReturnType3.ts, 63, 26))
type SettingCallback = (key: string, value: SettingValue, initialLoad?: boolean) => void;
>SettingCallback : Symbol(SettingCallback, Decl(dependentReturnType3.ts, 63, 93))
>key : Symbol(key, Decl(dependentReturnType3.ts, 64, 24))
>value : Symbol(value, Decl(dependentReturnType3.ts, 64, 36))
>SettingValue : Symbol(SettingValue, Decl(dependentReturnType3.ts, 64, 89))
>initialLoad : Symbol(initialLoad, Decl(dependentReturnType3.ts, 64, 57))
type SettingValue = object;
>SettingValue : Symbol(SettingValue, Decl(dependentReturnType3.ts, 64, 89))
declare const Meteor: { settings: { [s: string]: any } };
>Meteor : Symbol(Meteor, Decl(dependentReturnType3.ts, 67, 13))
>settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23))
>s : Symbol(s, Decl(dependentReturnType3.ts, 67, 37))
declare const _: { isRegExp(x: unknown): x is RegExp; };
>_ : Symbol(_, Decl(dependentReturnType3.ts, 68, 13))
>isRegExp : Symbol(isRegExp, Decl(dependentReturnType3.ts, 68, 18))
>x : Symbol(x, Decl(dependentReturnType3.ts, 68, 28))
>x : Symbol(x, Decl(dependentReturnType3.ts, 68, 28))
>RegExp : Symbol(RegExp, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
declare function takesRegExp(x: RegExp): void;
>takesRegExp : Symbol(takesRegExp, Decl(dependentReturnType3.ts, 68, 56))
>x : Symbol(x, Decl(dependentReturnType3.ts, 69, 29))
>RegExp : Symbol(RegExp, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
declare function takesString(x: string): void;
>takesString : Symbol(takesString, Decl(dependentReturnType3.ts, 69, 46))
>x : Symbol(x, Decl(dependentReturnType3.ts, 70, 29))
class NewSettingsBase {
>NewSettingsBase : Symbol(NewSettingsBase, Decl(dependentReturnType3.ts, 70, 46))
public newGet<C extends SettingCallback | undefined, I extends string | RegExp, T extends SettingValue = SettingValue>(
>newGet : Symbol(NewSettingsBase.newGet, Decl(dependentReturnType3.ts, 72, 23))
>C : Symbol(C, Decl(dependentReturnType3.ts, 73, 18))
>SettingCallback : Symbol(SettingCallback, Decl(dependentReturnType3.ts, 63, 93))
>I : Symbol(I, Decl(dependentReturnType3.ts, 73, 56))
>RegExp : Symbol(RegExp, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
>T : Symbol(T, Decl(dependentReturnType3.ts, 73, 83))
>SettingValue : Symbol(SettingValue, Decl(dependentReturnType3.ts, 64, 89))
>SettingValue : Symbol(SettingValue, Decl(dependentReturnType3.ts, 64, 89))
_id: I,
>_id : Symbol(_id, Decl(dependentReturnType3.ts, 73, 123))
>I : Symbol(I, Decl(dependentReturnType3.ts, 73, 56))
callback?: C,
>callback : Symbol(callback, Decl(dependentReturnType3.ts, 74, 15))
>C : Symbol(C, Decl(dependentReturnType3.ts, 73, 18))
): HelperCond<C,
>HelperCond : Symbol(HelperCond, Decl(dependentReturnType3.ts, 0, 0))
>C : Symbol(C, Decl(dependentReturnType3.ts, 73, 18))
SettingCallback, void,
>SettingCallback : Symbol(SettingCallback, Decl(dependentReturnType3.ts, 63, 93))
undefined, HelperCond<I,
>HelperCond : Symbol(HelperCond, Decl(dependentReturnType3.ts, 0, 0))
>I : Symbol(I, Decl(dependentReturnType3.ts, 73, 56))
string, T | undefined,
>T : Symbol(T, Decl(dependentReturnType3.ts, 73, 83))
RegExp, SettingComposedValue<T>[]>> {
>RegExp : Symbol(RegExp, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
>SettingComposedValue : Symbol(SettingComposedValue, Decl(dependentReturnType3.ts, 60, 1))
>T : Symbol(T, Decl(dependentReturnType3.ts, 73, 83))
if (callback !== undefined) {
>callback : Symbol(callback, Decl(dependentReturnType3.ts, 74, 15))
>undefined : Symbol(undefined)
if (!Meteor.settings) {
>Meteor.settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23))
>Meteor : Symbol(Meteor, Decl(dependentReturnType3.ts, 67, 13))
>settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23))
return; // Ok
}
if (_id === '*') {
>_id : Symbol(_id, Decl(dependentReturnType3.ts, 73, 123))
return Object.keys(Meteor.settings).forEach((key) => { // Ok
>Object.keys(Meteor.settings).forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --))
>Object.keys : Symbol(ObjectConstructor.keys, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --))
>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>keys : Symbol(ObjectConstructor.keys, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --))
>Meteor.settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23))
>Meteor : Symbol(Meteor, Decl(dependentReturnType3.ts, 67, 13))
>settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23))
>forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --))
>key : Symbol(key, Decl(dependentReturnType3.ts, 86, 61))
const value = Meteor.settings[key];
>value : Symbol(value, Decl(dependentReturnType3.ts, 87, 25))
>Meteor.settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23))
>Meteor : Symbol(Meteor, Decl(dependentReturnType3.ts, 67, 13))
>settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23))
>key : Symbol(key, Decl(dependentReturnType3.ts, 86, 61))
callback(key, value);
>callback : Symbol(callback, Decl(dependentReturnType3.ts, 74, 15))
>key : Symbol(key, Decl(dependentReturnType3.ts, 86, 61))
>value : Symbol(value, Decl(dependentReturnType3.ts, 87, 25))
});
}
if (_.isRegExp(_id) && Meteor.settings) {
>_.isRegExp : Symbol(isRegExp, Decl(dependentReturnType3.ts, 68, 18))
>_ : Symbol(_, Decl(dependentReturnType3.ts, 68, 13))
>isRegExp : Symbol(isRegExp, Decl(dependentReturnType3.ts, 68, 18))
>_id : Symbol(_id, Decl(dependentReturnType3.ts, 73, 123))
>Meteor.settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23))
>Meteor : Symbol(Meteor, Decl(dependentReturnType3.ts, 67, 13))
>settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23))
return Object.keys(Meteor.settings).forEach((key) => { // Ok
>Object.keys(Meteor.settings).forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --))
>Object.keys : Symbol(ObjectConstructor.keys, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --))
>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>keys : Symbol(ObjectConstructor.keys, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --))
>Meteor.settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23))
>Meteor : Symbol(Meteor, Decl(dependentReturnType3.ts, 67, 13))
>settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23))
>forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --))
>key : Symbol(key, Decl(dependentReturnType3.ts, 92, 61))
if (!_id.test(key)) {
>_id.test : Symbol(RegExp.test, Decl(lib.es5.d.ts, --, --))
>_id : Symbol(_id, Decl(dependentReturnType3.ts, 73, 123))
>test : Symbol(RegExp.test, Decl(lib.es5.d.ts, --, --))
>key : Symbol(key, Decl(dependentReturnType3.ts, 92, 61))
return;
}
const value = Meteor.settings[key];
>value : Symbol(value, Decl(dependentReturnType3.ts, 96, 25))
>Meteor.settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23))
>Meteor : Symbol(Meteor, Decl(dependentReturnType3.ts, 67, 13))
>settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23))
>key : Symbol(key, Decl(dependentReturnType3.ts, 92, 61))
callback(key, value);
>callback : Symbol(callback, Decl(dependentReturnType3.ts, 74, 15))
>key : Symbol(key, Decl(dependentReturnType3.ts, 92, 61))
>value : Symbol(value, Decl(dependentReturnType3.ts, 96, 25))
});
}
if (typeof _id === 'string') {
>_id : Symbol(_id, Decl(dependentReturnType3.ts, 73, 123))
const value = Meteor.settings[_id];
>value : Symbol(value, Decl(dependentReturnType3.ts, 102, 21))
>Meteor.settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23))
>Meteor : Symbol(Meteor, Decl(dependentReturnType3.ts, 67, 13))
>settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23))
>_id : Symbol(_id, Decl(dependentReturnType3.ts, 73, 123))
if (value != null) {
>value : Symbol(value, Decl(dependentReturnType3.ts, 102, 21))
callback(_id, Meteor.settings[_id]);
>callback : Symbol(callback, Decl(dependentReturnType3.ts, 74, 15))
>_id : Symbol(_id, Decl(dependentReturnType3.ts, 73, 123))
>Meteor.settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23))
>Meteor : Symbol(Meteor, Decl(dependentReturnType3.ts, 67, 13))
>settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23))
>_id : Symbol(_id, Decl(dependentReturnType3.ts, 73, 123))
}
return; // Ok
}
return; // Ok, needed for exhaustiveness check
}
if (!Meteor.settings) { // Wrong: we don't know that _id is string here, cannot return undefined
>Meteor.settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23))
>Meteor : Symbol(Meteor, Decl(dependentReturnType3.ts, 67, 13))
>settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23))
return undefined; // Error
>undefined : Symbol(undefined)
}
if (_.isRegExp(_id)) {
>_.isRegExp : Symbol(isRegExp, Decl(dependentReturnType3.ts, 68, 18))
>_ : Symbol(_, Decl(dependentReturnType3.ts, 68, 13))
>isRegExp : Symbol(isRegExp, Decl(dependentReturnType3.ts, 68, 18))
>_id : Symbol(_id, Decl(dependentReturnType3.ts, 73, 123))
return Object.keys(Meteor.settings).reduce((items: SettingComposedValue<T>[], key) => {
>Object.keys(Meteor.settings).reduce : Symbol(Array.reduce, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>Object.keys : Symbol(ObjectConstructor.keys, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --))
>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>keys : Symbol(ObjectConstructor.keys, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --))
>Meteor.settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23))
>Meteor : Symbol(Meteor, Decl(dependentReturnType3.ts, 67, 13))
>settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23))
>reduce : Symbol(Array.reduce, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>items : Symbol(items, Decl(dependentReturnType3.ts, 117, 56))
>SettingComposedValue : Symbol(SettingComposedValue, Decl(dependentReturnType3.ts, 60, 1))
>T : Symbol(T, Decl(dependentReturnType3.ts, 73, 83))
>key : Symbol(key, Decl(dependentReturnType3.ts, 117, 89))
const value = Meteor.settings[key];
>value : Symbol(value, Decl(dependentReturnType3.ts, 118, 9))
>Meteor.settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23))
>Meteor : Symbol(Meteor, Decl(dependentReturnType3.ts, 67, 13))
>settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23))
>key : Symbol(key, Decl(dependentReturnType3.ts, 117, 89))
if (_id.test(key)) {
>_id.test : Symbol(RegExp.test, Decl(lib.es5.d.ts, --, --))
>_id : Symbol(_id, Decl(dependentReturnType3.ts, 73, 123))
>test : Symbol(RegExp.test, Decl(lib.es5.d.ts, --, --))
>key : Symbol(key, Decl(dependentReturnType3.ts, 117, 89))
items.push({
>items.push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --))
>items : Symbol(items, Decl(dependentReturnType3.ts, 117, 56))
>push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --))
key,
>key : Symbol(key, Decl(dependentReturnType3.ts, 120, 17))
value,
>value : Symbol(value, Decl(dependentReturnType3.ts, 121, 10))
});
}
return items;
>items : Symbol(items, Decl(dependentReturnType3.ts, 117, 56))
}, []); // Ok
}
return Meteor.settings?.[_id]; // Error
>Meteor.settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23))
>Meteor : Symbol(Meteor, Decl(dependentReturnType3.ts, 67, 13))
>settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23))
>_id : Symbol(_id, Decl(dependentReturnType3.ts, 73, 123))
// The indexing currently doesn't work because it doesn't use the narrowed type of `_id`.
}
}
// File: Rocket.Chat/apps/meteor/app/ui-utils/client/lib/messageBox.ts
type MessageBoxAction = object;
>MessageBoxAction : Symbol(MessageBoxAction, Decl(dependentReturnType3.ts, 132, 1))
function getWithBug<T extends string | undefined>(group: T):
>getWithBug : Symbol(getWithBug, Decl(dependentReturnType3.ts, 135, 31))
>T : Symbol(T, Decl(dependentReturnType3.ts, 137, 20))
>group : Symbol(group, Decl(dependentReturnType3.ts, 137, 50))
>T : Symbol(T, Decl(dependentReturnType3.ts, 137, 20))
HelperCond<T, string, MessageBoxAction[], undefined, Record<string, MessageBoxAction[]>> {
>HelperCond : Symbol(HelperCond, Decl(dependentReturnType3.ts, 0, 0))
>T : Symbol(T, Decl(dependentReturnType3.ts, 137, 20))
>MessageBoxAction : Symbol(MessageBoxAction, Decl(dependentReturnType3.ts, 132, 1))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>MessageBoxAction : Symbol(MessageBoxAction, Decl(dependentReturnType3.ts, 132, 1))
if (!group) {
>group : Symbol(group, Decl(dependentReturnType3.ts, 137, 50))
return {} as Record<string, MessageBoxAction[]>; // Error, could fall into this branch when group is empty string
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>MessageBoxAction : Symbol(MessageBoxAction, Decl(dependentReturnType3.ts, 132, 1))
}
return [] as MessageBoxAction[]; // Ok
>MessageBoxAction : Symbol(MessageBoxAction, Decl(dependentReturnType3.ts, 132, 1))
}
function getWithoutBug<T extends string | undefined>(group: T):
>getWithoutBug : Symbol(getWithoutBug, Decl(dependentReturnType3.ts, 144, 1))
>T : Symbol(T, Decl(dependentReturnType3.ts, 146, 23))
>group : Symbol(group, Decl(dependentReturnType3.ts, 146, 53))
>T : Symbol(T, Decl(dependentReturnType3.ts, 146, 23))
HelperCond<T, string, MessageBoxAction[], undefined, Record<string, MessageBoxAction[]>> {
>HelperCond : Symbol(HelperCond, Decl(dependentReturnType3.ts, 0, 0))
>T : Symbol(T, Decl(dependentReturnType3.ts, 146, 23))
>MessageBoxAction : Symbol(MessageBoxAction, Decl(dependentReturnType3.ts, 132, 1))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>MessageBoxAction : Symbol(MessageBoxAction, Decl(dependentReturnType3.ts, 132, 1))
if (group === undefined) {
>group : Symbol(group, Decl(dependentReturnType3.ts, 146, 53))
>undefined : Symbol(undefined)
return {} as Record<string, MessageBoxAction[]>; // Ok
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>MessageBoxAction : Symbol(MessageBoxAction, Decl(dependentReturnType3.ts, 132, 1))
}
return [] as MessageBoxAction[]; // Ok
>MessageBoxAction : Symbol(MessageBoxAction, Decl(dependentReturnType3.ts, 132, 1))
}
// File: Rocket.Chat/apps/meteor/ee/server/lib/engagementDashboard/date.ts
declare function mapDateForAPI(x: string): Date;
>mapDateForAPI : Symbol(mapDateForAPI, Decl(dependentReturnType3.ts, 153, 1))
>x : Symbol(x, Decl(dependentReturnType3.ts, 156, 31))
>Date : Symbol(Date, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.scripthost.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
export function transformDatesForAPI<T extends string | undefined>(
>transformDatesForAPI : Symbol(transformDatesForAPI, Decl(dependentReturnType3.ts, 156, 48))
>T : Symbol(T, Decl(dependentReturnType3.ts, 157, 37))
start: string,
>start : Symbol(start, Decl(dependentReturnType3.ts, 157, 67))
end?: T
>end : Symbol(end, Decl(dependentReturnType3.ts, 158, 18))
>T : Symbol(T, Decl(dependentReturnType3.ts, 157, 37))
): HelperCond<T, string, { start: Date, end: Date }, undefined, { start: Date, end: undefined }> {
>HelperCond : Symbol(HelperCond, Decl(dependentReturnType3.ts, 0, 0))
>T : Symbol(T, Decl(dependentReturnType3.ts, 157, 37))
>start : Symbol(start, Decl(dependentReturnType3.ts, 160, 26))
>Date : Symbol(Date, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.scripthost.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
>end : Symbol(end, Decl(dependentReturnType3.ts, 160, 39))
>Date : Symbol(Date, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.scripthost.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
>start : Symbol(start, Decl(dependentReturnType3.ts, 160, 65))
>Date : Symbol(Date, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.scripthost.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
>end : Symbol(end, Decl(dependentReturnType3.ts, 160, 78))
return end !== undefined ? // Ok
>end : Symbol(end, Decl(dependentReturnType3.ts, 158, 18))
>undefined : Symbol(undefined)
{
start: mapDateForAPI(start),
>start : Symbol(start, Decl(dependentReturnType3.ts, 162, 9))
>mapDateForAPI : Symbol(mapDateForAPI, Decl(dependentReturnType3.ts, 153, 1))
>start : Symbol(start, Decl(dependentReturnType3.ts, 157, 67))
end: mapDateForAPI(end),
>end : Symbol(end, Decl(dependentReturnType3.ts, 163, 40))
>mapDateForAPI : Symbol(mapDateForAPI, Decl(dependentReturnType3.ts, 153, 1))
>end : Symbol(end, Decl(dependentReturnType3.ts, 158, 18))
} :
{
start: mapDateForAPI(start),
>start : Symbol(start, Decl(dependentReturnType3.ts, 166, 9))
>mapDateForAPI : Symbol(mapDateForAPI, Decl(dependentReturnType3.ts, 153, 1))
>start : Symbol(start, Decl(dependentReturnType3.ts, 157, 67))
end: undefined
>end : Symbol(end, Decl(dependentReturnType3.ts, 167, 40))
>undefined : Symbol(undefined)
};
}
// File: Rocket.Chat/packages/agenda/src/Agenda.ts
type RepeatOptions = object;
>RepeatOptions : Symbol(RepeatOptions, Decl(dependentReturnType3.ts, 170, 1))
type Job = object;
>Job : Symbol(Job, Decl(dependentReturnType3.ts, 173, 28))
type IJob = { data: object };
>IJob : Symbol(IJob, Decl(dependentReturnType3.ts, 174, 18))
>data : Symbol(data, Decl(dependentReturnType3.ts, 175, 13))
class NewAgenda {
>NewAgenda : Symbol(NewAgenda, Decl(dependentReturnType3.ts, 175, 29))
public async _createIntervalJob(interval: string | number, name: string, data: IJob['data'], options: RepeatOptions): Promise<Job> { return undefined as any; }
>_createIntervalJob : Symbol(NewAgenda._createIntervalJob, Decl(dependentReturnType3.ts, 176, 17))
>interval : Symbol(interval, Decl(dependentReturnType3.ts, 177, 36))
>name : Symbol(name, Decl(dependentReturnType3.ts, 177, 62))
>data : Symbol(data, Decl(dependentReturnType3.ts, 177, 76))
>IJob : Symbol(IJob, Decl(dependentReturnType3.ts, 174, 18))
>options : Symbol(options, Decl(dependentReturnType3.ts, 177, 96))
>RepeatOptions : Symbol(RepeatOptions, Decl(dependentReturnType3.ts, 170, 1))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
>Job : Symbol(Job, Decl(dependentReturnType3.ts, 173, 28))
>undefined : Symbol(undefined)
private _createIntervalJobs(
>_createIntervalJobs : Symbol(NewAgenda._createIntervalJobs, Decl(dependentReturnType3.ts, 177, 163))
interval: string | number,
>interval : Symbol(interval, Decl(dependentReturnType3.ts, 178, 32))
names: string[],
>names : Symbol(names, Decl(dependentReturnType3.ts, 179, 34))
data: IJob['data'],
>data : Symbol(data, Decl(dependentReturnType3.ts, 180, 24))
>IJob : Symbol(IJob, Decl(dependentReturnType3.ts, 174, 18))
options: RepeatOptions,
>options : Symbol(options, Decl(dependentReturnType3.ts, 181, 27))
>RepeatOptions : Symbol(RepeatOptions, Decl(dependentReturnType3.ts, 170, 1))
): Promise<Job[]> | undefined { return undefined as any; }
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
>Job : Symbol(Job, Decl(dependentReturnType3.ts, 173, 28))
>undefined : Symbol(undefined)
public async newEvery<T extends string | string[]>(
>newEvery : Symbol(NewAgenda.newEvery, Decl(dependentReturnType3.ts, 183, 62))
>T : Symbol(T, Decl(dependentReturnType3.ts, 185, 26))
interval: string | number,
>interval : Symbol(interval, Decl(dependentReturnType3.ts, 185, 55))
name: T,
>name : Symbol(name, Decl(dependentReturnType3.ts, 186, 34))
>T : Symbol(T, Decl(dependentReturnType3.ts, 185, 26))
data: IJob['data'],
>data : Symbol(data, Decl(dependentReturnType3.ts, 187, 16))
>IJob : Symbol(IJob, Decl(dependentReturnType3.ts, 174, 18))
options: RepeatOptions): Promise<HelperCond<T, string, Job, string[], Job[] | undefined>> {
>options : Symbol(options, Decl(dependentReturnType3.ts, 188, 27))
>RepeatOptions : Symbol(RepeatOptions, Decl(dependentReturnType3.ts, 170, 1))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
>HelperCond : Symbol(HelperCond, Decl(dependentReturnType3.ts, 0, 0))
>T : Symbol(T, Decl(dependentReturnType3.ts, 185, 26))
>Job : Symbol(Job, Decl(dependentReturnType3.ts, 173, 28))
>Job : Symbol(Job, Decl(dependentReturnType3.ts, 173, 28))
if (typeof name === 'string') {
>name : Symbol(name, Decl(dependentReturnType3.ts, 186, 34))
return this._createIntervalJob(interval, name, data, options); // Ok
>this._createIntervalJob : Symbol(NewAgenda._createIntervalJob, Decl(dependentReturnType3.ts, 176, 17))
>this : Symbol(NewAgenda, Decl(dependentReturnType3.ts, 175, 29))
>_createIntervalJob : Symbol(NewAgenda._createIntervalJob, Decl(dependentReturnType3.ts, 176, 17))
>interval : Symbol(interval, Decl(dependentReturnType3.ts, 185, 55))
>name : Symbol(name, Decl(dependentReturnType3.ts, 186, 34))
>data : Symbol(data, Decl(dependentReturnType3.ts, 187, 16))
>options : Symbol(options, Decl(dependentReturnType3.ts, 188, 27))
}
if (Array.isArray(name)) {
>Array.isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --))
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
>isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --))
>name : Symbol(name, Decl(dependentReturnType3.ts, 186, 34))
return this._createIntervalJobs(interval, name, data, options); // Ok
>this._createIntervalJobs : Symbol(NewAgenda._createIntervalJobs, Decl(dependentReturnType3.ts, 177, 163))
>this : Symbol(NewAgenda, Decl(dependentReturnType3.ts, 175, 29))
>_createIntervalJobs : Symbol(NewAgenda._createIntervalJobs, Decl(dependentReturnType3.ts, 177, 163))
>interval : Symbol(interval, Decl(dependentReturnType3.ts, 185, 55))
>name : Symbol(name, Decl(dependentReturnType3.ts, 186, 34))
>data : Symbol(data, Decl(dependentReturnType3.ts, 187, 16))
>options : Symbol(options, Decl(dependentReturnType3.ts, 188, 27))
// Possible bug in original: createIntervalJobs can return undefined, but the original overload did not acount for that.
}
throw new Error('Unexpected error: Invalid job name(s)');
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
}
}
// File: angular/packages/common/src/pipes/case_conversion_pipes.ts
function transform1<T extends string | null | undefined>(value: T): HelperCond<T, string, string, null | undefined, null> {
>transform1 : Symbol(transform1, Decl(dependentReturnType3.ts, 201, 1))
>T : Symbol(T, Decl(dependentReturnType3.ts, 205, 20))
>value : Symbol(value, Decl(dependentReturnType3.ts, 205, 57))
>T : Symbol(T, Decl(dependentReturnType3.ts, 205, 20))
>HelperCond : Symbol(HelperCond, Decl(dependentReturnType3.ts, 0, 0))
>T : Symbol(T, Decl(dependentReturnType3.ts, 205, 20))
if (value == null) return null; // Ok
>value : Symbol(value, Decl(dependentReturnType3.ts, 205, 57))
if (typeof value !== 'string') {
>value : Symbol(value, Decl(dependentReturnType3.ts, 205, 57))
throw new Error();
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
}
return value.toLowerCase(); // Ok
>value.toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --))
>value : Symbol(value, Decl(dependentReturnType3.ts, 205, 57))
>toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --))
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,40 @@
dependentReturnType4.ts(28,9): error TS2322: Type '0' is not assignable to type 'T extends undefined ? 0 : T extends string ? 1 : never'.
dependentReturnType4.ts(30,5): error TS2322: Type '1' is not assignable to type 'T extends undefined ? 0 : T extends string ? 1 : never'.
==== dependentReturnType4.ts (2 errors) ====
declare const rand: { a?: never };
type Missing = typeof rand.a;
// Detection of valid optional parameter references
// Ok, will narrow return type
function bar1<T extends string | Missing>(x?: T):
T extends Missing ? 0 : T extends string ? 1 : never {
if (x === undefined) {
return 0;
}
return 1;
}
// Ok, will narrow return type
function bar2<T extends string | undefined>(x?: T):
T extends undefined ? 0 : T extends string ? 1 : never {
if (x === undefined) {
return 0;
}
return 1;
}
// Not ok, will not narrow return type
function bar3<T extends string>(x?: T):
T extends undefined ? 0 : T extends string ? 1 : never {
if (x === undefined) {
return 0;
~~~~~~
!!! error TS2322: Type '0' is not assignable to type 'T extends undefined ? 0 : T extends string ? 1 : never'.
}
return 1;
~~~~~~
!!! error TS2322: Type '1' is not assignable to type 'T extends undefined ? 0 : T extends string ? 1 : never'.
}

Просмотреть файл

@ -0,0 +1,76 @@
//// [tests/cases/compiler/dependentReturnType4.ts] ////
=== dependentReturnType4.ts ===
declare const rand: { a?: never };
>rand : Symbol(rand, Decl(dependentReturnType4.ts, 0, 13))
>a : Symbol(a, Decl(dependentReturnType4.ts, 0, 21))
type Missing = typeof rand.a;
>Missing : Symbol(Missing, Decl(dependentReturnType4.ts, 0, 34))
>rand.a : Symbol(a, Decl(dependentReturnType4.ts, 0, 21))
>rand : Symbol(rand, Decl(dependentReturnType4.ts, 0, 13))
>a : Symbol(a, Decl(dependentReturnType4.ts, 0, 21))
// Detection of valid optional parameter references
// Ok, will narrow return type
function bar1<T extends string | Missing>(x?: T):
>bar1 : Symbol(bar1, Decl(dependentReturnType4.ts, 1, 29))
>T : Symbol(T, Decl(dependentReturnType4.ts, 6, 14))
>Missing : Symbol(Missing, Decl(dependentReturnType4.ts, 0, 34))
>x : Symbol(x, Decl(dependentReturnType4.ts, 6, 42))
>T : Symbol(T, Decl(dependentReturnType4.ts, 6, 14))
T extends Missing ? 0 : T extends string ? 1 : never {
>T : Symbol(T, Decl(dependentReturnType4.ts, 6, 14))
>Missing : Symbol(Missing, Decl(dependentReturnType4.ts, 0, 34))
>T : Symbol(T, Decl(dependentReturnType4.ts, 6, 14))
if (x === undefined) {
>x : Symbol(x, Decl(dependentReturnType4.ts, 6, 42))
>undefined : Symbol(undefined)
return 0;
}
return 1;
}
// Ok, will narrow return type
function bar2<T extends string | undefined>(x?: T):
>bar2 : Symbol(bar2, Decl(dependentReturnType4.ts, 12, 1))
>T : Symbol(T, Decl(dependentReturnType4.ts, 15, 14))
>x : Symbol(x, Decl(dependentReturnType4.ts, 15, 44))
>T : Symbol(T, Decl(dependentReturnType4.ts, 15, 14))
T extends undefined ? 0 : T extends string ? 1 : never {
>T : Symbol(T, Decl(dependentReturnType4.ts, 15, 14))
>T : Symbol(T, Decl(dependentReturnType4.ts, 15, 14))
if (x === undefined) {
>x : Symbol(x, Decl(dependentReturnType4.ts, 15, 44))
>undefined : Symbol(undefined)
return 0;
}
return 1;
}
// Not ok, will not narrow return type
function bar3<T extends string>(x?: T):
>bar3 : Symbol(bar3, Decl(dependentReturnType4.ts, 21, 1))
>T : Symbol(T, Decl(dependentReturnType4.ts, 24, 14))
>x : Symbol(x, Decl(dependentReturnType4.ts, 24, 32))
>T : Symbol(T, Decl(dependentReturnType4.ts, 24, 14))
T extends undefined ? 0 : T extends string ? 1 : never {
>T : Symbol(T, Decl(dependentReturnType4.ts, 24, 14))
>T : Symbol(T, Decl(dependentReturnType4.ts, 24, 14))
if (x === undefined) {
>x : Symbol(x, Decl(dependentReturnType4.ts, 24, 32))
>undefined : Symbol(undefined)
return 0;
}
return 1;
}

Просмотреть файл

@ -0,0 +1,95 @@
//// [tests/cases/compiler/dependentReturnType4.ts] ////
=== dependentReturnType4.ts ===
declare const rand: { a?: never };
>rand : { a?: never; }
> : ^^^^^^ ^^^
>a : undefined
> : ^^^^^^^^^
type Missing = typeof rand.a;
>Missing : undefined
> : ^^^^^^^^^
>rand.a : undefined
> : ^^^^^^^^^
>rand : { a?: never; }
> : ^^^^^^ ^^^
>a : undefined
> : ^^^^^^^^^
// Detection of valid optional parameter references
// Ok, will narrow return type
function bar1<T extends string | Missing>(x?: T):
>bar1 : <T extends string | Missing>(x?: T) => T extends Missing ? 0 : T extends string ? 1 : never
> : ^ ^^^^^^^^^ ^^ ^^^ ^^^^^
>x : T | undefined
> : ^^^^^^^^^^^^^
T extends Missing ? 0 : T extends string ? 1 : never {
if (x === undefined) {
>x === undefined : boolean
> : ^^^^^^^
>x : T | undefined
> : ^^^^^^^^^^^^^
>undefined : undefined
> : ^^^^^^^^^
return 0;
>0 : 0
> : ^
}
return 1;
>1 : 1
> : ^
}
// Ok, will narrow return type
function bar2<T extends string | undefined>(x?: T):
>bar2 : <T extends string | undefined>(x?: T) => T extends undefined ? 0 : T extends string ? 1 : never
> : ^ ^^^^^^^^^ ^^ ^^^ ^^^^^
>x : T | undefined
> : ^^^^^^^^^^^^^
T extends undefined ? 0 : T extends string ? 1 : never {
if (x === undefined) {
>x === undefined : boolean
> : ^^^^^^^
>x : T | undefined
> : ^^^^^^^^^^^^^
>undefined : undefined
> : ^^^^^^^^^
return 0;
>0 : 0
> : ^
}
return 1;
>1 : 1
> : ^
}
// Not ok, will not narrow return type
function bar3<T extends string>(x?: T):
>bar3 : <T extends string>(x?: T) => T extends undefined ? 0 : T extends string ? 1 : never
> : ^ ^^^^^^^^^ ^^ ^^^ ^^^^^
>x : T | undefined
> : ^^^^^^^^^^^^^
T extends undefined ? 0 : T extends string ? 1 : never {
if (x === undefined) {
>x === undefined : boolean
> : ^^^^^^^
>x : T | undefined
> : ^^^^^^^^^^^^^
>undefined : undefined
> : ^^^^^^^^^
return 0;
>0 : 0
> : ^
}
return 1;
>1 : 1
> : ^
}

Просмотреть файл

@ -0,0 +1,109 @@
dependentReturnType5.ts(54,13): error TS2322: Type '2' is not assignable to type 'Comp[T]'.
Type '2' is not assignable to type '3'.
dependentReturnType5.ts(65,5): error TS2322: Type '2' is not assignable to type 'Comp[T]'.
Type '2' is not assignable to type '3'.
==== dependentReturnType5.ts (2 errors) ====
// Indexed access return type
interface A1 {
"prop": true;
[s: string]: boolean;
}
// This was already allowed but is unsound.
function foo1<T extends string>(x: T): A1[T] {
return false;
}
const rfoo1 = foo1("prop"); // Type says true, but actually returns false.
interface A2 {
"prop": true;
[n: number]: string;
}
// We could soundly allow that, because `"prop"` and `[n: number]` are disjoint types.
function foo2<T extends "prop" | number>(x: T): A2[T] {
if (x === "prop") {
return true;
}
return "some string";
}
const rfoo2 = foo2("prop");
const rfoo22 = foo2(34);
const rfoo222 = foo2(Math.random() ? "prop" : 34);
interface A3 {
[s: string]: boolean;
}
// No need for return type narrowing.
function foo3<T extends string>(x: T): A3[T] {
if (Math.random()) return true;
return false;
}
interface Comp {
foo: 2;
[n: number]: 3;
[s: string]: 2 | 3 | 4;
}
function indexedComp<T extends number | string>(x: T): Comp[T] {
if (x === "foo") {
if (Math.random()) {
return 3; // Error
}
return 2; // Ok
}
if (typeof x === "number") {
if (Math.random()) {
return 2; // Error
~~~~~~
!!! error TS2322: Type '2' is not assignable to type 'Comp[T]'.
!!! error TS2322: Type '2' is not assignable to type '3'.
}
return 3; // Ok
}
return 4; // Ok
}
function indexedComp2<T extends number | string>(x: T): Comp[T] {
if (Math.random()) {
return 3; // Bad, unsound
}
return 2; // Error
~~~~~~
!!! error TS2322: Type '2' is not assignable to type 'Comp[T]'.
!!! error TS2322: Type '2' is not assignable to type '3'.
}
// Most common case supported:
interface F {
"t": number,
"f": boolean,
}
// Ok
function depLikeFun<T extends "t" | "f">(str: T): F[T] {
if (str === "t") {
return 1;
} else {
return true;
}
}
depLikeFun("t"); // has type number
depLikeFun("f"); // has type boolean
type IndirectF<T extends keyof F> = F[T];
// Ok
function depLikeFun2<T extends "t" | "f">(str: T): IndirectF<T> {
if (str === "t") {
return 1;
} else {
return true;
}
}

Просмотреть файл

@ -0,0 +1,220 @@
//// [tests/cases/compiler/dependentReturnType5.ts] ////
=== dependentReturnType5.ts ===
// Indexed access return type
interface A1 {
>A1 : Symbol(A1, Decl(dependentReturnType5.ts, 0, 0))
"prop": true;
>"prop" : Symbol(A1["prop"], Decl(dependentReturnType5.ts, 1, 14))
[s: string]: boolean;
>s : Symbol(s, Decl(dependentReturnType5.ts, 3, 5))
}
// This was already allowed but is unsound.
function foo1<T extends string>(x: T): A1[T] {
>foo1 : Symbol(foo1, Decl(dependentReturnType5.ts, 4, 1))
>T : Symbol(T, Decl(dependentReturnType5.ts, 7, 14))
>x : Symbol(x, Decl(dependentReturnType5.ts, 7, 32))
>T : Symbol(T, Decl(dependentReturnType5.ts, 7, 14))
>A1 : Symbol(A1, Decl(dependentReturnType5.ts, 0, 0))
>T : Symbol(T, Decl(dependentReturnType5.ts, 7, 14))
return false;
}
const rfoo1 = foo1("prop"); // Type says true, but actually returns false.
>rfoo1 : Symbol(rfoo1, Decl(dependentReturnType5.ts, 10, 5))
>foo1 : Symbol(foo1, Decl(dependentReturnType5.ts, 4, 1))
interface A2 {
>A2 : Symbol(A2, Decl(dependentReturnType5.ts, 10, 27))
"prop": true;
>"prop" : Symbol(A2["prop"], Decl(dependentReturnType5.ts, 12, 14))
[n: number]: string;
>n : Symbol(n, Decl(dependentReturnType5.ts, 14, 5))
}
// We could soundly allow that, because `"prop"` and `[n: number]` are disjoint types.
function foo2<T extends "prop" | number>(x: T): A2[T] {
>foo2 : Symbol(foo2, Decl(dependentReturnType5.ts, 15, 1))
>T : Symbol(T, Decl(dependentReturnType5.ts, 18, 14))
>x : Symbol(x, Decl(dependentReturnType5.ts, 18, 41))
>T : Symbol(T, Decl(dependentReturnType5.ts, 18, 14))
>A2 : Symbol(A2, Decl(dependentReturnType5.ts, 10, 27))
>T : Symbol(T, Decl(dependentReturnType5.ts, 18, 14))
if (x === "prop") {
>x : Symbol(x, Decl(dependentReturnType5.ts, 18, 41))
return true;
}
return "some string";
}
const rfoo2 = foo2("prop");
>rfoo2 : Symbol(rfoo2, Decl(dependentReturnType5.ts, 24, 5))
>foo2 : Symbol(foo2, Decl(dependentReturnType5.ts, 15, 1))
const rfoo22 = foo2(34);
>rfoo22 : Symbol(rfoo22, Decl(dependentReturnType5.ts, 25, 5))
>foo2 : Symbol(foo2, Decl(dependentReturnType5.ts, 15, 1))
const rfoo222 = foo2(Math.random() ? "prop" : 34);
>rfoo222 : Symbol(rfoo222, Decl(dependentReturnType5.ts, 26, 5))
>foo2 : Symbol(foo2, Decl(dependentReturnType5.ts, 15, 1))
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
interface A3 {
>A3 : Symbol(A3, Decl(dependentReturnType5.ts, 26, 50))
[s: string]: boolean;
>s : Symbol(s, Decl(dependentReturnType5.ts, 29, 5))
}
// No need for return type narrowing.
function foo3<T extends string>(x: T): A3[T] {
>foo3 : Symbol(foo3, Decl(dependentReturnType5.ts, 30, 1))
>T : Symbol(T, Decl(dependentReturnType5.ts, 33, 14))
>x : Symbol(x, Decl(dependentReturnType5.ts, 33, 32))
>T : Symbol(T, Decl(dependentReturnType5.ts, 33, 14))
>A3 : Symbol(A3, Decl(dependentReturnType5.ts, 26, 50))
>T : Symbol(T, Decl(dependentReturnType5.ts, 33, 14))
if (Math.random()) return true;
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
return false;
}
interface Comp {
>Comp : Symbol(Comp, Decl(dependentReturnType5.ts, 36, 1))
foo: 2;
>foo : Symbol(Comp.foo, Decl(dependentReturnType5.ts, 38, 16))
[n: number]: 3;
>n : Symbol(n, Decl(dependentReturnType5.ts, 40, 5))
[s: string]: 2 | 3 | 4;
>s : Symbol(s, Decl(dependentReturnType5.ts, 41, 5))
}
function indexedComp<T extends number | string>(x: T): Comp[T] {
>indexedComp : Symbol(indexedComp, Decl(dependentReturnType5.ts, 42, 1))
>T : Symbol(T, Decl(dependentReturnType5.ts, 44, 21))
>x : Symbol(x, Decl(dependentReturnType5.ts, 44, 48))
>T : Symbol(T, Decl(dependentReturnType5.ts, 44, 21))
>Comp : Symbol(Comp, Decl(dependentReturnType5.ts, 36, 1))
>T : Symbol(T, Decl(dependentReturnType5.ts, 44, 21))
if (x === "foo") {
>x : Symbol(x, Decl(dependentReturnType5.ts, 44, 48))
if (Math.random()) {
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
return 3; // Error
}
return 2; // Ok
}
if (typeof x === "number") {
>x : Symbol(x, Decl(dependentReturnType5.ts, 44, 48))
if (Math.random()) {
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
return 2; // Error
}
return 3; // Ok
}
return 4; // Ok
}
function indexedComp2<T extends number | string>(x: T): Comp[T] {
>indexedComp2 : Symbol(indexedComp2, Decl(dependentReturnType5.ts, 58, 1))
>T : Symbol(T, Decl(dependentReturnType5.ts, 60, 22))
>x : Symbol(x, Decl(dependentReturnType5.ts, 60, 49))
>T : Symbol(T, Decl(dependentReturnType5.ts, 60, 22))
>Comp : Symbol(Comp, Decl(dependentReturnType5.ts, 36, 1))
>T : Symbol(T, Decl(dependentReturnType5.ts, 60, 22))
if (Math.random()) {
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
return 3; // Bad, unsound
}
return 2; // Error
}
// Most common case supported:
interface F {
>F : Symbol(F, Decl(dependentReturnType5.ts, 65, 1))
"t": number,
>"t" : Symbol(F["t"], Decl(dependentReturnType5.ts, 69, 13))
"f": boolean,
>"f" : Symbol(F["f"], Decl(dependentReturnType5.ts, 70, 16))
}
// Ok
function depLikeFun<T extends "t" | "f">(str: T): F[T] {
>depLikeFun : Symbol(depLikeFun, Decl(dependentReturnType5.ts, 72, 1))
>T : Symbol(T, Decl(dependentReturnType5.ts, 75, 20))
>str : Symbol(str, Decl(dependentReturnType5.ts, 75, 41))
>T : Symbol(T, Decl(dependentReturnType5.ts, 75, 20))
>F : Symbol(F, Decl(dependentReturnType5.ts, 65, 1))
>T : Symbol(T, Decl(dependentReturnType5.ts, 75, 20))
if (str === "t") {
>str : Symbol(str, Decl(dependentReturnType5.ts, 75, 41))
return 1;
} else {
return true;
}
}
depLikeFun("t"); // has type number
>depLikeFun : Symbol(depLikeFun, Decl(dependentReturnType5.ts, 72, 1))
depLikeFun("f"); // has type boolean
>depLikeFun : Symbol(depLikeFun, Decl(dependentReturnType5.ts, 72, 1))
type IndirectF<T extends keyof F> = F[T];
>IndirectF : Symbol(IndirectF, Decl(dependentReturnType5.ts, 84, 16))
>T : Symbol(T, Decl(dependentReturnType5.ts, 86, 15))
>F : Symbol(F, Decl(dependentReturnType5.ts, 65, 1))
>F : Symbol(F, Decl(dependentReturnType5.ts, 65, 1))
>T : Symbol(T, Decl(dependentReturnType5.ts, 86, 15))
// Ok
function depLikeFun2<T extends "t" | "f">(str: T): IndirectF<T> {
>depLikeFun2 : Symbol(depLikeFun2, Decl(dependentReturnType5.ts, 86, 41))
>T : Symbol(T, Decl(dependentReturnType5.ts, 89, 21))
>str : Symbol(str, Decl(dependentReturnType5.ts, 89, 42))
>T : Symbol(T, Decl(dependentReturnType5.ts, 89, 21))
>IndirectF : Symbol(IndirectF, Decl(dependentReturnType5.ts, 84, 16))
>T : Symbol(T, Decl(dependentReturnType5.ts, 89, 21))
if (str === "t") {
>str : Symbol(str, Decl(dependentReturnType5.ts, 89, 42))
return 1;
} else {
return true;
}
}

Просмотреть файл

@ -0,0 +1,331 @@
//// [tests/cases/compiler/dependentReturnType5.ts] ////
=== dependentReturnType5.ts ===
// Indexed access return type
interface A1 {
"prop": true;
>"prop" : true
> : ^^^^
>true : true
> : ^^^^
[s: string]: boolean;
>s : string
> : ^^^^^^
}
// This was already allowed but is unsound.
function foo1<T extends string>(x: T): A1[T] {
>foo1 : <T extends string>(x: T) => A1[T]
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^
>x : T
> : ^
return false;
>false : false
> : ^^^^^
}
const rfoo1 = foo1("prop"); // Type says true, but actually returns false.
>rfoo1 : true
> : ^^^^
>foo1("prop") : true
> : ^^^^
>foo1 : <T extends string>(x: T) => A1[T]
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^
>"prop" : "prop"
> : ^^^^^^
interface A2 {
"prop": true;
>"prop" : true
> : ^^^^
>true : true
> : ^^^^
[n: number]: string;
>n : number
> : ^^^^^^
}
// We could soundly allow that, because `"prop"` and `[n: number]` are disjoint types.
function foo2<T extends "prop" | number>(x: T): A2[T] {
>foo2 : <T extends "prop" | number>(x: T) => A2[T]
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^
>x : T
> : ^
if (x === "prop") {
>x === "prop" : boolean
> : ^^^^^^^
>x : T
> : ^
>"prop" : "prop"
> : ^^^^^^
return true;
>true : true
> : ^^^^
}
return "some string";
>"some string" : "some string"
> : ^^^^^^^^^^^^^
}
const rfoo2 = foo2("prop");
>rfoo2 : true
> : ^^^^
>foo2("prop") : true
> : ^^^^
>foo2 : <T extends "prop" | number>(x: T) => A2[T]
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^
>"prop" : "prop"
> : ^^^^^^
const rfoo22 = foo2(34);
>rfoo22 : string
> : ^^^^^^
>foo2(34) : string
> : ^^^^^^
>foo2 : <T extends "prop" | number>(x: T) => A2[T]
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^
>34 : 34
> : ^^
const rfoo222 = foo2(Math.random() ? "prop" : 34);
>rfoo222 : string | true
> : ^^^^^^^^^^^^^
>foo2(Math.random() ? "prop" : 34) : string | true
> : ^^^^^^^^^^^^^
>foo2 : <T extends "prop" | number>(x: T) => A2[T]
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^
>Math.random() ? "prop" : 34 : "prop" | 34
> : ^^^^^^^^^^^
>Math.random() : number
> : ^^^^^^
>Math.random : () => number
> : ^^^^^^
>Math : Math
> : ^^^^
>random : () => number
> : ^^^^^^
>"prop" : "prop"
> : ^^^^^^
>34 : 34
> : ^^
interface A3 {
[s: string]: boolean;
>s : string
> : ^^^^^^
}
// No need for return type narrowing.
function foo3<T extends string>(x: T): A3[T] {
>foo3 : <T extends string>(x: T) => A3[T]
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^
>x : T
> : ^
if (Math.random()) return true;
>Math.random() : number
> : ^^^^^^
>Math.random : () => number
> : ^^^^^^
>Math : Math
> : ^^^^
>random : () => number
> : ^^^^^^
>true : true
> : ^^^^
return false;
>false : false
> : ^^^^^
}
interface Comp {
foo: 2;
>foo : 2
> : ^
[n: number]: 3;
>n : number
> : ^^^^^^
[s: string]: 2 | 3 | 4;
>s : string
> : ^^^^^^
}
function indexedComp<T extends number | string>(x: T): Comp[T] {
>indexedComp : <T extends number | string>(x: T) => Comp[T]
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^
>x : T
> : ^
if (x === "foo") {
>x === "foo" : boolean
> : ^^^^^^^
>x : T
> : ^
>"foo" : "foo"
> : ^^^^^
if (Math.random()) {
>Math.random() : number
> : ^^^^^^
>Math.random : () => number
> : ^^^^^^
>Math : Math
> : ^^^^
>random : () => number
> : ^^^^^^
return 3; // Error
>3 : 3
> : ^
}
return 2; // Ok
>2 : 2
> : ^
}
if (typeof x === "number") {
>typeof x === "number" : boolean
> : ^^^^^^^
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>x : T
> : ^
>"number" : "number"
> : ^^^^^^^^
if (Math.random()) {
>Math.random() : number
> : ^^^^^^
>Math.random : () => number
> : ^^^^^^
>Math : Math
> : ^^^^
>random : () => number
> : ^^^^^^
return 2; // Error
>2 : 2
> : ^
}
return 3; // Ok
>3 : 3
> : ^
}
return 4; // Ok
>4 : 4
> : ^
}
function indexedComp2<T extends number | string>(x: T): Comp[T] {
>indexedComp2 : <T extends number | string>(x: T) => Comp[T]
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^
>x : T
> : ^
if (Math.random()) {
>Math.random() : number
> : ^^^^^^
>Math.random : () => number
> : ^^^^^^
>Math : Math
> : ^^^^
>random : () => number
> : ^^^^^^
return 3; // Bad, unsound
>3 : 3
> : ^
}
return 2; // Error
>2 : 2
> : ^
}
// Most common case supported:
interface F {
"t": number,
>"t" : number
> : ^^^^^^
"f": boolean,
>"f" : boolean
> : ^^^^^^^
}
// Ok
function depLikeFun<T extends "t" | "f">(str: T): F[T] {
>depLikeFun : <T extends "t" | "f">(str: T) => F[T]
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^
>str : T
> : ^
if (str === "t") {
>str === "t" : boolean
> : ^^^^^^^
>str : T
> : ^
>"t" : "t"
> : ^^^
return 1;
>1 : 1
> : ^
} else {
return true;
>true : true
> : ^^^^
}
}
depLikeFun("t"); // has type number
>depLikeFun("t") : number
> : ^^^^^^
>depLikeFun : <T extends "t" | "f">(str: T) => F[T]
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^
>"t" : "t"
> : ^^^
depLikeFun("f"); // has type boolean
>depLikeFun("f") : boolean
> : ^^^^^^^
>depLikeFun : <T extends "t" | "f">(str: T) => F[T]
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^
>"f" : "f"
> : ^^^
type IndirectF<T extends keyof F> = F[T];
>IndirectF : IndirectF<T>
> : ^^^^^^^^^^^^
// Ok
function depLikeFun2<T extends "t" | "f">(str: T): IndirectF<T> {
>depLikeFun2 : <T extends "t" | "f">(str: T) => IndirectF<T>
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^
>str : T
> : ^
if (str === "t") {
>str === "t" : boolean
> : ^^^^^^^
>str : T
> : ^
>"t" : "t"
> : ^^^
return 1;
>1 : 1
> : ^
} else {
return true;
>true : true
> : ^^^^
}
}

Просмотреть файл

@ -0,0 +1,193 @@
file.ts(28,26): error TS2322: Type 'true' is not assignable to type 'SomeInterfaceBad<T>[U]'.
Type 'true' is not assignable to type 'T extends 1 ? true : T extends 2 ? false : never'.
file.ts(28,33): error TS2322: Type 'false' is not assignable to type 'SomeInterfaceBad<T>[U]'.
Type 'false' is not assignable to type 'T extends 1 ? true : T extends 2 ? false : never'.
file.ts(30,16): error TS2322: Type '1' is not assignable to type 'SomeInterfaceBad<T>[U]'.
Type '1' is not assignable to type 'T extends true ? 1 : T extends false ? 2 : never'.
file.ts(30,20): error TS2322: Type '2' is not assignable to type 'SomeInterfaceBad<T>[U]'.
Type '2' is not assignable to type 'T extends true ? 1 : T extends false ? 2 : never'.
file.ts(80,13): error TS2322: Type '1' is not assignable to type 'this extends Sub1 ? 1 : this extends Sub2 ? 2 : never'.
file.ts(82,9): error TS2322: Type '2' is not assignable to type 'this extends Sub1 ? 1 : this extends Sub2 ? 2 : never'.
file.ts(94,16): error TS2322: Type '1' is not assignable to type 'T extends true ? 1 : T extends false ? 2 : never'.
file.ts(94,20): error TS2322: Type '2' is not assignable to type 'T extends true ? 1 : T extends false ? 2 : never'.
file.ts(98,100): error TS2322: Type '1 | 2' is not assignable to type 'T extends true ? 1 : T extends false ? 2 : never'.
Type '1' is not assignable to type 'T extends true ? 1 : T extends false ? 2 : never'.
file.ts(106,9): error TS2322: Type 'number' is not assignable to type 'SomeCond<T>'.
file.ts(106,9): error TS2589: Type instantiation is excessively deep and possibly infinite.
file.ts(108,5): error TS2322: Type 'number' is not assignable to type 'SomeCond<T>'.
file.ts(114,60): error TS2366: Function lacks ending return statement and return type does not include 'undefined'.
file.ts(116,9): error TS2322: Type '"one"' is not assignable to type 'OtherCond<U> | OtherCond<V>'.
file.ts(126,9): error TS2322: Type '"a"' is not assignable to type 'T extends (infer P)[] ? P : T extends number ? undefined : never'.
file.ts(128,5): error TS2322: Type 'undefined' is not assignable to type 'T extends (infer P)[] ? P : T extends number ? undefined : never'.
==== file.ts (16 errors) ====
// Type parameter in outer scope
function outer<T extends boolean>(x: T): number {
return inner();
function inner(): T extends true ? 1 : T extends false ? 2 : never {
return x ? 1 : 2;
}
}
// Overloads
function fun6<T extends boolean>(x: T, y: string): T extends true ? string : T extends false ? 2 : never;
function fun6<T extends boolean>(x: T, y: undefined): T extends true ? 1 : T extends false ? 2 : never;
function fun6(x: boolean): 1 | 2 | string;
function fun6<T extends boolean>(x: T, y?: string): T extends true ? 1 | string : T extends false ? 2 : never {
return x ? y !== undefined ? y : 1 : 2;
}
// Indexed access with conditional inside
// DOESN'T NARROW the nested conditional type of wrong shape
interface SomeInterfaceBad<T> {
prop1: T extends 1 ? true : T extends 2 ? false : never;
prop2: T extends true ? 1 : T extends false ? 2 : never;
}
function fun4bad<T, U extends keyof SomeInterfaceBad<unknown>>(x: T, y: U): SomeInterfaceBad<T>[U] {
if (y === "prop1") {
return x === 1 ? true : false;
~~~~
!!! error TS2322: Type 'true' is not assignable to type 'SomeInterfaceBad<T>[U]'.
!!! error TS2322: Type 'true' is not assignable to type 'T extends 1 ? true : T extends 2 ? false : never'.
~~~~~
!!! error TS2322: Type 'false' is not assignable to type 'SomeInterfaceBad<T>[U]'.
!!! error TS2322: Type 'false' is not assignable to type 'T extends 1 ? true : T extends 2 ? false : never'.
}
return x ? 1 : 2;
~
!!! error TS2322: Type '1' is not assignable to type 'SomeInterfaceBad<T>[U]'.
!!! error TS2322: Type '1' is not assignable to type 'T extends true ? 1 : T extends false ? 2 : never'.
~
!!! error TS2322: Type '2' is not assignable to type 'SomeInterfaceBad<T>[U]'.
!!! error TS2322: Type '2' is not assignable to type 'T extends true ? 1 : T extends false ? 2 : never'.
}
// Narrows nested conditional type of right shape
interface SomeInterfaceGood<T> {
prop1: T extends true ? 2 : T extends false ? 1 : never;
prop2: T extends true ? 1 : T extends false ? 2 : never;
}
function fun4good<T extends boolean, U extends keyof SomeInterfaceGood<unknown>>(x: T, y: U): SomeInterfaceGood<T>[U] {
if (y === "prop1") {
return x ? 2 : 1;
}
return x ? 1 : 2;
}
// Indexed access with indexed access inside - OK, narrows
interface BB {
"a": number;
"b": string;
}
interface AA<T extends keyof BB> {
"c": BB[T];
"d": boolean,
}
function reduction<T extends keyof BB, U extends keyof AA<any>>(x: T, y: U): AA<T>[U] {
if (x === "a" && y === "c") {
return 0; // Ok
}
return undefined as never;
}
// Conditional with indexed access inside - OK, narrows
function fun5<T extends 1 | 2, U extends keyof BB>(x: T, y: U): T extends 1 ? BB[U] : T extends 2 ? boolean : never {
if (x === 1) {
if (y === "a") {
return 0;
}
return "";
}
return true;
}
// `this` type parameter - Doesn't narrow
abstract class SomeClass {
fun3(this: Sub1 | Sub2): this extends Sub1 ? 1 : this extends Sub2 ? 2 : never {
if (this instanceof Sub1) {
return 1;
~~~~~~
!!! error TS2322: Type '1' is not assignable to type 'this extends Sub1 ? 1 : this extends Sub2 ? 2 : never'.
}
return 2;
~~~~~~
!!! error TS2322: Type '2' is not assignable to type 'this extends Sub1 ? 1 : this extends Sub2 ? 2 : never'.
}
}
class Sub1 extends SomeClass {
#sub1!: symbol;
};
class Sub2 extends SomeClass {
#sub2!: symbol;
};
// Detection of type parameter reference in presence of typeof
function fun2<T extends boolean>(x: T, y: typeof x): T extends true ? 1 : T extends false ? 2 : never {
return x ? 1 : 2;
~
!!! error TS2322: Type '1' is not assignable to type 'T extends true ? 1 : T extends false ? 2 : never'.
~
!!! error TS2322: Type '2' is not assignable to type 'T extends true ? 1 : T extends false ? 2 : never'.
}
// Contextually-typed lambdas
const fun1: <T extends boolean>(x: T) => T extends true ? 1 : T extends false ? 2 : never = (x) => x ? 1 : 2;
~~~~~~~~~
!!! error TS2322: Type '1 | 2' is not assignable to type 'T extends true ? 1 : T extends false ? 2 : never'.
!!! error TS2322: Type '1' is not assignable to type 'T extends true ? 1 : T extends false ? 2 : never'.
!!! related TS6502 file.ts:98:13: The expected type comes from the return type of this signature.
// Circular conditionals
type SomeCond<T> = T extends true ? 1 : T extends false ? SomeCond<T> : never;
function f7<T extends boolean>(x: T): SomeCond<T> {
if (x) {
return 1;
~~~~~~
!!! error TS2322: Type 'number' is not assignable to type 'SomeCond<T>'.
~~~~~~
!!! error TS2589: Type instantiation is excessively deep and possibly infinite.
}
return 2;
~~~~~~
!!! error TS2322: Type 'number' is not assignable to type 'SomeCond<T>'.
}
// Composite instantiation of conditional type
type OtherCond<T> = T extends 1 ? "one" : T extends 2 ? "two" : T extends 3 ? "three" : T extends 4 ? "four" : never;
function f8<U extends 1 | 2, V extends 3 | 4>(x: U, y: V): OtherCond<U | V> {
~~~~~~~~~~~~~~~~
!!! error TS2366: Function lacks ending return statement and return type does not include 'undefined'.
if (x === 1 && y === 3) {
return "one";
~~~~~~
!!! error TS2322: Type '"one"' is not assignable to type 'OtherCond<U> | OtherCond<V>'.
}
}
// Conditionals with `infer` - will not narrow, it is not safe to infer from the narrowed type into an `infer` type parameter
function f9<T extends "a"[] | "b"[] | number>(x: T): T extends Array<infer P> ? P : T extends number ? undefined : never {
if (Array.isArray(x)) {
// If we allowed narrowing of the conditional return type, when resolving the conditional `T & ("a"[] | "b"[]) extends Array<infer P> ? P : ...`,
// we could infer `"a" | "b"` for `P`, and allow "a" to be returned. However, when calling `f10`, `T` could be instantiated with `"b"[]`, and the return type would be `"b"`,
// so allowing an `"a"` return would be unsound.
return "a";
~~~~~~
!!! error TS2322: Type '"a"' is not assignable to type 'T extends (infer P)[] ? P : T extends number ? undefined : never'.
}
return undefined;
~~~~~~
!!! error TS2322: Type 'undefined' is not assignable to type 'T extends (infer P)[] ? P : T extends number ? undefined : never'.
}

Просмотреть файл

@ -0,0 +1,367 @@
//// [tests/cases/compiler/dependentReturnType6.ts] ////
=== file.ts ===
// Type parameter in outer scope
function outer<T extends boolean>(x: T): number {
>outer : Symbol(outer, Decl(file.ts, 0, 0))
>T : Symbol(T, Decl(file.ts, 1, 15))
>x : Symbol(x, Decl(file.ts, 1, 34))
>T : Symbol(T, Decl(file.ts, 1, 15))
return inner();
>inner : Symbol(inner, Decl(file.ts, 2, 19))
function inner(): T extends true ? 1 : T extends false ? 2 : never {
>inner : Symbol(inner, Decl(file.ts, 2, 19))
>T : Symbol(T, Decl(file.ts, 1, 15))
>T : Symbol(T, Decl(file.ts, 1, 15))
return x ? 1 : 2;
>x : Symbol(x, Decl(file.ts, 1, 34))
}
}
// Overloads
function fun6<T extends boolean>(x: T, y: string): T extends true ? string : T extends false ? 2 : never;
>fun6 : Symbol(fun6, Decl(file.ts, 7, 1), Decl(file.ts, 10, 105), Decl(file.ts, 11, 103), Decl(file.ts, 12, 42))
>T : Symbol(T, Decl(file.ts, 10, 14))
>x : Symbol(x, Decl(file.ts, 10, 33))
>T : Symbol(T, Decl(file.ts, 10, 14))
>y : Symbol(y, Decl(file.ts, 10, 38))
>T : Symbol(T, Decl(file.ts, 10, 14))
>T : Symbol(T, Decl(file.ts, 10, 14))
function fun6<T extends boolean>(x: T, y: undefined): T extends true ? 1 : T extends false ? 2 : never;
>fun6 : Symbol(fun6, Decl(file.ts, 7, 1), Decl(file.ts, 10, 105), Decl(file.ts, 11, 103), Decl(file.ts, 12, 42))
>T : Symbol(T, Decl(file.ts, 11, 14))
>x : Symbol(x, Decl(file.ts, 11, 33))
>T : Symbol(T, Decl(file.ts, 11, 14))
>y : Symbol(y, Decl(file.ts, 11, 38))
>T : Symbol(T, Decl(file.ts, 11, 14))
>T : Symbol(T, Decl(file.ts, 11, 14))
function fun6(x: boolean): 1 | 2 | string;
>fun6 : Symbol(fun6, Decl(file.ts, 7, 1), Decl(file.ts, 10, 105), Decl(file.ts, 11, 103), Decl(file.ts, 12, 42))
>x : Symbol(x, Decl(file.ts, 12, 14))
function fun6<T extends boolean>(x: T, y?: string): T extends true ? 1 | string : T extends false ? 2 : never {
>fun6 : Symbol(fun6, Decl(file.ts, 7, 1), Decl(file.ts, 10, 105), Decl(file.ts, 11, 103), Decl(file.ts, 12, 42))
>T : Symbol(T, Decl(file.ts, 13, 14))
>x : Symbol(x, Decl(file.ts, 13, 33))
>T : Symbol(T, Decl(file.ts, 13, 14))
>y : Symbol(y, Decl(file.ts, 13, 38))
>T : Symbol(T, Decl(file.ts, 13, 14))
>T : Symbol(T, Decl(file.ts, 13, 14))
return x ? y !== undefined ? y : 1 : 2;
>x : Symbol(x, Decl(file.ts, 13, 33))
>y : Symbol(y, Decl(file.ts, 13, 38))
>undefined : Symbol(undefined)
>y : Symbol(y, Decl(file.ts, 13, 38))
}
// Indexed access with conditional inside
// DOESN'T NARROW the nested conditional type of wrong shape
interface SomeInterfaceBad<T> {
>SomeInterfaceBad : Symbol(SomeInterfaceBad, Decl(file.ts, 15, 1))
>T : Symbol(T, Decl(file.ts, 20, 27))
prop1: T extends 1 ? true : T extends 2 ? false : never;
>prop1 : Symbol(SomeInterfaceBad.prop1, Decl(file.ts, 20, 31))
>T : Symbol(T, Decl(file.ts, 20, 27))
>T : Symbol(T, Decl(file.ts, 20, 27))
prop2: T extends true ? 1 : T extends false ? 2 : never;
>prop2 : Symbol(SomeInterfaceBad.prop2, Decl(file.ts, 21, 60))
>T : Symbol(T, Decl(file.ts, 20, 27))
>T : Symbol(T, Decl(file.ts, 20, 27))
}
function fun4bad<T, U extends keyof SomeInterfaceBad<unknown>>(x: T, y: U): SomeInterfaceBad<T>[U] {
>fun4bad : Symbol(fun4bad, Decl(file.ts, 23, 1))
>T : Symbol(T, Decl(file.ts, 25, 17))
>U : Symbol(U, Decl(file.ts, 25, 19))
>SomeInterfaceBad : Symbol(SomeInterfaceBad, Decl(file.ts, 15, 1))
>x : Symbol(x, Decl(file.ts, 25, 63))
>T : Symbol(T, Decl(file.ts, 25, 17))
>y : Symbol(y, Decl(file.ts, 25, 68))
>U : Symbol(U, Decl(file.ts, 25, 19))
>SomeInterfaceBad : Symbol(SomeInterfaceBad, Decl(file.ts, 15, 1))
>T : Symbol(T, Decl(file.ts, 25, 17))
>U : Symbol(U, Decl(file.ts, 25, 19))
if (y === "prop1") {
>y : Symbol(y, Decl(file.ts, 25, 68))
return x === 1 ? true : false;
>x : Symbol(x, Decl(file.ts, 25, 63))
}
return x ? 1 : 2;
>x : Symbol(x, Decl(file.ts, 25, 63))
}
// Narrows nested conditional type of right shape
interface SomeInterfaceGood<T> {
>SomeInterfaceGood : Symbol(SomeInterfaceGood, Decl(file.ts, 30, 1))
>T : Symbol(T, Decl(file.ts, 33, 28))
prop1: T extends true ? 2 : T extends false ? 1 : never;
>prop1 : Symbol(SomeInterfaceGood.prop1, Decl(file.ts, 33, 32))
>T : Symbol(T, Decl(file.ts, 33, 28))
>T : Symbol(T, Decl(file.ts, 33, 28))
prop2: T extends true ? 1 : T extends false ? 2 : never;
>prop2 : Symbol(SomeInterfaceGood.prop2, Decl(file.ts, 34, 60))
>T : Symbol(T, Decl(file.ts, 33, 28))
>T : Symbol(T, Decl(file.ts, 33, 28))
}
function fun4good<T extends boolean, U extends keyof SomeInterfaceGood<unknown>>(x: T, y: U): SomeInterfaceGood<T>[U] {
>fun4good : Symbol(fun4good, Decl(file.ts, 36, 1))
>T : Symbol(T, Decl(file.ts, 38, 18))
>U : Symbol(U, Decl(file.ts, 38, 36))
>SomeInterfaceGood : Symbol(SomeInterfaceGood, Decl(file.ts, 30, 1))
>x : Symbol(x, Decl(file.ts, 38, 81))
>T : Symbol(T, Decl(file.ts, 38, 18))
>y : Symbol(y, Decl(file.ts, 38, 86))
>U : Symbol(U, Decl(file.ts, 38, 36))
>SomeInterfaceGood : Symbol(SomeInterfaceGood, Decl(file.ts, 30, 1))
>T : Symbol(T, Decl(file.ts, 38, 18))
>U : Symbol(U, Decl(file.ts, 38, 36))
if (y === "prop1") {
>y : Symbol(y, Decl(file.ts, 38, 86))
return x ? 2 : 1;
>x : Symbol(x, Decl(file.ts, 38, 81))
}
return x ? 1 : 2;
>x : Symbol(x, Decl(file.ts, 38, 81))
}
// Indexed access with indexed access inside - OK, narrows
interface BB {
>BB : Symbol(BB, Decl(file.ts, 43, 1))
"a": number;
>"a" : Symbol(BB["a"], Decl(file.ts, 46, 14))
"b": string;
>"b" : Symbol(BB["b"], Decl(file.ts, 47, 16))
}
interface AA<T extends keyof BB> {
>AA : Symbol(AA, Decl(file.ts, 49, 1))
>T : Symbol(T, Decl(file.ts, 51, 13))
>BB : Symbol(BB, Decl(file.ts, 43, 1))
"c": BB[T];
>"c" : Symbol(AA["c"], Decl(file.ts, 51, 34))
>BB : Symbol(BB, Decl(file.ts, 43, 1))
>T : Symbol(T, Decl(file.ts, 51, 13))
"d": boolean,
>"d" : Symbol(AA["d"], Decl(file.ts, 52, 15))
}
function reduction<T extends keyof BB, U extends keyof AA<any>>(x: T, y: U): AA<T>[U] {
>reduction : Symbol(reduction, Decl(file.ts, 54, 1))
>T : Symbol(T, Decl(file.ts, 56, 19))
>BB : Symbol(BB, Decl(file.ts, 43, 1))
>U : Symbol(U, Decl(file.ts, 56, 38))
>AA : Symbol(AA, Decl(file.ts, 49, 1))
>x : Symbol(x, Decl(file.ts, 56, 64))
>T : Symbol(T, Decl(file.ts, 56, 19))
>y : Symbol(y, Decl(file.ts, 56, 69))
>U : Symbol(U, Decl(file.ts, 56, 38))
>AA : Symbol(AA, Decl(file.ts, 49, 1))
>T : Symbol(T, Decl(file.ts, 56, 19))
>U : Symbol(U, Decl(file.ts, 56, 38))
if (x === "a" && y === "c") {
>x : Symbol(x, Decl(file.ts, 56, 64))
>y : Symbol(y, Decl(file.ts, 56, 69))
return 0; // Ok
}
return undefined as never;
>undefined : Symbol(undefined)
}
// Conditional with indexed access inside - OK, narrows
function fun5<T extends 1 | 2, U extends keyof BB>(x: T, y: U): T extends 1 ? BB[U] : T extends 2 ? boolean : never {
>fun5 : Symbol(fun5, Decl(file.ts, 62, 1))
>T : Symbol(T, Decl(file.ts, 65, 14))
>U : Symbol(U, Decl(file.ts, 65, 30))
>BB : Symbol(BB, Decl(file.ts, 43, 1))
>x : Symbol(x, Decl(file.ts, 65, 51))
>T : Symbol(T, Decl(file.ts, 65, 14))
>y : Symbol(y, Decl(file.ts, 65, 56))
>U : Symbol(U, Decl(file.ts, 65, 30))
>T : Symbol(T, Decl(file.ts, 65, 14))
>BB : Symbol(BB, Decl(file.ts, 43, 1))
>U : Symbol(U, Decl(file.ts, 65, 30))
>T : Symbol(T, Decl(file.ts, 65, 14))
if (x === 1) {
>x : Symbol(x, Decl(file.ts, 65, 51))
if (y === "a") {
>y : Symbol(y, Decl(file.ts, 65, 56))
return 0;
}
return "";
}
return true;
}
// `this` type parameter - Doesn't narrow
abstract class SomeClass {
>SomeClass : Symbol(SomeClass, Decl(file.ts, 73, 1))
fun3(this: Sub1 | Sub2): this extends Sub1 ? 1 : this extends Sub2 ? 2 : never {
>fun3 : Symbol(SomeClass.fun3, Decl(file.ts, 76, 26))
>this : Symbol(this, Decl(file.ts, 77, 9))
>Sub1 : Symbol(Sub1, Decl(file.ts, 83, 1))
>Sub2 : Symbol(Sub2, Decl(file.ts, 86, 2))
>Sub1 : Symbol(Sub1, Decl(file.ts, 83, 1))
>Sub2 : Symbol(Sub2, Decl(file.ts, 86, 2))
if (this instanceof Sub1) {
>this : Symbol(this, Decl(file.ts, 77, 9))
>Sub1 : Symbol(Sub1, Decl(file.ts, 83, 1))
return 1;
}
return 2;
}
}
class Sub1 extends SomeClass {
>Sub1 : Symbol(Sub1, Decl(file.ts, 83, 1))
>SomeClass : Symbol(SomeClass, Decl(file.ts, 73, 1))
#sub1!: symbol;
>#sub1 : Symbol(Sub1.#sub1, Decl(file.ts, 84, 30))
};
class Sub2 extends SomeClass {
>Sub2 : Symbol(Sub2, Decl(file.ts, 86, 2))
>SomeClass : Symbol(SomeClass, Decl(file.ts, 73, 1))
#sub2!: symbol;
>#sub2 : Symbol(Sub2.#sub2, Decl(file.ts, 87, 30))
};
// Detection of type parameter reference in presence of typeof
function fun2<T extends boolean>(x: T, y: typeof x): T extends true ? 1 : T extends false ? 2 : never {
>fun2 : Symbol(fun2, Decl(file.ts, 89, 2))
>T : Symbol(T, Decl(file.ts, 92, 14))
>x : Symbol(x, Decl(file.ts, 92, 33))
>T : Symbol(T, Decl(file.ts, 92, 14))
>y : Symbol(y, Decl(file.ts, 92, 38))
>x : Symbol(x, Decl(file.ts, 92, 33))
>T : Symbol(T, Decl(file.ts, 92, 14))
>T : Symbol(T, Decl(file.ts, 92, 14))
return x ? 1 : 2;
>x : Symbol(x, Decl(file.ts, 92, 33))
}
// Contextually-typed lambdas
const fun1: <T extends boolean>(x: T) => T extends true ? 1 : T extends false ? 2 : never = (x) => x ? 1 : 2;
>fun1 : Symbol(fun1, Decl(file.ts, 97, 5))
>T : Symbol(T, Decl(file.ts, 97, 13))
>x : Symbol(x, Decl(file.ts, 97, 32))
>T : Symbol(T, Decl(file.ts, 97, 13))
>T : Symbol(T, Decl(file.ts, 97, 13))
>T : Symbol(T, Decl(file.ts, 97, 13))
>x : Symbol(x, Decl(file.ts, 97, 93))
>x : Symbol(x, Decl(file.ts, 97, 93))
// Circular conditionals
type SomeCond<T> = T extends true ? 1 : T extends false ? SomeCond<T> : never;
>SomeCond : Symbol(SomeCond, Decl(file.ts, 97, 109))
>T : Symbol(T, Decl(file.ts, 101, 14))
>T : Symbol(T, Decl(file.ts, 101, 14))
>T : Symbol(T, Decl(file.ts, 101, 14))
>SomeCond : Symbol(SomeCond, Decl(file.ts, 97, 109))
>T : Symbol(T, Decl(file.ts, 101, 14))
function f7<T extends boolean>(x: T): SomeCond<T> {
>f7 : Symbol(f7, Decl(file.ts, 101, 78))
>T : Symbol(T, Decl(file.ts, 103, 12))
>x : Symbol(x, Decl(file.ts, 103, 31))
>T : Symbol(T, Decl(file.ts, 103, 12))
>SomeCond : Symbol(SomeCond, Decl(file.ts, 97, 109))
>T : Symbol(T, Decl(file.ts, 103, 12))
if (x) {
>x : Symbol(x, Decl(file.ts, 103, 31))
return 1;
}
return 2;
}
// Composite instantiation of conditional type
type OtherCond<T> = T extends 1 ? "one" : T extends 2 ? "two" : T extends 3 ? "three" : T extends 4 ? "four" : never;
>OtherCond : Symbol(OtherCond, Decl(file.ts, 108, 1))
>T : Symbol(T, Decl(file.ts, 111, 15))
>T : Symbol(T, Decl(file.ts, 111, 15))
>T : Symbol(T, Decl(file.ts, 111, 15))
>T : Symbol(T, Decl(file.ts, 111, 15))
>T : Symbol(T, Decl(file.ts, 111, 15))
function f8<U extends 1 | 2, V extends 3 | 4>(x: U, y: V): OtherCond<U | V> {
>f8 : Symbol(f8, Decl(file.ts, 111, 117))
>U : Symbol(U, Decl(file.ts, 113, 12))
>V : Symbol(V, Decl(file.ts, 113, 28))
>x : Symbol(x, Decl(file.ts, 113, 46))
>U : Symbol(U, Decl(file.ts, 113, 12))
>y : Symbol(y, Decl(file.ts, 113, 51))
>V : Symbol(V, Decl(file.ts, 113, 28))
>OtherCond : Symbol(OtherCond, Decl(file.ts, 108, 1))
>U : Symbol(U, Decl(file.ts, 113, 12))
>V : Symbol(V, Decl(file.ts, 113, 28))
if (x === 1 && y === 3) {
>x : Symbol(x, Decl(file.ts, 113, 46))
>y : Symbol(y, Decl(file.ts, 113, 51))
return "one";
}
}
// Conditionals with `infer` - will not narrow, it is not safe to infer from the narrowed type into an `infer` type parameter
function f9<T extends "a"[] | "b"[] | number>(x: T): T extends Array<infer P> ? P : T extends number ? undefined : never {
>f9 : Symbol(f9, Decl(file.ts, 117, 1))
>T : Symbol(T, Decl(file.ts, 120, 12))
>x : Symbol(x, Decl(file.ts, 120, 46))
>T : Symbol(T, Decl(file.ts, 120, 12))
>T : Symbol(T, Decl(file.ts, 120, 12))
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 4 more)
>P : Symbol(P, Decl(file.ts, 120, 74))
>P : Symbol(P, Decl(file.ts, 120, 74))
>T : Symbol(T, Decl(file.ts, 120, 12))
if (Array.isArray(x)) {
>Array.isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --))
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 4 more)
>isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --))
>x : Symbol(x, Decl(file.ts, 120, 46))
// If we allowed narrowing of the conditional return type, when resolving the conditional `T & ("a"[] | "b"[]) extends Array<infer P> ? P : ...`,
// we could infer `"a" | "b"` for `P`, and allow "a" to be returned. However, when calling `f10`, `T` could be instantiated with `"b"[]`, and the return type would be `"b"`,
// so allowing an `"a"` return would be unsound.
return "a";
}
return undefined;
>undefined : Symbol(undefined)
}

Просмотреть файл

@ -0,0 +1,512 @@
//// [tests/cases/compiler/dependentReturnType6.ts] ////
=== Performance Stats ===
Instantiation count: 5,000
=== file.ts ===
// Type parameter in outer scope
function outer<T extends boolean>(x: T): number {
>outer : <T extends boolean>(x: T) => number
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^
>x : T
> : ^
return inner();
>inner() : T extends true ? 1 : T extends false ? 2 : never
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>inner : () => T extends true ? 1 : T extends false ? 2 : never
> : ^^^^^^
function inner(): T extends true ? 1 : T extends false ? 2 : never {
>inner : () => T extends true ? 1 : T extends false ? 2 : never
> : ^^^^^^
>true : true
> : ^^^^
>false : false
> : ^^^^^
return x ? 1 : 2;
>x ? 1 : 2 : 1 | 2
> : ^^^^^
>x : T
> : ^
>1 : 1
> : ^
>2 : 2
> : ^
}
}
// Overloads
function fun6<T extends boolean>(x: T, y: string): T extends true ? string : T extends false ? 2 : never;
>fun6 : { <T extends boolean>(x: T, y: string): T extends true ? string : T extends false ? 2 : never; <T_1 extends boolean>(x: T_1, y: undefined): T_1 extends true ? 1 : T_1 extends false ? 2 : never; (x: boolean): 1 | 2 | string; }
> : ^^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^ ^^^^^^^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^ ^^^ ^^ ^^^ ^^^
>x : T
> : ^
>y : string
> : ^^^^^^
>true : true
> : ^^^^
>false : false
> : ^^^^^
function fun6<T extends boolean>(x: T, y: undefined): T extends true ? 1 : T extends false ? 2 : never;
>fun6 : { <T_1 extends boolean>(x: T_1, y: string): T_1 extends true ? string : T_1 extends false ? 2 : never; <T extends boolean>(x: T, y: undefined): T extends true ? 1 : T extends false ? 2 : never; (x: boolean): 1 | 2 | string; }
> : ^^^^^^^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^ ^^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^ ^^^ ^^ ^^^ ^^^
>x : T
> : ^
>y : undefined
> : ^^^^^^^^^
>true : true
> : ^^^^
>false : false
> : ^^^^^
function fun6(x: boolean): 1 | 2 | string;
>fun6 : { <T extends boolean>(x: T, y: string): T extends true ? string : T extends false ? 2 : never; <T extends boolean>(x: T, y: undefined): T extends true ? 1 : T extends false ? 2 : never; (x: boolean): 1 | 2 | string; }
> : ^^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^ ^^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^ ^^^ ^^ ^^^ ^^^
>x : boolean
> : ^^^^^^^
function fun6<T extends boolean>(x: T, y?: string): T extends true ? 1 | string : T extends false ? 2 : never {
>fun6 : { <T_1 extends boolean>(x: T_1, y: string): T_1 extends true ? string : T_1 extends false ? 2 : never; <T_1 extends boolean>(x: T_1, y: undefined): T_1 extends true ? 1 : T_1 extends false ? 2 : never; (x: boolean): 1 | 2 | string; }
> : ^^^^^^^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^ ^^^^^^^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^ ^^^ ^^ ^^^ ^^^
>x : T
> : ^
>y : string | undefined
> : ^^^^^^^^^^^^^^^^^^
>true : true
> : ^^^^
>false : false
> : ^^^^^
return x ? y !== undefined ? y : 1 : 2;
>x ? y !== undefined ? y : 1 : 2 : string | 1 | 2
> : ^^^^^^^^^^^^^^
>x : T
> : ^
>y !== undefined ? y : 1 : string | 1
> : ^^^^^^^^^^
>y !== undefined : boolean
> : ^^^^^^^
>y : string | undefined
> : ^^^^^^^^^^^^^^^^^^
>undefined : undefined
> : ^^^^^^^^^
>y : string
> : ^^^^^^
>1 : 1
> : ^
>2 : 2
> : ^
}
// Indexed access with conditional inside
// DOESN'T NARROW the nested conditional type of wrong shape
interface SomeInterfaceBad<T> {
prop1: T extends 1 ? true : T extends 2 ? false : never;
>prop1 : T extends 1 ? true : T extends 2 ? false : never
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>true : true
> : ^^^^
>false : false
> : ^^^^^
prop2: T extends true ? 1 : T extends false ? 2 : never;
>prop2 : T extends true ? 1 : T extends false ? 2 : never
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>true : true
> : ^^^^
>false : false
> : ^^^^^
}
function fun4bad<T, U extends keyof SomeInterfaceBad<unknown>>(x: T, y: U): SomeInterfaceBad<T>[U] {
>fun4bad : <T, U extends keyof SomeInterfaceBad<unknown>>(x: T, y: U) => SomeInterfaceBad<T>[U]
> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^
>x : T
> : ^
>y : U
> : ^
if (y === "prop1") {
>y === "prop1" : boolean
> : ^^^^^^^
>y : U
> : ^
>"prop1" : "prop1"
> : ^^^^^^^
return x === 1 ? true : false;
>x === 1 ? true : false : boolean
> : ^^^^^^^
>x === 1 : boolean
> : ^^^^^^^
>x : T
> : ^
>1 : 1
> : ^
>true : true
> : ^^^^
>false : false
> : ^^^^^
}
return x ? 1 : 2;
>x ? 1 : 2 : 1 | 2
> : ^^^^^
>x : T
> : ^
>1 : 1
> : ^
>2 : 2
> : ^
}
// Narrows nested conditional type of right shape
interface SomeInterfaceGood<T> {
prop1: T extends true ? 2 : T extends false ? 1 : never;
>prop1 : T extends true ? 2 : T extends false ? 1 : never
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>true : true
> : ^^^^
>false : false
> : ^^^^^
prop2: T extends true ? 1 : T extends false ? 2 : never;
>prop2 : T extends true ? 1 : T extends false ? 2 : never
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>true : true
> : ^^^^
>false : false
> : ^^^^^
}
function fun4good<T extends boolean, U extends keyof SomeInterfaceGood<unknown>>(x: T, y: U): SomeInterfaceGood<T>[U] {
>fun4good : <T extends boolean, U extends keyof SomeInterfaceGood<unknown>>(x: T, y: U) => SomeInterfaceGood<T>[U]
> : ^ ^^^^^^^^^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^
>x : T
> : ^
>y : U
> : ^
if (y === "prop1") {
>y === "prop1" : boolean
> : ^^^^^^^
>y : U
> : ^
>"prop1" : "prop1"
> : ^^^^^^^
return x ? 2 : 1;
>x ? 2 : 1 : 1 | 2
> : ^^^^^
>x : T
> : ^
>2 : 2
> : ^
>1 : 1
> : ^
}
return x ? 1 : 2;
>x ? 1 : 2 : 1 | 2
> : ^^^^^
>x : T
> : ^
>1 : 1
> : ^
>2 : 2
> : ^
}
// Indexed access with indexed access inside - OK, narrows
interface BB {
"a": number;
>"a" : number
> : ^^^^^^
"b": string;
>"b" : string
> : ^^^^^^
}
interface AA<T extends keyof BB> {
"c": BB[T];
>"c" : BB[T]
> : ^^^^^
"d": boolean,
>"d" : boolean
> : ^^^^^^^
}
function reduction<T extends keyof BB, U extends keyof AA<any>>(x: T, y: U): AA<T>[U] {
>reduction : <T extends keyof BB, U extends keyof AA<any>>(x: T, y: U) => AA<T>[U]
> : ^ ^^^^^^^^^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^
>x : T
> : ^
>y : U
> : ^
if (x === "a" && y === "c") {
>x === "a" && y === "c" : boolean
> : ^^^^^^^
>x === "a" : boolean
> : ^^^^^^^
>x : T
> : ^
>"a" : "a"
> : ^^^
>y === "c" : boolean
> : ^^^^^^^
>y : U
> : ^
>"c" : "c"
> : ^^^
return 0; // Ok
>0 : 0
> : ^
}
return undefined as never;
>undefined as never : never
> : ^^^^^
>undefined : undefined
> : ^^^^^^^^^
}
// Conditional with indexed access inside - OK, narrows
function fun5<T extends 1 | 2, U extends keyof BB>(x: T, y: U): T extends 1 ? BB[U] : T extends 2 ? boolean : never {
>fun5 : <T extends 1 | 2, U extends keyof BB>(x: T, y: U) => T extends 1 ? BB[U] : T extends 2 ? boolean : never
> : ^ ^^^^^^^^^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^
>x : T
> : ^
>y : U
> : ^
if (x === 1) {
>x === 1 : boolean
> : ^^^^^^^
>x : T
> : ^
>1 : 1
> : ^
if (y === "a") {
>y === "a" : boolean
> : ^^^^^^^
>y : U
> : ^
>"a" : "a"
> : ^^^
return 0;
>0 : 0
> : ^
}
return "";
>"" : ""
> : ^^
}
return true;
>true : true
> : ^^^^
}
// `this` type parameter - Doesn't narrow
abstract class SomeClass {
>SomeClass : SomeClass
> : ^^^^^^^^^
fun3(this: Sub1 | Sub2): this extends Sub1 ? 1 : this extends Sub2 ? 2 : never {
>fun3 : (this: Sub1 | Sub2) => this extends Sub1 ? 1 : this extends Sub2 ? 2 : never
> : ^ ^^ ^^^^^
>this : Sub1 | Sub2
> : ^^^^^^^^^^^
if (this instanceof Sub1) {
>this instanceof Sub1 : boolean
> : ^^^^^^^
>this : Sub1 | Sub2
> : ^^^^^^^^^^^
>Sub1 : typeof Sub1
> : ^^^^^^^^^^^
return 1;
>1 : 1
> : ^
}
return 2;
>2 : 2
> : ^
}
}
class Sub1 extends SomeClass {
>Sub1 : Sub1
> : ^^^^
>SomeClass : SomeClass
> : ^^^^^^^^^
#sub1!: symbol;
>#sub1 : symbol
> : ^^^^^^
};
class Sub2 extends SomeClass {
>Sub2 : Sub2
> : ^^^^
>SomeClass : SomeClass
> : ^^^^^^^^^
#sub2!: symbol;
>#sub2 : symbol
> : ^^^^^^
};
// Detection of type parameter reference in presence of typeof
function fun2<T extends boolean>(x: T, y: typeof x): T extends true ? 1 : T extends false ? 2 : never {
>fun2 : <T extends boolean>(x: T, y: typeof x) => T extends true ? 1 : T extends false ? 2 : never
> : ^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^
>x : T
> : ^
>y : T
> : ^
>x : T
> : ^
>true : true
> : ^^^^
>false : false
> : ^^^^^
return x ? 1 : 2;
>x ? 1 : 2 : 1 | 2
> : ^^^^^
>x : T
> : ^
>1 : 1
> : ^
>2 : 2
> : ^
}
// Contextually-typed lambdas
const fun1: <T extends boolean>(x: T) => T extends true ? 1 : T extends false ? 2 : never = (x) => x ? 1 : 2;
>fun1 : <T extends boolean>(x: T) => T extends true ? 1 : T extends false ? 2 : never
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^
>x : T
> : ^
>true : true
> : ^^^^
>false : false
> : ^^^^^
>(x) => x ? 1 : 2 : <T extends boolean>(x: T) => 1 | 2
> : ^ ^^^^^^^^^ ^^ ^^^^^^^^^^^^^
>x : T
> : ^
>x ? 1 : 2 : 1 | 2
> : ^^^^^
>x : T
> : ^
>1 : 1
> : ^
>2 : 2
> : ^
// Circular conditionals
type SomeCond<T> = T extends true ? 1 : T extends false ? SomeCond<T> : never;
>SomeCond : SomeCond<T>
> : ^^^^^^^^^^^
>true : true
> : ^^^^
>false : false
> : ^^^^^
function f7<T extends boolean>(x: T): SomeCond<T> {
>f7 : <T extends boolean>(x: T) => SomeCond<T>
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^
>x : T
> : ^
if (x) {
>x : T
> : ^
return 1;
>1 : 1
> : ^
}
return 2;
>2 : 2
> : ^
}
// Composite instantiation of conditional type
type OtherCond<T> = T extends 1 ? "one" : T extends 2 ? "two" : T extends 3 ? "three" : T extends 4 ? "four" : never;
>OtherCond : OtherCond<T>
> : ^^^^^^^^^^^^
function f8<U extends 1 | 2, V extends 3 | 4>(x: U, y: V): OtherCond<U | V> {
>f8 : <U extends 1 | 2, V extends 3 | 4>(x: U, y: V) => OtherCond<U | V>
> : ^ ^^^^^^^^^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^
>x : U
> : ^
>y : V
> : ^
if (x === 1 && y === 3) {
>x === 1 && y === 3 : boolean
> : ^^^^^^^
>x === 1 : boolean
> : ^^^^^^^
>x : U
> : ^
>1 : 1
> : ^
>y === 3 : boolean
> : ^^^^^^^
>y : V
> : ^
>3 : 3
> : ^
return "one";
>"one" : "one"
> : ^^^^^
}
}
// Conditionals with `infer` - will not narrow, it is not safe to infer from the narrowed type into an `infer` type parameter
function f9<T extends "a"[] | "b"[] | number>(x: T): T extends Array<infer P> ? P : T extends number ? undefined : never {
>f9 : <T extends "a"[] | "b"[] | number>(x: T) => T extends Array<infer P> ? P : T extends number ? undefined : never
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^
>x : T
> : ^
if (Array.isArray(x)) {
>Array.isArray(x) : boolean
> : ^^^^^^^
>Array.isArray : (arg: any) => arg is any[]
> : ^ ^^ ^^^^^
>Array : ArrayConstructor
> : ^^^^^^^^^^^^^^^^
>isArray : (arg: any) => arg is any[]
> : ^ ^^ ^^^^^
>x : number | "a"[] | "b"[]
> : ^^^^^^^^^^^^^^^^^^^^^^
// If we allowed narrowing of the conditional return type, when resolving the conditional `T & ("a"[] | "b"[]) extends Array<infer P> ? P : ...`,
// we could infer `"a" | "b"` for `P`, and allow "a" to be returned. However, when calling `f10`, `T` could be instantiated with `"b"[]`, and the return type would be `"b"`,
// so allowing an `"a"` return would be unsound.
return "a";
>"a" : "a"
> : ^^^
}
return undefined;
>undefined : undefined
> : ^^^^^^^^^
}

Просмотреть файл

@ -0,0 +1,30 @@
//// [tests/cases/compiler/dependentReturnType8.ts] ////
=== dependentReturnType8.ts ===
export {};
declare const record: Record<string, string[]>;
>record : Symbol(record, Decl(dependentReturnType8.ts, 2, 13))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
declare const array: string[];
>array : Symbol(array, Decl(dependentReturnType8.ts, 3, 13))
// Arrow function with expression body
const getObject =
>getObject : Symbol(getObject, Decl(dependentReturnType8.ts, 6, 5))
<T extends string | undefined>(group: T): T extends string ? string[] : T extends undefined ? Record<string, string[]> : never =>
>T : Symbol(T, Decl(dependentReturnType8.ts, 7, 5))
>group : Symbol(group, Decl(dependentReturnType8.ts, 7, 35))
>T : Symbol(T, Decl(dependentReturnType8.ts, 7, 5))
>T : Symbol(T, Decl(dependentReturnType8.ts, 7, 5))
>T : Symbol(T, Decl(dependentReturnType8.ts, 7, 5))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
group === undefined ? record : array;
>group : Symbol(group, Decl(dependentReturnType8.ts, 7, 35))
>undefined : Symbol(undefined)
>record : Symbol(record, Decl(dependentReturnType8.ts, 2, 13))
>array : Symbol(array, Decl(dependentReturnType8.ts, 3, 13))

Просмотреть файл

@ -0,0 +1,38 @@
//// [tests/cases/compiler/dependentReturnType8.ts] ////
=== dependentReturnType8.ts ===
export {};
declare const record: Record<string, string[]>;
>record : Record<string, string[]>
> : ^^^^^^^^^^^^^^^^^^^^^^^^
declare const array: string[];
>array : string[]
> : ^^^^^^^^
// Arrow function with expression body
const getObject =
>getObject : <T extends string | undefined>(group: T) => T extends string ? string[] : T extends undefined ? Record<string, string[]> : never
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^
<T extends string | undefined>(group: T): T extends string ? string[] : T extends undefined ? Record<string, string[]> : never =>
><T extends string | undefined>(group: T): T extends string ? string[] : T extends undefined ? Record<string, string[]> : never => group === undefined ? record : array : <T extends string | undefined>(group: T) => T extends string ? string[] : T extends undefined ? Record<string, string[]> : never
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^
>group : T
> : ^
group === undefined ? record : array;
>group === undefined ? record : array : string[] | Record<string, string[]>
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>group === undefined : boolean
> : ^^^^^^^
>group : T
> : ^
>undefined : undefined
> : ^^^^^^^^^
>record : Record<string, string[]>
> : ^^^^^^^^^^^^^^^^^^^^^^^^
>array : string[]
> : ^^^^^^^^

Просмотреть файл

@ -0,0 +1,35 @@
//// [tests/cases/compiler/returnConditionalExpressionJSDocCast.ts] ////
=== file.js ===
// Don't peek into conditional return expression if it's wrapped in a cast
/** @type {Map<string, string>} */
const sources = new Map();
>sources : Symbol(sources, Decl(file.js, 2, 5))
>Map : Symbol(Map, Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
/**
* @param {string=} type the type of source that should be generated
* @returns {String}
*/
function source(type = "javascript") {
>source : Symbol(source, Decl(file.js, 2, 26))
>type : Symbol(type, Decl(file.js, 8, 16))
return /** @type {String} */ (
type
>type : Symbol(type, Decl(file.js, 8, 16))
? sources.get(type)
>sources.get : Symbol(Map.get, Decl(lib.es2015.collection.d.ts, --, --))
>sources : Symbol(sources, Decl(file.js, 2, 5))
>get : Symbol(Map.get, Decl(lib.es2015.collection.d.ts, --, --))
>type : Symbol(type, Decl(file.js, 8, 16))
: sources.get("some other thing")
>sources.get : Symbol(Map.get, Decl(lib.es2015.collection.d.ts, --, --))
>sources : Symbol(sources, Decl(file.js, 2, 5))
>get : Symbol(Map.get, Decl(lib.es2015.collection.d.ts, --, --))
);
}

Просмотреть файл

@ -0,0 +1,66 @@
//// [tests/cases/compiler/returnConditionalExpressionJSDocCast.ts] ////
=== Performance Stats ===
Type Count: 1,000
Instantiation count: 2,500
=== file.js ===
// Don't peek into conditional return expression if it's wrapped in a cast
/** @type {Map<string, string>} */
const sources = new Map();
>sources : Map<string, string>
> : ^^^^^^^^^^^^^^^^^^^
>new Map() : Map<any, any>
> : ^^^^^^^^^^^^^
>Map : MapConstructor
> : ^^^^^^^^^^^^^^
/**
* @param {string=} type the type of source that should be generated
* @returns {String}
*/
function source(type = "javascript") {
>source : (type?: string | undefined) => string
> : ^ ^^^ ^^^^^^^^^^^^^^^^^^^^
>type : string | undefined
> : ^^^^^^^^^^^^^^^^^^
>"javascript" : "javascript"
> : ^^^^^^^^^^^^
return /** @type {String} */ (
>( type ? sources.get(type) : sources.get("some other thing") ) : string
> : ^^^^^^
type
>type ? sources.get(type) : sources.get("some other thing") : string | undefined
> : ^^^^^^^^^^^^^^^^^^
>type : string
> : ^^^^^^
? sources.get(type)
>sources.get(type) : string | undefined
> : ^^^^^^^^^^^^^^^^^^
>sources.get : (key: string) => string | undefined
> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>sources : Map<string, string>
> : ^^^^^^^^^^^^^^^^^^^
>get : (key: string) => string | undefined
> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>type : string
> : ^^^^^^
: sources.get("some other thing")
>sources.get("some other thing") : string | undefined
> : ^^^^^^^^^^^^^^^^^^
>sources.get : (key: string) => string | undefined
> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>sources : Map<string, string>
> : ^^^^^^^^^^^^^^^^^^^
>get : (key: string) => string | undefined
> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>"some other thing" : "some other thing"
> : ^^^^^^^^^^^^^^^^^^
);
}

Просмотреть файл

@ -0,0 +1,19 @@
//// [tests/cases/compiler/unusedLocalsInRecursiveReturn.ts] ////
=== unusedLocalsInRecursiveReturn.ts ===
// Test that we unconditionally check return expression, even if we don't need its type.
function recursive(arg: string, other: string) {
>recursive : Symbol(recursive, Decl(unusedLocalsInRecursiveReturn.ts, 0, 0))
>arg : Symbol(arg, Decl(unusedLocalsInRecursiveReturn.ts, 1, 19))
>other : Symbol(other, Decl(unusedLocalsInRecursiveReturn.ts, 1, 31))
const someLocalVar = arg + other;
>someLocalVar : Symbol(someLocalVar, Decl(unusedLocalsInRecursiveReturn.ts, 2, 9))
>arg : Symbol(arg, Decl(unusedLocalsInRecursiveReturn.ts, 1, 19))
>other : Symbol(other, Decl(unusedLocalsInRecursiveReturn.ts, 1, 31))
return recursive(someLocalVar, arg);
>recursive : Symbol(recursive, Decl(unusedLocalsInRecursiveReturn.ts, 0, 0))
>someLocalVar : Symbol(someLocalVar, Decl(unusedLocalsInRecursiveReturn.ts, 2, 9))
>arg : Symbol(arg, Decl(unusedLocalsInRecursiveReturn.ts, 1, 19))
}

Просмотреть файл

@ -0,0 +1,32 @@
//// [tests/cases/compiler/unusedLocalsInRecursiveReturn.ts] ////
=== unusedLocalsInRecursiveReturn.ts ===
// Test that we unconditionally check return expression, even if we don't need its type.
function recursive(arg: string, other: string) {
>recursive : (arg: string, other: string) => never
> : ^ ^^ ^^ ^^ ^^^^^^^^^^
>arg : string
> : ^^^^^^
>other : string
> : ^^^^^^
const someLocalVar = arg + other;
>someLocalVar : string
> : ^^^^^^
>arg + other : string
> : ^^^^^^
>arg : string
> : ^^^^^^
>other : string
> : ^^^^^^
return recursive(someLocalVar, arg);
>recursive(someLocalVar, arg) : never
> : ^^^^^
>recursive : (arg: string, other: string) => never
> : ^ ^^ ^^ ^^ ^^^^^^^^^^
>someLocalVar : string
> : ^^^^^^
>arg : string
> : ^^^^^^
}

Просмотреть файл

@ -0,0 +1,13 @@
// @strict: true
// @noEmit: true
// @checkJs: true
// @allowJs: true
// @filename: mytest.js
/**
* @template T
* @param {T|undefined} value value or not
* @returns {T} result value
*/
const cloneObjectGood = value => /** @type {T} */({ ...value });

Просмотреть файл

@ -0,0 +1,519 @@
// @strict: true
// @noEmit: true
// @target: esnext
interface A {
1: number;
2: string;
}
function f1<T extends 1 | 2>(x: T): A[T] {
if (x === 1) {
return 0; // Ok
}
else {
return 1; // Error
}
}
interface C {
1: number;
2: string;
3: boolean;
}
function f2<T extends 1 | 2 | 3>(x: T): C[T] {
if (x === 1) {
return 0; // Ok
}
else {
return ""; // Error, returned expression needs to have type string & boolean (= never)
}
}
function f3<T extends 1 | 2 | 3>(x: T): T extends 1 ? number : T extends 2 ? string : T extends 3 ? boolean : never {
if (x === 1) {
return 0; // Ok
}
else {
return ""; // Error, returned expression needs to have type string & boolean (= never)
}
}
interface One {
a: "a";
b: "b";
c: "c";
d: "d";
}
interface Two {
a: "a";
b: "b";
e: "e";
f: "f";
}
interface Three {
a: "a";
c: "c";
e: "e";
g: "g";
}
interface Four {
a: "a";
d: "d";
f: "f";
g: "g";
}
// Badly written conditional return type, will not trigger narrowing
function f10<T extends 1 | 2 | 3 | 4>(x: T): T extends 1 ? One : T extends 2 ? Two : T extends 3 ? Three : Four {
if (x === 1 || x === 2) {
return { a: "a", b: "b", c: "c", d: "d", e: "e", f: "f" }; // Error
}
return { a: "a", b: "b", c: "c", d: "d", e: "e", f: "f", g: "g" }; // Error
}
// Well written conditional
function f101<T extends 1 | 2 | 3 | 4>(x: T): T extends 1 ? One : T extends 2 ? Two : T extends 3 ? Three : T extends 4 ? Four : never {
if (x === 1 || x === 2) {
return { a: "a", b: "b", c: "c", d: "d", e: "e", f: "f" }; // Ok
}
// Excess property becomes a problem with the change,
// because we now check assignability to a narrower type...
return { a: "a", b: "b", c: "c", d: "d", e: "e", f: "f", g: "g" }; // EPC Error
}
// This will not work for several reasons:
// - first because the constraint of type parameter `Arg` is generic,
// so attempting to narrow the type of `arg` in the `if` would result in type `Arg & LeftIn`,
// which when substituted in the conditional return type, would not further resolve that conditional type
// - second because the `else` branch would never work because we don't narrow the type of `arg` to `Arg & RightIn`
function conditionalProducingIf<LeftIn, RightIn, LeftOut, RightOut, Arg extends LeftIn | RightIn>(
arg: Arg,
cond: (arg: LeftIn | RightIn) => arg is LeftIn,
produceLeftOut: (arg: LeftIn) => LeftOut,
produceRightOut: (arg: RightIn) => RightOut):
Arg extends LeftIn ? LeftOut : Arg extends RightIn ? RightOut : never
{
if (cond(arg)) {
return produceLeftOut(arg);
} else {
return produceRightOut(arg as RightIn);
}
}
interface Animal {
name: string;
}
interface Dog extends Animal {
bark: () => string;
}
// This would be unsafe to narrow.
declare function isDog(x: Animal): x is Dog;
declare function doggy(x: Dog): number;
function f12<T extends Animal>(x: T): T extends Dog ? number : string {
if (isDog(x)) { // `x` has type `T & Dog` here
return doggy(x);
}
return ""; // Error: Should not work because we can't express "not a Dog" in the type system
}
// Cannot narrow `keyof` too eagerly or something like the below breaks
function f<Entry extends { [index: string]: number | boolean }, EntryId extends keyof Entry>(entry: EntryId): Entry[EntryId] {
const entries = {} as Entry;
return entries[entry];
}
// Works the same as before
declare function takeA(val: 'A'): void;
export function bounceAndTakeIfA<AB extends 'A' | 'B'>(value: AB): AB {
if (value === 'A') {
takeA(value);
takeAB(value);
return value;
}
return value;
function takeAB(val: AB): void {}
}
// Works the same as before
export function bbb<AB extends "a" | "b">(value: AB): "a" {
if (value === "a") {
return value;
}
return "a";
}
class Unnamed {
root!: { name: string };
// Error: No narrowing because parameter is optional but `T` doesn't allow for undefined
name<T extends string>(name?: T): T extends string ? this : T extends undefined ? string : never {
if (typeof name === 'undefined') {
return this.root.name;
}
return this;
}
// Good conditional
name2<T extends string | undefined>(name?: T): T extends string ? this : T extends undefined ? string : never {
if (typeof name === 'undefined') {
return this.root.name; // Ok
}
this.root.name = name;
return this; // Ok
}
// Good conditional, wrong return expressions
name3<T extends string | undefined>(name?: T): T extends string ? this : T extends undefined ? string : never {
if (typeof name === 'undefined') {
return this; // Error
}
this.root.name = name;
return name; // Error
}
}
// Conditional expressions
interface Aa {
1: number;
2: string;
3: boolean;
}
function trivialConditional<T extends 1 | 2 | 3>(x: T): Aa[T] {
if (x !== 1) {
return x === 2 ? "" : true;
}
else {
return 0;
}
}
function conditional<T extends boolean>(x: T):
T extends true ? 1 : T extends false ? 2 : never {
return x ? 1 : 2; // Ok
}
function contextualConditional<T extends "a" | "b">(
x: T
): T extends "a" ? "a" : T extends "b" ? number : never {
return x === "a" ? x : parseInt(x); // Ok
}
function conditionalWithError<T extends "a" | "b">(
x: T
): T extends "a" ? number : T extends "b" ? string : never {
return x === "a" ? x : parseInt(x); // Error
}
// Multiple indexed type reductions
interface BB {
"a": number;
[y: number]: string;
}
interface AA<T extends keyof BB> {
"c": BB[T];
"d": boolean,
}
function reduction<T extends keyof BB, U extends "c" | "d">(x: T, y: U): AA<T>[U] {
if (y === "c" && x === "a") {
// AA<T>[U='c'] -> BB[T]
// BB[T='a'] -> number
return 0; // Ok
}
return undefined as never;
}
// Substitution types are not narrowed
function subsCond<T extends 1 | 2 | 3>(
x: T,
): T extends 1 | 2
? T extends 1
? string
: T extends 2
? boolean
: never
: T extends 3
? number
: never {
if (x === 1) {
return "";
} else if (x == 2) {
return true;
}
return 3;
}
// Unsafe: check types overlap
declare function q(x: object): x is { b: number };
function foo<T extends { a: string } | { b: number }>(
x: T,
): T extends { a: string } ? number : T extends { b: number } ? string : never {
if (q(x)) {
x.b;
return "";
}
x.a;
return 1;
}
let y = { a: "", b: 1 }
const r = foo<{ a: string }>(y); // type says number but actually string
type HelperCond<T, A, R1, B, R2> = T extends A ? R1 : T extends B ? R2 : never;
// We don't narrow the return type because the conditionals are not distributive
function foo2<U extends string | number, V extends boolean>(x: U, y: V):
HelperCond<{ x: U, y: V },
{ x: string, y: true }, 1,
{ x: number, y: false }, 2> {
if (typeof x === "string" && y === true) {
return 1; // Error
}
if (typeof x === "number" && y === false) {
return 2; // Error
}
return 0; // Error
}
// From https://github.com/microsoft/TypeScript/issues/24929#issue-332087943
declare function isString(s: unknown): s is string;
// capitalize a string or each element of an array of strings
function capitalize<T extends string | string[]>(
input: T
): T extends string[] ? string[] : T extends string ? string : never {
if (isString(input)) {
return input[0].toUpperCase() + input.slice(1); // Ok
} else {
return input.map(elt => capitalize(elt)); // Ok
}
}
function badCapitalize<T extends string | string[]>(
input: T
): T extends string[] ? string[] : T extends string ? string : never {
if (isString(input)) {
return input[0].toUpperCase() + input.slice(1); // Ok
} else {
return input[0].toUpperCase() + input.slice(1); // Bad, error
}
}
// No narrowing because conditional's extends type is different from type parameter constraint types
function voidRet<T extends { a: string } | undefined>(
x: T
): T extends {} ? void : T extends undefined ? number : never {
if (x) {
return;
}
return 1;
}
// Multiple type parameters at once
function woo<T extends string | number, U extends string | number>(
x: T,
y: U,
): T extends string
? U extends string
? 1
: U extends number
? 2
: never
: T extends number
? U extends number
? 3
: U extends string
? 4
: never
: never {
if (typeof x === "number" && typeof y === "string") {
return 1; // Good error
}
return undefined as any;
}
function ttt<T extends string | number, U extends string | number>(
x: T,
y: U,
): T extends string
? U extends string
? 1
: U extends number
? 2
: never
: T extends number
? U extends number
? 3
: U extends string
? 4
: never
: never {
if (typeof x === "number" && typeof y === "string") {
return 4; // Ok
}
return undefined as any;
}
// Shadowing of the narrowed reference
function shadowing<T extends 1 | 2>(x: T): T extends 1 ? number : T extends 2 ? string : never {
if (true) {
let x: number = Math.random() ? 1 : 2;
if (x === 1) {
return 1; // Error
}
return ""; // Error
}
}
function noShadowing<T extends 1 | 2>(x: T): T extends 1 ? number : T extends 2 ? string : never {
if (true) {
if (x === 1) {
return 1; // Ok
}
return ""; // Ok
}
}
// If the narrowing reference is out of scope, we simply won't narrow its type
declare let someX: boolean;
function scope2<T extends boolean>(a: T): T extends true ? 1 : T extends false ? 2 : never {
if ((true)) {
const someX = a;
if (someX) { // We narrow `someX` and the return type here
return 1;
}
}
if (!someX) { // This is a different `someX`, so we don't narrow here
return 2;
}
return undefined as any;
}
function moreShadowing<T extends 1 | 2>(x: T): T extends 1 ? number : T extends 2 ? string : never {
if (x === 2) {
let x: number = Math.random() ? 1 : 2;
if (x === 1) {
return 1; // Error
}
return ""; // Ok
}
return 0; // Ok
}
// This would be unsafe to narrow due to `infer` type.
function withInfer<T extends [string] | number>(x: T): T extends [infer R] ? R : T extends number ? boolean : never {
if (typeof x === "number") {
return true;
}
return "";
}
const withInferResult = withInfer(["a"] as const); // The type says it returns `"a"`, but the function actually returns `""`.
// Ok
async function abool<T extends true | false>(x: T): Promise<T extends true ? 1 : T extends false ? 2 : never> {
if (x) {
return 1;
}
return 2;
}
// Ok
function* bbool<T extends true | false>(x: T): Generator<number, T extends true ? 1 : T extends false ? 2 : never, unknown> {
yield 3;
if (x) {
return 1;
}
return 2;
}
// We don't do the same type of narrowing for `yield` statements
function* cbool<T extends true | false>(x: T): Generator<T extends true ? 1 : T extends false ? 2 : never, number, unknown> {
if (x) {
yield 1;
}
yield 2;
return 0;
}
// From #33912
abstract class Operation<T, R> {
abstract perform(t: T): R;
}
type ConditionalReturnType<T, R, EOp extends Operation<T, R> | undefined> =
EOp extends Operation<T, R> ? R : EOp extends undefined ? T | R : never;
class ConditionalOperation<
T,
R,
EOp extends Operation<T, R> | undefined,
> extends Operation<T, ConditionalReturnType<T, R, EOp>> {
constructor(
private predicate: (value: T) => boolean,
private thenOp: Operation<T, R>,
private elseOp?: EOp,
) {
super();
}
// We won't try to narrow the return type because `T` is declared on the class and we don't analyze this case.
perform(t: T): ConditionalReturnType<T, R, EOp> {
if (this.predicate(t)) {
return this.thenOp.perform(t); // Bad: this is assignable to all of the branches of the conditional, but we still can't return it
} else if (typeof this.elseOp !== "undefined") {
return this.elseOp.perform(t); // Would be ok
} else {
return t; // Would be ok
}
}
}
// Like the version above, we will not attempt to narrow because there's more than one reference to `T`,
// because `T` shows up in the type of `predicate`.
function perform<T, R, EOp extends Operation<T, R> | undefined>(
t: T,
predicate: (value: T) => boolean,
thenOp: Operation<T, R>,
elseOp?: EOp,
): ConditionalReturnType<T, R, EOp> {
if (predicate(t)) {
return thenOp.perform(t); // Bad: this is assignable to all of the branches of the conditional, but we still can't return it
} else if (elseOp !== undefined) {
return elseOp.perform(t); // Would be ok
} else {
return t; // Would be ok
}
}
// Return conditional expressions with parentheses
function returnStuff1<T extends boolean>(x: T ): T extends true ? 1 : T extends false ? 2 : never {
return (x ? (1) : 2);
}
function returnStuff2<T extends 1 | 2 | "a">(x: T ):
T extends 1 ? "one" : T extends 2 ? "two" : T extends "a" ? 0 : never {
return (typeof x === "string" ? 0 : (x === 1 ? ("one") : "two"));
}
// If the conditional type's input is `never`, then it resolves to `never`:
function neverOk<T extends boolean>(x: T): T extends true ? 1 : T extends false ? 2 : never {
if (x === true) {
return 1;
}
if (x === false) {
return 2;
}
return 1;
}

Просмотреть файл

@ -0,0 +1,307 @@
// @strict: true
// @noEmit: true
// @target: esnext
// @checkJs: true
// @filename: file.js
// Adapted from ts-error-deltas repos
/**
* @template T
* @template A
* @template R1
* @template B
* @template R2
* @typedef {T extends A ? R1 : T extends B ? R2 : never} HelperCond
*/
/**
* @typedef IMessage
* @property {string} [html]
* @property {Object[]} [tokens]
*/
class NewKatex {
/**
* @param {string} s
* @returns {string}
*/
render(s) {
return "";
}
/**
* @template {string | IMessage} T
* @param {T} message
* @returns {T extends string ? string : T extends IMessage ? IMessage : never}
*/
renderMessage(message) {
if (typeof message === 'string') {
return this.render(message); // Ok
}
if (!message.html?.trim()) {
return message; // Ok
}
if (!message.tokens) {
message.tokens = [];
}
message.html = this.render(message.html);
return message; // Ok
}
}
/**
* @template {true | false} T
* @param {{ dollarSyntax: boolean; parenthesisSyntax: boolean; }} options
* @param {T} _isMessage
* @returns {T extends true ? (message: IMessage) => IMessage : T extends false ? (message: string) => string : never}
*/
function createKatexMessageRendering(options, _isMessage) {
const instance = new NewKatex();
if (_isMessage) {
return (/** @type {IMessage} */ message) => instance.renderMessage(message); // Ok
}
return (/** @type {string} */ message) => instance.renderMessage(message); // Ok
}
// File: Rocket.Chat/apps/meteor/app/settings/lib/settings.ts
/**
* @typedef {Record<any, any>} MyObj
*/
/**
* @typedef {MyObj} SettingValue
*/
/**
* @template {SettingValue} T
* @typedef {Object} SettingComposedValue
* @property {string} key
* @property {SettingValue} value
*/
/**
* @callback SettingCallback
* @param {string} key
* @param {SettingValue} value
* @param {boolean} [initialLoad]
* @returns {void}
*/
/** @type {{ settings: { [s: string]: any } }} */
const Meteor = /** @type {any} */ (undefined);
/** @type {{ isRegExp(x: unknown): x is RegExp; }} */
const _ = /** @type {any} */ (undefined);
/**
* @param {RegExp} x
* @returns {void}
*/
function takesRegExp(x) {
return /** @type {any} */ undefined;
}
/**
* @param {string} x
* @returns {void}
*/
function takesString(x) {
return /** @type {any} */ undefined;
}
/**
* @class NewSettingsBase
*/
class NewSettingsBase {
/**
* @template {SettingCallback | undefined} C
* @template {string | RegExp} I
* @template {SettingValue} T
* @param {I} _id
* @param {C} [callback]
* @returns {HelperCond<C, SettingCallback, void, undefined, HelperCond<I, string, T | undefined, RegExp, SettingComposedValue<T>[]>>}
*/
newGet(_id, callback) {
if (callback !== undefined) {
if (!Meteor.settings) {
return; // Ok
}
if (_id === '*') {
return Object.keys(Meteor.settings).forEach((key) => {
const value = Meteor.settings[key];
callback(key, value);
});
}
if (_.isRegExp(_id) && Meteor.settings) {
return Object.keys(Meteor.settings).forEach((key) => {
if (!_id.test(key)) {
return;
}
const value = Meteor.settings[key];
callback(key, value);
});
}
if (typeof _id === 'string') {
const value = Meteor.settings[_id];
if (value != null) {
callback(_id, Meteor.settings[_id]);
}
return; // Ok
}
return; // Ok, needed for exhaustiveness check
}
if (!Meteor.settings) {
return undefined; // Error
}
if (_.isRegExp(_id)) {
return Object.keys(Meteor.settings).reduce((/** @type {SettingComposedValue<T>[]} */ items, key) => {
const value = Meteor.settings[key];
if (_id.test(key)) {
items.push({ key, value });
}
return items;
}, []); // Ok
}
return Meteor.settings?.[_id]; // Error
}
}
// File: Rocket.Chat/apps/meteor/app/ui-utils/client/lib/messageBox.ts
/**
* @typedef {MyObj} MessageBoxAction
*/
/**
* @template {string | undefined} T
* @param {T} group
* @returns {HelperCond<T, string, MessageBoxAction[], undefined, Record<string, MessageBoxAction[]>>}
*/
function getWithBug(group) {
if (!group) {
return /** @type {Record<string, MessageBoxAction[]>} */({}); // Error
}
return /** @type {MessageBoxAction[]} */([]); // Ok
}
/**
* @template {string | undefined} T
* @param {T} group
* @returns {HelperCond<T, string, MessageBoxAction[], undefined, Record<string, MessageBoxAction[]>>}
*/
function getWithoutBug(group) {
if (group === undefined) {
return /** @type {Record<string, MessageBoxAction[]>} */({}); // Ok
}
return /** @type {MessageBoxAction[]} */([]); // Ok
}
// File: Rocket.Chat/apps/meteor/ee/server/lib/engagementDashboard/date.ts
/**
* @param {string} x
* @returns {Date}
*/
function mapDateForAPI(x) {
return /** @type {any} */ (undefined);
}
/**
* @template {string | undefined} T
* @param {string} start
* @param {T} [end]
* @returns {HelperCond<T, string, { start: Date, end: Date }, undefined, { start: Date, end: undefined }>}
*/
function transformDatesForAPI(start, end) {
return end !== undefined ?
{
start: mapDateForAPI(start),
end: mapDateForAPI(end),
} :
{
start: mapDateForAPI(start),
end: undefined
};
}
// File: Rocket.Chat/packages/agenda/src/Agenda.ts
/**
* @typedef {MyObj} RepeatOptions
*/
/**
* @typedef {MyObj} Job
*/
/**
* @typedef {Object} IJob
* @property {MyObj} data
*/
class NewAgenda {
/**
* @param {string | number} interval
* @param {string} name
* @param {IJob['data']} data
* @param {RepeatOptions} options
* @returns {Promise<Job>}
*/
async _createIntervalJob(interval, name, data, options) {
return /** @type {any} */ (undefined);
}
/**
* @param {string | number} interval
* @param {string[]} names
* @param {IJob['data']} data
* @param {RepeatOptions} options
* @returns {Promise<Job[]> | undefined}
*/
_createIntervalJobs(interval, names, data, options) {
return undefined;
}
/**
* @template {string | string[]} T
* @param {string | number} interval
* @param {T} name
* @param {IJob['data']} data
* @param {RepeatOptions} options
* @returns {Promise<HelperCond<T, string, Job, string[], Job[] | undefined>>}
*/
async newEvery(interval, name, data, options) {
if (typeof name === 'string') {
return this._createIntervalJob(interval, name, data, options); // Ok
}
if (Array.isArray(name)) {
return this._createIntervalJobs(interval, name, data, options); // Ok
}
throw new Error('Unexpected error: Invalid job name(s)');
}
}
// File: angular/packages/common/src/pipes/case_conversion_pipes.ts
/**
* @template {string | null | undefined} T
* @param {T} value
* @returns {HelperCond<T, string, string, null | undefined, null>}
*/
function transform1(value) {
if (value == null) return null; // Ok
if (typeof value !== 'string') {
throw new Error();
}
return value.toLowerCase(); // Ok
}

Просмотреть файл

@ -0,0 +1,216 @@
// @strict: true
// @noEmit: true
// @target: ES6
// Adapted from ts-error-deltas repos
type HelperCond<T, A, R1, B, R2> =
T extends A
? R1
: T extends B
? R2
: never;
// File: Rocket.Chat/apps/meteor/app/katex/client/index.ts
interface IMessage {
html?: string;
tokens?: {}[];
}
class NewKatex {
render(s: string): string {
return "";
}
renderMessage<T extends string | IMessage>(message: T):
T extends string
? string
: T extends IMessage
? IMessage
: never {
if (typeof message === 'string') {
return this.render(message); // Ok
}
if (!message.html?.trim()) {
return message; // Ok
}
if (!message.tokens) {
message.tokens = [];
}
message.html = this.render(message.html);
return message; // Ok
}
}
export function createKatexMessageRendering<T extends true | false>(
options: {
dollarSyntax: boolean;
parenthesisSyntax: boolean;
},
_isMessage: T,
): T extends true
? (message: IMessage) => IMessage
: T extends false
? (message: string) => string
: never {
const instance = new NewKatex();
if (_isMessage) {
return (message: IMessage): IMessage => instance.renderMessage(message); // Ok
}
return (message: string): string => instance.renderMessage(message); // Ok
}
// File: Rocket.Chat/apps/meteor/app/settings/lib/settings.ts
type SettingComposedValue<T extends SettingValue = SettingValue> = { key: string; value: T };
type SettingCallback = (key: string, value: SettingValue, initialLoad?: boolean) => void;
type SettingValue = object;
declare const Meteor: { settings: { [s: string]: any } };
declare const _: { isRegExp(x: unknown): x is RegExp; };
declare function takesRegExp(x: RegExp): void;
declare function takesString(x: string): void;
class NewSettingsBase {
public newGet<C extends SettingCallback | undefined, I extends string | RegExp, T extends SettingValue = SettingValue>(
_id: I,
callback?: C,
): HelperCond<C,
SettingCallback, void,
undefined, HelperCond<I,
string, T | undefined,
RegExp, SettingComposedValue<T>[]>> {
if (callback !== undefined) {
if (!Meteor.settings) {
return; // Ok
}
if (_id === '*') {
return Object.keys(Meteor.settings).forEach((key) => { // Ok
const value = Meteor.settings[key];
callback(key, value);
});
}
if (_.isRegExp(_id) && Meteor.settings) {
return Object.keys(Meteor.settings).forEach((key) => { // Ok
if (!_id.test(key)) {
return;
}
const value = Meteor.settings[key];
callback(key, value);
});
}
if (typeof _id === 'string') {
const value = Meteor.settings[_id];
if (value != null) {
callback(_id, Meteor.settings[_id]);
}
return; // Ok
}
return; // Ok, needed for exhaustiveness check
}
if (!Meteor.settings) { // Wrong: we don't know that _id is string here, cannot return undefined
return undefined; // Error
}
if (_.isRegExp(_id)) {
return Object.keys(Meteor.settings).reduce((items: SettingComposedValue<T>[], key) => {
const value = Meteor.settings[key];
if (_id.test(key)) {
items.push({
key,
value,
});
}
return items;
}, []); // Ok
}
return Meteor.settings?.[_id]; // Error
// The indexing currently doesn't work because it doesn't use the narrowed type of `_id`.
}
}
// File: Rocket.Chat/apps/meteor/app/ui-utils/client/lib/messageBox.ts
type MessageBoxAction = object;
function getWithBug<T extends string | undefined>(group: T):
HelperCond<T, string, MessageBoxAction[], undefined, Record<string, MessageBoxAction[]>> {
if (!group) {
return {} as Record<string, MessageBoxAction[]>; // Error, could fall into this branch when group is empty string
}
return [] as MessageBoxAction[]; // Ok
}
function getWithoutBug<T extends string | undefined>(group: T):
HelperCond<T, string, MessageBoxAction[], undefined, Record<string, MessageBoxAction[]>> {
if (group === undefined) {
return {} as Record<string, MessageBoxAction[]>; // Ok
}
return [] as MessageBoxAction[]; // Ok
}
// File: Rocket.Chat/apps/meteor/ee/server/lib/engagementDashboard/date.ts
declare function mapDateForAPI(x: string): Date;
export function transformDatesForAPI<T extends string | undefined>(
start: string,
end?: T
): HelperCond<T, string, { start: Date, end: Date }, undefined, { start: Date, end: undefined }> {
return end !== undefined ? // Ok
{
start: mapDateForAPI(start),
end: mapDateForAPI(end),
} :
{
start: mapDateForAPI(start),
end: undefined
};
}
// File: Rocket.Chat/packages/agenda/src/Agenda.ts
type RepeatOptions = object;
type Job = object;
type IJob = { data: object };
class NewAgenda {
public async _createIntervalJob(interval: string | number, name: string, data: IJob['data'], options: RepeatOptions): Promise<Job> { return undefined as any; }
private _createIntervalJobs(
interval: string | number,
names: string[],
data: IJob['data'],
options: RepeatOptions,
): Promise<Job[]> | undefined { return undefined as any; }
public async newEvery<T extends string | string[]>(
interval: string | number,
name: T,
data: IJob['data'],
options: RepeatOptions): Promise<HelperCond<T, string, Job, string[], Job[] | undefined>> {
if (typeof name === 'string') {
return this._createIntervalJob(interval, name, data, options); // Ok
}
if (Array.isArray(name)) {
return this._createIntervalJobs(interval, name, data, options); // Ok
// Possible bug in original: createIntervalJobs can return undefined, but the original overload did not acount for that.
}
throw new Error('Unexpected error: Invalid job name(s)');
}
}
// File: angular/packages/common/src/pipes/case_conversion_pipes.ts
function transform1<T extends string | null | undefined>(value: T): HelperCond<T, string, string, null | undefined, null> {
if (value == null) return null; // Ok
if (typeof value !== 'string') {
throw new Error();
}
return value.toLowerCase(); // Ok
}

Просмотреть файл

@ -0,0 +1,36 @@
// @strict: true
// @noEmit: true
// @target: ES2022
// @exactOptionalPropertyTypes: true
declare const rand: { a?: never };
type Missing = typeof rand.a;
// Detection of valid optional parameter references
// Ok, will narrow return type
function bar1<T extends string | Missing>(x?: T):
T extends Missing ? 0 : T extends string ? 1 : never {
if (x === undefined) {
return 0;
}
return 1;
}
// Ok, will narrow return type
function bar2<T extends string | undefined>(x?: T):
T extends undefined ? 0 : T extends string ? 1 : never {
if (x === undefined) {
return 0;
}
return 1;
}
// Not ok, will not narrow return type
function bar3<T extends string>(x?: T):
T extends undefined ? 0 : T extends string ? 1 : never {
if (x === undefined) {
return 0;
}
return 1;
}

Просмотреть файл

@ -0,0 +1,99 @@
// @strict: true
// @noEmit: true
// Indexed access return type
interface A1 {
"prop": true;
[s: string]: boolean;
}
// This was already allowed but is unsound.
function foo1<T extends string>(x: T): A1[T] {
return false;
}
const rfoo1 = foo1("prop"); // Type says true, but actually returns false.
interface A2 {
"prop": true;
[n: number]: string;
}
// We could soundly allow that, because `"prop"` and `[n: number]` are disjoint types.
function foo2<T extends "prop" | number>(x: T): A2[T] {
if (x === "prop") {
return true;
}
return "some string";
}
const rfoo2 = foo2("prop");
const rfoo22 = foo2(34);
const rfoo222 = foo2(Math.random() ? "prop" : 34);
interface A3 {
[s: string]: boolean;
}
// No need for return type narrowing.
function foo3<T extends string>(x: T): A3[T] {
if (Math.random()) return true;
return false;
}
interface Comp {
foo: 2;
[n: number]: 3;
[s: string]: 2 | 3 | 4;
}
function indexedComp<T extends number | string>(x: T): Comp[T] {
if (x === "foo") {
if (Math.random()) {
return 3; // Error
}
return 2; // Ok
}
if (typeof x === "number") {
if (Math.random()) {
return 2; // Error
}
return 3; // Ok
}
return 4; // Ok
}
function indexedComp2<T extends number | string>(x: T): Comp[T] {
if (Math.random()) {
return 3; // Bad, unsound
}
return 2; // Error
}
// Most common case supported:
interface F {
"t": number,
"f": boolean,
}
// Ok
function depLikeFun<T extends "t" | "f">(str: T): F[T] {
if (str === "t") {
return 1;
} else {
return true;
}
}
depLikeFun("t"); // has type number
depLikeFun("f"); // has type boolean
type IndirectF<T extends keyof F> = F[T];
// Ok
function depLikeFun2<T extends "t" | "f">(str: T): IndirectF<T> {
if (str === "t") {
return 1;
} else {
return true;
}
}

Просмотреть файл

@ -0,0 +1,137 @@
// @strict: true
// @noEmit: true
// @target: esnext
// Tests for when return type narrowing can and cannot happen
// @filename: file.ts
// Type parameter in outer scope
function outer<T extends boolean>(x: T): number {
return inner();
function inner(): T extends true ? 1 : T extends false ? 2 : never {
return x ? 1 : 2;
}
}
// Overloads
function fun6<T extends boolean>(x: T, y: string): T extends true ? string : T extends false ? 2 : never;
function fun6<T extends boolean>(x: T, y: undefined): T extends true ? 1 : T extends false ? 2 : never;
function fun6(x: boolean): 1 | 2 | string;
function fun6<T extends boolean>(x: T, y?: string): T extends true ? 1 | string : T extends false ? 2 : never {
return x ? y !== undefined ? y : 1 : 2;
}
// Indexed access with conditional inside
// DOESN'T NARROW the nested conditional type of wrong shape
interface SomeInterfaceBad<T> {
prop1: T extends 1 ? true : T extends 2 ? false : never;
prop2: T extends true ? 1 : T extends false ? 2 : never;
}
function fun4bad<T, U extends keyof SomeInterfaceBad<unknown>>(x: T, y: U): SomeInterfaceBad<T>[U] {
if (y === "prop1") {
return x === 1 ? true : false;
}
return x ? 1 : 2;
}
// Narrows nested conditional type of right shape
interface SomeInterfaceGood<T> {
prop1: T extends true ? 2 : T extends false ? 1 : never;
prop2: T extends true ? 1 : T extends false ? 2 : never;
}
function fun4good<T extends boolean, U extends keyof SomeInterfaceGood<unknown>>(x: T, y: U): SomeInterfaceGood<T>[U] {
if (y === "prop1") {
return x ? 2 : 1;
}
return x ? 1 : 2;
}
// Indexed access with indexed access inside - OK, narrows
interface BB {
"a": number;
"b": string;
}
interface AA<T extends keyof BB> {
"c": BB[T];
"d": boolean,
}
function reduction<T extends keyof BB, U extends keyof AA<any>>(x: T, y: U): AA<T>[U] {
if (x === "a" && y === "c") {
return 0; // Ok
}
return undefined as never;
}
// Conditional with indexed access inside - OK, narrows
function fun5<T extends 1 | 2, U extends keyof BB>(x: T, y: U): T extends 1 ? BB[U] : T extends 2 ? boolean : never {
if (x === 1) {
if (y === "a") {
return 0;
}
return "";
}
return true;
}
// `this` type parameter - Doesn't narrow
abstract class SomeClass {
fun3(this: Sub1 | Sub2): this extends Sub1 ? 1 : this extends Sub2 ? 2 : never {
if (this instanceof Sub1) {
return 1;
}
return 2;
}
}
class Sub1 extends SomeClass {
#sub1!: symbol;
};
class Sub2 extends SomeClass {
#sub2!: symbol;
};
// Detection of type parameter reference in presence of typeof
function fun2<T extends boolean>(x: T, y: typeof x): T extends true ? 1 : T extends false ? 2 : never {
return x ? 1 : 2;
}
// Contextually-typed lambdas
const fun1: <T extends boolean>(x: T) => T extends true ? 1 : T extends false ? 2 : never = (x) => x ? 1 : 2;
// Circular conditionals
type SomeCond<T> = T extends true ? 1 : T extends false ? SomeCond<T> : never;
function f7<T extends boolean>(x: T): SomeCond<T> {
if (x) {
return 1;
}
return 2;
}
// Composite instantiation of conditional type
type OtherCond<T> = T extends 1 ? "one" : T extends 2 ? "two" : T extends 3 ? "three" : T extends 4 ? "four" : never;
function f8<U extends 1 | 2, V extends 3 | 4>(x: U, y: V): OtherCond<U | V> {
if (x === 1 && y === 3) {
return "one";
}
}
// Conditionals with `infer` - will not narrow, it is not safe to infer from the narrowed type into an `infer` type parameter
function f9<T extends "a"[] | "b"[] | number>(x: T): T extends Array<infer P> ? P : T extends number ? undefined : never {
if (Array.isArray(x)) {
// If we allowed narrowing of the conditional return type, when resolving the conditional `T & ("a"[] | "b"[]) extends Array<infer P> ? P : ...`,
// we could infer `"a" | "b"` for `P`, and allow "a" to be returned. However, when calling `f10`, `T` could be instantiated with `"b"[]`, and the return type would be `"b"`,
// so allowing an `"a"` return would be unsound.
return "a";
}
return undefined;
}

Просмотреть файл

@ -0,0 +1,13 @@
// @strict: true
// @noEmit: true
export {};
declare const record: Record<string, string[]>;
declare const array: string[];
// Arrow function with expression body
const getObject =
<T extends string | undefined>(group: T): T extends string ? string[] : T extends undefined ? Record<string, string[]> : never =>
group === undefined ? record : array;

Просмотреть файл

@ -0,0 +1,22 @@
// @strict: true
// @noEmit: true
// @target: esnext
// @checkJs: true
// @filename: file.js
// Don't peek into conditional return expression if it's wrapped in a cast
/** @type {Map<string, string>} */
const sources = new Map();
/**
* @param {string=} type the type of source that should be generated
* @returns {String}
*/
function source(type = "javascript") {
return /** @type {String} */ (
type
? sources.get(type)
: sources.get("some other thing")
);
}

Просмотреть файл

@ -0,0 +1,9 @@
// @strict: true
// @noEmit: true
// @noUnusedLocals: true
// Test that we unconditionally check return expression, even if we don't need its type.
function recursive(arg: string, other: string) {
const someLocalVar = arg + other;
return recursive(someLocalVar, arg);
}

Просмотреть файл

@ -0,0 +1,12 @@
/// <reference path="fourslash.ts" />
// @strict: true
//// function h<T extends boolean>(x: T): T extends true ? 1 : T extends false ? 2 : never {
//// if (x) {
//// return 1;
//// }
//// return 2;
//// }
verify.encodedSemanticClassificationsLength("2020", 21);
verify.noErrors();