Narrow generic conditional and indexed access return types when checking return statements (#56941)
This commit is contained in:
Родитель
5e2e32120b
Коммит
30979c2651
|
@ -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();
|
Загрузка…
Ссылка в новой задаче