mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-04 21:53:42 -06:00
Fix nullability intersections in CFA and relations (#57724)
This commit is contained in:
parent
e24d886305
commit
ef6a4ab5c7
@ -21171,7 +21171,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
if (reduced !== type) {
|
||||
return reduced;
|
||||
}
|
||||
if (type.flags & TypeFlags.Intersection && some((type as IntersectionType).types, isEmptyAnonymousObjectType)) {
|
||||
if (type.flags & TypeFlags.Intersection && shouldNormalizeIntersection(type as IntersectionType)) {
|
||||
// Normalization handles cases like
|
||||
// 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
|
||||
const normalizedTypes = sameMap(type.types, t => getNormalizedType(t, writing));
|
||||
if (normalizedTypes !== type.types) {
|
||||
return getIntersectionType(normalizedTypes);
|
||||
@ -21180,6 +21186,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return type;
|
||||
}
|
||||
|
||||
function shouldNormalizeIntersection(type: IntersectionType) {
|
||||
let hasInstantiable = false;
|
||||
let hasNullableOrEmpty = false;
|
||||
for (const t of type.types) {
|
||||
hasInstantiable ||= !!(t.flags & TypeFlags.Instantiable);
|
||||
hasNullableOrEmpty ||= !!(t.flags & TypeFlags.Nullable) || isEmptyAnonymousObjectType(t);
|
||||
if (hasInstantiable && hasNullableOrEmpty) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function getNormalizedTupleType(type: TupleTypeReference, writing: boolean): Type {
|
||||
const elements = getElementTypes(type);
|
||||
const normalizedElements = sameMap(elements, t => t.flags & TypeFlags.Simplifiable ? getSimplifiedType(t, writing) : t);
|
||||
@ -26968,9 +26985,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
if (strictNullChecks) {
|
||||
switch (facts) {
|
||||
case TypeFacts.NEUndefined:
|
||||
return mapType(reduced, t => hasTypeFacts(t, TypeFacts.EQUndefined) ? getIntersectionType([t, hasTypeFacts(t, TypeFacts.EQNull) && !maybeTypeOfKind(reduced, TypeFlags.Null) ? getUnionType([emptyObjectType, nullType]) : emptyObjectType]) : t);
|
||||
return removeNullableByIntersection(reduced, TypeFacts.EQUndefined, TypeFacts.EQNull, TypeFacts.IsNull, nullType);
|
||||
case TypeFacts.NENull:
|
||||
return mapType(reduced, t => hasTypeFacts(t, TypeFacts.EQNull) ? getIntersectionType([t, hasTypeFacts(t, TypeFacts.EQUndefined) && !maybeTypeOfKind(reduced, TypeFlags.Undefined) ? getUnionType([emptyObjectType, undefinedType]) : emptyObjectType]) : t);
|
||||
return removeNullableByIntersection(reduced, TypeFacts.EQNull, TypeFacts.EQUndefined, TypeFacts.IsUndefined, undefinedType);
|
||||
case TypeFacts.NEUndefinedOrNull:
|
||||
case TypeFacts.Truthy:
|
||||
return mapType(reduced, t => hasTypeFacts(t, TypeFacts.EQUndefinedOrNull) ? getGlobalNonNullableTypeInstantiation(t) : t);
|
||||
@ -26979,6 +26996,20 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return reduced;
|
||||
}
|
||||
|
||||
function removeNullableByIntersection(type: Type, targetFacts: TypeFacts, otherFacts: TypeFacts, otherIncludesFacts: TypeFacts, otherType: Type) {
|
||||
const facts = getTypeFacts(type, TypeFacts.EQUndefined | TypeFacts.EQNull | TypeFacts.IsUndefined | TypeFacts.IsNull);
|
||||
// Simply return the type if it never compares equal to the target nullable.
|
||||
if (!(facts & targetFacts)) {
|
||||
return type;
|
||||
}
|
||||
// By default we intersect with a union of {} and the opposite nullable.
|
||||
const emptyAndOtherUnion = getUnionType([emptyObjectType, otherType]);
|
||||
// For each constituent type that can compare equal to the target nullable, intersect with the above union
|
||||
// if the type doesn't already include the opppsite nullable and the constituent can compare equal to the
|
||||
// opposite nullable; otherwise, just intersect with {}.
|
||||
return mapType(type, t => hasTypeFacts(t, targetFacts) ? getIntersectionType([t, !(facts & otherIncludesFacts) && hasTypeFacts(t, otherFacts) ? emptyAndOtherUnion : emptyObjectType]) : t);
|
||||
}
|
||||
|
||||
function recombineUnknownType(type: Type) {
|
||||
return type === unknownUnionType ? unknownType : type;
|
||||
}
|
||||
|
||||
@ -0,0 +1,177 @@
|
||||
//// [tests/cases/compiler/indexedAccessAndNullableNarrowing.ts] ////
|
||||
|
||||
=== indexedAccessAndNullableNarrowing.ts ===
|
||||
function f1<T extends Record<string, any>, K extends keyof T>(x: T[K] | undefined) {
|
||||
>f1 : Symbol(f1, Decl(indexedAccessAndNullableNarrowing.ts, 0, 0))
|
||||
>T : Symbol(T, Decl(indexedAccessAndNullableNarrowing.ts, 0, 12))
|
||||
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
|
||||
>K : Symbol(K, Decl(indexedAccessAndNullableNarrowing.ts, 0, 42))
|
||||
>T : Symbol(T, Decl(indexedAccessAndNullableNarrowing.ts, 0, 12))
|
||||
>x : Symbol(x, Decl(indexedAccessAndNullableNarrowing.ts, 0, 62))
|
||||
>T : Symbol(T, Decl(indexedAccessAndNullableNarrowing.ts, 0, 12))
|
||||
>K : Symbol(K, Decl(indexedAccessAndNullableNarrowing.ts, 0, 42))
|
||||
|
||||
if (x === undefined) return;
|
||||
>x : Symbol(x, Decl(indexedAccessAndNullableNarrowing.ts, 0, 62))
|
||||
>undefined : Symbol(undefined)
|
||||
|
||||
x; // T[K] & ({} | null)
|
||||
>x : Symbol(x, Decl(indexedAccessAndNullableNarrowing.ts, 0, 62))
|
||||
|
||||
if (x === undefined) return;
|
||||
>x : Symbol(x, Decl(indexedAccessAndNullableNarrowing.ts, 0, 62))
|
||||
>undefined : Symbol(undefined)
|
||||
|
||||
x; // T[K] & ({} | null)
|
||||
>x : Symbol(x, Decl(indexedAccessAndNullableNarrowing.ts, 0, 62))
|
||||
}
|
||||
|
||||
function f2<T extends Record<string, any>, K extends keyof T>(x: T[K] | null) {
|
||||
>f2 : Symbol(f2, Decl(indexedAccessAndNullableNarrowing.ts, 5, 1))
|
||||
>T : Symbol(T, Decl(indexedAccessAndNullableNarrowing.ts, 7, 12))
|
||||
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
|
||||
>K : Symbol(K, Decl(indexedAccessAndNullableNarrowing.ts, 7, 42))
|
||||
>T : Symbol(T, Decl(indexedAccessAndNullableNarrowing.ts, 7, 12))
|
||||
>x : Symbol(x, Decl(indexedAccessAndNullableNarrowing.ts, 7, 62))
|
||||
>T : Symbol(T, Decl(indexedAccessAndNullableNarrowing.ts, 7, 12))
|
||||
>K : Symbol(K, Decl(indexedAccessAndNullableNarrowing.ts, 7, 42))
|
||||
|
||||
if (x === null) return;
|
||||
>x : Symbol(x, Decl(indexedAccessAndNullableNarrowing.ts, 7, 62))
|
||||
|
||||
x; // T[K] & ({} | undefined)
|
||||
>x : Symbol(x, Decl(indexedAccessAndNullableNarrowing.ts, 7, 62))
|
||||
|
||||
if (x === null) return;
|
||||
>x : Symbol(x, Decl(indexedAccessAndNullableNarrowing.ts, 7, 62))
|
||||
|
||||
x; // T[K] & ({} | undefined)
|
||||
>x : Symbol(x, Decl(indexedAccessAndNullableNarrowing.ts, 7, 62))
|
||||
}
|
||||
|
||||
function f3<T, K extends keyof T>(t: T[K], p1: Partial<T>[K] & {}, p2: Partial<T>[K] & ({} | null)) {
|
||||
>f3 : Symbol(f3, Decl(indexedAccessAndNullableNarrowing.ts, 12, 1))
|
||||
>T : Symbol(T, Decl(indexedAccessAndNullableNarrowing.ts, 14, 12))
|
||||
>K : Symbol(K, Decl(indexedAccessAndNullableNarrowing.ts, 14, 14))
|
||||
>T : Symbol(T, Decl(indexedAccessAndNullableNarrowing.ts, 14, 12))
|
||||
>t : Symbol(t, Decl(indexedAccessAndNullableNarrowing.ts, 14, 34))
|
||||
>T : Symbol(T, Decl(indexedAccessAndNullableNarrowing.ts, 14, 12))
|
||||
>K : Symbol(K, Decl(indexedAccessAndNullableNarrowing.ts, 14, 14))
|
||||
>p1 : Symbol(p1, Decl(indexedAccessAndNullableNarrowing.ts, 14, 42))
|
||||
>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --))
|
||||
>T : Symbol(T, Decl(indexedAccessAndNullableNarrowing.ts, 14, 12))
|
||||
>K : Symbol(K, Decl(indexedAccessAndNullableNarrowing.ts, 14, 14))
|
||||
>p2 : Symbol(p2, Decl(indexedAccessAndNullableNarrowing.ts, 14, 66))
|
||||
>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --))
|
||||
>T : Symbol(T, Decl(indexedAccessAndNullableNarrowing.ts, 14, 12))
|
||||
>K : Symbol(K, Decl(indexedAccessAndNullableNarrowing.ts, 14, 14))
|
||||
|
||||
t = p1;
|
||||
>t : Symbol(t, Decl(indexedAccessAndNullableNarrowing.ts, 14, 34))
|
||||
>p1 : Symbol(p1, Decl(indexedAccessAndNullableNarrowing.ts, 14, 42))
|
||||
|
||||
t = p2;
|
||||
>t : Symbol(t, Decl(indexedAccessAndNullableNarrowing.ts, 14, 34))
|
||||
>p2 : Symbol(p2, Decl(indexedAccessAndNullableNarrowing.ts, 14, 66))
|
||||
}
|
||||
|
||||
// https://github.com/microsoft/TypeScript/issues/57693
|
||||
|
||||
type AnyObject = Record<string, any>;
|
||||
>AnyObject : Symbol(AnyObject, Decl(indexedAccessAndNullableNarrowing.ts, 17, 1))
|
||||
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
|
||||
|
||||
type State = AnyObject;
|
||||
>State : Symbol(State, Decl(indexedAccessAndNullableNarrowing.ts, 21, 37))
|
||||
>AnyObject : Symbol(AnyObject, Decl(indexedAccessAndNullableNarrowing.ts, 17, 1))
|
||||
|
||||
declare function hasOwnProperty<T extends AnyObject>(
|
||||
>hasOwnProperty : Symbol(hasOwnProperty, Decl(indexedAccessAndNullableNarrowing.ts, 22, 23))
|
||||
>T : Symbol(T, Decl(indexedAccessAndNullableNarrowing.ts, 24, 32))
|
||||
>AnyObject : Symbol(AnyObject, Decl(indexedAccessAndNullableNarrowing.ts, 17, 1))
|
||||
|
||||
object: T,
|
||||
>object : Symbol(object, Decl(indexedAccessAndNullableNarrowing.ts, 24, 53))
|
||||
>T : Symbol(T, Decl(indexedAccessAndNullableNarrowing.ts, 24, 32))
|
||||
|
||||
prop: PropertyKey,
|
||||
>prop : Symbol(prop, Decl(indexedAccessAndNullableNarrowing.ts, 25, 14))
|
||||
>PropertyKey : Symbol(PropertyKey, Decl(lib.es5.d.ts, --, --))
|
||||
|
||||
): prop is keyof T;
|
||||
>prop : Symbol(prop, Decl(indexedAccessAndNullableNarrowing.ts, 25, 14))
|
||||
>T : Symbol(T, Decl(indexedAccessAndNullableNarrowing.ts, 24, 32))
|
||||
|
||||
interface Store<S = State> {
|
||||
>Store : Symbol(Store, Decl(indexedAccessAndNullableNarrowing.ts, 27, 19))
|
||||
>S : Symbol(S, Decl(indexedAccessAndNullableNarrowing.ts, 29, 16))
|
||||
>State : Symbol(State, Decl(indexedAccessAndNullableNarrowing.ts, 21, 37))
|
||||
|
||||
setState<K extends keyof S>(key: K, value: S[K]): void;
|
||||
>setState : Symbol(Store.setState, Decl(indexedAccessAndNullableNarrowing.ts, 29, 28))
|
||||
>K : Symbol(K, Decl(indexedAccessAndNullableNarrowing.ts, 30, 13))
|
||||
>S : Symbol(S, Decl(indexedAccessAndNullableNarrowing.ts, 29, 16))
|
||||
>key : Symbol(key, Decl(indexedAccessAndNullableNarrowing.ts, 30, 32))
|
||||
>K : Symbol(K, Decl(indexedAccessAndNullableNarrowing.ts, 30, 13))
|
||||
>value : Symbol(value, Decl(indexedAccessAndNullableNarrowing.ts, 30, 39))
|
||||
>S : Symbol(S, Decl(indexedAccessAndNullableNarrowing.ts, 29, 16))
|
||||
>K : Symbol(K, Decl(indexedAccessAndNullableNarrowing.ts, 30, 13))
|
||||
}
|
||||
|
||||
export function syncStoreProp<
|
||||
>syncStoreProp : Symbol(syncStoreProp, Decl(indexedAccessAndNullableNarrowing.ts, 31, 1))
|
||||
|
||||
S extends State,
|
||||
>S : Symbol(S, Decl(indexedAccessAndNullableNarrowing.ts, 33, 30))
|
||||
>State : Symbol(State, Decl(indexedAccessAndNullableNarrowing.ts, 21, 37))
|
||||
|
||||
P extends Partial<S>,
|
||||
>P : Symbol(P, Decl(indexedAccessAndNullableNarrowing.ts, 34, 20))
|
||||
>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --))
|
||||
>S : Symbol(S, Decl(indexedAccessAndNullableNarrowing.ts, 33, 30))
|
||||
|
||||
K extends keyof S,
|
||||
>K : Symbol(K, Decl(indexedAccessAndNullableNarrowing.ts, 35, 25))
|
||||
>S : Symbol(S, Decl(indexedAccessAndNullableNarrowing.ts, 33, 30))
|
||||
|
||||
>(store: Store<S>, props: P, key: K) {
|
||||
>store : Symbol(store, Decl(indexedAccessAndNullableNarrowing.ts, 37, 2))
|
||||
>Store : Symbol(Store, Decl(indexedAccessAndNullableNarrowing.ts, 27, 19))
|
||||
>S : Symbol(S, Decl(indexedAccessAndNullableNarrowing.ts, 33, 30))
|
||||
>props : Symbol(props, Decl(indexedAccessAndNullableNarrowing.ts, 37, 18))
|
||||
>P : Symbol(P, Decl(indexedAccessAndNullableNarrowing.ts, 34, 20))
|
||||
>key : Symbol(key, Decl(indexedAccessAndNullableNarrowing.ts, 37, 28))
|
||||
>K : Symbol(K, Decl(indexedAccessAndNullableNarrowing.ts, 35, 25))
|
||||
|
||||
const value = hasOwnProperty(props, key) ? props[key] : undefined;
|
||||
>value : Symbol(value, Decl(indexedAccessAndNullableNarrowing.ts, 38, 9))
|
||||
>hasOwnProperty : Symbol(hasOwnProperty, Decl(indexedAccessAndNullableNarrowing.ts, 22, 23))
|
||||
>props : Symbol(props, Decl(indexedAccessAndNullableNarrowing.ts, 37, 18))
|
||||
>key : Symbol(key, Decl(indexedAccessAndNullableNarrowing.ts, 37, 28))
|
||||
>props : Symbol(props, Decl(indexedAccessAndNullableNarrowing.ts, 37, 18))
|
||||
>key : Symbol(key, Decl(indexedAccessAndNullableNarrowing.ts, 37, 28))
|
||||
>undefined : Symbol(undefined)
|
||||
|
||||
if (value === undefined) return;
|
||||
>value : Symbol(value, Decl(indexedAccessAndNullableNarrowing.ts, 38, 9))
|
||||
>undefined : Symbol(undefined)
|
||||
|
||||
store.setState(key, value);
|
||||
>store.setState : Symbol(Store.setState, Decl(indexedAccessAndNullableNarrowing.ts, 29, 28))
|
||||
>store : Symbol(store, Decl(indexedAccessAndNullableNarrowing.ts, 37, 2))
|
||||
>setState : Symbol(Store.setState, Decl(indexedAccessAndNullableNarrowing.ts, 29, 28))
|
||||
>key : Symbol(key, Decl(indexedAccessAndNullableNarrowing.ts, 37, 28))
|
||||
>value : Symbol(value, Decl(indexedAccessAndNullableNarrowing.ts, 38, 9))
|
||||
|
||||
if (value === undefined) return;
|
||||
>value : Symbol(value, Decl(indexedAccessAndNullableNarrowing.ts, 38, 9))
|
||||
>undefined : Symbol(undefined)
|
||||
|
||||
store.setState(key, value);
|
||||
>store.setState : Symbol(Store.setState, Decl(indexedAccessAndNullableNarrowing.ts, 29, 28))
|
||||
>store : Symbol(store, Decl(indexedAccessAndNullableNarrowing.ts, 37, 2))
|
||||
>setState : Symbol(Store.setState, Decl(indexedAccessAndNullableNarrowing.ts, 29, 28))
|
||||
>key : Symbol(key, Decl(indexedAccessAndNullableNarrowing.ts, 37, 28))
|
||||
>value : Symbol(value, Decl(indexedAccessAndNullableNarrowing.ts, 38, 9))
|
||||
}
|
||||
|
||||
@ -0,0 +1,136 @@
|
||||
//// [tests/cases/compiler/indexedAccessAndNullableNarrowing.ts] ////
|
||||
|
||||
=== indexedAccessAndNullableNarrowing.ts ===
|
||||
function f1<T extends Record<string, any>, K extends keyof T>(x: T[K] | undefined) {
|
||||
>f1 : <T extends Record<string, any>, K extends keyof T>(x: T[K] | undefined) => void
|
||||
>x : T[K] | undefined
|
||||
|
||||
if (x === undefined) return;
|
||||
>x === undefined : boolean
|
||||
>x : T[K] | undefined
|
||||
>undefined : undefined
|
||||
|
||||
x; // T[K] & ({} | null)
|
||||
>x : T[K] & ({} | null)
|
||||
|
||||
if (x === undefined) return;
|
||||
>x === undefined : boolean
|
||||
>x : T[K] & ({} | null)
|
||||
>undefined : undefined
|
||||
|
||||
x; // T[K] & ({} | null)
|
||||
>x : T[K] & ({} | null)
|
||||
}
|
||||
|
||||
function f2<T extends Record<string, any>, K extends keyof T>(x: T[K] | null) {
|
||||
>f2 : <T extends Record<string, any>, K extends keyof T>(x: T[K] | null) => void
|
||||
>x : T[K] | null
|
||||
|
||||
if (x === null) return;
|
||||
>x === null : boolean
|
||||
>x : T[K] | null
|
||||
|
||||
x; // T[K] & ({} | undefined)
|
||||
>x : T[K] & ({} | undefined)
|
||||
|
||||
if (x === null) return;
|
||||
>x === null : boolean
|
||||
>x : T[K] & ({} | undefined)
|
||||
|
||||
x; // T[K] & ({} | undefined)
|
||||
>x : T[K] & ({} | undefined)
|
||||
}
|
||||
|
||||
function f3<T, K extends keyof T>(t: T[K], p1: Partial<T>[K] & {}, p2: Partial<T>[K] & ({} | null)) {
|
||||
>f3 : <T, K extends keyof T>(t: T[K], p1: Partial<T>[K] & {}, p2: Partial<T>[K] & ({} | null)) => void
|
||||
>t : T[K]
|
||||
>p1 : Partial<T>[K] & {}
|
||||
>p2 : Partial<T>[K] & ({} | null)
|
||||
|
||||
t = p1;
|
||||
>t = p1 : Partial<T>[K] & {}
|
||||
>t : T[K]
|
||||
>p1 : Partial<T>[K] & {}
|
||||
|
||||
t = p2;
|
||||
>t = p2 : Partial<T>[K] & ({} | null)
|
||||
>t : T[K]
|
||||
>p2 : Partial<T>[K] & ({} | null)
|
||||
}
|
||||
|
||||
// https://github.com/microsoft/TypeScript/issues/57693
|
||||
|
||||
type AnyObject = Record<string, any>;
|
||||
>AnyObject : { [x: string]: any; }
|
||||
|
||||
type State = AnyObject;
|
||||
>State : AnyObject
|
||||
|
||||
declare function hasOwnProperty<T extends AnyObject>(
|
||||
>hasOwnProperty : <T extends AnyObject>(object: T, prop: PropertyKey) => prop is keyof T
|
||||
|
||||
object: T,
|
||||
>object : T
|
||||
|
||||
prop: PropertyKey,
|
||||
>prop : PropertyKey
|
||||
|
||||
): prop is keyof T;
|
||||
|
||||
interface Store<S = State> {
|
||||
setState<K extends keyof S>(key: K, value: S[K]): void;
|
||||
>setState : <K extends keyof S>(key: K, value: S[K]) => void
|
||||
>key : K
|
||||
>value : S[K]
|
||||
}
|
||||
|
||||
export function syncStoreProp<
|
||||
>syncStoreProp : <S extends AnyObject, P extends Partial<S>, K extends keyof S>(store: Store<S>, props: P, key: K) => void
|
||||
|
||||
S extends State,
|
||||
P extends Partial<S>,
|
||||
K extends keyof S,
|
||||
>(store: Store<S>, props: P, key: K) {
|
||||
>store : Store<S>
|
||||
>props : P
|
||||
>key : K
|
||||
|
||||
const value = hasOwnProperty(props, key) ? props[key] : undefined;
|
||||
>value : P[K] | undefined
|
||||
>hasOwnProperty(props, key) ? props[key] : undefined : P[K] | undefined
|
||||
>hasOwnProperty(props, key) : boolean
|
||||
>hasOwnProperty : <T extends AnyObject>(object: T, prop: PropertyKey) => prop is keyof T
|
||||
>props : P
|
||||
>key : string | number | symbol
|
||||
>props[key] : P[K]
|
||||
>props : P
|
||||
>key : K
|
||||
>undefined : undefined
|
||||
|
||||
if (value === undefined) return;
|
||||
>value === undefined : boolean
|
||||
>value : P[K] | undefined
|
||||
>undefined : undefined
|
||||
|
||||
store.setState(key, value);
|
||||
>store.setState(key, value) : void
|
||||
>store.setState : <K_1 extends keyof S>(key: K_1, value: S[K_1]) => void
|
||||
>store : Store<S>
|
||||
>setState : <K_1 extends keyof S>(key: K_1, value: S[K_1]) => void
|
||||
>key : K
|
||||
>value : P[K] & ({} | null)
|
||||
|
||||
if (value === undefined) return;
|
||||
>value === undefined : boolean
|
||||
>value : P[K] & ({} | null)
|
||||
>undefined : undefined
|
||||
|
||||
store.setState(key, value);
|
||||
>store.setState(key, value) : void
|
||||
>store.setState : <K_1 extends keyof S>(key: K_1, value: S[K_1]) => void
|
||||
>store : Store<S>
|
||||
>setState : <K_1 extends keyof S>(key: K_1, value: S[K_1]) => void
|
||||
>key : K
|
||||
>value : P[K] & ({} | null)
|
||||
}
|
||||
|
||||
47
tests/cases/compiler/indexedAccessAndNullableNarrowing.ts
Normal file
47
tests/cases/compiler/indexedAccessAndNullableNarrowing.ts
Normal file
@ -0,0 +1,47 @@
|
||||
// @strict: true
|
||||
// @noEmit: true
|
||||
|
||||
function f1<T extends Record<string, any>, K extends keyof T>(x: T[K] | undefined) {
|
||||
if (x === undefined) return;
|
||||
x; // T[K] & ({} | null)
|
||||
if (x === undefined) return;
|
||||
x; // T[K] & ({} | null)
|
||||
}
|
||||
|
||||
function f2<T extends Record<string, any>, K extends keyof T>(x: T[K] | null) {
|
||||
if (x === null) return;
|
||||
x; // T[K] & ({} | undefined)
|
||||
if (x === null) return;
|
||||
x; // T[K] & ({} | undefined)
|
||||
}
|
||||
|
||||
function f3<T, K extends keyof T>(t: T[K], p1: Partial<T>[K] & {}, p2: Partial<T>[K] & ({} | null)) {
|
||||
t = p1;
|
||||
t = p2;
|
||||
}
|
||||
|
||||
// https://github.com/microsoft/TypeScript/issues/57693
|
||||
|
||||
type AnyObject = Record<string, any>;
|
||||
type State = AnyObject;
|
||||
|
||||
declare function hasOwnProperty<T extends AnyObject>(
|
||||
object: T,
|
||||
prop: PropertyKey,
|
||||
): prop is keyof T;
|
||||
|
||||
interface Store<S = State> {
|
||||
setState<K extends keyof S>(key: K, value: S[K]): void;
|
||||
}
|
||||
|
||||
export function syncStoreProp<
|
||||
S extends State,
|
||||
P extends Partial<S>,
|
||||
K extends keyof S,
|
||||
>(store: Store<S>, props: P, key: K) {
|
||||
const value = hasOwnProperty(props, key) ? props[key] : undefined;
|
||||
if (value === undefined) return;
|
||||
store.setState(key, value);
|
||||
if (value === undefined) return;
|
||||
store.setState(key, value);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user