forked from babel/babel
-
Notifications
You must be signed in to change notification settings - Fork 0
/
inference.js
177 lines (166 loc) · 8.6 KB
/
inference.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
import traverse from "../lib";
import assert from "assert";
import { parse } from "babylon";
import * as t from "babel-types";
function getPath(code) {
const ast = parse(code, {plugins: ["flow", "asyncGenerators"]});
let path;
traverse(ast, {
Program: function (_path) {
path = _path;
_path.stop();
}
});
return path;
}
describe("inference", function () {
describe("baseTypeStrictlyMatches", function () {
it("it should work with null", function () {
const path = getPath("var x = null; x === null").get("body")[1].get("expression");
const left = path.get("left");
const right = path.get("right");
const strictMatch = left.baseTypeStrictlyMatches(right);
assert.ok(strictMatch, "null should be equal to null");
});
it("it should work with numbers", function () {
const path = getPath("var x = 1; x === 2").get("body")[1].get("expression");
const left = path.get("left");
const right = path.get("right");
const strictMatch = left.baseTypeStrictlyMatches(right);
assert.ok(strictMatch, "number should be equal to number");
});
it("it should bail when type changes", function () {
const path = getPath("var x = 1; if (foo) x = null;else x = 3; x === 2").get("body")[2].get("expression");
const left = path.get("left");
const right = path.get("right");
const strictMatch = left.baseTypeStrictlyMatches(right);
assert.ok(!strictMatch, "type might change in if statement");
});
it("it should differentiate between null and undefined", function () {
const path = getPath("var x; x === null").get("body")[1].get("expression");
const left = path.get("left");
const right = path.get("right");
const strictMatch = left.baseTypeStrictlyMatches(right);
assert.ok(!strictMatch, "null should not match undefined");
});
});
describe("getTypeAnnotation", function () {
it("should infer from type cast", function () {
const path = getPath("(x: number)").get("body")[0].get("expression");
assert.ok(t.isNumberTypeAnnotation(path.getTypeAnnotation()), "should be number");
});
it("should infer string from template literal", function () {
const path = getPath("`hey`").get("body")[0].get("expression");
assert.ok(t.isStringTypeAnnotation(path.getTypeAnnotation()), "should be string");
});
it("should infer number from +x", function () {
const path = getPath("+x").get("body")[0].get("expression");
assert.ok(t.isNumberTypeAnnotation(path.getTypeAnnotation()), "should be number");
});
it("should infer T from new T", function () {
const path = getPath("new T").get("body")[0].get("expression");
const type = path.getTypeAnnotation();
assert.ok(t.isGenericTypeAnnotation(type) && type.id.name === "T", "should be T");
});
it("should infer number from ++x", function () {
const path = getPath("++x").get("body")[0].get("expression");
assert.ok(t.isNumberTypeAnnotation(path.getTypeAnnotation()), "should be number");
});
it("should infer number from --x", function () {
const path = getPath("--x").get("body")[0].get("expression");
assert.ok(t.isNumberTypeAnnotation(path.getTypeAnnotation()), "should be number");
});
it("should infer void from void x", function () {
const path = getPath("void x").get("body")[0].get("expression");
assert.ok(t.isVoidTypeAnnotation(path.getTypeAnnotation()), "should be void");
});
it("should infer string from typeof x", function () {
const path = getPath("typeof x").get("body")[0].get("expression");
assert.ok(t.isStringTypeAnnotation(path.getTypeAnnotation()), "should be string");
});
it("should infer boolean from !x", function () {
const path = getPath("!x").get("body")[0].get("expression");
assert.ok(t.isBooleanTypeAnnotation(path.getTypeAnnotation()), "should be boolean");
});
it("should infer type of sequence expression", function () {
const path = getPath("a,1").get("body")[0].get("expression");
assert.ok(t.isNumberTypeAnnotation(path.getTypeAnnotation()), "should be number");
});
it("should infer type of logical expression", function () {
const path = getPath("'a' && 1").get("body")[0].get("expression");
const type = path.getTypeAnnotation();
assert.ok(t.isUnionTypeAnnotation(type), "should be a union");
assert.ok(t.isStringTypeAnnotation(type.types[0]), "first type in union should be string");
assert.ok(t.isNumberTypeAnnotation(type.types[1]), "second type in union should be number");
});
it("should infer type of conditional expression", function () {
const path = getPath("q ? true : 0").get("body")[0].get("expression");
const type = path.getTypeAnnotation();
assert.ok(t.isUnionTypeAnnotation(type), "should be a union");
assert.ok(t.isBooleanTypeAnnotation(type.types[0]), "first type in union should be boolean");
assert.ok(t.isNumberTypeAnnotation(type.types[1]), "second type in union should be number");
});
it("should infer RegExp from RegExp literal", function () {
const path = getPath("/.+/").get("body")[0].get("expression");
const type = path.getTypeAnnotation();
assert.ok(t.isGenericTypeAnnotation(type) && type.id.name === "RegExp", "should be RegExp");
});
it("should infer Object from object expression", function () {
const path = getPath("({ a: 5 })").get("body")[0].get("expression");
const type = path.getTypeAnnotation();
assert.ok(t.isGenericTypeAnnotation(type) && type.id.name === "Object", "should be Object");
});
it("should infer Array from array expression", function () {
const path = getPath("[ 5 ]").get("body")[0].get("expression");
const type = path.getTypeAnnotation();
assert.ok(t.isGenericTypeAnnotation(type) && type.id.name === "Array", "should be Array");
});
it("should infer Function from function", function () {
const path = getPath("(function (): string {})").get("body")[0].get("expression");
const type = path.getTypeAnnotation();
assert.ok(t.isGenericTypeAnnotation(type) && type.id.name === "Function", "should be Function");
});
it("should infer call return type using function", function () {
const path = getPath("(function (): string {})()").get("body")[0].get("expression");
const type = path.getTypeAnnotation();
assert.ok(t.isStringTypeAnnotation(type), "should be string");
});
it("should infer call return type using async function", function () {
const path = getPath("(async function (): string {})()").get("body")[0].get("expression");
const type = path.getTypeAnnotation();
assert.ok(t.isGenericTypeAnnotation(type) && type.id.name === "Promise", "should be Promise");
});
it("should infer call return type using async generator function", function () {
const path = getPath("(async function * (): string {})()").get("body")[0].get("expression");
const type = path.getTypeAnnotation();
assert.ok(t.isGenericTypeAnnotation(type) && type.id.name === "AsyncIterator", "should be AsyncIterator");
});
it("should infer number from x/y", function () {
const path = getPath("x/y").get("body")[0].get("expression");
const type = path.getTypeAnnotation();
assert.ok(t.isNumberTypeAnnotation(type), "should be number");
});
it("should infer boolean from x instanceof y", function () {
const path = getPath("x instanceof y").get("body")[0].get("expression");
const type = path.getTypeAnnotation();
assert.ok(t.isBooleanTypeAnnotation(type), "should be boolean");
});
it("should infer number from 1 + 2", function () {
const path = getPath("1 + 2").get("body")[0].get("expression");
const type = path.getTypeAnnotation();
assert.ok(t.isNumberTypeAnnotation(type), "should be number");
});
it("should infer string|number from x + y", function () {
const path = getPath("x + y").get("body")[0].get("expression");
const type = path.getTypeAnnotation();
assert.ok(t.isUnionTypeAnnotation(type), "should be a union");
assert.ok(t.isStringTypeAnnotation(type.types[0]), "first type in union should be string");
assert.ok(t.isNumberTypeAnnotation(type.types[1]), "second type in union should be number");
});
it("should infer type of tagged template literal", function () {
const path = getPath("(function (): RegExp {}) `hey`").get("body")[0].get("expression");
const type = path.getTypeAnnotation();
assert.ok(t.isGenericTypeAnnotation(type) && type.id.name === "RegExp", "should be RegExp");
});
});
});