Skip to content

Commit 3ebc1af

Browse files
committed
add and and or mask to typefacts
1 parent 28c2084 commit 3ebc1af

File tree

5 files changed

+168
-3
lines changed

5 files changed

+168
-3
lines changed

src/compiler/checker.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,9 @@ namespace ts {
134134
EmptyObjectStrictFacts = All & ~(EQUndefined | EQNull | EQUndefinedOrNull),
135135
AllTypeofNE = TypeofNEString | TypeofNENumber | TypeofNEBigInt | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | NEUndefined,
136136
EmptyObjectFacts = All,
137+
// Masks
138+
OrFactsMask = TypeofEQFunction | TypeofEQObject | TypeofNEObject,
139+
AndFactsMask = All & ~OrFactsMask,
137140
}
138141

139142
const typeofEQFacts: ReadonlyESMap<string, TypeFacts> = new Map(getEntries({
@@ -22982,14 +22985,25 @@ namespace ts {
2298222985
return reduceLeft((type as UnionType).types, (facts, t) => facts | getTypeFacts(t, ignoreObjects), TypeFacts.None);
2298322986
}
2298422987
if (flags & TypeFlags.Intersection) {
22985-
// When an intersection contains a primitive type we ignore object type constituents as they are
22986-
// presumably type tags. For example, in string & { __kind__: "name" } we ignore the object type.
22988+
// // When an intersection contains a primitive type we ignore object type constituents as they are
22989+
// // presumably type tags. For example, in string & { __kind__: "name" } we ignore the object type.
2298722990
ignoreObjects ||= maybeTypeOfKind(type, TypeFlags.Primitive);
22988-
return reduceLeft((type as UnionType).types, (facts, t) => facts & getTypeFacts(t, ignoreObjects), TypeFacts.All);
22991+
return getIntersectionTypeFacts(type as IntersectionType, ignoreObjects);
2298922992
}
2299022993
return TypeFacts.All;
2299122994
}
2299222995

22996+
function getIntersectionTypeFacts(type: IntersectionType, ignoreObjects: boolean): TypeFacts {
22997+
let oredFacts = TypeFacts.None;
22998+
let andedFacts = TypeFacts.All;
22999+
for (const t of type.types) {
23000+
const f = getTypeFacts(t, ignoreObjects);
23001+
oredFacts |= f;
23002+
andedFacts &= f;
23003+
}
23004+
return oredFacts & TypeFacts.OrFactsMask | andedFacts & TypeFacts.AndFactsMask;
23005+
}
23006+
2299323007
function getTypeWithFacts(type: Type, include: TypeFacts) {
2299423008
return filterType(type, t => (getTypeFacts(t) & include) !== 0);
2299523009
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//// [narrowingTypeofFunction.ts]
2+
type Meta = { foo: string }
3+
interface F { (): string }
4+
5+
function f1(a: (F & Meta) | string) {
6+
if (typeof a === "function") {
7+
a;
8+
}
9+
else {
10+
a;
11+
}
12+
}
13+
14+
function f2<T>(x: (T & F) | T & string) {
15+
if (typeof x === "function") {
16+
x;
17+
}
18+
else {
19+
x;
20+
}
21+
}
22+
23+
//// [narrowingTypeofFunction.js]
24+
"use strict";
25+
function f1(a) {
26+
if (typeof a === "function") {
27+
a;
28+
}
29+
else {
30+
a;
31+
}
32+
}
33+
function f2(x) {
34+
if (typeof x === "function") {
35+
x;
36+
}
37+
else {
38+
x;
39+
}
40+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
=== tests/cases/compiler/narrowingTypeofFunction.ts ===
2+
type Meta = { foo: string }
3+
>Meta : Symbol(Meta, Decl(narrowingTypeofFunction.ts, 0, 0))
4+
>foo : Symbol(foo, Decl(narrowingTypeofFunction.ts, 0, 13))
5+
6+
interface F { (): string }
7+
>F : Symbol(F, Decl(narrowingTypeofFunction.ts, 0, 27))
8+
9+
function f1(a: (F & Meta) | string) {
10+
>f1 : Symbol(f1, Decl(narrowingTypeofFunction.ts, 1, 26))
11+
>a : Symbol(a, Decl(narrowingTypeofFunction.ts, 3, 12))
12+
>F : Symbol(F, Decl(narrowingTypeofFunction.ts, 0, 27))
13+
>Meta : Symbol(Meta, Decl(narrowingTypeofFunction.ts, 0, 0))
14+
15+
if (typeof a === "function") {
16+
>a : Symbol(a, Decl(narrowingTypeofFunction.ts, 3, 12))
17+
18+
a;
19+
>a : Symbol(a, Decl(narrowingTypeofFunction.ts, 3, 12))
20+
}
21+
else {
22+
a;
23+
>a : Symbol(a, Decl(narrowingTypeofFunction.ts, 3, 12))
24+
}
25+
}
26+
27+
function f2<T>(x: (T & F) | T & string) {
28+
>f2 : Symbol(f2, Decl(narrowingTypeofFunction.ts, 10, 1))
29+
>T : Symbol(T, Decl(narrowingTypeofFunction.ts, 12, 12))
30+
>x : Symbol(x, Decl(narrowingTypeofFunction.ts, 12, 15))
31+
>T : Symbol(T, Decl(narrowingTypeofFunction.ts, 12, 12))
32+
>F : Symbol(F, Decl(narrowingTypeofFunction.ts, 0, 27))
33+
>T : Symbol(T, Decl(narrowingTypeofFunction.ts, 12, 12))
34+
35+
if (typeof x === "function") {
36+
>x : Symbol(x, Decl(narrowingTypeofFunction.ts, 12, 15))
37+
38+
x;
39+
>x : Symbol(x, Decl(narrowingTypeofFunction.ts, 12, 15))
40+
}
41+
else {
42+
x;
43+
>x : Symbol(x, Decl(narrowingTypeofFunction.ts, 12, 15))
44+
}
45+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
=== tests/cases/compiler/narrowingTypeofFunction.ts ===
2+
type Meta = { foo: string }
3+
>Meta : Meta
4+
>foo : string
5+
6+
interface F { (): string }
7+
8+
function f1(a: (F & Meta) | string) {
9+
>f1 : (a: (F & Meta) | string) => void
10+
>a : string | (F & Meta)
11+
12+
if (typeof a === "function") {
13+
>typeof a === "function" : boolean
14+
>typeof a : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
15+
>a : string | (F & Meta)
16+
>"function" : "function"
17+
18+
a;
19+
>a : F & Meta
20+
}
21+
else {
22+
a;
23+
>a : string
24+
}
25+
}
26+
27+
function f2<T>(x: (T & F) | T & string) {
28+
>f2 : <T>(x: (T & F) | (T & string)) => void
29+
>x : (T & F) | (T & string)
30+
31+
if (typeof x === "function") {
32+
>typeof x === "function" : boolean
33+
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
34+
>x : (T & F) | (T & string)
35+
>"function" : "function"
36+
37+
x;
38+
>x : (T & F) | (T & string)
39+
}
40+
else {
41+
x;
42+
>x : T & string
43+
}
44+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// @strict: true
2+
3+
type Meta = { foo: string }
4+
interface F { (): string }
5+
6+
function f1(a: (F & Meta) | string) {
7+
if (typeof a === "function") {
8+
a;
9+
}
10+
else {
11+
a;
12+
}
13+
}
14+
15+
function f2<T>(x: (T & F) | T & string) {
16+
if (typeof x === "function") {
17+
x;
18+
}
19+
else {
20+
x;
21+
}
22+
}

0 commit comments

Comments
 (0)