Fixed type predicate inference for discriminated union parameters (#57952)

This commit is contained in:
Mateusz Burzyński
2024-04-02 20:49:21 +02:00
committed by GitHub
parent 4cedfe40b0
commit 42a215c8fb
6 changed files with 133 additions and 3 deletions

View File

@@ -37690,8 +37690,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
{ flags: FlowFlags.Start };
const trueCondition: FlowCondition = {
flags: FlowFlags.TrueCondition,
node: expr,
antecedent,
node: expr,
};
const trueType = getFlowTypeOfReference(param.name, initType, initType, func, trueCondition);
@@ -37700,10 +37700,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// "x is T" means that x is T if and only if it returns true. If it returns false then x is not T.
// This means that if the function is called with an argument of type trueType, there can't be anything left in the `else` branch. It must reduce to `never`.
const falseCondition: FlowCondition = {
...trueCondition,
flags: FlowFlags.FalseCondition,
antecedent,
node: expr,
};
const falseSubtype = getFlowTypeOfReference(param.name, trueType, trueType, func, falseCondition);
const falseSubtype = getFlowTypeOfReference(param.name, initType, trueType, func, falseCondition);
return falseSubtype.flags & TypeFlags.Never ? trueType : undefined;
}

View File

@@ -315,4 +315,14 @@ inferTypePredicates.ts(205,7): error TS2741: Property 'z' is missing in type 'C1
function inferWithRest(x: string | null, ...f: ["a", "b"]) {
return typeof x === 'string';
}
// https://github.com/microsoft/TypeScript/issues/57947
declare const foobar:
| { type: "foo"; foo: number }
| { type: "bar"; bar: string };
const foobarPred = (fb: typeof foobar) => fb.type === "foo";
if (foobarPred(foobar)) {
foobar.foo;
}

View File

@@ -269,6 +269,16 @@ const noInferenceFromImpossibleRest = (...f: []) => typeof f === "undefined";
function inferWithRest(x: string | null, ...f: ["a", "b"]) {
return typeof x === 'string';
}
// https://github.com/microsoft/TypeScript/issues/57947
declare const foobar:
| { type: "foo"; foo: number }
| { type: "bar"; bar: string };
const foobarPred = (fb: typeof foobar) => fb.type === "foo";
if (foobarPred(foobar)) {
foobar.foo;
}
//// [inferTypePredicates.js]
@@ -524,6 +534,10 @@ function inferWithRest(x) {
}
return typeof x === 'string';
}
var foobarPred = function (fb) { return fb.type === "foo"; };
if (foobarPred(foobar)) {
foobar.foo;
}
//// [inferTypePredicates.d.ts]
@@ -605,3 +619,14 @@ declare function narrowFromAny(x: any): x is number;
declare const noInferenceFromRest: (f_0: "a" | "b") => boolean;
declare const noInferenceFromImpossibleRest: () => boolean;
declare function inferWithRest(x: string | null, ...f: ["a", "b"]): x is string;
declare const foobar: {
type: "foo";
foo: number;
} | {
type: "bar";
bar: string;
};
declare const foobarPred: (fb: typeof foobar) => fb is {
type: "foo";
foo: number;
};

View File

