mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Co-authored-by: Anders Hejlsberg <andersh@microsoft.com>
This commit is contained in:
co-authored by
Anders Hejlsberg
parent
b0afbd6aaa
commit
79bdd93ec5
@@ -19108,6 +19108,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return isTypeRelatedTo(source, target, subtypeRelation);
|
||||
}
|
||||
|
||||
function isTypeStrictSubtypeOf(source: Type, target: Type): boolean {
|
||||
return isTypeRelatedTo(source, target, strictSubtypeRelation);
|
||||
}
|
||||
|
||||
function isTypeAssignableTo(source: Type, target: Type): boolean {
|
||||
return isTypeRelatedTo(source, target, assignableRelation);
|
||||
}
|
||||
@@ -27262,7 +27266,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
// prototype object types.
|
||||
const directlyRelated = mapType(matching || type, checkDerived ?
|
||||
t => isTypeDerivedFrom(t, c) ? t : isTypeDerivedFrom(c, t) ? c : neverType :
|
||||
t => isTypeSubtypeOf(c, t) && !isTypeIdenticalTo(c, t) ? c : isTypeSubtypeOf(t, c) ? t : neverType);
|
||||
t => isTypeStrictSubtypeOf(t, c) ? t : isTypeStrictSubtypeOf(c, t) ? c : isTypeSubtypeOf(t, c) ? t : isTypeSubtypeOf(c, t) ? c : neverType);
|
||||
// If no constituents are directly related, create intersections for any generic constituents that
|
||||
// are related by constraint.
|
||||
return directlyRelated.flags & TypeFlags.Never ?
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
tests/cases/compiler/controlFlowFavorAssertedTypeThroughTypePredicate.ts(26,5): error TS7053: Element implicitly has an 'any' type because expression of type '"attr"' can't be used to index type '{}'.
|
||||
Property 'attr' does not exist on type '{}'.
|
||||
tests/cases/compiler/controlFlowFavorAssertedTypeThroughTypePredicate.ts(34,5): error TS7053: Element implicitly has an 'any' type because expression of type '"attr"' can't be used to index type '{}'.
|
||||
Property 'attr' does not exist on type '{}'.
|
||||
|
||||
|
||||
==== tests/cases/compiler/controlFlowFavorAssertedTypeThroughTypePredicate.ts (2 errors) ====
|
||||
// repro 49988#issuecomment-1192016929
|
||||
|
||||
declare function isObject1(value: unknown): value is Record<string, unknown>;
|
||||
|
||||
declare const obj1: {};
|
||||
if (isObject1(obj1)) {
|
||||
obj1;
|
||||
obj1['attr'];
|
||||
}
|
||||
// check type after conditional block
|
||||
obj1;
|
||||
|
||||
declare const obj2: {} | undefined;
|
||||
if (isObject1(obj2)) {
|
||||
obj2;
|
||||
obj2['attr'];
|
||||
}
|
||||
// check type after conditional block
|
||||
obj2;
|
||||
|
||||
declare function isObject2(value: unknown): value is {};
|
||||
|
||||
declare const obj3: Record<string, unknown>;
|
||||
if (isObject2(obj3)) {
|
||||
obj3;
|
||||
obj3['attr'];
|
||||
~~~~~~~~~~~~
|
||||
!!! error TS7053: Element implicitly has an 'any' type because expression of type '"attr"' can't be used to index type '{}'.
|
||||
!!! error TS7053: Property 'attr' does not exist on type '{}'.
|
||||
}
|
||||
// check type after conditional block
|
||||
obj3;
|
||||
|
||||
declare const obj4: Record<string, unknown> | undefined;
|
||||
if (isObject2(obj4)) {
|
||||
obj4;
|
||||
obj4['attr'];
|
||||
~~~~~~~~~~~~
|
||||
!!! error TS7053: Element implicitly has an 'any' type because expression of type '"attr"' can't be used to index type '{}'.
|
||||
!!! error TS7053: Property 'attr' does not exist on type '{}'.
|
||||
}
|
||||
// check type after conditional block
|
||||
obj4;
|
||||
|
||||
@@ -58,11 +58,11 @@ if (isObject2(obj3)) {
|
||||
>obj3 : Record<string, unknown>
|
||||
|
||||
obj3;
|
||||
>obj3 : {}
|
||||
>obj3 : Record<string, unknown>
|
||||
|
||||
obj3['attr'];
|
||||
>obj3['attr'] : any
|
||||
>obj3 : {}
|
||||
>obj3['attr'] : unknown
|
||||
>obj3 : Record<string, unknown>
|
||||
>'attr' : "attr"
|
||||
}
|
||||
// check type after conditional block
|
||||
@@ -78,11 +78,11 @@ if (isObject2(obj4)) {
|
||||
>obj4 : Record<string, unknown> | undefined
|
||||
|
||||
obj4;
|
||||
>obj4 : {}
|
||||
>obj4 : Record<string, unknown>
|
||||
|
||||
obj4['attr'];
|
||||
>obj4['attr'] : any
|
||||
>obj4 : {}
|
||||
>obj4['attr'] : unknown
|
||||
>obj4 : Record<string, unknown>
|
||||
>'attr' : "attr"
|
||||
}
|
||||
// check type after conditional block
|
||||
|
||||
@@ -123,11 +123,11 @@ function gg2(x: Record<string, unknown>) {
|
||||
>x : Record<string, unknown>
|
||||
|
||||
x; // {}
|
||||
>x : {}
|
||||
>x : Record<string, unknown>
|
||||
}
|
||||
else {
|
||||
x; // Record<string, unknown>
|
||||
>x : Record<string, unknown>
|
||||
>x : never
|
||||
}
|
||||
x; // Record<string, unknown>
|
||||
>x : Record<string, unknown>
|
||||
|
||||
@@ -146,4 +146,104 @@ tests/cases/compiler/strictSubtypeAndNarrowing.ts(129,26): error TS2322: Type '{
|
||||
!!! error TS2322: Type '{ x: number; y: number; }' is not assignable to type '{ x?: number | undefined; }'.
|
||||
!!! error TS2322: Object literal may only specify known properties, and 'y' does not exist in type '{ x?: number | undefined; }'.
|
||||
}
|
||||
|
||||
// Repros from #52827
|
||||
|
||||
declare function isArrayLike(value: any): value is { length: number };
|
||||
|
||||
function ff1(value: { [index: number]: boolean, length: number } | undefined) {
|
||||
if (isArrayLike(value)) {
|
||||
value;
|
||||
} else {
|
||||
value;
|
||||
}
|
||||
value;
|
||||
}
|
||||
|
||||
function ff2(value: { [index: number]: boolean, length: number } | string) {
|
||||
if (isArrayLike(value)) {
|
||||
value;
|
||||
} else {
|
||||
value;
|
||||
}
|
||||
value;
|
||||
}
|
||||
|
||||
function ff3(value: string | string[] | { [index: number]: boolean, length: number } | [number, boolean] | number | { length: string } | { a: string } | null | undefined) {
|
||||
if (isArrayLike(value)) {
|
||||
value;
|
||||
} else {
|
||||
value;
|
||||
}
|
||||
value;
|
||||
}
|
||||
|
||||
// Repro from comment in #52984
|
||||
|
||||
type DistributedKeyOf<T> = T extends unknown ? keyof T : never;
|
||||
|
||||
type NarrowByKeyValue<ObjT, KeyT extends PropertyKey, ValueT> = ObjT extends unknown
|
||||
? KeyT extends keyof ObjT
|
||||
? ValueT extends ObjT[KeyT]
|
||||
? ObjT & Readonly<Record<KeyT, ValueT>>
|
||||
: never
|
||||
: never
|
||||
: never;
|
||||
|
||||
type NarrowByDeepValue<ObjT, DeepPathT, ValueT> = DeepPathT extends readonly [
|
||||
infer Head extends DistributedKeyOf<ObjT>,
|
||||
]
|
||||
? NarrowByKeyValue<ObjT, Head, ValueT>
|
||||
: DeepPathT extends readonly [infer Head extends DistributedKeyOf<ObjT>, ...infer Rest]
|
||||
? NarrowByKeyValue<ObjT, Head, NarrowByDeepValue<NonNullable<ObjT[Head]>, Rest, ValueT>>
|
||||
: never;
|
||||
|
||||
|
||||
declare function doesValueAtDeepPathSatisfy<
|
||||
ObjT extends object,
|
||||
const DeepPathT extends ReadonlyArray<number | string>,
|
||||
ValueT,
|
||||
>(
|
||||
obj: ObjT,
|
||||
deepPath: DeepPathT,
|
||||
predicate: (arg: unknown) => arg is ValueT,
|
||||
): obj is NarrowByDeepValue<ObjT, DeepPathT, ValueT>;
|
||||
|
||||
|
||||
type Foo = {value: {type: 'A'}; a?: number} | {value: {type: 'B'}; b?: number};
|
||||
|
||||
declare function isA(arg: unknown): arg is 'A';
|
||||
declare function isB(arg: unknown): arg is 'B';
|
||||
|
||||
declare function assert(condition: boolean): asserts condition;
|
||||
|
||||
function test1(foo: Foo): {value: {type: 'A'}; a?: number} {
|
||||
assert(doesValueAtDeepPathSatisfy(foo, ['value', 'type'], isA));
|
||||
return foo;
|
||||
}
|
||||
|
||||
function test2(foo: Foo): {value: {type: 'A'}; a?: number} {
|
||||
assert(!doesValueAtDeepPathSatisfy(foo, ['value', 'type'], isB));
|
||||
return foo;
|
||||
}
|
||||
|
||||
// Repro from #53063
|
||||
|
||||
interface Free {
|
||||
premium: false;
|
||||
}
|
||||
|
||||
interface Premium {
|
||||
premium: true;
|
||||
}
|
||||
|
||||
type Union = { premium: false } | { premium: true };
|
||||
|
||||
declare const checkIsPremium: (a: Union) => a is Union & Premium;
|
||||
|
||||
const f = (value: Union) => {
|
||||
if (!checkIsPremium(value)) {
|
||||
value.premium;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -129,6 +129,106 @@ function fx11(): { x?: number } {
|
||||
let obj: { x?: number, y?: number };
|
||||
return obj = { x: 1, y: 2 };
|
||||
}
|
||||
|
||||
// Repros from #52827
|
||||
|
||||
declare function isArrayLike(value: any): value is { length: number };
|
||||
|
||||
function ff1(value: { [index: number]: boolean, length: number } | undefined) {
|
||||
if (isArrayLike(value)) {
|
||||
value;
|
||||
} else {
|
||||
value;
|
||||
}
|
||||
value;
|
||||
}
|
||||
|
||||
function ff2(value: { [index: number]: boolean, length: number } | string) {
|
||||
if (isArrayLike(value)) {
|
||||
value;
|
||||
} else {
|
||||
value;
|
||||
}
|
||||
value;
|
||||
}
|
||||
|
||||
function ff3(value: string | string[] | { [index: number]: boolean, length: number } | [number, boolean] | number | { length: string } | { a: string } | null | undefined) {
|
||||
if (isArrayLike(value)) {
|
||||
value;
|
||||
} else {
|
||||
value;
|
||||
}
|
||||
value;
|
||||
}
|
||||
|
||||
// Repro from comment in #52984
|
||||
|
||||
type DistributedKeyOf<T> = T extends unknown ? keyof T : never;
|
||||
|
||||
type NarrowByKeyValue<ObjT, KeyT extends PropertyKey, ValueT> = ObjT extends unknown
|
||||
? KeyT extends keyof ObjT
|
||||
? ValueT extends ObjT[KeyT]
|
||||
? ObjT & Readonly<Record<KeyT, ValueT>>
|
||||
: never
|
||||
: never
|
||||
: never;
|
||||
|
||||
type NarrowByDeepValue<ObjT, DeepPathT, ValueT> = DeepPathT extends readonly [
|
||||
infer Head extends DistributedKeyOf<ObjT>,
|
||||
]
|
||||
? NarrowByKeyValue<ObjT, Head, ValueT>
|
||||
: DeepPathT extends readonly [infer Head extends DistributedKeyOf<ObjT>, ...infer Rest]
|
||||
? NarrowByKeyValue<ObjT, Head, NarrowByDeepValue<NonNullable<ObjT[Head]>, Rest, ValueT>>
|
||||
: never;
|
||||
|
||||
|
||||
declare function doesValueAtDeepPathSatisfy<
|
||||
ObjT extends object,
|
||||
const DeepPathT extends ReadonlyArray<number | string>,
|
||||
ValueT,
|
||||
>(
|
||||
obj: ObjT,
|
||||
deepPath: DeepPathT,
|
||||
predicate: (arg: unknown) => arg is ValueT,
|
||||
): obj is NarrowByDeepValue<ObjT, DeepPathT, ValueT>;
|
||||
|
||||
|
||||
type Foo = {value: {type: 'A'}; a?: number} | {value: {type: 'B'}; b?: number};
|
||||
|
||||
declare function isA(arg: unknown): arg is 'A';
|
||||
declare function isB(arg: unknown): arg is 'B';
|
||||
|
||||
declare function assert(condition: boolean): asserts condition;
|
||||
|
||||
function test1(foo: Foo): {value: {type: 'A'}; a?: number} {
|
||||
assert(doesValueAtDeepPathSatisfy(foo, ['value', 'type'], isA));
|
||||
return foo;
|
||||
}
|
||||
|
||||
function test2(foo: Foo): {value: {type: 'A'}; a?: number} {
|
||||
assert(!doesValueAtDeepPathSatisfy(foo, ['value', 'type'], isB));
|
||||
return foo;
|
||||
}
|
||||
|
||||
// Repro from #53063
|
||||
|
||||
interface Free {
|
||||
premium: false;
|
||||
}
|
||||
|
||||
interface Premium {
|
||||
premium: true;
|
||||
}
|
||||
|
||||
type Union = { premium: false } | { premium: true };
|
||||
|
||||
declare const checkIsPremium: (a: Union) => a is Union & Premium;
|
||||
|
||||
const f = (value: Union) => {
|
||||
if (!checkIsPremium(value)) {
|
||||
value.premium;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//// [strictSubtypeAndNarrowing.js]
|
||||
@@ -226,3 +326,43 @@ function fx11() {
|
||||
var obj;
|
||||
return obj = { x: 1, y: 2 };
|
||||
}
|
||||
function ff1(value) {
|
||||
if (isArrayLike(value)) {
|
||||
value;
|
||||
}
|
||||
else {
|
||||
value;
|
||||
}
|
||||
value;
|
||||
}
|
||||
function ff2(value) {
|
||||
if (isArrayLike(value)) {
|
||||
value;
|
||||
}
|
||||
else {
|
||||
value;
|
||||
}
|
||||
value;
|
||||
}
|
||||
function ff3(value) {
|
||||
if (isArrayLike(value)) {
|
||||
value;
|
||||
}
|
||||
else {
|
||||
value;
|
||||
}
|
||||
value;
|
||||
}
|
||||
function test1(foo) {
|
||||
assert(doesValueAtDeepPathSatisfy(foo, ['value', 'type'], isA));
|
||||
return foo;
|
||||
}
|
||||
function test2(foo) {
|
||||
assert(!doesValueAtDeepPathSatisfy(foo, ['value', 'type'], isB));
|
||||
return foo;
|
||||
}
|
||||
var f = function (value) {
|
||||
if (!checkIsPremium(value)) {
|
||||
value.premium;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -311,3 +311,293 @@ function fx11(): { x?: number } {
|
||||
>y : Symbol(y, Decl(strictSubtypeAndNarrowing.ts, 128, 24))
|
||||
}
|
||||
|
||||
// Repros from #52827
|
||||
|
||||
declare function isArrayLike(value: any): value is { length: number };
|
||||
>isArrayLike : Symbol(isArrayLike, Decl(strictSubtypeAndNarrowing.ts, 129, 1))
|
||||
>value : Symbol(value, Decl(strictSubtypeAndNarrowing.ts, 133, 29))
|
||||
>value : Symbol(value, Decl(strictSubtypeAndNarrowing.ts, 133, 29))
|
||||
>length : Symbol(length, Decl(strictSubtypeAndNarrowing.ts, 133, 52))
|
||||
|
||||
function ff1(value: { [index: number]: boolean, length: number } | undefined) {
|
||||
>ff1 : Symbol(ff1, Decl(strictSubtypeAndNarrowing.ts, 133, 70))
|
||||
>value : Symbol(value, Decl(strictSubtypeAndNarrowing.ts, 135, 13))
|
||||
>index : Symbol(index, Decl(strictSubtypeAndNarrowing.ts, 135, 23))
|
||||
>length : Symbol(length, Decl(strictSubtypeAndNarrowing.ts, 135, 47))
|
||||
|
||||
if (isArrayLike(value)) {
|
||||
>isArrayLike : Symbol(isArrayLike, Decl(strictSubtypeAndNarrowing.ts, 129, 1))
|
||||
>value : Symbol(value, Decl(strictSubtypeAndNarrowing.ts, 135, 13))
|
||||
|
||||
value;
|
||||
>value : Symbol(value, Decl(strictSubtypeAndNarrowing.ts, 135, 13))
|
||||
|
||||
} else {
|
||||
value;
|
||||
>value : Symbol(value, Decl(strictSubtypeAndNarrowing.ts, 135, 13))
|
||||
}
|
||||
value;
|
||||
>value : Symbol(value, Decl(strictSubtypeAndNarrowing.ts, 135, 13))
|
||||
}
|
||||
|
||||
function ff2(value: { [index: number]: boolean, length: number } | string) {
|
||||
>ff2 : Symbol(ff2, Decl(strictSubtypeAndNarrowing.ts, 142, 1))
|
||||
>value : Symbol(value, Decl(strictSubtypeAndNarrowing.ts, 144, 13))
|
||||
>index : Symbol(index, Decl(strictSubtypeAndNarrowing.ts, 144, 23))
|
||||
>length : Symbol(length, Decl(strictSubtypeAndNarrowing.ts, 144, 47))
|
||||
|
||||
if (isArrayLike(value)) {
|
||||
>isArrayLike : Symbol(isArrayLike, Decl(strictSubtypeAndNarrowing.ts, 129, 1))
|
||||
>value : Symbol(value, Decl(strictSubtypeAndNarrowing.ts, 144, 13))
|
||||
|
||||
value;
|
||||
>value : Symbol(value, Decl(strictSubtypeAndNarrowing.ts, 144, 13))
|
||||
|
||||
} else {
|
||||
value;
|
||||
>value : Symbol(value, Decl(strictSubtypeAndNarrowing.ts, 144, 13))
|
||||
}
|
||||
value;
|
||||
>value : Symbol(value, Decl(strictSubtypeAndNarrowing.ts, 144, 13))
|
||||
}
|
||||
|
||||
function ff3(value: string | string[] | { [index: number]: boolean, length: number } | [number, boolean] | number | { length: string } | { a: string } | null | undefined) {
|
||||
>ff3 : Symbol(ff3, Decl(strictSubtypeAndNarrowing.ts, 151, 1))
|
||||
>value : Symbol(value, Decl(strictSubtypeAndNarrowing.ts, 153, 13))
|
||||
>index : Symbol(index, Decl(strictSubtypeAndNarrowing.ts, 153, 43))
|
||||
>length : Symbol(length, Decl(strictSubtypeAndNarrowing.ts, 153, 67))
|
||||
>length : Symbol(length, Decl(strictSubtypeAndNarrowing.ts, 153, 117))
|
||||
>a : Symbol(a, Decl(strictSubtypeAndNarrowing.ts, 153, 138))
|
||||
|
||||
if (isArrayLike(value)) {
|
||||
>isArrayLike : Symbol(isArrayLike, Decl(strictSubtypeAndNarrowing.ts, 129, 1))
|
||||
>value : Symbol(value, Decl(strictSubtypeAndNarrowing.ts, 153, 13))
|
||||
|
||||
value;
|
||||
>value : Symbol(value, Decl(strictSubtypeAndNarrowing.ts, 153, 13))
|
||||
|
||||
} else {
|
||||
value;
|
||||
>value : Symbol(value, Decl(strictSubtypeAndNarrowing.ts, 153, 13))
|
||||
}
|
||||
value;
|
||||
>value : Symbol(value, Decl(strictSubtypeAndNarrowing.ts, 153, 13))
|
||||
}
|
||||
|
||||
// Repro from comment in #52984
|
||||
|
||||
type DistributedKeyOf<T> = T extends unknown ? keyof T : never;
|
||||
>DistributedKeyOf : Symbol(DistributedKeyOf, Decl(strictSubtypeAndNarrowing.ts, 160, 1))
|
||||
>T : Symbol(T, Decl(strictSubtypeAndNarrowing.ts, 164, 22))
|
||||
>T : Symbol(T, Decl(strictSubtypeAndNarrowing.ts, 164, 22))
|
||||
>T : Symbol(T, Decl(strictSubtypeAndNarrowing.ts, 164, 22))
|
||||
|
||||
type NarrowByKeyValue<ObjT, KeyT extends PropertyKey, ValueT> = ObjT extends unknown
|
||||
>NarrowByKeyValue : Symbol(NarrowByKeyValue, Decl(strictSubtypeAndNarrowing.ts, 164, 63))
|
||||
>ObjT : Symbol(ObjT, Decl(strictSubtypeAndNarrowing.ts, 166, 22))
|
||||
>KeyT : Symbol(KeyT, Decl(strictSubtypeAndNarrowing.ts, 166, 27))
|
||||
>PropertyKey : Symbol(PropertyKey, Decl(lib.es5.d.ts, --, --))
|
||||
>ValueT : Symbol(ValueT, Decl(strictSubtypeAndNarrowing.ts, 166, 53))
|
||||
>ObjT : Symbol(ObjT, Decl(strictSubtypeAndNarrowing.ts, 166, 22))
|
||||
|
||||
? KeyT extends keyof ObjT
|
||||
>KeyT : Symbol(KeyT, Decl(strictSubtypeAndNarrowing.ts, 166, 27))
|
||||
>ObjT : Symbol(ObjT, Decl(strictSubtypeAndNarrowing.ts, 166, 22))
|
||||
|
||||
? ValueT extends ObjT[KeyT]
|
||||
>ValueT : Symbol(ValueT, Decl(strictSubtypeAndNarrowing.ts, 166, 53))
|
||||
>ObjT : Symbol(ObjT, Decl(strictSubtypeAndNarrowing.ts, 166, 22))
|
||||
>KeyT : Symbol(KeyT, Decl(strictSubtypeAndNarrowing.ts, 166, 27))
|
||||
|
||||
? ObjT & Readonly<Record<KeyT, ValueT>>
|
||||
>ObjT : Symbol(ObjT, Decl(strictSubtypeAndNarrowing.ts, 166, 22))
|
||||
>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --))
|
||||
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
|
||||
>KeyT : Symbol(KeyT, Decl(strictSubtypeAndNarrowing.ts, 166, 27))
|
||||
>ValueT : Symbol(ValueT, Decl(strictSubtypeAndNarrowing.ts, 166, 53))
|
||||
|
||||
: never
|
||||
: never
|
||||
: never;
|
||||
|
||||
type NarrowByDeepValue<ObjT, DeepPathT, ValueT> = DeepPathT extends readonly [
|
||||
>NarrowByDeepValue : Symbol(NarrowByDeepValue, Decl(strictSubtypeAndNarrowing.ts, 172, 12))
|
||||
>ObjT : Symbol(ObjT, Decl(strictSubtypeAndNarrowing.ts, 174, 23))
|
||||
>DeepPathT : Symbol(DeepPathT, Decl(strictSubtypeAndNarrowing.ts, 174, 28))
|
||||
>ValueT : Symbol(ValueT, Decl(strictSubtypeAndNarrowing.ts, 174, 39))
|
||||
>DeepPathT : Symbol(DeepPathT, Decl(strictSubtypeAndNarrowing.ts, 174, 28))
|
||||
|
||||
infer Head extends DistributedKeyOf<ObjT>,
|
||||
>Head : Symbol(Head, Decl(strictSubtypeAndNarrowing.ts, 175, 9))
|
||||
>DistributedKeyOf : Symbol(DistributedKeyOf, Decl(strictSubtypeAndNarrowing.ts, 160, 1))
|
||||
>ObjT : Symbol(ObjT, Decl(strictSubtypeAndNarrowing.ts, 174, 23))
|
||||
|
||||
]
|
||||
? NarrowByKeyValue<ObjT, Head, ValueT>
|
||||
>NarrowByKeyValue : Symbol(NarrowByKeyValue, Decl(strictSubtypeAndNarrowing.ts, 164, 63))
|
||||
>ObjT : Symbol(ObjT, Decl(strictSubtypeAndNarrowing.ts, 174, 23))
|
||||
>Head : Symbol(Head, Decl(strictSubtypeAndNarrowing.ts, 175, 9))
|
||||
>ValueT : Symbol(ValueT, Decl(strictSubtypeAndNarrowing.ts, 174, 39))
|
||||
|
||||
: DeepPathT extends readonly [infer Head extends DistributedKeyOf<ObjT>, ...infer Rest]
|
||||
>DeepPathT : Symbol(DeepPathT, Decl(strictSubtypeAndNarrowing.ts, 174, 28))
|
||||
>Head : Symbol(Head, Decl(strictSubtypeAndNarrowing.ts, 178, 39))
|
||||
>DistributedKeyOf : Symbol(DistributedKeyOf, Decl(strictSubtypeAndNarrowing.ts, 160, 1))
|
||||
>ObjT : Symbol(ObjT, Decl(strictSubtypeAndNarrowing.ts, 174, 23))
|
||||
>Rest : Symbol(Rest, Decl(strictSubtypeAndNarrowing.ts, 178, 85))
|
||||
|
||||
? NarrowByKeyValue<ObjT, Head, NarrowByDeepValue<NonNullable<ObjT[Head]>, Rest, ValueT>>
|
||||
>NarrowByKeyValue : Symbol(NarrowByKeyValue, Decl(strictSubtypeAndNarrowing.ts, 164, 63))
|
||||
>ObjT : Symbol(ObjT, Decl(strictSubtypeAndNarrowing.ts, 174, 23))
|
||||
>Head : Symbol(Head, Decl(strictSubtypeAndNarrowing.ts, 178, 39))
|
||||
>NarrowByDeepValue : Symbol(NarrowByDeepValue, Decl(strictSubtypeAndNarrowing.ts, 172, 12))
|
||||
>NonNullable : Symbol(NonNullable, Decl(lib.es5.d.ts, --, --))
|
||||
>ObjT : Symbol(ObjT, Decl(strictSubtypeAndNarrowing.ts, 174, 23))
|
||||
>Head : Symbol(Head, Decl(strictSubtypeAndNarrowing.ts, 178, 39))
|
||||
>Rest : Symbol(Rest, Decl(strictSubtypeAndNarrowing.ts, 178, 85))
|
||||
>ValueT : Symbol(ValueT, Decl(strictSubtypeAndNarrowing.ts, 174, 39))
|
||||
|
||||
: never;
|
||||
|
||||
|
||||
declare function doesValueAtDeepPathSatisfy<
|
||||
>doesValueAtDeepPathSatisfy : Symbol(doesValueAtDeepPathSatisfy, Decl(strictSubtypeAndNarrowing.ts, 180, 12))
|
||||
|
||||
ObjT extends object,
|
||||
>ObjT : Symbol(ObjT, Decl(strictSubtypeAndNarrowing.ts, 183, 44))
|
||||
|
||||
const DeepPathT extends ReadonlyArray<number | string>,
|
||||
>DeepPathT : Symbol(DeepPathT, Decl(strictSubtypeAndNarrowing.ts, 184, 24))
|
||||
>ReadonlyArray : Symbol(ReadonlyArray, Decl(lib.es5.d.ts, --, --))
|
||||
|
||||
ValueT,
|
||||
>ValueT : Symbol(ValueT, Decl(strictSubtypeAndNarrowing.ts, 185, 59))
|
||||
|
||||
>(
|
||||
obj: ObjT,
|
||||
>obj : Symbol(obj, Decl(strictSubtypeAndNarrowing.ts, 187, 2))
|
||||
>ObjT : Symbol(ObjT, Decl(strictSubtypeAndNarrowing.ts, 183, 44))
|
||||
|
||||
deepPath: DeepPathT,
|
||||
>deepPath : Symbol(deepPath, Decl(strictSubtypeAndNarrowing.ts, 188, 14))
|
||||
>DeepPathT : Symbol(DeepPathT, Decl(strictSubtypeAndNarrowing.ts, 184, 24))
|
||||
|
||||
predicate: (arg: unknown) => arg is ValueT,
|
||||
>predicate : Symbol(predicate, Decl(strictSubtypeAndNarrowing.ts, 189, 24))
|
||||
>arg : Symbol(arg, Decl(strictSubtypeAndNarrowing.ts, 190, 16))
|
||||
>arg : Symbol(arg, Decl(strictSubtypeAndNarrowing.ts, 190, 16))
|
||||
>ValueT : Symbol(ValueT, Decl(strictSubtypeAndNarrowing.ts, 185, 59))
|
||||
|
||||
): obj is NarrowByDeepValue<ObjT, DeepPathT, ValueT>;
|
||||
>obj : Symbol(obj, Decl(strictSubtypeAndNarrowing.ts, 187, 2))
|
||||
>NarrowByDeepValue : Symbol(NarrowByDeepValue, Decl(strictSubtypeAndNarrowing.ts, 172, 12))
|
||||
>ObjT : Symbol(ObjT, Decl(strictSubtypeAndNarrowing.ts, 183, 44))
|
||||
>DeepPathT : Symbol(DeepPathT, Decl(strictSubtypeAndNarrowing.ts, 184, 24))
|
||||
>ValueT : Symbol(ValueT, Decl(strictSubtypeAndNarrowing.ts, 185, 59))
|
||||
|
||||
|
||||
type Foo = {value: {type: 'A'}; a?: number} | {value: {type: 'B'}; b?: number};
|
||||
>Foo : Symbol(Foo, Decl(strictSubtypeAndNarrowing.ts, 191, 53))
|
||||
>value : Symbol(value, Decl(strictSubtypeAndNarrowing.ts, 194, 12))
|
||||
>type : Symbol(type, Decl(strictSubtypeAndNarrowing.ts, 194, 20))
|
||||
>a : Symbol(a, Decl(strictSubtypeAndNarrowing.ts, 194, 31))
|
||||
>value : Symbol(value, Decl(strictSubtypeAndNarrowing.ts, 194, 47))
|
||||
>type : Symbol(type, Decl(strictSubtypeAndNarrowing.ts, 194, 55))
|
||||
>b : Symbol(b, Decl(strictSubtypeAndNarrowing.ts, 194, 66))
|
||||
|
||||
declare function isA(arg: unknown): arg is 'A';
|
||||
>isA : Symbol(isA, Decl(strictSubtypeAndNarrowing.ts, 194, 79))
|
||||
>arg : Symbol(arg, Decl(strictSubtypeAndNarrowing.ts, 196, 21))
|
||||
>arg : Symbol(arg, Decl(strictSubtypeAndNarrowing.ts, 196, 21))
|
||||
|
||||
declare function isB(arg: unknown): arg is 'B';
|
||||
>isB : Symbol(isB, Decl(strictSubtypeAndNarrowing.ts, 196, 47))
|
||||
>arg : Symbol(arg, Decl(strictSubtypeAndNarrowing.ts, 197, 21))
|
||||
>arg : Symbol(arg, Decl(strictSubtypeAndNarrowing.ts, 197, 21))
|
||||
|
||||
declare function assert(condition: boolean): asserts condition;
|
||||
>assert : Symbol(assert, Decl(strictSubtypeAndNarrowing.ts, 197, 47))
|
||||
>condition : Symbol(condition, Decl(strictSubtypeAndNarrowing.ts, 199, 24))
|
||||
>condition : Symbol(condition, Decl(strictSubtypeAndNarrowing.ts, 199, 24))
|
||||
|
||||
function test1(foo: Foo): {value: {type: 'A'}; a?: number} {
|
||||
>test1 : Symbol(test1, Decl(strictSubtypeAndNarrowing.ts, 199, 63))
|
||||
>foo : Symbol(foo, Decl(strictSubtypeAndNarrowing.ts, 201, 15))
|
||||
>Foo : Symbol(Foo, Decl(strictSubtypeAndNarrowing.ts, 191, 53))
|
||||
>value : Symbol(value, Decl(strictSubtypeAndNarrowing.ts, 201, 27))
|
||||
>type : Symbol(type, Decl(strictSubtypeAndNarrowing.ts, 201, 35))
|
||||
>a : Symbol(a, Decl(strictSubtypeAndNarrowing.ts, 201, 46))
|
||||
|
||||
assert(doesValueAtDeepPathSatisfy(foo, ['value', 'type'], isA));
|
||||
>assert : Symbol(assert, Decl(strictSubtypeAndNarrowing.ts, 197, 47))
|
||||
>doesValueAtDeepPathSatisfy : Symbol(doesValueAtDeepPathSatisfy, Decl(strictSubtypeAndNarrowing.ts, 180, 12))
|
||||
>foo : Symbol(foo, Decl(strictSubtypeAndNarrowing.ts, 201, 15))
|
||||
>isA : Symbol(isA, Decl(strictSubtypeAndNarrowing.ts, 194, 79))
|
||||
|
||||
return foo;
|
||||
>foo : Symbol(foo, Decl(strictSubtypeAndNarrowing.ts, 201, 15))
|
||||
}
|
||||
|
||||
function test2(foo: Foo): {value: {type: 'A'}; a?: number} {
|
||||
>test2 : Symbol(test2, Decl(strictSubtypeAndNarrowing.ts, 204, 1))
|
||||
>foo : Symbol(foo, Decl(strictSubtypeAndNarrowing.ts, 206, 15))
|
||||
>Foo : Symbol(Foo, Decl(strictSubtypeAndNarrowing.ts, 191, 53))
|
||||
>value : Symbol(value, Decl(strictSubtypeAndNarrowing.ts, 206, 27))
|
||||
>type : Symbol(type, Decl(strictSubtypeAndNarrowing.ts, 206, 35))
|
||||
>a : Symbol(a, Decl(strictSubtypeAndNarrowing.ts, 206, 46))
|
||||
|
||||
assert(!doesValueAtDeepPathSatisfy(foo, ['value', 'type'], isB));
|
||||
>assert : Symbol(assert, Decl(strictSubtypeAndNarrowing.ts, 197, 47))
|
||||
>doesValueAtDeepPathSatisfy : Symbol(doesValueAtDeepPathSatisfy, Decl(strictSubtypeAndNarrowing.ts, 180, 12))
|
||||
>foo : Symbol(foo, Decl(strictSubtypeAndNarrowing.ts, 206, 15))
|
||||
>isB : Symbol(isB, Decl(strictSubtypeAndNarrowing.ts, 196, 47))
|
||||
|
||||
return foo;
|
||||
>foo : Symbol(foo, Decl(strictSubtypeAndNarrowing.ts, 206, 15))
|
||||
}
|
||||
|
||||
// Repro from #53063
|
||||
|
||||
interface Free {
|
||||
>Free : Symbol(Free, Decl(strictSubtypeAndNarrowing.ts, 209, 1))
|
||||
|
||||
premium: false;
|
||||
>premium : Symbol(Free.premium, Decl(strictSubtypeAndNarrowing.ts, 213, 16))
|
||||
}
|
||||
|
||||
interface Premium {
|
||||
>Premium : Symbol(Premium, Decl(strictSubtypeAndNarrowing.ts, 215, 1))
|
||||
|
||||
premium: true;
|
||||
>premium : Symbol(Premium.premium, Decl(strictSubtypeAndNarrowing.ts, 217, 19))
|
||||
}
|
||||
|
||||
type Union = { premium: false } | { premium: true };
|
||||
>Union : Symbol(Union, Decl(strictSubtypeAndNarrowing.ts, 219, 1))
|
||||
>premium : Symbol(premium, Decl(strictSubtypeAndNarrowing.ts, 221, 14))
|
||||
>premium : Symbol(premium, Decl(strictSubtypeAndNarrowing.ts, 221, 35))
|
||||
|
||||
declare const checkIsPremium: (a: Union) => a is Union & Premium;
|
||||
>checkIsPremium : Symbol(checkIsPremium, Decl(strictSubtypeAndNarrowing.ts, 223, 13))
|
||||
>a : Symbol(a, Decl(strictSubtypeAndNarrowing.ts, 223, 31))
|
||||
>Union : Symbol(Union, Decl(strictSubtypeAndNarrowing.ts, 219, 1))
|
||||
>a : Symbol(a, Decl(strictSubtypeAndNarrowing.ts, 223, 31))
|
||||
>Union : Symbol(Union, Decl(strictSubtypeAndNarrowing.ts, 219, 1))
|
||||
>Premium : Symbol(Premium, Decl(strictSubtypeAndNarrowing.ts, 215, 1))
|
||||
|
||||
const f = (value: Union) => {
|
||||
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 225, 5))
|
||||
>value : Symbol(value, Decl(strictSubtypeAndNarrowing.ts, 225, 11))
|
||||
>Union : Symbol(Union, Decl(strictSubtypeAndNarrowing.ts, 219, 1))
|
||||
|
||||
if (!checkIsPremium(value)) {
|
||||
>checkIsPremium : Symbol(checkIsPremium, Decl(strictSubtypeAndNarrowing.ts, 223, 13))
|
||||
>value : Symbol(value, Decl(strictSubtypeAndNarrowing.ts, 225, 11))
|
||||
|
||||
value.premium;
|
||||
>value.premium : Symbol(premium, Decl(strictSubtypeAndNarrowing.ts, 221, 14))
|
||||
>value : Symbol(value, Decl(strictSubtypeAndNarrowing.ts, 225, 11))
|
||||
>premium : Symbol(premium, Decl(strictSubtypeAndNarrowing.ts, 221, 14))
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -326,3 +326,234 @@ function fx11(): { x?: number } {
|
||||
>2 : 2
|
||||
}
|
||||
|
||||
// Repros from #52827
|
||||
|
||||
declare function isArrayLike(value: any): value is { length: number };
|
||||
>isArrayLike : (value: any) => value is { length: number; }
|
||||
>value : any
|
||||
>length : number
|
||||
|
||||
function ff1(value: { [index: number]: boolean, length: number } | undefined) {
|
||||
>ff1 : (value: { [index: number]: boolean; length: number; } | undefined) => void
|
||||
>value : { [index: number]: boolean; length: number; } | undefined
|
||||
>index : number
|
||||
>length : number
|
||||
|
||||
if (isArrayLike(value)) {
|
||||
>isArrayLike(value) : boolean
|
||||
>isArrayLike : (value: any) => value is { length: number; }
|
||||
>value : { [index: number]: boolean; length: number; } | undefined
|
||||
|
||||
value;
|
||||
>value : { [index: number]: boolean; length: number; }
|
||||
|
||||
} else {
|
||||
value;
|
||||
>value : undefined
|
||||
}
|
||||
value;
|
||||
>value : { [index: number]: boolean; length: number; } | undefined
|
||||
}
|
||||
|
||||
function ff2(value: { [index: number]: boolean, length: number } | string) {
|
||||
>ff2 : (value: string | { [index: number]: boolean; length: number; }) => void
|
||||
>value : string | { [index: number]: boolean; length: number; }
|
||||
>index : number
|
||||
>length : number
|
||||
|
||||
if (isArrayLike(value)) {
|
||||
>isArrayLike(value) : boolean
|
||||
>isArrayLike : (value: any) => value is { length: number; }
|
||||
>value : string | { [index: number]: boolean; length: number; }
|
||||
|
||||
value;
|
||||
>value : string | { [index: number]: boolean; length: number; }
|
||||
|
||||
} else {
|
||||
value;
|
||||
>value : never
|
||||
}
|
||||
value;
|
||||
>value : string | { [index: number]: boolean; length: number; }
|
||||
}
|
||||
|
||||
function ff3(value: string | string[] | { [index: number]: boolean, length: number } | [number, boolean] | number | { length: string } | { a: string } | null | undefined) {
|
||||
>ff3 : (value: string | number | { [index: number]: boolean; length: number; } | [number, boolean] | { length: string; } | { a: string; } | string[] | null | undefined) => void
|
||||
>value : string | number | { [index: number]: boolean; length: number; } | [number, boolean] | { length: string; } | { a: string; } | string[] | null | undefined
|
||||
>index : number
|
||||
>length : number
|
||||
>length : string
|
||||
>a : string
|
||||
>null : null
|
||||
|
||||
if (isArrayLike(value)) {
|
||||
>isArrayLike(value) : boolean
|
||||
>isArrayLike : (value: any) => value is { length: number; }
|
||||
>value : string | number | { [index: number]: boolean; length: number; } | [number, boolean] | { length: string; } | { a: string; } | string[] | null | undefined
|
||||
|
||||
value;
|
||||
>value : string | { [index: number]: boolean; length: number; } | [number, boolean] | string[]
|
||||
|
||||
} else {
|
||||
value;
|
||||
>value : number | { length: string; } | { a: string; } | null | undefined
|
||||
}
|
||||
value;
|
||||
>value : string | number | { [index: number]: boolean; length: number; } | [number, boolean] | { length: string; } | { a: string; } | string[] | null | undefined
|
||||
}
|
||||
|
||||
// Repro from comment in #52984
|
||||
|
||||
type DistributedKeyOf<T> = T extends unknown ? keyof T : never;
|
||||
>DistributedKeyOf : DistributedKeyOf<T>
|
||||
|
||||
type NarrowByKeyValue<ObjT, KeyT extends PropertyKey, ValueT> = ObjT extends unknown
|
||||
>NarrowByKeyValue : NarrowByKeyValue<ObjT, KeyT, ValueT>
|
||||
|
||||
? KeyT extends keyof ObjT
|
||||
? ValueT extends ObjT[KeyT]
|
||||
? ObjT & Readonly<Record<KeyT, ValueT>>
|
||||
: never
|
||||
: never
|
||||
: never;
|
||||
|
||||
type NarrowByDeepValue<ObjT, DeepPathT, ValueT> = DeepPathT extends readonly [
|
||||
>NarrowByDeepValue : NarrowByDeepValue<ObjT, DeepPathT, ValueT>
|
||||
|
||||
infer Head extends DistributedKeyOf<ObjT>,
|
||||
]
|
||||
? NarrowByKeyValue<ObjT, Head, ValueT>
|
||||
: DeepPathT extends readonly [infer Head extends DistributedKeyOf<ObjT>, ...infer Rest]
|
||||
? NarrowByKeyValue<ObjT, Head, NarrowByDeepValue<NonNullable<ObjT[Head]>, Rest, ValueT>>
|
||||
: never;
|
||||
|
||||
|
||||
declare function doesValueAtDeepPathSatisfy<
|
||||
>doesValueAtDeepPathSatisfy : <ObjT extends object, const DeepPathT extends readonly (string | number)[], ValueT>(obj: ObjT, deepPath: DeepPathT, predicate: (arg: unknown) => arg is ValueT) => obj is NarrowByDeepValue<ObjT, DeepPathT, ValueT>
|
||||
|
||||
ObjT extends object,
|
||||
const DeepPathT extends ReadonlyArray<number | string>,
|
||||
ValueT,
|
||||
>(
|
||||
obj: ObjT,
|
||||
>obj : ObjT
|
||||
|
||||
deepPath: DeepPathT,
|
||||
>deepPath : DeepPathT
|
||||
|
||||
predicate: (arg: unknown) => arg is ValueT,
|
||||
>predicate : (arg: unknown) => arg is ValueT
|
||||
>arg : unknown
|
||||
|
||||
): obj is NarrowByDeepValue<ObjT, DeepPathT, ValueT>;
|
||||
|
||||
|
||||
type Foo = {value: {type: 'A'}; a?: number} | {value: {type: 'B'}; b?: number};
|
||||
>Foo : { value: { type: 'A';}; a?: number | undefined; } | { value: { type: 'B';}; b?: number | undefined; }
|
||||
>value : { type: 'A'; }
|
||||
>type : "A"
|
||||
>a : number | undefined
|
||||
>value : { type: 'B'; }
|
||||
>type : "B"
|
||||
>b : number | undefined
|
||||
|
||||
declare function isA(arg: unknown): arg is 'A';
|
||||
>isA : (arg: unknown) => arg is "A"
|
||||
>arg : unknown
|
||||
|
||||
declare function isB(arg: unknown): arg is 'B';
|
||||
>isB : (arg: unknown) => arg is "B"
|
||||
>arg : unknown
|
||||
|
||||
declare function assert(condition: boolean): asserts condition;
|
||||
>assert : (condition: boolean) => asserts condition
|
||||
>condition : boolean
|
||||
|
||||
function test1(foo: Foo): {value: {type: 'A'}; a?: number} {
|
||||
>test1 : (foo: Foo) => { value: { type: 'A'; }; a?: number;}
|
||||
>foo : Foo
|
||||
>value : { type: 'A'; }
|
||||
>type : "A"
|
||||
>a : number | undefined
|
||||
|
||||
assert(doesValueAtDeepPathSatisfy(foo, ['value', 'type'], isA));
|
||||
>assert(doesValueAtDeepPathSatisfy(foo, ['value', 'type'], isA)) : void
|
||||
>assert : (condition: boolean) => asserts condition
|
||||
>doesValueAtDeepPathSatisfy(foo, ['value', 'type'], isA) : boolean
|
||||
>doesValueAtDeepPathSatisfy : <ObjT extends object, const DeepPathT extends readonly (string | number)[], ValueT>(obj: ObjT, deepPath: DeepPathT, predicate: (arg: unknown) => arg is ValueT) => obj is NarrowByDeepValue<ObjT, DeepPathT, ValueT>
|
||||
>foo : Foo
|
||||
>['value', 'type'] : ["value", "type"]
|
||||
>'value' : "value"
|
||||
>'type' : "type"
|
||||
>isA : (arg: unknown) => arg is "A"
|
||||
|
||||
return foo;
|
||||
>foo : { value: { type: "A"; }; a?: number | undefined; }
|
||||
}
|
||||
|
||||
function test2(foo: Foo): {value: {type: 'A'}; a?: number} {
|
||||
>test2 : (foo: Foo) => { value: { type: 'A'; }; a?: number;}
|
||||
>foo : Foo
|
||||
>value : { type: 'A'; }
|
||||
>type : "A"
|
||||
>a : number | undefined
|
||||
|
||||
assert(!doesValueAtDeepPathSatisfy(foo, ['value', 'type'], isB));
|
||||
>assert(!doesValueAtDeepPathSatisfy(foo, ['value', 'type'], isB)) : void
|
||||
>assert : (condition: boolean) => asserts condition
|
||||
>!doesValueAtDeepPathSatisfy(foo, ['value', 'type'], isB) : boolean
|
||||
>doesValueAtDeepPathSatisfy(foo, ['value', 'type'], isB) : boolean
|
||||
>doesValueAtDeepPathSatisfy : <ObjT extends object, const DeepPathT extends readonly (string | number)[], ValueT>(obj: ObjT, deepPath: DeepPathT, predicate: (arg: unknown) => arg is ValueT) => obj is NarrowByDeepValue<ObjT, DeepPathT, ValueT>
|
||||
>foo : Foo
|
||||
>['value', 'type'] : ["value", "type"]
|
||||
>'value' : "value"
|
||||
>'type' : "type"
|
||||
>isB : (arg: unknown) => arg is "B"
|
||||
|
||||
return foo;
|
||||
>foo : { value: { type: "A"; }; a?: number | undefined; }
|
||||
}
|
||||
|
||||
// Repro from #53063
|
||||
|
||||
interface Free {
|
||||
premium: false;
|
||||
>premium : false
|
||||
>false : false
|
||||
}
|
||||
|
||||
interface Premium {
|
||||
premium: true;
|
||||
>premium : true
|
||||
>true : true
|
||||
}
|
||||
|
||||
type Union = { premium: false } | { premium: true };
|
||||
>Union : { premium: false; } | { premium: true; }
|
||||
>premium : false
|
||||
>false : false
|
||||
>premium : true
|
||||
>true : true
|
||||
|
||||
declare const checkIsPremium: (a: Union) => a is Union & Premium;
|
||||
>checkIsPremium : (a: Union) => a is { premium: true; } & Premium
|
||||
>a : Union
|
||||
|
||||
const f = (value: Union) => {
|
||||
>f : (value: Union) => void
|
||||
>(value: Union) => { if (!checkIsPremium(value)) { value.premium; }} : (value: Union) => void
|
||||
>value : Union
|
||||
|
||||
if (!checkIsPremium(value)) {
|
||||
>!checkIsPremium(value) : boolean
|
||||
>checkIsPremium(value) : boolean
|
||||
>checkIsPremium : (a: Union) => a is { premium: true; } & Premium
|
||||
>value : Union
|
||||
|
||||
value.premium;
|
||||
>value.premium : false
|
||||
>value : { premium: false; }
|
||||
>premium : false
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -130,3 +130,103 @@ function fx11(): { x?: number } {
|
||||
let obj: { x?: number, y?: number };
|
||||
return obj = { x: 1, y: 2 };
|
||||
}
|
||||
|
||||
// Repros from #52827
|
||||
|
||||
declare function isArrayLike(value: any): value is { length: number };
|
||||
|
||||
function ff1(value: { [index: number]: boolean, length: number } | undefined) {
|
||||
if (isArrayLike(value)) {
|
||||
value;
|
||||
} else {
|
||||
value;
|
||||
}
|
||||
value;
|
||||
}
|
||||
|
||||
function ff2(value: { [index: number]: boolean, length: number } | string) {
|
||||
if (isArrayLike(value)) {
|
||||
value;
|
||||
} else {
|
||||
value;
|
||||
}
|
||||
value;
|
||||
}
|
||||
|
||||
function ff3(value: string | string[] | { [index: number]: boolean, length: number } | [number, boolean] | number | { length: string } | { a: string } | null | undefined) {
|
||||
if (isArrayLike(value)) {
|
||||
value;
|
||||
} else {
|
||||
value;
|
||||
}
|
||||
value;
|
||||
}
|
||||
|
||||
// Repro from comment in #52984
|
||||
|
||||
type DistributedKeyOf<T> = T extends unknown ? keyof T : never;
|
||||
|
||||
type NarrowByKeyValue<ObjT, KeyT extends PropertyKey, ValueT> = ObjT extends unknown
|
||||
? KeyT extends keyof ObjT
|
||||
? ValueT extends ObjT[KeyT]
|
||||
? ObjT & Readonly<Record<KeyT, ValueT>>
|
||||
: never
|
||||
: never
|
||||
: never;
|
||||
|
||||
type NarrowByDeepValue<ObjT, DeepPathT, ValueT> = DeepPathT extends readonly [
|
||||
infer Head extends DistributedKeyOf<ObjT>,
|
||||
]
|
||||
? NarrowByKeyValue<ObjT, Head, ValueT>
|
||||
: DeepPathT extends readonly [infer Head extends DistributedKeyOf<ObjT>, ...infer Rest]
|
||||
? NarrowByKeyValue<ObjT, Head, NarrowByDeepValue<NonNullable<ObjT[Head]>, Rest, ValueT>>
|
||||
: never;
|
||||
|
||||
|
||||
declare function doesValueAtDeepPathSatisfy<
|
||||
ObjT extends object,
|
||||
const DeepPathT extends ReadonlyArray<number | string>,
|
||||
ValueT,
|
||||
>(
|
||||
obj: ObjT,
|
||||
deepPath: DeepPathT,
|
||||
predicate: (arg: unknown) => arg is ValueT,
|
||||
): obj is NarrowByDeepValue<ObjT, DeepPathT, ValueT>;
|
||||
|
||||
|
||||
type Foo = {value: {type: 'A'}; a?: number} | {value: {type: 'B'}; b?: number};
|
||||
|
||||
declare function isA(arg: unknown): arg is 'A';
|
||||
declare function isB(arg: unknown): arg is 'B';
|
||||
|
||||
declare function assert(condition: boolean): asserts condition;
|
||||
|
||||
function test1(foo: Foo): {value: {type: 'A'}; a?: number} {
|
||||
assert(doesValueAtDeepPathSatisfy(foo, ['value', 'type'], isA));
|
||||
return foo;
|
||||
}
|
||||
|
||||
function test2(foo: Foo): {value: {type: 'A'}; a?: number} {
|
||||
assert(!doesValueAtDeepPathSatisfy(foo, ['value', 'type'], isB));
|
||||
return foo;
|
||||
}
|
||||
|
||||
// Repro from #53063
|
||||
|
||||
interface Free {
|
||||
premium: false;
|
||||
}
|
||||
|
||||
interface Premium {
|
||||
premium: true;
|
||||
}
|
||||
|
||||
type Union = { premium: false } | { premium: true };
|
||||
|
||||
declare const checkIsPremium: (a: Union) => a is Union & Premium;
|
||||
|
||||
const f = (value: Union) => {
|
||||
if (!checkIsPremium(value)) {
|
||||
value.premium;
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user