mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-20 22:51:17 -05:00
Revert "Allow (non-assert) type predicates to narrow by discriminant" (#57750)
This commit is contained in:
committed by
GitHub
parent
e5bf594753
commit
b009837298
@@ -26799,11 +26799,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
function hasMatchingArgument(expression: CallExpression | NewExpression, reference: Node) {
|
||||
if (expression.arguments) {
|
||||
for (const argument of expression.arguments) {
|
||||
if (
|
||||
isOrContainsMatchingReference(reference, argument)
|
||||
|| optionalChainContainsReference(argument, reference)
|
||||
|| getCandidateDiscriminantPropertyAccess(argument, reference)
|
||||
) {
|
||||
if (isOrContainsMatchingReference(reference, argument) || optionalChainContainsReference(argument, reference)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -26817,51 +26813,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return false;
|
||||
}
|
||||
|
||||
function getCandidateDiscriminantPropertyAccess(expr: Expression, reference: Node) {
|
||||
if (isBindingPattern(reference) || isFunctionExpressionOrArrowFunction(reference) || isObjectLiteralMethod(reference)) {
|
||||
// When the reference is a binding pattern or function or arrow expression, we are narrowing a pesudo-reference in
|
||||
// getNarrowedTypeOfSymbol. An identifier for a destructuring variable declared in the same binding pattern or
|
||||
// parameter declared in the same parameter list is a candidate.
|
||||
if (isIdentifier(expr)) {
|
||||
const symbol = getResolvedSymbol(expr);
|
||||
const declaration = symbol.valueDeclaration;
|
||||
if (declaration && (isBindingElement(declaration) || isParameter(declaration)) && reference === declaration.parent && !declaration.initializer && !declaration.dotDotDotToken) {
|
||||
return declaration;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (isAccessExpression(expr)) {
|
||||
// An access expression is a candidate if the reference matches the left hand expression.
|
||||
if (isMatchingReference(reference, expr.expression)) {
|
||||
return expr;
|
||||
}
|
||||
}
|
||||
else if (isIdentifier(expr)) {
|
||||
const symbol = getResolvedSymbol(expr);
|
||||
if (isConstantVariable(symbol)) {
|
||||
const declaration = symbol.valueDeclaration!;
|
||||
// Given 'const x = obj.kind', allow 'x' as an alias for 'obj.kind'
|
||||
if (
|
||||
isVariableDeclaration(declaration) && !declaration.type && declaration.initializer && isAccessExpression(declaration.initializer) &&
|
||||
isMatchingReference(reference, declaration.initializer.expression)
|
||||
) {
|
||||
return declaration.initializer;
|
||||
}
|
||||
// Given 'const { kind: x } = obj', allow 'x' as an alias for 'obj.kind'
|
||||
if (isBindingElement(declaration) && !declaration.initializer) {
|
||||
const parent = declaration.parent.parent;
|
||||
if (
|
||||
isVariableDeclaration(parent) && !parent.type && parent.initializer && (isIdentifier(parent.initializer) || isAccessExpression(parent.initializer)) &&
|
||||
isMatchingReference(reference, parent.initializer)
|
||||
) {
|
||||
return declaration;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getFlowNodeId(flow: FlowNode): number {
|
||||
if (!flow.id || flow.id < 0) {
|
||||
flow.id = nextFlowId;
|
||||
@@ -28232,12 +28183,57 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return result;
|
||||
}
|
||||
|
||||
function getCandidateDiscriminantPropertyAccess(expr: Expression) {
|
||||
if (isBindingPattern(reference) || isFunctionExpressionOrArrowFunction(reference) || isObjectLiteralMethod(reference)) {
|
||||
// When the reference is a binding pattern or function or arrow expression, we are narrowing a pesudo-reference in
|
||||
// getNarrowedTypeOfSymbol. An identifier for a destructuring variable declared in the same binding pattern or
|
||||
// parameter declared in the same parameter list is a candidate.
|
||||
if (isIdentifier(expr)) {
|
||||
const symbol = getResolvedSymbol(expr);
|
||||
const declaration = symbol.valueDeclaration;
|
||||
if (declaration && (isBindingElement(declaration) || isParameter(declaration)) && reference === declaration.parent && !declaration.initializer && !declaration.dotDotDotToken) {
|
||||
return declaration;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (isAccessExpression(expr)) {
|
||||
// An access expression is a candidate if the reference matches the left hand expression.
|
||||
if (isMatchingReference(reference, expr.expression)) {
|
||||
return expr;
|
||||
}
|
||||
}
|
||||
else if (isIdentifier(expr)) {
|
||||
const symbol = getResolvedSymbol(expr);
|
||||
if (isConstantVariable(symbol)) {
|
||||
const declaration = symbol.valueDeclaration!;
|
||||
// Given 'const x = obj.kind', allow 'x' as an alias for 'obj.kind'
|
||||
if (
|
||||
isVariableDeclaration(declaration) && !declaration.type && declaration.initializer && isAccessExpression(declaration.initializer) &&
|
||||
isMatchingReference(reference, declaration.initializer.expression)
|
||||
) {
|
||||
return declaration.initializer;
|
||||
}
|
||||
// Given 'const { kind: x } = obj', allow 'x' as an alias for 'obj.kind'
|
||||
if (isBindingElement(declaration) && !declaration.initializer) {
|
||||
const parent = declaration.parent.parent;
|
||||
if (
|
||||
isVariableDeclaration(parent) && !parent.type && parent.initializer && (isIdentifier(parent.initializer) || isAccessExpression(parent.initializer)) &&
|
||||
isMatchingReference(reference, parent.initializer)
|
||||
) {
|
||||
return declaration;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getDiscriminantPropertyAccess(expr: Expression, computedType: Type) {
|
||||
// As long as the computed type is a subset of the declared type, we use the full declared type to detect
|
||||
// a discriminant property. In cases where the computed type isn't a subset, e.g because of a preceding type
|
||||
// predicate narrowing, we use the actual computed type.
|
||||
if (declaredType.flags & TypeFlags.Union || computedType.flags & TypeFlags.Union) {
|
||||
const access = getCandidateDiscriminantPropertyAccess(expr, reference);
|
||||
const access = getCandidateDiscriminantPropertyAccess(expr);
|
||||
if (access) {
|
||||
const name = getAccessedPropertyName(access);
|
||||
if (name) {
|
||||
|
||||
@@ -3512,7 +3512,8 @@ export function convertJsonOption(
|
||||
convertJsonOption(opt.element, value, basePath, errors, propertyAssignment, valueExpression, sourceFile);
|
||||
}
|
||||
else if (!isString(opt.type)) {
|
||||
return convertJsonOptionOfCustomType(opt, value as string, errors, valueExpression, sourceFile);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||
return convertJsonOptionOfCustomType(opt as CommandLineOptionOfCustomType, value as string, errors, valueExpression, sourceFile);
|
||||
}
|
||||
const validatedValue = validateJsonOptionValue(opt, value, errors, valueExpression, sourceFile);
|
||||
return isNullOrUndefined(validatedValue) ? validatedValue : normalizeNonListOptionValue(opt, basePath, validatedValue);
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
//// [tests/cases/compiler/discriminantNarrowingCouldBeCircular.ts] ////
|
||||
|
||||
//// [discriminantNarrowingCouldBeCircular.ts]
|
||||
// #57705, 57690
|
||||
declare function is<T>(v: T): v is T;
|
||||
const o: Record<string, string> | undefined = {};
|
||||
if (o) {
|
||||
for (const key in o) {
|
||||
const value = o[key];
|
||||
if (is<string>(value)) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type SomeRecord = { a: string };
|
||||
declare const kPresentationInheritanceParents: { [tagName: string]: string[] };
|
||||
declare function parentElementOrShadowHost(element: SomeRecord): SomeRecord | undefined;
|
||||
|
||||
function getImplicitAriaRole(element: SomeRecord) {
|
||||
let ancestor: SomeRecord | null = element;
|
||||
while (ancestor) {
|
||||
const parent = parentElementOrShadowHost(ancestor);
|
||||
const parents = kPresentationInheritanceParents[ancestor.a];
|
||||
if (!parents || !parent || !parents.includes(parent.a))
|
||||
break;
|
||||
ancestor = parent;
|
||||
}
|
||||
}
|
||||
|
||||
declare function isPlainObject2<T>(
|
||||
data: unknown,
|
||||
): data is Record<PropertyKey, unknown>;
|
||||
|
||||
declare const myObj2: unknown;
|
||||
if (isPlainObject2(myObj2)) {
|
||||
for (const key of ["a", "b", "c"]) {
|
||||
const deeper = myObj2[key];
|
||||
const deeperKeys = isPlainObject2(deeper) ? Object.keys(deeper) : [];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//// [discriminantNarrowingCouldBeCircular.js]
|
||||
"use strict";
|
||||
var o = {};
|
||||
if (o) {
|
||||
for (var key in o) {
|
||||
var value = o[key];
|
||||
if (is(value)) {
|
||||
}
|
||||
}
|
||||
}
|
||||
function getImplicitAriaRole(element) {
|
||||
var ancestor = element;
|
||||
while (ancestor) {
|
||||
var parent = parentElementOrShadowHost(ancestor);
|
||||
var parents = kPresentationInheritanceParents[ancestor.a];
|
||||
if (!parents || !parent || !parents.includes(parent.a))
|
||||
break;
|
||||
ancestor = parent;
|
||||
}
|
||||
}
|
||||
if (isPlainObject2(myObj2)) {
|
||||
for (var _i = 0, _a = ["a", "b", "c"]; _i < _a.length; _i++) {
|
||||
var key = _a[_i];
|
||||
var deeper = myObj2[key];
|
||||
var deeperKeys = isPlainObject2(deeper) ? Object.keys(deeper) : [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
//// [tests/cases/compiler/discriminantNarrowingCouldBeCircular.ts] ////
|
||||
|
||||
=== discriminantNarrowingCouldBeCircular.ts ===
|
||||
// #57705, 57690
|
||||
declare function is<T>(v: T): v is T;
|
||||
>is : Symbol(is, Decl(discriminantNarrowingCouldBeCircular.ts, 0, 0))
|
||||
>T : Symbol(T, Decl(discriminantNarrowingCouldBeCircular.ts, 1, 20))
|
||||
>v : Symbol(v, Decl(discriminantNarrowingCouldBeCircular.ts, 1, 23))
|
||||
>T : Symbol(T, Decl(discriminantNarrowingCouldBeCircular.ts, 1, 20))
|
||||
>v : Symbol(v, Decl(discriminantNarrowingCouldBeCircular.ts, 1, 23))
|
||||
>T : Symbol(T, Decl(discriminantNarrowingCouldBeCircular.ts, 1, 20))
|
||||
|
||||
const o: Record<string, string> | undefined = {};
|
||||
>o : Symbol(o, Decl(discriminantNarrowingCouldBeCircular.ts, 2, 5))
|
||||
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
|
||||
|
||||
if (o) {
|
||||
>o : Symbol(o, Decl(discriminantNarrowingCouldBeCircular.ts, 2, 5))
|
||||
|
||||
for (const key in o) {
|
||||
>key : Symbol(key, Decl(discriminantNarrowingCouldBeCircular.ts, 4, 12))
|
||||
>o : Symbol(o, Decl(discriminantNarrowingCouldBeCircular.ts, 2, 5))
|
||||
|
||||
const value = o[key];
|
||||
>value : Symbol(value, Decl(discriminantNarrowingCouldBeCircular.ts, 5, 9))
|
||||
>o : Symbol(o, Decl(discriminantNarrowingCouldBeCircular.ts, 2, 5))
|
||||
>key : Symbol(key, Decl(discriminantNarrowingCouldBeCircular.ts, 4, 12))
|
||||
|
||||
if (is<string>(value)) {
|
||||
>is : Symbol(is, Decl(discriminantNarrowingCouldBeCircular.ts, 0, 0))
|
||||
>value : Symbol(value, Decl(discriminantNarrowingCouldBeCircular.ts, 5, 9))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type SomeRecord = { a: string };
|
||||
>SomeRecord : Symbol(SomeRecord, Decl(discriminantNarrowingCouldBeCircular.ts, 9, 1))
|
||||
>a : Symbol(a, Decl(discriminantNarrowingCouldBeCircular.ts, 11, 19))
|
||||
|
||||
declare const kPresentationInheritanceParents: { [tagName: string]: string[] };
|
||||
>kPresentationInheritanceParents : Symbol(kPresentationInheritanceParents, Decl(discriminantNarrowingCouldBeCircular.ts, 12, 13))
|
||||
>tagName : Symbol(tagName, Decl(discriminantNarrowingCouldBeCircular.ts, 12, 50))
|
||||
|
||||
declare function parentElementOrShadowHost(element: SomeRecord): SomeRecord | undefined;
|
||||
>parentElementOrShadowHost : Symbol(parentElementOrShadowHost, Decl(discriminantNarrowingCouldBeCircular.ts, 12, 79))
|
||||
>element : Symbol(element, Decl(discriminantNarrowingCouldBeCircular.ts, 13, 43))
|
||||
>SomeRecord : Symbol(SomeRecord, Decl(discriminantNarrowingCouldBeCircular.ts, 9, 1))
|
||||
>SomeRecord : Symbol(SomeRecord, Decl(discriminantNarrowingCouldBeCircular.ts, 9, 1))
|
||||
|
||||
function getImplicitAriaRole(element: SomeRecord) {
|
||||
>getImplicitAriaRole : Symbol(getImplicitAriaRole, Decl(discriminantNarrowingCouldBeCircular.ts, 13, 88))
|
||||
>element : Symbol(element, Decl(discriminantNarrowingCouldBeCircular.ts, 15, 29))
|
||||
>SomeRecord : Symbol(SomeRecord, Decl(discriminantNarrowingCouldBeCircular.ts, 9, 1))
|
||||
|
||||
let ancestor: SomeRecord | null = element;
|
||||
>ancestor : Symbol(ancestor, Decl(discriminantNarrowingCouldBeCircular.ts, 16, 5))
|
||||
>SomeRecord : Symbol(SomeRecord, Decl(discriminantNarrowingCouldBeCircular.ts, 9, 1))
|
||||
>element : Symbol(element, Decl(discriminantNarrowingCouldBeCircular.ts, 15, 29))
|
||||
|
||||
while (ancestor) {
|
||||
>ancestor : Symbol(ancestor, Decl(discriminantNarrowingCouldBeCircular.ts, 16, 5))
|
||||
|
||||
const parent = parentElementOrShadowHost(ancestor);
|
||||
>parent : Symbol(parent, Decl(discriminantNarrowingCouldBeCircular.ts, 18, 9))
|
||||
>parentElementOrShadowHost : Symbol(parentElementOrShadowHost, Decl(discriminantNarrowingCouldBeCircular.ts, 12, 79))
|
||||
>ancestor : Symbol(ancestor, Decl(discriminantNarrowingCouldBeCircular.ts, 16, 5))
|
||||
|
||||
const parents = kPresentationInheritanceParents[ancestor.a];
|
||||
>parents : Symbol(parents, Decl(discriminantNarrowingCouldBeCircular.ts, 19, 9))
|
||||
>kPresentationInheritanceParents : Symbol(kPresentationInheritanceParents, Decl(discriminantNarrowingCouldBeCircular.ts, 12, 13))
|
||||
>ancestor.a : Symbol(a, Decl(discriminantNarrowingCouldBeCircular.ts, 11, 19))
|
||||
>ancestor : Symbol(ancestor, Decl(discriminantNarrowingCouldBeCircular.ts, 16, 5))
|
||||
>a : Symbol(a, Decl(discriminantNarrowingCouldBeCircular.ts, 11, 19))
|
||||
|
||||
if (!parents || !parent || !parents.includes(parent.a))
|
||||
>parents : Symbol(parents, Decl(discriminantNarrowingCouldBeCircular.ts, 19, 9))
|
||||
>parent : Symbol(parent, Decl(discriminantNarrowingCouldBeCircular.ts, 18, 9))
|
||||
>parents.includes : Symbol(Array.includes, Decl(lib.es2016.array.include.d.ts, --, --))
|
||||
>parents : Symbol(parents, Decl(discriminantNarrowingCouldBeCircular.ts, 19, 9))
|
||||
>includes : Symbol(Array.includes, Decl(lib.es2016.array.include.d.ts, --, --))
|
||||
>parent.a : Symbol(a, Decl(discriminantNarrowingCouldBeCircular.ts, 11, 19))
|
||||
>parent : Symbol(parent, Decl(discriminantNarrowingCouldBeCircular.ts, 18, 9))
|
||||
>a : Symbol(a, Decl(discriminantNarrowingCouldBeCircular.ts, 11, 19))
|
||||
|
||||
break;
|
||||
ancestor = parent;
|
||||
>ancestor : Symbol(ancestor, Decl(discriminantNarrowingCouldBeCircular.ts, 16, 5))
|
||||
>parent : Symbol(parent, Decl(discriminantNarrowingCouldBeCircular.ts, 18, 9))
|
||||
}
|
||||
}
|
||||
|
||||
declare function isPlainObject2<T>(
|
||||
>isPlainObject2 : Symbol(isPlainObject2, Decl(discriminantNarrowingCouldBeCircular.ts, 24, 1))
|
||||
>T : Symbol(T, Decl(discriminantNarrowingCouldBeCircular.ts, 26, 32))
|
||||
|
||||
data: unknown,
|
||||
>data : Symbol(data, Decl(discriminantNarrowingCouldBeCircular.ts, 26, 35))
|
||||
|
||||
): data is Record<PropertyKey, unknown>;
|
||||
>data : Symbol(data, Decl(discriminantNarrowingCouldBeCircular.ts, 26, 35))
|
||||
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
|
||||
>PropertyKey : Symbol(PropertyKey, Decl(lib.es5.d.ts, --, --))
|
||||
|
||||
declare const myObj2: unknown;
|
||||
>myObj2 : Symbol(myObj2, Decl(discriminantNarrowingCouldBeCircular.ts, 30, 15))
|
||||
|
||||
if (isPlainObject2(myObj2)) {
|
||||
>isPlainObject2 : Symbol(isPlainObject2, Decl(discriminantNarrowingCouldBeCircular.ts, 24, 1))
|
||||
>myObj2 : Symbol(myObj2, Decl(discriminantNarrowingCouldBeCircular.ts, 30, 15))
|
||||
|
||||
for (const key of ["a", "b", "c"]) {
|
||||
>key : Symbol(key, Decl(discriminantNarrowingCouldBeCircular.ts, 32, 16))
|
||||
|
||||
const deeper = myObj2[key];
|
||||
>deeper : Symbol(deeper, Decl(discriminantNarrowingCouldBeCircular.ts, 33, 13))
|
||||
>myObj2 : Symbol(myObj2, Decl(discriminantNarrowingCouldBeCircular.ts, 30, 15))
|
||||
>key : Symbol(key, Decl(discriminantNarrowingCouldBeCircular.ts, 32, 16))
|
||||
|
||||
const deeperKeys = isPlainObject2(deeper) ? Object.keys(deeper) : [];
|
||||
>deeperKeys : Symbol(deeperKeys, Decl(discriminantNarrowingCouldBeCircular.ts, 34, 13))
|
||||
>isPlainObject2 : Symbol(isPlainObject2, Decl(discriminantNarrowingCouldBeCircular.ts, 24, 1))
|
||||
>deeper : Symbol(deeper, Decl(discriminantNarrowingCouldBeCircular.ts, 33, 13))
|
||||
>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, --, --))
|
||||
>deeper : Symbol(deeper, Decl(discriminantNarrowingCouldBeCircular.ts, 33, 13))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
//// [tests/cases/compiler/discriminantNarrowingCouldBeCircular.ts] ////
|
||||
|
||||
=== discriminantNarrowingCouldBeCircular.ts ===
|
||||
// #57705, 57690
|
||||
declare function is<T>(v: T): v is T;
|
||||
>is : <T>(v: T) => v is T
|
||||
>v : T
|
||||
|
||||
const o: Record<string, string> | undefined = {};
|
||||
>o : Record<string, string> | undefined
|
||||
>{} : {}
|
||||
|
||||
if (o) {
|
||||
>o : Record<string, string>
|
||||
|
||||
for (const key in o) {
|
||||
>key : string
|
||||
>o : Record<string, string>
|
||||
|
||||
const value = o[key];
|
||||
>value : string
|
||||
>o[key] : string
|
||||
>o : Record<string, string>
|
||||
>key : string
|
||||
|
||||
if (is<string>(value)) {
|
||||
>is<string>(value) : boolean
|
||||
>is : <T>(v: T) => v is T
|
||||
>value : string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type SomeRecord = { a: string };
|
||||
>SomeRecord : { a: string; }
|
||||
>a : string
|
||||
|
||||
declare const kPresentationInheritanceParents: { [tagName: string]: string[] };
|
||||
>kPresentationInheritanceParents : { [tagName: string]: string[]; }
|
||||
>tagName : string
|
||||
|
||||
declare function parentElementOrShadowHost(element: SomeRecord): SomeRecord | undefined;
|
||||
>parentElementOrShadowHost : (element: SomeRecord) => SomeRecord | undefined
|
||||
>element : SomeRecord
|
||||
|
||||
function getImplicitAriaRole(element: SomeRecord) {
|
||||
>getImplicitAriaRole : (element: SomeRecord) => void
|
||||
>element : SomeRecord
|
||||
|
||||
let ancestor: SomeRecord | null = element;
|
||||
>ancestor : SomeRecord | null
|
||||
>element : SomeRecord
|
||||
|
||||
while (ancestor) {
|
||||
>ancestor : SomeRecord
|
||||
|
||||
const parent = parentElementOrShadowHost(ancestor);
|
||||
>parent : SomeRecord | undefined
|
||||
>parentElementOrShadowHost(ancestor) : SomeRecord | undefined
|
||||
>parentElementOrShadowHost : (element: SomeRecord) => SomeRecord | undefined
|
||||
>ancestor : SomeRecord
|
||||
|
||||
const parents = kPresentationInheritanceParents[ancestor.a];
|
||||
>parents : string[]
|
||||
>kPresentationInheritanceParents[ancestor.a] : string[]
|
||||
>kPresentationInheritanceParents : { [tagName: string]: string[]; }
|
||||
>ancestor.a : string
|
||||
>ancestor : SomeRecord
|
||||
>a : string
|
||||
|
||||
if (!parents || !parent || !parents.includes(parent.a))
|
||||
>!parents || !parent || !parents.includes(parent.a) : boolean
|
||||
>!parents || !parent : boolean
|
||||
>!parents : false
|
||||
>parents : string[]
|
||||
>!parent : boolean
|
||||
>parent : SomeRecord | undefined
|
||||
>!parents.includes(parent.a) : boolean
|
||||
>parents.includes(parent.a) : boolean
|
||||
>parents.includes : (searchElement: string, fromIndex?: number | undefined) => boolean
|
||||
>parents : string[]
|
||||
>includes : (searchElement: string, fromIndex?: number | undefined) => boolean
|
||||
>parent.a : string
|
||||
>parent : SomeRecord
|
||||
>a : string
|
||||
|
||||
break;
|
||||
ancestor = parent;
|
||||
>ancestor = parent : SomeRecord
|
||||
>ancestor : SomeRecord | null
|
||||
>parent : SomeRecord
|
||||
}
|
||||
}
|
||||
|
||||
declare function isPlainObject2<T>(
|
||||
>isPlainObject2 : <T>(data: unknown) => data is Record<PropertyKey, unknown>
|
||||
|
||||
data: unknown,
|
||||
>data : unknown
|
||||
|
||||
): data is Record<PropertyKey, unknown>;
|
||||
|
||||
declare const myObj2: unknown;
|
||||
>myObj2 : unknown
|
||||
|
||||
if (isPlainObject2(myObj2)) {
|
||||
>isPlainObject2(myObj2) : boolean
|
||||
>isPlainObject2 : <T>(data: unknown) => data is Record<PropertyKey, unknown>
|
||||
>myObj2 : unknown
|
||||
|
||||
for (const key of ["a", "b", "c"]) {
|
||||
>key : string
|
||||
>["a", "b", "c"] : string[]
|
||||
>"a" : "a"
|
||||
>"b" : "b"
|
||||
>"c" : "c"
|
||||
|
||||
const deeper = myObj2[key];
|
||||
>deeper : unknown
|
||||
>myObj2[key] : unknown
|
||||
>myObj2 : Record<PropertyKey, unknown>
|
||||
>key : string
|
||||
|
||||
const deeperKeys = isPlainObject2(deeper) ? Object.keys(deeper) : [];
|
||||
>deeperKeys : string[]
|
||||
>isPlainObject2(deeper) ? Object.keys(deeper) : [] : string[]
|
||||
>isPlainObject2(deeper) : boolean
|
||||
>isPlainObject2 : <T>(data: unknown) => data is Record<PropertyKey, unknown>
|
||||
>deeper : unknown
|
||||
>Object.keys(deeper) : string[]
|
||||
>Object.keys : { (o: object): string[]; (o: {}): string[]; }
|
||||
>Object : ObjectConstructor
|
||||
>keys : { (o: object): string[]; (o: {}): string[]; }
|
||||
>deeper : Record<PropertyKey, unknown>
|
||||
>[] : never[]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,9 +28,9 @@ if (isOneOf(fruit.kind, ['apple', 'banana'] as const)) {
|
||||
>const : Symbol(const)
|
||||
|
||||
fruit.kind
|
||||
>fruit.kind : Symbol(kind, Decl(typePredicatesCanNarrowByDiscriminant.ts, 1, 22), Decl(typePredicatesCanNarrowByDiscriminant.ts, 1, 41))
|
||||
>fruit.kind : Symbol(kind, Decl(typePredicatesCanNarrowByDiscriminant.ts, 1, 22), Decl(typePredicatesCanNarrowByDiscriminant.ts, 1, 41), Decl(typePredicatesCanNarrowByDiscriminant.ts, 1, 62))
|
||||
>fruit : Symbol(fruit, Decl(typePredicatesCanNarrowByDiscriminant.ts, 1, 13))
|
||||
>kind : Symbol(kind, Decl(typePredicatesCanNarrowByDiscriminant.ts, 1, 22), Decl(typePredicatesCanNarrowByDiscriminant.ts, 1, 41))
|
||||
>kind : Symbol(kind, Decl(typePredicatesCanNarrowByDiscriminant.ts, 1, 22), Decl(typePredicatesCanNarrowByDiscriminant.ts, 1, 41), Decl(typePredicatesCanNarrowByDiscriminant.ts, 1, 62))
|
||||
|
||||
fruit
|
||||
>fruit : Symbol(fruit, Decl(typePredicatesCanNarrowByDiscriminant.ts, 1, 13))
|
||||
@@ -54,9 +54,9 @@ if (isOneOf(kind, ['apple', 'banana'] as const)) {
|
||||
>const : Symbol(const)
|
||||
|
||||
fruit2.kind
|
||||
>fruit2.kind : Symbol(kind, Decl(typePredicatesCanNarrowByDiscriminant.ts, 9, 23), Decl(typePredicatesCanNarrowByDiscriminant.ts, 9, 42))
|
||||
>fruit2.kind : Symbol(kind, Decl(typePredicatesCanNarrowByDiscriminant.ts, 9, 23), Decl(typePredicatesCanNarrowByDiscriminant.ts, 9, 42), Decl(typePredicatesCanNarrowByDiscriminant.ts, 9, 63))
|
||||
>fruit2 : Symbol(fruit2, Decl(typePredicatesCanNarrowByDiscriminant.ts, 9, 13))
|
||||
>kind : Symbol(kind, Decl(typePredicatesCanNarrowByDiscriminant.ts, 9, 23), Decl(typePredicatesCanNarrowByDiscriminant.ts, 9, 42))
|
||||
>kind : Symbol(kind, Decl(typePredicatesCanNarrowByDiscriminant.ts, 9, 23), Decl(typePredicatesCanNarrowByDiscriminant.ts, 9, 42), Decl(typePredicatesCanNarrowByDiscriminant.ts, 9, 63))
|
||||
|
||||
fruit2
|
||||
>fruit2 : Symbol(fruit2, Decl(typePredicatesCanNarrowByDiscriminant.ts, 9, 13))
|
||||
|
||||
@@ -26,11 +26,11 @@ if (isOneOf(fruit.kind, ['apple', 'banana'] as const)) {
|
||||
|
||||
fruit.kind
|
||||
>fruit.kind : "apple" | "banana"
|
||||
>fruit : { kind: "apple"; } | { kind: "banana"; }
|
||||
>fruit : { kind: "apple"; } | { kind: "banana"; } | { kind: "cherry"; }
|
||||
>kind : "apple" | "banana"
|
||||
|
||||
fruit
|
||||
>fruit : { kind: "apple"; } | { kind: "banana"; }
|
||||
>fruit : { kind: "apple"; } | { kind: "banana"; } | { kind: "cherry"; }
|
||||
}
|
||||
|
||||
declare const fruit2: { kind: 'apple'} | { kind: 'banana' } | { kind: 'cherry' }
|
||||
@@ -55,10 +55,10 @@ if (isOneOf(kind, ['apple', 'banana'] as const)) {
|
||||
>'banana' : "banana"
|
||||
|
||||
fruit2.kind
|
||||
>fruit2.kind : "apple" | "banana"
|
||||
>fruit2 : { kind: "apple"; } | { kind: "banana"; }
|
||||
>kind : "apple" | "banana"
|
||||
>fruit2.kind : "apple" | "banana" | "cherry"
|
||||
>fruit2 : { kind: "apple"; } | { kind: "banana"; } | { kind: "cherry"; }
|
||||
>kind : "apple" | "banana" | "cherry"
|
||||
|
||||
fruit2
|
||||
>fruit2 : { kind: "apple"; } | { kind: "banana"; }
|
||||
>fruit2 : { kind: "apple"; } | { kind: "banana"; } | { kind: "cherry"; }
|
||||
}
|
||||
|
||||
40
tests/cases/compiler/discriminantNarrowingCouldBeCircular.ts
Normal file
40
tests/cases/compiler/discriminantNarrowingCouldBeCircular.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
// @strict: true
|
||||
// @lib: es2022
|
||||
|
||||
// #57705, 57690
|
||||
declare function is<T>(v: T): v is T;
|
||||
const o: Record<string, string> | undefined = {};
|
||||
if (o) {
|
||||
for (const key in o) {
|
||||
const value = o[key];
|
||||
if (is<string>(value)) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type SomeRecord = { a: string };
|
||||
declare const kPresentationInheritanceParents: { [tagName: string]: string[] };
|
||||
declare function parentElementOrShadowHost(element: SomeRecord): SomeRecord | undefined;
|
||||
|
||||
function getImplicitAriaRole(element: SomeRecord) {
|
||||
let ancestor: SomeRecord | null = element;
|
||||
while (ancestor) {
|
||||
const parent = parentElementOrShadowHost(ancestor);
|
||||
const parents = kPresentationInheritanceParents[ancestor.a];
|
||||
if (!parents || !parent || !parents.includes(parent.a))
|
||||
break;
|
||||
ancestor = parent;
|
||||
}
|
||||
}
|
||||
|
||||
declare function isPlainObject2<T>(
|
||||
data: unknown,
|
||||
): data is Record<PropertyKey, unknown>;
|
||||
|
||||
declare const myObj2: unknown;
|
||||
if (isPlainObject2(myObj2)) {
|
||||
for (const key of ["a", "b", "c"]) {
|
||||
const deeper = myObj2[key];
|
||||
const deeperKeys = isPlainObject2(deeper) ? Object.keys(deeper) : [];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user