mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-04 12:32:08 -06:00
Fix discriminant property check in CFA (#56156)
This commit is contained in:
parent
3c221fc086
commit
ac75a5ec9a
@ -27677,13 +27677,18 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
|
||||
function getDiscriminantPropertyAccess(expr: Expression, computedType: Type) {
|
||||
const type = !(computedType.flags & TypeFlags.Union) && declaredType.flags & TypeFlags.Union ? declaredType : computedType;
|
||||
if (type.flags & TypeFlags.Union) {
|
||||
// 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);
|
||||
if (access) {
|
||||
const name = getAccessedPropertyName(access);
|
||||
if (name && isDiscriminantProperty(type, name)) {
|
||||
return access;
|
||||
if (name) {
|
||||
const type = declaredType.flags & TypeFlags.Union && isTypeSubsetOf(computedType, declaredType) ? declaredType : computedType;
|
||||
if (isDiscriminantProperty(type, name)) {
|
||||
return access;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -226,6 +226,43 @@ declare const workingAgain: Record<string, any> | undefined | unknown;
|
||||
isMyDiscriminatedUnion(working) && working.type === 'A' && working.aProp;
|
||||
isMyDiscriminatedUnion(broken) && broken.type === 'A' && broken.aProp;
|
||||
isMyDiscriminatedUnion(workingAgain) && workingAgain.type === 'A' && workingAgain.aProp;
|
||||
|
||||
// Repro from #56144
|
||||
|
||||
type Union =
|
||||
| { type: 'a'; variant: 1 }
|
||||
| { type: 'a'; variant: 2 }
|
||||
| { type: 'b' };
|
||||
|
||||
function example1(value: Union): { type: 'a'; variant: 2 } | null {
|
||||
if (value.type !== 'a') {
|
||||
return null;
|
||||
}
|
||||
if (value.variant === 1) {
|
||||
return null;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function example2(value: Union): { type: 'a'; variant: 2 } | null {
|
||||
if (value.type !== 'a') {
|
||||
return null;
|
||||
}
|
||||
if (value.type === 'a' && value.variant === 1) {
|
||||
return null;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function example3(value: Union): { type: 'a'; variant: 2 } | null {
|
||||
if (value.type !== 'a') {
|
||||
return null;
|
||||
}
|
||||
if (value.type && value.variant === 1) {
|
||||
return null;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
//// [narrowingUnionToUnion.js]
|
||||
@ -387,6 +424,33 @@ function f1x(obj) {
|
||||
isMyDiscriminatedUnion(working) && working.type === 'A' && working.aProp;
|
||||
isMyDiscriminatedUnion(broken) && broken.type === 'A' && broken.aProp;
|
||||
isMyDiscriminatedUnion(workingAgain) && workingAgain.type === 'A' && workingAgain.aProp;
|
||||
function example1(value) {
|
||||
if (value.type !== 'a') {
|
||||
return null;
|
||||
}
|
||||
if (value.variant === 1) {
|
||||
return null;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
function example2(value) {
|
||||
if (value.type !== 'a') {
|
||||
return null;
|
||||
}
|
||||
if (value.type === 'a' && value.variant === 1) {
|
||||
return null;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
function example3(value) {
|
||||
if (value.type !== 'a') {
|
||||
return null;
|
||||
}
|
||||
if (value.type && value.variant === 1) {
|
||||
return null;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
//// [narrowingUnionToUnion.d.ts]
|
||||
@ -451,3 +515,24 @@ declare function isMyDiscriminatedUnion(item: unknown): item is MyDiscriminatedU
|
||||
declare const working: unknown;
|
||||
declare const broken: Record<string, any> | undefined;
|
||||
declare const workingAgain: Record<string, any> | undefined | unknown;
|
||||
type Union = {
|
||||
type: 'a';
|
||||
variant: 1;
|
||||
} | {
|
||||
type: 'a';
|
||||
variant: 2;
|
||||
} | {
|
||||
type: 'b';
|
||||
};
|
||||
declare function example1(value: Union): {
|
||||
type: 'a';
|
||||
variant: 2;
|
||||
} | null;
|
||||
declare function example2(value: Union): {
|
||||
type: 'a';
|
||||
variant: 2;
|
||||
} | null;
|
||||
declare function example3(value: Union): {
|
||||
type: 'a';
|
||||
variant: 2;
|
||||
} | null;
|
||||
|
||||
@ -538,3 +538,100 @@ isMyDiscriminatedUnion(workingAgain) && workingAgain.type === 'A' && workingAgai
|
||||
>workingAgain : Symbol(workingAgain, Decl(narrowingUnionToUnion.ts, 220, 13))
|
||||
>aProp : Symbol(aProp, Decl(narrowingUnionToUnion.ts, 214, 40))
|
||||
|
||||
// Repro from #56144
|
||||
|
||||
type Union =
|
||||
>Union : Symbol(Union, Decl(narrowingUnionToUnion.ts, 224, 88))
|
||||
|
||||
| { type: 'a'; variant: 1 }
|
||||
>type : Symbol(type, Decl(narrowingUnionToUnion.ts, 229, 7))
|
||||
>variant : Symbol(variant, Decl(narrowingUnionToUnion.ts, 229, 18))
|
||||
|
||||
| { type: 'a'; variant: 2 }
|
||||
>type : Symbol(type, Decl(narrowingUnionToUnion.ts, 230, 7))
|
||||
>variant : Symbol(variant, Decl(narrowingUnionToUnion.ts, 230, 18))
|
||||
|
||||
| { type: 'b' };
|
||||
>type : Symbol(type, Decl(narrowingUnionToUnion.ts, 231, 7))
|
||||
|
||||
function example1(value: Union): { type: 'a'; variant: 2 } | null {
|
||||
>example1 : Symbol(example1, Decl(narrowingUnionToUnion.ts, 231, 20))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 233, 18))
|
||||
>Union : Symbol(Union, Decl(narrowingUnionToUnion.ts, 224, 88))
|
||||
>type : Symbol(type, Decl(narrowingUnionToUnion.ts, 233, 34))
|
||||
>variant : Symbol(variant, Decl(narrowingUnionToUnion.ts, 233, 45))
|
||||
|
||||
if (value.type !== 'a') {
|
||||
>value.type : Symbol(type, Decl(narrowingUnionToUnion.ts, 229, 7), Decl(narrowingUnionToUnion.ts, 230, 7), Decl(narrowingUnionToUnion.ts, 231, 7))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 233, 18))
|
||||
>type : Symbol(type, Decl(narrowingUnionToUnion.ts, 229, 7), Decl(narrowingUnionToUnion.ts, 230, 7), Decl(narrowingUnionToUnion.ts, 231, 7))
|
||||
|
||||
return null;
|
||||
}
|
||||
if (value.variant === 1) {
|
||||
>value.variant : Symbol(variant, Decl(narrowingUnionToUnion.ts, 229, 18), Decl(narrowingUnionToUnion.ts, 230, 18))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 233, 18))
|
||||
>variant : Symbol(variant, Decl(narrowingUnionToUnion.ts, 229, 18), Decl(narrowingUnionToUnion.ts, 230, 18))
|
||||
|
||||
return null;
|
||||
}
|
||||
return value;
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 233, 18))
|
||||
}
|
||||
|
||||
function example2(value: Union): { type: 'a'; variant: 2 } | null {
|
||||
>example2 : Symbol(example2, Decl(narrowingUnionToUnion.ts, 241, 1))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 243, 18))
|
||||
>Union : Symbol(Union, Decl(narrowingUnionToUnion.ts, 224, 88))
|
||||
>type : Symbol(type, Decl(narrowingUnionToUnion.ts, 243, 34))
|
||||
>variant : Symbol(variant, Decl(narrowingUnionToUnion.ts, 243, 45))
|
||||
|
||||
if (value.type !== 'a') {
|
||||
>value.type : Symbol(type, Decl(narrowingUnionToUnion.ts, 229, 7), Decl(narrowingUnionToUnion.ts, 230, 7), Decl(narrowingUnionToUnion.ts, 231, 7))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 243, 18))
|
||||
>type : Symbol(type, Decl(narrowingUnionToUnion.ts, 229, 7), Decl(narrowingUnionToUnion.ts, 230, 7), Decl(narrowingUnionToUnion.ts, 231, 7))
|
||||
|
||||
return null;
|
||||
}
|
||||
if (value.type === 'a' && value.variant === 1) {
|
||||
>value.type : Symbol(type, Decl(narrowingUnionToUnion.ts, 229, 7), Decl(narrowingUnionToUnion.ts, 230, 7))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 243, 18))
|
||||
>type : Symbol(type, Decl(narrowingUnionToUnion.ts, 229, 7), Decl(narrowingUnionToUnion.ts, 230, 7))
|
||||
>value.variant : Symbol(variant, Decl(narrowingUnionToUnion.ts, 229, 18), Decl(narrowingUnionToUnion.ts, 230, 18))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 243, 18))
|
||||
>variant : Symbol(variant, Decl(narrowingUnionToUnion.ts, 229, 18), Decl(narrowingUnionToUnion.ts, 230, 18))
|
||||
|
||||
return null;
|
||||
}
|
||||
return value;
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 243, 18))
|
||||
}
|
||||
|
||||
function example3(value: Union): { type: 'a'; variant: 2 } | null {
|
||||
>example3 : Symbol(example3, Decl(narrowingUnionToUnion.ts, 251, 1))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 253, 18))
|
||||
>Union : Symbol(Union, Decl(narrowingUnionToUnion.ts, 224, 88))
|
||||
>type : Symbol(type, Decl(narrowingUnionToUnion.ts, 253, 34))
|
||||
>variant : Symbol(variant, Decl(narrowingUnionToUnion.ts, 253, 45))
|
||||
|
||||
if (value.type !== 'a') {
|
||||
>value.type : Symbol(type, Decl(narrowingUnionToUnion.ts, 229, 7), Decl(narrowingUnionToUnion.ts, 230, 7), Decl(narrowingUnionToUnion.ts, 231, 7))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 253, 18))
|
||||
>type : Symbol(type, Decl(narrowingUnionToUnion.ts, 229, 7), Decl(narrowingUnionToUnion.ts, 230, 7), Decl(narrowingUnionToUnion.ts, 231, 7))
|
||||
|
||||
return null;
|
||||
}
|
||||
if (value.type && value.variant === 1) {
|
||||
>value.type : Symbol(type, Decl(narrowingUnionToUnion.ts, 229, 7), Decl(narrowingUnionToUnion.ts, 230, 7))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 253, 18))
|
||||
>type : Symbol(type, Decl(narrowingUnionToUnion.ts, 229, 7), Decl(narrowingUnionToUnion.ts, 230, 7))
|
||||
>value.variant : Symbol(variant, Decl(narrowingUnionToUnion.ts, 229, 18), Decl(narrowingUnionToUnion.ts, 230, 18))
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 253, 18))
|
||||
>variant : Symbol(variant, Decl(narrowingUnionToUnion.ts, 229, 18), Decl(narrowingUnionToUnion.ts, 230, 18))
|
||||
|
||||
return null;
|
||||
}
|
||||
return value;
|
||||
>value : Symbol(value, Decl(narrowingUnionToUnion.ts, 253, 18))
|
||||
}
|
||||
|
||||
|
||||
@ -574,3 +574,113 @@ isMyDiscriminatedUnion(workingAgain) && workingAgain.type === 'A' && workingAgai
|
||||
>workingAgain : { type: "A"; aProp: number; }
|
||||
>aProp : number
|
||||
|
||||
// Repro from #56144
|
||||
|
||||
type Union =
|
||||
>Union : { type: 'a'; variant: 1; } | { type: 'a'; variant: 2; } | { type: 'b'; }
|
||||
|
||||
| { type: 'a'; variant: 1 }
|
||||
>type : "a"
|
||||
>variant : 1
|
||||
|
||||
| { type: 'a'; variant: 2 }
|
||||
>type : "a"
|
||||
>variant : 2
|
||||
|
||||
| { type: 'b' };
|
||||
>type : "b"
|
||||
|
||||
function example1(value: Union): { type: 'a'; variant: 2 } | null {
|
||||
>example1 : (value: Union) => { type: 'a'; variant: 2;} | null
|
||||
>value : Union
|
||||
>type : "a"
|
||||
>variant : 2
|
||||
|
||||
if (value.type !== 'a') {
|
||||
>value.type !== 'a' : boolean
|
||||
>value.type : "a" | "b"
|
||||
>value : Union
|
||||
>type : "a" | "b"
|
||||
>'a' : "a"
|
||||
|
||||
return null;
|
||||
}
|
||||
if (value.variant === 1) {
|
||||
>value.variant === 1 : boolean
|
||||
>value.variant : 1 | 2
|
||||
>value : { type: "a"; variant: 1; } | { type: "a"; variant: 2; }
|
||||
>variant : 1 | 2
|
||||
>1 : 1
|
||||
|
||||
return null;
|
||||
}
|
||||
return value;
|
||||
>value : { type: "a"; variant: 2; }
|
||||
}
|
||||
|
||||
function example2(value: Union): { type: 'a'; variant: 2 } | null {
|
||||
>example2 : (value: Union) => { type: 'a'; variant: 2;} | null
|
||||
>value : Union
|
||||
>type : "a"
|
||||
>variant : 2
|
||||
|
||||
if (value.type !== 'a') {
|
||||
>value.type !== 'a' : boolean
|
||||
>value.type : "a" | "b"
|
||||
>value : Union
|
||||
>type : "a" | "b"
|
||||
>'a' : "a"
|
||||
|
||||
return null;
|
||||
}
|
||||
if (value.type === 'a' && value.variant === 1) {
|
||||
>value.type === 'a' && value.variant === 1 : boolean
|
||||
>value.type === 'a' : boolean
|
||||
>value.type : "a"
|
||||
>value : { type: "a"; variant: 1; } | { type: "a"; variant: 2; }
|
||||
>type : "a"
|
||||
>'a' : "a"
|
||||
>value.variant === 1 : boolean
|
||||
>value.variant : 1 | 2
|
||||
>value : { type: "a"; variant: 1; } | { type: "a"; variant: 2; }
|
||||
>variant : 1 | 2
|
||||
>1 : 1
|
||||
|
||||
return null;
|
||||
}
|
||||
return value;
|
||||
>value : { type: "a"; variant: 2; }
|
||||
}
|
||||
|
||||
function example3(value: Union): { type: 'a'; variant: 2 } | null {
|
||||
>example3 : (value: Union) => { type: 'a'; variant: 2;} | null
|
||||
>value : Union
|
||||
>type : "a"
|
||||
>variant : 2
|
||||
|
||||
if (value.type !== 'a') {
|
||||
>value.type !== 'a' : boolean
|
||||
>value.type : "a" | "b"
|
||||
>value : Union
|
||||
>type : "a" | "b"
|
||||
>'a' : "a"
|
||||
|
||||
return null;
|
||||
}
|
||||
if (value.type && value.variant === 1) {
|
||||
>value.type && value.variant === 1 : boolean
|
||||
>value.type : "a"
|
||||
>value : { type: "a"; variant: 1; } | { type: "a"; variant: 2; }
|
||||
>type : "a"
|
||||
>value.variant === 1 : boolean
|
||||
>value.variant : 1 | 2
|
||||
>value : { type: "a"; variant: 1; } | { type: "a"; variant: 2; }
|
||||
>variant : 1 | 2
|
||||
>1 : 1
|
||||
|
||||
return null;
|
||||
}
|
||||
return value;
|
||||
>value : { type: "a"; variant: 2; }
|
||||
}
|
||||
|
||||
|
||||
@ -226,3 +226,40 @@ declare const workingAgain: Record<string, any> | undefined | unknown;
|
||||
isMyDiscriminatedUnion(working) && working.type === 'A' && working.aProp;
|
||||
isMyDiscriminatedUnion(broken) && broken.type === 'A' && broken.aProp;
|
||||
isMyDiscriminatedUnion(workingAgain) && workingAgain.type === 'A' && workingAgain.aProp;
|
||||
|
||||
// Repro from #56144
|
||||
|
||||
type Union =
|
||||
| { type: 'a'; variant: 1 }
|
||||
| { type: 'a'; variant: 2 }
|
||||
| { type: 'b' };
|
||||
|
||||
function example1(value: Union): { type: 'a'; variant: 2 } | null {
|
||||
if (value.type !== 'a') {
|
||||
return null;
|
||||
}
|
||||
if (value.variant === 1) {
|
||||
return null;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function example2(value: Union): { type: 'a'; variant: 2 } | null {
|
||||
if (value.type !== 'a') {
|
||||
return null;
|
||||
}
|
||||
if (value.type === 'a' && value.variant === 1) {
|
||||
return null;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function example3(value: Union): { type: 'a'; variant: 2 } | null {
|
||||
if (value.type !== 'a') {
|
||||
return null;
|
||||
}
|
||||
if (value.type && value.variant === 1) {
|
||||
return null;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user