Skip to content

Commit efe07a0

Browse files
authored
Narrow types by satisfies expressions (#60782)
1 parent 7f802bb commit efe07a0

File tree

6 files changed

+125
-2
lines changed

6 files changed

+125
-2
lines changed

src/compiler/checker.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -27185,7 +27185,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2718527185
return target.kind === SyntaxKind.SuperKeyword;
2718627186
case SyntaxKind.NonNullExpression:
2718727187
case SyntaxKind.ParenthesizedExpression:
27188-
return isMatchingReference((source as NonNullExpression | ParenthesizedExpression).expression, target);
27188+
case SyntaxKind.SatisfiesExpression:
27189+
return isMatchingReference((source as NonNullExpression | ParenthesizedExpression | SatisfiesExpression).expression, target);
2718927190
case SyntaxKind.PropertyAccessExpression:
2719027191
case SyntaxKind.ElementAccessExpression:
2719127192
const sourcePropertyName = getAccessedPropertyName(source as AccessExpression);
@@ -29537,7 +29538,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2953729538
return narrowTypeByCallExpression(type, expr as CallExpression, assumeTrue);
2953829539
case SyntaxKind.ParenthesizedExpression:
2953929540
case SyntaxKind.NonNullExpression:
29540-
return narrowType(type, (expr as ParenthesizedExpression | NonNullExpression).expression, assumeTrue);
29541+
case SyntaxKind.SatisfiesExpression:
29542+
return narrowType(type, (expr as ParenthesizedExpression | NonNullExpression | SatisfiesExpression).expression, assumeTrue);
2954129543
case SyntaxKind.BinaryExpression:
2954229544
return narrowTypeByBinaryExpression(type, expr as BinaryExpression, assumeTrue);
2954329545
case SyntaxKind.PrefixUnaryExpression:

tests/baselines/reference/inferTypePredicates.errors.txt

+10
Original file line numberDiff line numberDiff line change
@@ -333,4 +333,14 @@ inferTypePredicates.ts(205,7): error TS2741: Property 'z' is missing in type 'C1
333333
if (foobarPred(foobar)) {
334334
foobar.foo;
335335
}
336+
337+
// https://github.com/microsoft/TypeScript/issues/60778
338+
const arrTest: Array<number> = [1, 2, null, 3].filter(
339+
(x) => (x != null) satisfies boolean,
340+
);
341+
342+
function isEmptyString(x: unknown) {
343+
const rv = x === "";
344+
return rv satisfies boolean;
345+
}
336346

tests/baselines/reference/inferTypePredicates.js

+18
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,16 @@ const foobarPred = (fb: typeof foobar) => fb.type === "foo";
279279
if (foobarPred(foobar)) {
280280
foobar.foo;
281281
}
282+
283+
// https://github.com/microsoft/TypeScript/issues/60778
284+
const arrTest: Array<number> = [1, 2, null, 3].filter(
285+
(x) => (x != null) satisfies boolean,
286+
);
287+
288+
function isEmptyString(x: unknown) {
289+
const rv = x === "";
290+
return rv satisfies boolean;
291+
}
282292

283293

284294
//// [inferTypePredicates.js]
@@ -538,6 +548,12 @@ var foobarPred = function (fb) { return fb.type === "foo"; };
538548
if (foobarPred(foobar)) {
539549
foobar.foo;
540550
}
551+
// https://github.com/microsoft/TypeScript/issues/60778
552+
var arrTest = [1, 2, null, 3].filter(function (x) { return (x != null); });
553+
function isEmptyString(x) {
554+
var rv = x === "";
555+
return rv;
556+
}
541557

542558

543559
//// [inferTypePredicates.d.ts]
@@ -630,3 +646,5 @@ declare const foobarPred: (fb: typeof foobar) => fb is {
630646
type: "foo";
631647
foo: number;
632648
};
649+
declare const arrTest: Array<number>;
650+
declare function isEmptyString(x: unknown): x is "";

tests/baselines/reference/inferTypePredicates.symbols

+25
Original file line numberDiff line numberDiff line change
@@ -777,3 +777,28 @@ if (foobarPred(foobar)) {
777777
>foo : Symbol(foo, Decl(inferTypePredicates.ts, 271, 18))
778778
}
779779

780+
// https://github.com/microsoft/TypeScript/issues/60778
781+
const arrTest: Array<number> = [1, 2, null, 3].filter(
782+
>arrTest : Symbol(arrTest, Decl(inferTypePredicates.ts, 280, 5))
783+
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
784+
>[1, 2, null, 3].filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
785+
>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
786+
787+
(x) => (x != null) satisfies boolean,
788+
>x : Symbol(x, Decl(inferTypePredicates.ts, 281, 3))
789+
>x : Symbol(x, Decl(inferTypePredicates.ts, 281, 3))
790+
791+
);
792+
793+
function isEmptyString(x: unknown) {
794+
>isEmptyString : Symbol(isEmptyString, Decl(inferTypePredicates.ts, 282, 2))
795+
>x : Symbol(x, Decl(inferTypePredicates.ts, 284, 23))
796+
797+
const rv = x === "";
798+
>rv : Symbol(rv, Decl(inferTypePredicates.ts, 285, 7))
799+
>x : Symbol(x, Decl(inferTypePredicates.ts, 284, 23))
800+
801+
return rv satisfies boolean;
802+
>rv : Symbol(rv, Decl(inferTypePredicates.ts, 285, 7))
803+
}
804+

tests/baselines/reference/inferTypePredicates.types

+58
Original file line numberDiff line numberDiff line change
@@ -1649,3 +1649,61 @@ if (foobarPred(foobar)) {
16491649
> : ^^^^^^
16501650
}
16511651

1652+
// https://github.com/microsoft/TypeScript/issues/60778
1653+
const arrTest: Array<number> = [1, 2, null, 3].filter(
1654+
>arrTest : number[]
1655+
> : ^^^^^^^^
1656+
>[1, 2, null, 3].filter( (x) => (x != null) satisfies boolean,) : number[]
1657+
> : ^^^^^^^^
1658+
>[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)[]; }
1659+
> : ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^^ ^^^ ^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^^ ^^^^^^^^^^^^^^^^^^^^^^^
1660+
>[1, 2, null, 3] : (number | null)[]
1661+
> : ^^^^^^^^^^^^^^^^^
1662+
>1 : 1
1663+
> : ^
1664+
>2 : 2
1665+
> : ^
1666+
>3 : 3
1667+
> : ^
1668+
>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)[]; }
1669+
> : ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^^ ^^^ ^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^^ ^^^^^^^^^^^^^^^^^^^^^^^
1670+
1671+
(x) => (x != null) satisfies boolean,
1672+
>(x) => (x != null) satisfies boolean : (x: number | null) => x is number
1673+
> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1674+
>x : number | null
1675+
> : ^^^^^^^^^^^^^
1676+
>(x != null) satisfies boolean : boolean
1677+
> : ^^^^^^^
1678+
>(x != null) : boolean
1679+
> : ^^^^^^^
1680+
>x != null : boolean
1681+
> : ^^^^^^^
1682+
>x : number | null
1683+
> : ^^^^^^^^^^^^^
1684+
1685+
);
1686+
1687+
function isEmptyString(x: unknown) {
1688+
>isEmptyString : (x: unknown) => x is ""
1689+
> : ^ ^^ ^^^^^^^^^^^^
1690+
>x : unknown
1691+
> : ^^^^^^^
1692+
1693+
const rv = x === "";
1694+
>rv : boolean
1695+
> : ^^^^^^^
1696+
>x === "" : boolean
1697+
> : ^^^^^^^
1698+
>x : unknown
1699+
> : ^^^^^^^
1700+
>"" : ""
1701+
> : ^^
1702+
1703+
return rv satisfies boolean;
1704+
>rv satisfies boolean : boolean
1705+
> : ^^^^^^^
1706+
>rv : boolean
1707+
> : ^^^^^^^
1708+
}
1709+

tests/cases/compiler/inferTypePredicates.ts

+10
Original file line numberDiff line numberDiff line change
@@ -279,3 +279,13 @@ const foobarPred = (fb: typeof foobar) => fb.type === "foo";
279279
if (foobarPred(foobar)) {
280280
foobar.foo;
281281
}
282+
283+
// https://github.com/microsoft/TypeScript/issues/60778
284+
const arrTest: Array<number> = [1, 2, null, 3].filter(
285+
(x) => (x != null) satisfies boolean,
286+
);
287+
288+
function isEmptyString(x: unknown) {
289+
const rv = x === "";
290+
return rv satisfies boolean;
291+
}

0 commit comments

Comments
 (0)