Skip to content

Commit

Permalink
Fixed type predicate inference for discriminated union parameters (#5…
Browse files Browse the repository at this point in the history
  • Loading branch information
Andarist authored Apr 2, 2024
1 parent 4cedfe4 commit 42a215c
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 3 deletions.
7 changes: 4 additions & 3 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
}

Expand Down
10 changes: 10 additions & 0 deletions tests/baselines/reference/inferTypePredicates.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

25 changes: 25 additions & 0 deletions tests/baselines/reference/inferTypePredicates.js
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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;
};
30 changes: 30 additions & 0 deletions tests/baselines/reference/inferTypePredicates.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}

54 changes: 54 additions & 0 deletions tests/baselines/reference/inferTypePredicates.types
Original file line number Diff line number Diff line change
Expand Up @@ -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
> : ^^^^^^
}

10 changes: 10 additions & 0 deletions tests/cases/compiler/inferTypePredicates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

0 comments on commit 42a215c

Please sign in to comment.