@@ -747,3 +747,33 @@ function inferWithRest(x: string | null, ...f: ["a", "b"]) {
>x : Symbol(x, Decl(inferTypePredicates.ts, 265, 23))
}
// https://github.com/microsoft/TypeScript/issues/57947
declare const foobar:
>foobar : Symbol(foobar, Decl(inferTypePredicates.ts, 270, 13))
| { type: "foo"; foo: number }
>type : Symbol(type, Decl(inferTypePredicates.ts, 271, 5))
>foo : Symbol(foo, Decl(inferTypePredicates.ts, 271, 18))
| { type: "bar"; bar: string };
>type : Symbol(type, Decl(inferTypePredicates.ts, 272, 5))
>bar : Symbol(bar, Decl(inferTypePredicates.ts, 272, 18))
const foobarPred = (fb: typeof foobar) => fb.type === "foo";
>foobarPred : Symbol(foobarPred, Decl(inferTypePredicates.ts, 274, 5))
>fb : Symbol(fb, Decl(inferTypePredicates.ts, 274, 20))
>foobar : Symbol(foobar, Decl(inferTypePredicates.ts, 270, 13))
>fb.type : Symbol(type, Decl(inferTypePredicates.ts, 271, 5), Decl(inferTypePredicates.ts, 272, 5))
>fb : Symbol(fb, Decl(inferTypePredicates.ts, 274, 20))
>type : Symbol(type, Decl(inferTypePredicates.ts, 271, 5), Decl(inferTypePredicates.ts, 272, 5))
if (foobarPred(foobar)) {
>foobarPred : Symbol(foobarPred, Decl(inferTypePredicates.ts, 274, 5))
>foobar : Symbol(foobar, Decl(inferTypePredicates.ts, 270, 13))
foobar.foo;
>foobar.foo : Symbol(foo, Decl(inferTypePredicates.ts, 271, 18))
>foobar : Symbol(foobar, Decl(inferTypePredicates.ts, 270, 13))
>foo : Symbol(foo, Decl(inferTypePredicates.ts, 271, 18))
}

View File

@@ -1595,3 +1595,57 @@ function inferWithRest(x: string | null, ...f: ["a", "b"]) {
> : ^^^^^^^^
}
// https://github.com/microsoft/TypeScript/issues/57947
declare const foobar:
>foobar : { type: "foo"; foo: number; } | { type: "bar"; bar: string; }
> : ^^^^^^^^ ^^^^^^^ ^^^^^^^^^^^^^^ ^^^^^^^ ^^^
| { type: "foo"; foo: number }
>type : "foo"
> : ^^^^^
>foo : number
> : ^^^^^^
| { type: "bar"; bar: string };
>type : "bar"
> : ^^^^^
>bar : string
> : ^^^^^^
const foobarPred = (fb: typeof foobar) => fb.type === "foo";
>foobarPred : (fb: typeof foobar) => fb is { type: "foo"; foo: number; }
> : ^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>(fb: typeof foobar) => fb.type === "foo" : (fb: typeof foobar) => fb is { type: "foo"; foo: number; }
> : ^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>fb : { type: "foo"; foo: number; } | { type: "bar"; bar: string; }
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>foobar : { type: "foo"; foo: number; } | { type: "bar"; bar: string; }
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>fb.type === "foo" : boolean
> : ^^^^^^^
>fb.type : "bar" | "foo"
> : ^^^^^^^^^^^^^
>fb : { type: "foo"; foo: number; } | { type: "bar"; bar: string; }
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>type : "bar" | "foo"
> : ^^^^^^^^^^^^^
>"foo" : "foo"
> : ^^^^^
if (foobarPred(foobar)) {
>foobarPred(foobar) : boolean
> : ^^^^^^^
>foobarPred : (fb: { type: "foo"; foo: number; } | { type: "bar"; bar: string; }) => fb is { type: "foo"; foo: number; }
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>foobar : { type: "foo"; foo: number; } | { type: "bar"; bar: string; }
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
foobar.foo;
>foobar.foo : number
> : ^^^^^^
>foobar : { type: "foo"; foo: number; }
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>foo : number
> : ^^^^^^
}

View File

@@ -269,3 +269,13 @@ const noInferenceFromImpossibleRest = (...f: []) => typeof f === "undefined";
function inferWithRest(x: string | null, ...f: ["a", "b"]) {
return typeof x === 'string';
}
// https://github.com/microsoft/TypeScript/issues/57947
declare const foobar:
| { type: "foo"; foo: number }
| { type: "bar"; bar: string };
const foobarPred = (fb: typeof foobar) => fb.type === "foo";
if (foobarPred(foobar)) {
foobar.foo;
}