Skip to content

Commit

Permalink
Narrow types by satisfies expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
Andarist committed Dec 17, 2024
1 parent ac2cfcc commit f468771
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 2 deletions.
6 changes: 4 additions & 2 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27181,7 +27181,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return target.kind === SyntaxKind.SuperKeyword;
case SyntaxKind.NonNullExpression:
case SyntaxKind.ParenthesizedExpression:
return isMatchingReference((source as NonNullExpression | ParenthesizedExpression).expression, target);
case SyntaxKind.SatisfiesExpression:
return isMatchingReference((source as NonNullExpression | ParenthesizedExpression | SatisfiesExpression).expression, target);
case SyntaxKind.PropertyAccessExpression:
case SyntaxKind.ElementAccessExpression:
const sourcePropertyName = getAccessedPropertyName(source as AccessExpression);
Expand Down Expand Up @@ -29532,7 +29533,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return narrowTypeByCallExpression(type, expr as CallExpression, assumeTrue);
case SyntaxKind.ParenthesizedExpression:
case SyntaxKind.NonNullExpression:
return narrowType(type, (expr as ParenthesizedExpression | NonNullExpression).expression, assumeTrue);
case SyntaxKind.SatisfiesExpression:
return narrowType(type, (expr as ParenthesizedExpression | NonNullExpression | SatisfiesExpression).expression, assumeTrue);
case SyntaxKind.BinaryExpression:
return narrowTypeByBinaryExpression(type, expr as BinaryExpression, assumeTrue);
case SyntaxKind.PrefixUnaryExpression:
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 @@ -333,4 +333,14 @@ inferTypePredicates.ts(205,7): error TS2741: Property 'z' is missing in type 'C1
if (foobarPred(foobar)) {
foobar.foo;
}

// https://github.com/microsoft/TypeScript/issues/60778
const arrTest: Array<number> = [1, 2, null, 3].filter(
(x) => (x != null) satisfies boolean,
);

function isEmptyString(x: unknown) {
const rv = x === "";
return rv satisfies boolean;
}

18 changes: 18 additions & 0 deletions tests/baselines/reference/inferTypePredicates.js
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,16 @@ const foobarPred = (fb: typeof foobar) => fb.type === "foo";
if (foobarPred(foobar)) {
foobar.foo;
}

// https://github.com/microsoft/TypeScript/issues/60778
const arrTest: Array<number> = [1, 2, null, 3].filter(
(x) => (x != null) satisfies boolean,
);

function isEmptyString(x: unknown) {
const rv = x === "";
return rv satisfies boolean;
}


//// [inferTypePredicates.js]
Expand Down Expand Up @@ -538,6 +548,12 @@ var foobarPred = function (fb) { return fb.type === "foo"; };
if (foobarPred(foobar)) {
foobar.foo;
}
// https://github.com/microsoft/TypeScript/issues/60778
var arrTest = [1, 2, null, 3].filter(function (x) { return (x != null); });
function isEmptyString(x) {
var rv = x === "";
return rv;
}


//// [inferTypePredicates.d.ts]
Expand Down Expand Up @@ -630,3 +646,5 @@ declare const foobarPred: (fb: typeof foobar) => fb is {
type: "foo";
foo: number;
};
declare const arrTest: Array<number>;
declare function isEmptyString(x: unknown): x is "";
25 changes: 25 additions & 0 deletions tests/baselines/reference/inferTypePredicates.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -777,3 +777,28 @@ if (foobarPred(foobar)) {
>foo : Symbol(foo, Decl(inferTypePredicates.ts, 271, 18))
}

// https://github.com/microsoft/TypeScript/issues/60778
const arrTest: Array<number> = [1, 2, null, 3].filter(
>arrTest : Symbol(arrTest, Decl(inferTypePredicates.ts, 280, 5))
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>[1, 2, null, 3].filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))

(x) => (x != null) satisfies boolean,
>x : Symbol(x, Decl(inferTypePredicates.ts, 281, 3))
>x : Symbol(x, Decl(inferTypePredicates.ts, 281, 3))

);

function isEmptyString(x: unknown) {
>isEmptyString : Symbol(isEmptyString, Decl(inferTypePredicates.ts, 282, 2))
>x : Symbol(x, Decl(inferTypePredicates.ts, 284, 23))

const rv = x === "";
>rv : Symbol(rv, Decl(inferTypePredicates.ts, 285, 7))
>x : Symbol(x, Decl(inferTypePredicates.ts, 284, 23))

return rv satisfies boolean;
>rv : Symbol(rv, Decl(inferTypePredicates.ts, 285, 7))
}

58 changes: 58 additions & 0 deletions tests/baselines/reference/inferTypePredicates.types
Original file line number Diff line number Diff line change
Expand Up @@ -1649,3 +1649,61 @@ if (foobarPred(foobar)) {
> : ^^^^^^
}

// https://github.com/microsoft/TypeScript/issues/60778
const arrTest: Array<number> = [1, 2, null, 3].filter(
>arrTest : number[]
> : ^^^^^^^^
>[1, 2, null, 3].filter( (x) => (x != null) satisfies boolean,) : number[]
> : ^^^^^^^^
>[1, 2, null, 3].filter : { <S extends number | null>(predicate: (value: number | null, index: number, array: (number | null)[]) => value is S, thisArg?: any): S[]; (predicate: (value: number | null, index: number, array: (number | null)[]) => unknown, thisArg?: any): (number | null)[]; }
> : ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^^ ^^^ ^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^^ ^^^^^^^^^^^^^^^^^^^^^^^
>[1, 2, null, 3] : (number | null)[]
> : ^^^^^^^^^^^^^^^^^
>1 : 1
> : ^
>2 : 2
> : ^
>3 : 3
> : ^
>filter : { <S extends number | null>(predicate: (value: number | null, index: number, array: (number | null)[]) => value is S, thisArg?: any): S[]; (predicate: (value: number | null, index: number, array: (number | null)[]) => unknown, thisArg?: any): (number | null)[]; }
> : ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^^ ^^^ ^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^^ ^^^^^^^^^^^^^^^^^^^^^^^

(x) => (x != null) satisfies boolean,
>(x) => (x != null) satisfies boolean : (x: number | null) => x is number
> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>x : number | null
> : ^^^^^^^^^^^^^
>(x != null) satisfies boolean : boolean
> : ^^^^^^^
>(x != null) : boolean
> : ^^^^^^^
>x != null : boolean
> : ^^^^^^^
>x : number | null
> : ^^^^^^^^^^^^^

);

function isEmptyString(x: unknown) {
>isEmptyString : (x: unknown) => x is ""
> : ^ ^^ ^^^^^^^^^^^^
>x : unknown
> : ^^^^^^^

const rv = x === "";
>rv : boolean
> : ^^^^^^^
>x === "" : boolean
> : ^^^^^^^
>x : unknown
> : ^^^^^^^
>"" : ""
> : ^^

return rv satisfies boolean;
>rv satisfies boolean : boolean
> : ^^^^^^^
>rv : boolean
> : ^^^^^^^
}

0 comments on commit f468771

Please sign in to comment.