diff --git a/src/collector/globals.js b/src/collector/globals.js
index 24d744a..e5501e8 100644
--- a/src/collector/globals.js
+++ b/src/collector/globals.js
@@ -135,6 +135,39 @@ function values(params: (?Type)[], resolve: TypeId => Type): ?Type {
return t.createUnion(variants);
}
+function diff(params: (?Type)[], resolve: TypeId => Type): ?Type {
+ invariant(params.length === 2);
+
+ let [minuend, subtrahend] = params;
+
+ invariant(minuend && subtrahend);
+ invariant(minuend.kind === 'reference');
+
+ minuend = resolve(minuend.to);
+
+ invariant(minuend.kind === 'record');
+
+ if (subtrahend.kind === 'reference') {
+ subtrahend = resolve(subtrahend.to);
+ }
+
+ invariant(subtrahend.kind === 'record');
+
+ // TODO: more clever subtraction.
+ const blacklist = wu(subtrahend.fields).pluck('name').toArray();
+
+ const fields = wu(minuend.fields)
+ .filter(field => !blacklist.includes(field.name))
+ .map(field => ({
+ name: field.name,
+ value: t.clone(field.value),
+ required: field.required,
+ }))
+ .toArray();
+
+ return t.createRecord(fields);
+}
+
export default {
Object: object,
Buffer: buffer,
@@ -148,7 +181,7 @@ export default {
$Exact: unwrap, // TODO: another semantic for exact types?
$Keys: keys,
$Values: values,
- // TODO: $Diff
+ $Diff: diff,
// TODO: $All
// TODO: $Either
};
diff --git a/tests/samples/diffs/source.js b/tests/samples/diffs/source.js
new file mode 100644
index 0000000..b619672
--- /dev/null
+++ b/tests/samples/diffs/source.js
@@ -0,0 +1,13 @@
+type A = {
+ x: string,
+ y: boolean,
+};
+
+type B = {
+ y: boolean,
+};
+
+type X = $Diff;
+type Y = $Diff;
+
+export {X, Y};
diff --git a/tests/samples/diffs/types.yaml b/tests/samples/diffs/types.yaml
new file mode 100644
index 0000000..2d8bc94
--- /dev/null
+++ b/tests/samples/diffs/types.yaml
@@ -0,0 +1,27 @@
+- kind: record
+ fields:
+ - name: x
+ value: {kind: string}
+ required: true
+ - name: y
+ value: {kind: boolean}
+ required: true
+ id: [diffs, A]
+- kind: record
+ fields:
+ - name: y
+ value: {kind: boolean}
+ required: true
+ id: [diffs, B]
+- kind: record
+ fields:
+ - name: x
+ value: {kind: string}
+ required: true
+ id: [diffs, X]
+- kind: record
+ fields:
+ - name: y
+ value: {kind: boolean}
+ required: true
+ id: [diffs, Y]