Sync the Flow tests (#1163)

* Extract custom tests from tests/flow/

Approach:

1. Remove all .js files in tests/flow except .snap.js files.
2. Copy over all .js files from tests/ in the flow repo.
3. Go through the diff looking for deletions.
   - It was easy to see which deletions were due to changes in the tests
     due to updates in the flow repo.
   - For the rest of the deletions, I used `git blame` to verify that
     they had been added by us since the flow tests were copied over.

This makes tests/flow/ simply a copy of the tests from the flow repo,
making it easier to sync with the upstream flow tests in the future.

* Add a script for syncing the flow tests

* Sync the flow tests
master
Simon Lydell 2017-04-08 16:52:38 +02:00 committed by Christopher Chedeau
parent bfd5fa4515
commit dc93bdc983
238 changed files with 7129 additions and 413 deletions

View File

@ -442,23 +442,30 @@ Show the world you're using *Prettier* → [![styled with prettier](https://img.
## Contributing
We will work on better docs over time, but in the mean time, here are
a few notes if you are interested in contributing:
To get up and running, install the dependencies and run the tests:
* You should be able to get up and running with just `yarn`.
* This uses [Jest](https://facebook.github.io/jest/) snapshots for tests. The entire Flow test suite is
included here. You can make changes and run `jest -u`, and then
`git diff` to see the styles that changed. Always update the
snapshots if opening a PR.
* If you can, look at [commands.md](commands.md) and check out
[Wadler's paper](http://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf)
to understand how this works. I will try to write a better explanation soon.
* I haven't set up any automated tests yet, but for now as long as you
run `jest -u` to update the snapshots and I see them in the PR, that's fine.
* You can run `AST_COMPARE=1 jest` for a more robust test run. That
formats each file, re-parses it, and compares the new AST with the
original one and makes sure they are semantically equivalent.
* Each test folder has a `jsfmt.spec.js` that runs the tests.
Normally you can just put `run_spec(__dirname);` there but if you want to pass
specific options, you can add the options object as the 2nd parameter like:
`run_spec(__dirname, { parser: 'babylon' });`
```
yarn
yarn test
```
Here's what you need to know about the tests:
* The tests uses [Jest](https://facebook.github.io/jest/) snapshots.
* You can make changes and run `jest -u` to update the snapshots. Then run `git
diff` to take a look at what changed. Always update the snapshots when opening
a PR.
* You can run `AST_COMPARE=1 jest` for a more robust test run. That formats each
file, re-parses it, and compares the new AST with the original one and makes
sure they are semantically equivalent.
* Each test folder has a `jsfmt.spec.js` that runs the tests. Normally you can
just put `run_spec(__dirname);` there. You can also pass options and
additional parsers, like this:
`run_spec(__dirname, { trailingComma: "es5" }, ["babylon"]);`
* `tests/flow/` contains the Flow test suite, and is not supposed to be edited
by hand. To update it, clone the Flow repo next to the Prettier repo and run:
`node scripts/sync-flow-tests.js ../flow/tests/`.
If you can, take look at [commands.md](commands.md) and check out [Wadler's
paper](http://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf) to
understand how Prettier works.

View File

@ -24,6 +24,8 @@
"devDependencies": {
"diff": "3.2.0",
"jest": "19.0.1",
"mkdirp": "^0.5.1",
"rimraf": "^2.6.1",
"rollup": "0.41.1",
"rollup-plugin-commonjs": "7.0.0",
"rollup-plugin-json": "2.1.0",

125
scripts/sync-flow-tests.js Normal file
View File

@ -0,0 +1,125 @@
const fs = require("fs");
const flowParser = require("flow-parser");
const glob = require("glob");
const mkdirp = require("mkdirp");
const path = require("path");
const rimraf = require("rimraf");
const DEFAULT_SPEC_CONTENT = "run_spec(__dirname);\n";
const SPEC_FILE_NAME = "jsfmt.spec.js";
const FLOW_TESTS_DIR = path.join(__dirname, "..", "tests", "flow");
function tryParse(file, content) {
const ast = flowParser.parse(content, {
esproposal_class_instance_fields: true,
esproposal_class_static_fields: true,
esproposal_export_star_as: true
});
if (ast.errors.length > 0) {
const line = ast.errors[0].loc.start.line;
const column = ast.errors[0].loc.start.column;
const message = ast.errors[0].message;
return `${file}:${line}:${column}: ${message}`;
}
return null;
}
function syncTests(syncDir) {
const specFiles = glob.sync(path.join(FLOW_TESTS_DIR, "**", SPEC_FILE_NAME));
const filesToCopy = glob.sync(path.join(syncDir, "**/*.js"));
if (filesToCopy.length === 0) {
throw new Error(
[
"Couldn't find any files to copy.",
`Please make sure that \`${syncDir}\` exists and contains the flow tests.`
].join("\n")
)
}
const specContents = specFiles.reduce((obj, specFile) => {
obj[specFile] = fs.readFileSync(specFile, "utf8");
return obj;
}, {});
const skipped = [];
rimraf.sync(FLOW_TESTS_DIR);
filesToCopy.forEach(file => {
const content = fs.readFileSync(file, "utf8");
const parseError = tryParse(file, content);
if (parseError) {
skipped.push(parseError);
return;
}
const newFile = path.join(FLOW_TESTS_DIR, path.relative(syncDir, file));
const dirname = path.dirname(newFile);
const specFile = path.join(dirname, SPEC_FILE_NAME);
const specContent = specContents[specFile] || DEFAULT_SPEC_CONTENT;
mkdirp.sync(dirname);
fs.writeFileSync(newFile, content);
fs.writeFileSync(specFile, specContent);
});
return skipped;
}
function run(argv) {
if (argv.length !== 1) {
console.error(
[
"You must provide the path to a flow tests directory to sync from!",
"Example: node scripts/sync-flow-tests.js ../flow/tests/"
].join("\n")
);
return 1;
}
const syncDir = argv[0];
let skipped = [];
try {
skipped = syncTests(syncDir);
} catch (error) {
console.error(`Failed to sync.\n${error}`);
return 1;
}
if (skipped.length > 0) {
console.log(
[
"Some files were skipped due to syntax errors.",
"This is expected since flow tests for handling invalid code,",
"but that's not interesting for Prettier's tests.",
"This is the skipped stuff:",
""
]
.concat(skipped, "")
.join("\n")
);
}
console.log(
[
"Done syncing! Now you need to:",
"",
`1. Optional: Adjust some ${SPEC_FILE_NAME} files.`,
"2. Run `jest -u` to create snapshots.",
"3. Run `git diff` to check how tests and snapshots have changed",
"4. Take a look at new snapshots to see if they're OK."
].join("\n")
);
return 0;
}
if (require.main === module) {
const exitCode = run(process.argv.slice(2));
process.exit(exitCode);
}

View File

@ -39,3 +39,14 @@ function foo() {
}
"
`;
exports[`toplevel_throw.js 1`] = `
"// @flow
throw new Error('foo'); // no error
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// @flow
throw new Error(\\"foo\\"); // no error
"
`;

View File

@ -0,0 +1,3 @@
// @flow
throw new Error('foo'); // no error

View File

@ -61,38 +61,6 @@ var zer : null = null;
function foobar(n : ?number) : number | null | void { return n; }
function barfoo(n : number | null | void) : ?number { return n; }
type Banana = {
eat: string => boolean,
};
type Hex = {n: 0x01};
type T = { method: (a) => void };
type T = { method(a): void };
declare class X { method(a): void }
declare function f(a): void;
var f: (a) => void;
interface F { m(string): number }
interface F { m: (string) => number }
function f(o: { f: (string) => void }) {}
function f(o: { f(string): void }) {}
type f = (...arg) => void;
type f = (/* comment */ arg) => void;
type f = (arg /* comment */) => void;
type f = (?arg) => void;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
function foo(str: string, i: number): string {
return str;
@ -163,38 +131,6 @@ function foobar(n: ?number): number | null | void {
function barfoo(n: number | null | void): ?number {
return n;
}
type Banana = {
eat: string => boolean
};
type Hex = { n: 0x01 };
type T = { method: a => void };
type T = { method(a): void };
declare class X { method(a): void }
declare function f(a): void;
var f: a => void;
interface F { m(string): number }
interface F { m: string => number }
function f(o: { f: string => void }) {}
function f(o: { f(string): void }) {}
type f = (...arg) => void;
type f = /* comment */ arg => void;
type f = arg /* comment */ => void;
type f = ?arg => void;
"
`;

View File

@ -58,35 +58,3 @@ var zer : null = null;
function foobar(n : ?number) : number | null | void { return n; }
function barfoo(n : number | null | void) : ?number { return n; }
type Banana = {
eat: string => boolean,
};
type Hex = {n: 0x01};
type T = { method: (a) => void };
type T = { method(a): void };
declare class X { method(a): void }
declare function f(a): void;
var f: (a) => void;
interface F { m(string): number }
interface F { m: (string) => number }
function f(o: { f: (string) => void }) {}
function f(o: { f(string): void }) {}
type f = (...arg) => void;
type f = (/* comment */ arg) => void;
type f = (arg /* comment */) => void;
type f = (?arg) => void;

View File

@ -82,27 +82,6 @@ module.exports = \\"arrays\\";
"
`;
exports[`comments.js 1`] = `
"export type FileMetaData = [
/* id */ string,
/* mtime */ number,
/* visited */ 0|1,
/* dependencies */ Array<string>,
];
export type ModuleMetaData = [Path, /* type */ number];
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
export type FileMetaData = [
/* id */ string,
/* mtime */ number,
/* visited */ 0 | 1,
/* dependencies */ Array<string>
];
export type ModuleMetaData = [Path, /* type */ number];
"
`;
exports[`numeric_elem.js 1`] = `
"var arr = [];
var day = new Date;
@ -121,10 +100,3 @@ arr[day] = 0;
(arr[day]: string); // error: number ~> string
"
`;
exports[`union.js 1`] = `
"let arr: (number|string)[] = [];
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
let arr: (number | string)[] = [];
"
`;

View File

@ -0,0 +1,101 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`test.js 1`] = `
"// @flow
const Immutable = require('immutable');
const tasksPerStatusMap = new Map(
[].map(taskStatus => [taskStatus, new Map()]),
);
for (let [taskStatus, tasksMap] of tasksPerStatusMap) {
tasksPerStatusMap.set(taskStatus, Immutable.Map(tasksMap));
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// @flow
const Immutable = require(\\"immutable\\");
const tasksPerStatusMap = new Map(
[].map(taskStatus => [taskStatus, new Map()])
);
for (let [taskStatus, tasksMap] of tasksPerStatusMap) {
tasksPerStatusMap.set(taskStatus, Immutable.Map(tasksMap));
}
"
`;
exports[`test2.js 1`] = `
"/* @flow */
declare class Bar<K> {
update<K_>(updater: (value: this) => Bar<K_>): Bar<K_>;
}
declare function foo<U>(
initialValue: U,
callbackfn: (previousValue: U) => U
): U;
declare var items: Bar<string>;
declare var updater: (value: Bar<string>) => Bar<string>;
foo(
items,
(acc) => acc.update(updater)
);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/* @flow */
declare class Bar<K> {
update<K_>(updater: (value: this) => Bar<K_>): Bar<K_>
}
declare function foo<U>(
initialValue: U,
callbackfn: (previousValue: U) => U
): U;
declare var items: Bar<string>;
declare var updater: (value: Bar<string>) => Bar<string>;
foo(items, acc => acc.update(updater));
"
`;
exports[`test3.js 1`] = `
"// @flow
declare class ImmBox<T> {
static <U>(x: any): ImmBox<U>;
static (x: any): any;
}
declare class Box<T> {
constructor(x: T): void;
set(value: T): void;
get(): T;
}
const outer = new Box();
const inner = outer.get();
outer.set(ImmBox(inner));
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// @flow
declare class ImmBox<T> {
static <U>(x: any): ImmBox<U>,
static (x: any): any
}
declare class Box<T> {
constructor(x: T): void,
set(value: T): void,
get(): T
}
const outer = new Box();
const inner = outer.get();
outer.set(ImmBox(inner));
"
`;

View File

@ -0,0 +1,80 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`core.js 1`] = `
"declare class Array<T> {
@@iterator(): Iterator<T>;
map<U>(callbackfn: (value: T, index: number, array: Array<T>) => U, thisArg?: any): Array<U>;
}
type IteratorResult<Yield,Return> =
| { done: true, value?: Return }
| { done: false, value: Yield };
interface $Iterator<+Yield,+Return,-Next> {
@@iterator(): $Iterator<Yield,Return,Next>;
next(value?: Next): IteratorResult<Yield,Return>;
}
type Iterator<+T> = $Iterator<T,void,void>;
interface $Iterable<+Yield,+Return,-Next> {
@@iterator(): $Iterator<Yield,Return,Next>;
}
type Iterable<+T> = $Iterable<T,void,void>;
declare class Map<K, V> {
@@iterator(): Iterator<[K, V]>;
constructor(iterable: ?Iterable<[K, V]>): void;
set(key: K, value: V): Map<K, V>;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
declare class Array<T> {
@@iterator(): Iterator<T>,
map<U>(
callbackfn: (value: T, index: number, array: Array<T>) => U,
thisArg?: any
): Array<U>
}
type IteratorResult<Yield, Return> =
| { done: true, value?: Return }
| { done: false, value: Yield };
interface $Iterator<+Yield, +Return, -Next> {
@@iterator(): $Iterator<Yield, Return, Next>,
next(value?: Next): IteratorResult<Yield, Return>
}
type Iterator<+T> = $Iterator<T, void, void>;
interface $Iterable<+Yield, +Return, -Next> {
@@iterator(): $Iterator<Yield, Return, Next>
}
type Iterable<+T> = $Iterable<T, void, void>;
declare class Map<K, V> {
@@iterator(): Iterator<[K, V]>,
constructor(iterable: ?Iterable<[K, V]>): void,
set(key: K, value: V): Map<K, V>
}
"
`;
exports[`immutable.js 1`] = `
"declare module \\"immutable\\" {
declare class Map<K,V> {
static <K,V>(iter: Iterator<[K,V]>): Map<K,V>;
static <K:string,V>(object: {+[k:K]:V}): Map<K,V>;
set(key: K, value: V): this;
}
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
declare module \\"immutable\\" {
declare class Map<K, V> {
static <K, V>(iter: Iterator<[K, V]>): Map<K, V>,
static <K: string, V>(object: { +[k: K]: V }): Map<K, V>,
set(key: K, value: V): this
}
}
"
`;

View File

@ -0,0 +1,25 @@
declare class Array<T> {
@@iterator(): Iterator<T>;
map<U>(callbackfn: (value: T, index: number, array: Array<T>) => U, thisArg?: any): Array<U>;
}
type IteratorResult<Yield,Return> =
| { done: true, value?: Return }
| { done: false, value: Yield };
interface $Iterator<+Yield,+Return,-Next> {
@@iterator(): $Iterator<Yield,Return,Next>;
next(value?: Next): IteratorResult<Yield,Return>;
}
type Iterator<+T> = $Iterator<T,void,void>;
interface $Iterable<+Yield,+Return,-Next> {
@@iterator(): $Iterator<Yield,Return,Next>;
}
type Iterable<+T> = $Iterable<T,void,void>;
declare class Map<K, V> {
@@iterator(): Iterator<[K, V]>;
constructor(iterable: ?Iterable<[K, V]>): void;
set(key: K, value: V): Map<K, V>;
}

View File

@ -0,0 +1,8 @@
declare module "immutable" {
declare class Map<K,V> {
static <K,V>(iter: Iterator<[K,V]>): Map<K,V>;
static <K:string,V>(object: {+[k:K]:V}): Map<K,V>;
set(key: K, value: V): this;
}
}

View File

@ -0,0 +1 @@
run_spec(__dirname);

View File

@ -0,0 +1,10 @@
// @flow
const Immutable = require('immutable');
const tasksPerStatusMap = new Map(
[].map(taskStatus => [taskStatus, new Map()]),
);
for (let [taskStatus, tasksMap] of tasksPerStatusMap) {
tasksPerStatusMap.set(taskStatus, Immutable.Map(tasksMap));
}

View File

@ -0,0 +1,18 @@
/* @flow */
declare class Bar<K> {
update<K_>(updater: (value: this) => Bar<K_>): Bar<K_>;
}
declare function foo<U>(
initialValue: U,
callbackfn: (previousValue: U) => U
): U;
declare var items: Bar<string>;
declare var updater: (value: Bar<string>) => Bar<string>;
foo(
items,
(acc) => acc.update(updater)
);

View File

@ -0,0 +1,16 @@
// @flow
declare class ImmBox<T> {
static <U>(x: any): ImmBox<U>;
static (x: any): any;
}
declare class Box<T> {
constructor(x: T): void;
set(value: T): void;
get(): T;
}
const outer = new Box();
const inner = outer.get();
outer.set(ImmBox(inner));

View File

@ -0,0 +1,16 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`test.js 1`] = `
"// @flow
function Foo(items: ?Iterable<number>) {
Iterable(items || []).size;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// @flow
function Foo(items: ?Iterable<number>) {
Iterable(items || []).size;
}
"
`;

View File

@ -0,0 +1 @@
run_spec(__dirname);

View File

@ -0,0 +1,24 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`immutable.js 1`] = `
"// Copyright 2004-present Facebook. All Rights Reserved.
declare class Array<T> { }
declare class Iterable<S> {
static <V,Iter:Iterable<V>>(iter: Iter): Iter;
static <T>(iter: Array<T>): Iterable<T>;
size: number;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Copyright 2004-present Facebook. All Rights Reserved.
declare class Array<T> {}
declare class Iterable<S> {
static <V, Iter: Iterable<V>>(iter: Iter): Iter,
static <T>(iter: Array<T>): Iterable<T>,
size: number
}
"
`;

View File

@ -0,0 +1,9 @@
// Copyright 2004-present Facebook. All Rights Reserved.
declare class Array<T> { }
declare class Iterable<S> {
static <V,Iter:Iterable<V>>(iter: Iter): Iter;
static <T>(iter: Array<T>): Iterable<T>;
size: number;
}

View File

@ -0,0 +1 @@
run_spec(__dirname);

View File

@ -0,0 +1,5 @@
// @flow
function Foo(items: ?Iterable<number>) {
Iterable(items || []).size;
}

View File

@ -0,0 +1,573 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`base_class.js 1`] = `
"// @flow
class Base {
unannotatedField;
annotatedField: number;
initializedField = 42;
initializedFieldWithThis = this.initializedField;
annotatedInitializedFieldValid: ?number = 42;
annotatedInitializedFieldInvalid: number = 'asdf'; // Error: string ~> number
static unannotatedField;
static annotatedField: number;
static initializedField = 'asdf';
static initializedFieldWithThis = this.initializedField;
static annotatedInitializedFieldValid: ?number = 42;
static annotatedInitializedFieldInvalid: number = 'asdf'; // Error: string ~> number
}
var o = new Base();
/**
* Unannotated fields are open.
*/
(o.unannotatedField: string);
(o.unannotatedField: number);
(Base.unannotatedField: string);
(Base.unannotatedField: number);
/**
* Annotated (but uninitialized) fields still have a type.
*/
(o.annotatedField: number);
(o.annotatedField: string); // Error: number ~> string
(Base.annotatedField: number);
(Base.annotatedField: string); // Error: number ~> string
/**
* Initialized (but unannotated) fields assume the type of their initializer.
*/
(o.initializedField: number);
(o.initializedField: string); // Error: number ~> string
(Base.initializedField: string);
(Base.initializedField: number); // Error: string ~> number
/**
* Initialized fields can reference \`this\`.
*/
(o.initializedFieldWithThis: number);
(o.initializedFieldWithThis: string); // Error: number ~> string
(Base.initializedFieldWithThis: string);
(Base.initializedFieldWithThis: number); // Error: string ~> number
/**
* Initialized + annotated fields take the type of the annotation.
* (Note that this matters when the annotation is more general than the type of
* the initializer)
*/
(o.annotatedInitializedFieldValid: ?number);
(o.annotatedInitializedFieldValid: number); // Error: ?number ~> number
(Base.annotatedInitializedFieldValid: ?number);
(Base.annotatedInitializedFieldValid: number); // Error: ?number ~> number
/**
* Initialized + annotated fields where the init/annot combo is a mismatch
* should assume the type of the annotation.
*
* (This happens in addition to erroring at the site of initialization)
*/
(o.annotatedInitializedFieldInvalid: number);
(o.annotatedInitializedFieldInvalid: string); // Error: number ~> string
(Base.annotatedInitializedFieldInvalid: number);
(Base.annotatedInitializedFieldInvalid: string); // Error: number ~> string
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// @flow
class Base {
unannotatedField;
annotatedField: number;
initializedField = 42;
initializedFieldWithThis = this.initializedField;
annotatedInitializedFieldValid: ?number = 42;
annotatedInitializedFieldInvalid: number = \\"asdf\\"; // Error: string ~> number
static unannotatedField;
static annotatedField: number;
static initializedField = \\"asdf\\";
static initializedFieldWithThis = this.initializedField;
static annotatedInitializedFieldValid: ?number = 42;
static annotatedInitializedFieldInvalid: number = \\"asdf\\"; // Error: string ~> number
}
var o = new Base();
/**
* Unannotated fields are open.
*/
(o.unannotatedField: string);
(o.unannotatedField: number);
(Base.unannotatedField: string);
(Base.unannotatedField: number);
/**
* Annotated (but uninitialized) fields still have a type.
*/
(o.annotatedField: number);
(o.annotatedField: string); // Error: number ~> string
(Base.annotatedField: number);
(Base.annotatedField: string); // Error: number ~> string
/**
* Initialized (but unannotated) fields assume the type of their initializer.
*/
(o.initializedField: number);
(o.initializedField: string); // Error: number ~> string
(Base.initializedField: string);
(Base.initializedField: number); // Error: string ~> number
/**
* Initialized fields can reference \`this\`.
*/
(o.initializedFieldWithThis: number);
(o.initializedFieldWithThis: string); // Error: number ~> string
(Base.initializedFieldWithThis: string);
(Base.initializedFieldWithThis: number); // Error: string ~> number
/**
* Initialized + annotated fields take the type of the annotation.
* (Note that this matters when the annotation is more general than the type of
* the initializer)
*/
(o.annotatedInitializedFieldValid: ?number);
(o.annotatedInitializedFieldValid: number); // Error: ?number ~> number
(Base.annotatedInitializedFieldValid: ?number);
(Base.annotatedInitializedFieldValid: number); // Error: ?number ~> number
/**
* Initialized + annotated fields where the init/annot combo is a mismatch
* should assume the type of the annotation.
*
* (This happens in addition to erroring at the site of initialization)
*/
(o.annotatedInitializedFieldInvalid: number);
(o.annotatedInitializedFieldInvalid: string); // Error: number ~> string
(Base.annotatedInitializedFieldInvalid: number);
(Base.annotatedInitializedFieldInvalid: string); // Error: number ~> string
"
`;
exports[`derived_class.js 1`] = `
"// @flow
class Base {
base_unannotatedField;
base_annotatedField: number;
base_initializedField = 42;
base_initializedFieldWithThis = this.base_initializedField;
base_annotatedInitializedFieldValid: ?number = 42;
base_annotatedInitializedFieldInvalid: number = 'asdf'; // Error: string ~> number
static base_unannotatedField;
static base_annotatedField: number;
static base_initializedField = 'asdf';
static base_initializedFieldWithThis = this.base_initializedField;
static base_annotatedInitializedFieldValid: ?number = 42;
static base_annotatedInitializedFieldInvalid: number = 'asdf'; // Error: string ~> number
inherited_initializer = 42;
static inherited_initializer = 42;
}
class Child extends Base {
child_unannotatedField;
child_annotatedField: number;
child_initializedField = 42;
child_initializedFieldWithThis = this.child_initializedField;
child_annotatedInitializedFieldValid: ?number = 42;
child_annotatedInitializedFieldInvalid: number = 'asdf'; // Error: string ~> number
static child_unannotatedField;
static child_annotatedField: number;
static child_initializedField = 'asdf';
static child_initializedFieldWithThis = this.child_initializedField;
static child_annotatedInitializedFieldValid: ?number = 42;
static child_annotatedInitializedFieldInvalid: number = 'asdf'; // Error: string ~> number
inherited_initializer;
static inherited_initializer;
}
var o = new Child();
/**
* Unannotated fields are open.
*/
(o.base_unannotatedField: string);
(o.base_unannotatedField: number);
(Child.base_unannotatedField: string);
(Child.base_unannotatedField: number);
(o.child_unannotatedField: string);
(o.child_unannotatedField: number);
(Child.child_unannotatedField: string);
(Child.child_unannotatedField: number);
/**
* Annotated (but uninitialized) fields still have a type.
*/
(o.base_annotatedField: number);
(o.base_annotatedField: string); // Error: number ~> string
(Child.base_annotatedField: number);
(Child.base_annotatedField: string); // Error: number ~> string
(o.child_annotatedField: number);
(o.child_annotatedField: string); // Error: number ~> string
(Child.child_annotatedField: number);
(Child.child_annotatedField: string); // Error: number ~> string
/**
* Initialized (but unannotated) fields assume the type of their initializer.
*/
(o.base_initializedField: number);
(o.base_initializedField: string); // Error: number ~> string
(Child.base_initializedField: string);
(Child.base_initializedField: number); // Error: string ~> number
(o.child_initializedField: number);
(o.child_initializedField: string); // Error: number ~> string
(Child.child_initializedField: string);
(Child.child_initializedField: number); // Error: string ~> number
/**
* Initialized fields can reference \`this\`.
*/
(o.base_initializedFieldWithThis: number);
(o.base_initializedFieldWithThis: string); // Error: number ~> string
(Child.base_initializedFieldWithThis: string);
(Child.base_initializedFieldWithThis: number); // Error: string ~> number
(o.child_initializedFieldWithThis: number);
(o.child_initializedFieldWithThis: string); // Error: number ~> string
(Child.child_initializedFieldWithThis: string);
(Child.child_initializedFieldWithThis: number); // Error: string ~> number
/**
* Initialized + annotated fields take the type of the annotation.
* (Note that this matters when the annotation is more general than the type of
* the initializer)
*/
(o.base_annotatedInitializedFieldValid: ?number);
(o.base_annotatedInitializedFieldValid: number); // Error: ?number ~> number
(Child.base_annotatedInitializedFieldValid: ?number);
(Child.base_annotatedInitializedFieldValid: number); // Error: ?number ~> number
(o.child_annotatedInitializedFieldValid: ?number);
(o.child_annotatedInitializedFieldValid: number); // Error: ?number ~> number
(Child.child_annotatedInitializedFieldValid: ?number);
(Child.child_annotatedInitializedFieldValid: number); // Error: ?number ~> number
/**
* Initialized + annotated fields where the init/annot combo is a mismatch
* should assume the type of the annotation.
*
* (This happens in addition to erroring at the site of initialization)
*/
(o.base_annotatedInitializedFieldInvalid: number);
(o.base_annotatedInitializedFieldInvalid: string); // Error: number ~> string
(Child.base_annotatedInitializedFieldInvalid: number);
(Child.base_annotatedInitializedFieldInvalid: string); // Error: number ~> string
(o.child_annotatedInitializedFieldInvalid: number);
(o.child_annotatedInitializedFieldInvalid: string); // Error: number ~> string
(Child.child_annotatedInitializedFieldInvalid: number);
(Child.child_annotatedInitializedFieldInvalid: string); // Error: number ~> string
/**
* Derived fields without an initializer that shadow base fields *with* an
* initializer should have the type of the base field.
*/
(o.inherited_initializer: number);
(o.inherited_initializer: string); // Error: number ~> string
(Child.inherited_initializer: number);
(Child.inherited_initializer: string); // Error: number ~> string
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// @flow
class Base {
base_unannotatedField;
base_annotatedField: number;
base_initializedField = 42;
base_initializedFieldWithThis = this.base_initializedField;
base_annotatedInitializedFieldValid: ?number = 42;
base_annotatedInitializedFieldInvalid: number = \\"asdf\\"; // Error: string ~> number
static base_unannotatedField;
static base_annotatedField: number;
static base_initializedField = \\"asdf\\";
static base_initializedFieldWithThis = this.base_initializedField;
static base_annotatedInitializedFieldValid: ?number = 42;
static base_annotatedInitializedFieldInvalid: number = \\"asdf\\"; // Error: string ~> number
inherited_initializer = 42;
static inherited_initializer = 42;
}
class Child extends Base {
child_unannotatedField;
child_annotatedField: number;
child_initializedField = 42;
child_initializedFieldWithThis = this.child_initializedField;
child_annotatedInitializedFieldValid: ?number = 42;
child_annotatedInitializedFieldInvalid: number = \\"asdf\\"; // Error: string ~> number
static child_unannotatedField;
static child_annotatedField: number;
static child_initializedField = \\"asdf\\";
static child_initializedFieldWithThis = this.child_initializedField;
static child_annotatedInitializedFieldValid: ?number = 42;
static child_annotatedInitializedFieldInvalid: number = \\"asdf\\"; // Error: string ~> number
inherited_initializer;
static inherited_initializer;
}
var o = new Child();
/**
* Unannotated fields are open.
*/
(o.base_unannotatedField: string);
(o.base_unannotatedField: number);
(Child.base_unannotatedField: string);
(Child.base_unannotatedField: number);
(o.child_unannotatedField: string);
(o.child_unannotatedField: number);
(Child.child_unannotatedField: string);
(Child.child_unannotatedField: number);
/**
* Annotated (but uninitialized) fields still have a type.
*/
(o.base_annotatedField: number);
(o.base_annotatedField: string); // Error: number ~> string
(Child.base_annotatedField: number);
(Child.base_annotatedField: string); // Error: number ~> string
(o.child_annotatedField: number);
(o.child_annotatedField: string); // Error: number ~> string
(Child.child_annotatedField: number);
(Child.child_annotatedField: string); // Error: number ~> string
/**
* Initialized (but unannotated) fields assume the type of their initializer.
*/
(o.base_initializedField: number);
(o.base_initializedField: string); // Error: number ~> string
(Child.base_initializedField: string);
(Child.base_initializedField: number); // Error: string ~> number
(o.child_initializedField: number);
(o.child_initializedField: string); // Error: number ~> string
(Child.child_initializedField: string);
(Child.child_initializedField: number); // Error: string ~> number
/**
* Initialized fields can reference \`this\`.
*/
(o.base_initializedFieldWithThis: number);
(o.base_initializedFieldWithThis: string); // Error: number ~> string
(Child.base_initializedFieldWithThis: string);
(Child.base_initializedFieldWithThis: number); // Error: string ~> number
(o.child_initializedFieldWithThis: number);
(o.child_initializedFieldWithThis: string); // Error: number ~> string
(Child.child_initializedFieldWithThis: string);
(Child.child_initializedFieldWithThis: number); // Error: string ~> number
/**
* Initialized + annotated fields take the type of the annotation.
* (Note that this matters when the annotation is more general than the type of
* the initializer)
*/
(o.base_annotatedInitializedFieldValid: ?number);
(o.base_annotatedInitializedFieldValid: number); // Error: ?number ~> number
(Child.base_annotatedInitializedFieldValid: ?number);
(Child.base_annotatedInitializedFieldValid: number); // Error: ?number ~> number
(o.child_annotatedInitializedFieldValid: ?number);
(o.child_annotatedInitializedFieldValid: number); // Error: ?number ~> number
(Child.child_annotatedInitializedFieldValid: ?number);
(Child.child_annotatedInitializedFieldValid: number); // Error: ?number ~> number
/**
* Initialized + annotated fields where the init/annot combo is a mismatch
* should assume the type of the annotation.
*
* (This happens in addition to erroring at the site of initialization)
*/
(o.base_annotatedInitializedFieldInvalid: number);
(o.base_annotatedInitializedFieldInvalid: string); // Error: number ~> string
(Child.base_annotatedInitializedFieldInvalid: number);
(Child.base_annotatedInitializedFieldInvalid: string); // Error: number ~> string
(o.child_annotatedInitializedFieldInvalid: number);
(o.child_annotatedInitializedFieldInvalid: string); // Error: number ~> string
(Child.child_annotatedInitializedFieldInvalid: number);
(Child.child_annotatedInitializedFieldInvalid: string); // Error: number ~> string
/**
* Derived fields without an initializer that shadow base fields *with* an
* initializer should have the type of the base field.
*/
(o.inherited_initializer: number);
(o.inherited_initializer: string); // Error: number ~> string
(Child.inherited_initializer: number);
(Child.inherited_initializer: string); // Error: number ~> string
"
`;
exports[`generic_class.js 1`] = `
"// @flow
/**
* Fields annotated with a generic should assume a type once the type param
* is instantiated.
*/
class ClassAnnotated<T> {
p: T;
static p: T;
}
var o1 = new ClassAnnotated();
o1.p = 42;
(o1.p: number);
(o1.p: string); // Error: number ~> string
ClassAnnotated.p = 42;
(ClassAnnotated.p: number);
(ClassAnnotated.p: string); // Error: number ~> string
/**
* It's always an error to initialized a generically-typed field with an
* expression of any type other than the generic itself.
*/
class ClassGenericInitialized<T, U> {
invalid: T = 42; // Error: number ~> Generic<T>
valid: T = ((42:any):T);
static invalid: T = 42; // Error: number ~> Generic<T>
static valid: T = ((42:any):T);
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// @flow
/**
* Fields annotated with a generic should assume a type once the type param
* is instantiated.
*/
class ClassAnnotated<T> {
p: T;
static p: T;
}
var o1 = new ClassAnnotated();
o1.p = 42;
(o1.p: number);
(o1.p: string); // Error: number ~> string
ClassAnnotated.p = 42;
(ClassAnnotated.p: number);
(ClassAnnotated.p: string); // Error: number ~> string
/**
* It's always an error to initialized a generically-typed field with an
* expression of any type other than the generic itself.
*/
class ClassGenericInitialized<T, U> {
invalid: T = 42; // Error: number ~> Generic<T>
valid: T = ((42: any): T);
static invalid: T = 42; // Error: number ~> Generic<T>
static valid: T = ((42: any): T);
}
"
`;
exports[`scoping.js 1`] = `
"// @flow
var someVar = 42;
class Foo {
outer = someVar;
selfTyped: Foo;
selfTypedInit = new Foo();
static outer = someVar;
static selfTyped: Foo;
static selfTypedInit = new Foo();
constructor() {
var someVar = 'asdf';
}
}
/**
* Field initializers execute in a scope immediately under the scope outside the
* class definition.
*/
(new Foo().outer: number);
(new Foo().outer: string); // Error: number ~> string
(Foo.outer: number);
(Foo.outer: string); // Error: number ~> string
/**
* Field initializers should be able to refer to the class type in their type
* annotations.
*/
(new Foo().selfTyped: Foo);
(new Foo().selfTyped: number); // Error: Foo ~> number
(Foo.selfTyped: Foo);
(Foo.selfTyped: number); // Error: Foo ~> number
(new Foo().selfTypedInit: Foo);
(new Foo().selfTypedInit: number); // Error: Foo ~> number
(Foo.selfTypedInit: Foo);
(Foo.selfTypedInit: number); // Error: Foo ~> number
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// @flow
var someVar = 42;
class Foo {
outer = someVar;
selfTyped: Foo;
selfTypedInit = new Foo();
static outer = someVar;
static selfTyped: Foo;
static selfTypedInit = new Foo();
constructor() {
var someVar = \\"asdf\\";
}
}
/**
* Field initializers execute in a scope immediately under the scope outside the
* class definition.
*/
(new Foo().outer: number);
(new Foo().outer: string); // Error: number ~> string
(Foo.outer: number);
(Foo.outer: string); // Error: number ~> string
/**
* Field initializers should be able to refer to the class type in their type
* annotations.
*/
(new Foo().selfTyped: Foo);
(new Foo().selfTyped: number); // Error: Foo ~> number
(Foo.selfTyped: Foo);
(Foo.selfTyped: number); // Error: Foo ~> number
(new Foo().selfTypedInit: Foo);
(new Foo().selfTypedInit: number); // Error: Foo ~> number
(Foo.selfTypedInit: Foo);
(Foo.selfTypedInit: number); // Error: Foo ~> number
"
`;

View File

@ -0,0 +1,72 @@
// @flow
class Base {
unannotatedField;
annotatedField: number;
initializedField = 42;
initializedFieldWithThis = this.initializedField;
annotatedInitializedFieldValid: ?number = 42;
annotatedInitializedFieldInvalid: number = 'asdf'; // Error: string ~> number
static unannotatedField;
static annotatedField: number;
static initializedField = 'asdf';
static initializedFieldWithThis = this.initializedField;
static annotatedInitializedFieldValid: ?number = 42;
static annotatedInitializedFieldInvalid: number = 'asdf'; // Error: string ~> number
}
var o = new Base();
/**
* Unannotated fields are open.
*/
(o.unannotatedField: string);
(o.unannotatedField: number);
(Base.unannotatedField: string);
(Base.unannotatedField: number);
/**
* Annotated (but uninitialized) fields still have a type.
*/
(o.annotatedField: number);
(o.annotatedField: string); // Error: number ~> string
(Base.annotatedField: number);
(Base.annotatedField: string); // Error: number ~> string
/**
* Initialized (but unannotated) fields assume the type of their initializer.
*/
(o.initializedField: number);
(o.initializedField: string); // Error: number ~> string
(Base.initializedField: string);
(Base.initializedField: number); // Error: string ~> number
/**
* Initialized fields can reference `this`.
*/
(o.initializedFieldWithThis: number);
(o.initializedFieldWithThis: string); // Error: number ~> string
(Base.initializedFieldWithThis: string);
(Base.initializedFieldWithThis: number); // Error: string ~> number
/**
* Initialized + annotated fields take the type of the annotation.
* (Note that this matters when the annotation is more general than the type of
* the initializer)
*/
(o.annotatedInitializedFieldValid: ?number);
(o.annotatedInitializedFieldValid: number); // Error: ?number ~> number
(Base.annotatedInitializedFieldValid: ?number);
(Base.annotatedInitializedFieldValid: number); // Error: ?number ~> number
/**
* Initialized + annotated fields where the init/annot combo is a mismatch
* should assume the type of the annotation.
*
* (This happens in addition to erroring at the site of initialization)
*/
(o.annotatedInitializedFieldInvalid: number);
(o.annotatedInitializedFieldInvalid: string); // Error: number ~> string
(Base.annotatedInitializedFieldInvalid: number);
(Base.annotatedInitializedFieldInvalid: string); // Error: number ~> string

View File

@ -0,0 +1,134 @@
// @flow
class Base {
base_unannotatedField;
base_annotatedField: number;
base_initializedField = 42;
base_initializedFieldWithThis = this.base_initializedField;
base_annotatedInitializedFieldValid: ?number = 42;
base_annotatedInitializedFieldInvalid: number = 'asdf'; // Error: string ~> number
static base_unannotatedField;
static base_annotatedField: number;
static base_initializedField = 'asdf';
static base_initializedFieldWithThis = this.base_initializedField;
static base_annotatedInitializedFieldValid: ?number = 42;
static base_annotatedInitializedFieldInvalid: number = 'asdf'; // Error: string ~> number
inherited_initializer = 42;
static inherited_initializer = 42;
}
class Child extends Base {
child_unannotatedField;
child_annotatedField: number;
child_initializedField = 42;
child_initializedFieldWithThis = this.child_initializedField;
child_annotatedInitializedFieldValid: ?number = 42;
child_annotatedInitializedFieldInvalid: number = 'asdf'; // Error: string ~> number
static child_unannotatedField;
static child_annotatedField: number;
static child_initializedField = 'asdf';
static child_initializedFieldWithThis = this.child_initializedField;
static child_annotatedInitializedFieldValid: ?number = 42;
static child_annotatedInitializedFieldInvalid: number = 'asdf'; // Error: string ~> number
inherited_initializer;
static inherited_initializer;
}
var o = new Child();
/**
* Unannotated fields are open.
*/
(o.base_unannotatedField: string);
(o.base_unannotatedField: number);
(Child.base_unannotatedField: string);
(Child.base_unannotatedField: number);
(o.child_unannotatedField: string);
(o.child_unannotatedField: number);
(Child.child_unannotatedField: string);
(Child.child_unannotatedField: number);
/**
* Annotated (but uninitialized) fields still have a type.
*/
(o.base_annotatedField: number);
(o.base_annotatedField: string); // Error: number ~> string
(Child.base_annotatedField: number);
(Child.base_annotatedField: string); // Error: number ~> string
(o.child_annotatedField: number);
(o.child_annotatedField: string); // Error: number ~> string
(Child.child_annotatedField: number);
(Child.child_annotatedField: string); // Error: number ~> string
/**
* Initialized (but unannotated) fields assume the type of their initializer.
*/
(o.base_initializedField: number);
(o.base_initializedField: string); // Error: number ~> string
(Child.base_initializedField: string);
(Child.base_initializedField: number); // Error: string ~> number
(o.child_initializedField: number);
(o.child_initializedField: string); // Error: number ~> string
(Child.child_initializedField: string);
(Child.child_initializedField: number); // Error: string ~> number
/**
* Initialized fields can reference `this`.
*/
(o.base_initializedFieldWithThis: number);
(o.base_initializedFieldWithThis: string); // Error: number ~> string
(Child.base_initializedFieldWithThis: string);
(Child.base_initializedFieldWithThis: number); // Error: string ~> number
(o.child_initializedFieldWithThis: number);
(o.child_initializedFieldWithThis: string); // Error: number ~> string
(Child.child_initializedFieldWithThis: string);
(Child.child_initializedFieldWithThis: number); // Error: string ~> number
/**
* Initialized + annotated fields take the type of the annotation.
* (Note that this matters when the annotation is more general than the type of
* the initializer)
*/
(o.base_annotatedInitializedFieldValid: ?number);
(o.base_annotatedInitializedFieldValid: number); // Error: ?number ~> number
(Child.base_annotatedInitializedFieldValid: ?number);
(Child.base_annotatedInitializedFieldValid: number); // Error: ?number ~> number
(o.child_annotatedInitializedFieldValid: ?number);
(o.child_annotatedInitializedFieldValid: number); // Error: ?number ~> number
(Child.child_annotatedInitializedFieldValid: ?number);
(Child.child_annotatedInitializedFieldValid: number); // Error: ?number ~> number
/**
* Initialized + annotated fields where the init/annot combo is a mismatch
* should assume the type of the annotation.
*
* (This happens in addition to erroring at the site of initialization)
*/
(o.base_annotatedInitializedFieldInvalid: number);
(o.base_annotatedInitializedFieldInvalid: string); // Error: number ~> string
(Child.base_annotatedInitializedFieldInvalid: number);
(Child.base_annotatedInitializedFieldInvalid: string); // Error: number ~> string
(o.child_annotatedInitializedFieldInvalid: number);
(o.child_annotatedInitializedFieldInvalid: string); // Error: number ~> string
(Child.child_annotatedInitializedFieldInvalid: number);
(Child.child_annotatedInitializedFieldInvalid: string); // Error: number ~> string
/**
* Derived fields without an initializer that shadow base fields *with* an
* initializer should have the type of the base field.
*/
(o.inherited_initializer: number);
(o.inherited_initializer: string); // Error: number ~> string
(Child.inherited_initializer: number);
(Child.inherited_initializer: string); // Error: number ~> string

View File

@ -0,0 +1,31 @@
// @flow
/**
* Fields annotated with a generic should assume a type once the type param
* is instantiated.
*/
class ClassAnnotated<T> {
p: T;
static p: T;
}
var o1 = new ClassAnnotated();
o1.p = 42;
(o1.p: number);
(o1.p: string); // Error: number ~> string
ClassAnnotated.p = 42;
(ClassAnnotated.p: number);
(ClassAnnotated.p: string); // Error: number ~> string
/**
* It's always an error to initialized a generically-typed field with an
* expression of any type other than the generic itself.
*/
class ClassGenericInitialized<T, U> {
invalid: T = 42; // Error: number ~> Generic<T>
valid: T = ((42:any):T);
static invalid: T = 42; // Error: number ~> Generic<T>
static valid: T = ((42:any):T);
}

View File

@ -0,0 +1 @@
run_spec(__dirname);

View File

@ -0,0 +1,40 @@
// @flow
var someVar = 42;
class Foo {
outer = someVar;
selfTyped: Foo;
selfTypedInit = new Foo();
static outer = someVar;
static selfTyped: Foo;
static selfTypedInit = new Foo();
constructor() {
var someVar = 'asdf';
}
}
/**
* Field initializers execute in a scope immediately under the scope outside the
* class definition.
*/
(new Foo().outer: number);
(new Foo().outer: string); // Error: number ~> string
(Foo.outer: number);
(Foo.outer: string); // Error: number ~> string
/**
* Field initializers should be able to refer to the class type in their type
* annotations.
*/
(new Foo().selfTyped: Foo);
(new Foo().selfTyped: number); // Error: Foo ~> number
(Foo.selfTyped: Foo);
(Foo.selfTyped: number); // Error: Foo ~> number
(new Foo().selfTypedInit: Foo);
(new Foo().selfTypedInit: number); // Error: Foo ~> number
(Foo.selfTypedInit: Foo);
(Foo.selfTypedInit: number); // Error: Foo ~> number

View File

@ -0,0 +1,46 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`class_method_default_parameters.js 1`] = `
"class A {
b: string;
c(d = this.b) { // ok - can use \`this\` in function default parameter values
}
e() {
return this.b;
}
f(g = this.e()) { // ok - can use \`this\` in function default parameter values
}
h(i: number = this.b) { // error
}
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class A {
b: string;
c(d = this.b) {
// ok - can use \`this\` in function default parameter values
}
e() {
return this.b;
}
f(g = this.e()) {
// ok - can use \`this\` in function default parameter values
}
h(i: number = this.b) {
// error
}
}
"
`;

View File

@ -0,0 +1,21 @@
class A {
b: string;
c(d = this.b) { // ok - can use `this` in function default parameter values
}
e() {
return this.b;
}
f(g = this.e()) { // ok - can use `this` in function default parameter values
}
h(i: number = this.b) { // error
}
}

View File

@ -0,0 +1 @@
run_spec(__dirname);

View File

@ -213,6 +213,21 @@ var alias2: Alias = _Alias.factory(); // error: bad pun
"
`;
exports[`extends_any.js 1`] = `
"const Base: any = class {}
class Derived1 extends Base {}
class Derived2 extends Derived1 {
m() {}
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
const Base: any = class {};
class Derived1 extends Base {}
class Derived2 extends Derived1 {
m() {}
}
"
`;
exports[`loc.js 1`] = `
"/* @flow */

View File

@ -0,0 +1,5 @@
const Base: any = class {}
class Derived1 extends Base {}
class Derived2 extends Derived1 {
m() {}
}

View File

@ -9,7 +9,20 @@ class D {
constructor():number { }
}
module.exports = C;
// the return type of a constructor overrides the type of the class
declare class Bar<T> {}
declare class Foo<T> {
constructor<U>(iterable: U): Bar<U>;
}
(new Foo('x'): Bar<string>); // ok
(new Foo(123): Bar<string>); // error, number !~> string
// also overrides when it returns a different specialization of the same class
declare class Baz<T> {
constructor<U>(iterable: U): Baz<U>;
}
(new Baz('x'): Baz<string>); // ok
(new Baz(123): Baz<string>); // error, number !~> string
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class C {
constructor() {}
@ -19,6 +32,19 @@ class D {
constructor(): number {}
}
module.exports = C;
// the return type of a constructor overrides the type of the class
declare class Bar<T> {}
declare class Foo<T> {
constructor<U>(iterable: U): Bar<U>
}
(new Foo(\\"x\\"): Bar<string>); // ok
(new Foo(123): Bar<string>); // error, number !~> string
// also overrides when it returns a different specialization of the same class
declare class Baz<T> {
constructor<U>(iterable: U): Baz<U>
}
(new Baz(\\"x\\"): Baz<string>); // ok
(new Baz(123): Baz<string>); // error, number !~> string
"
`;

View File

@ -6,4 +6,17 @@ class D {
constructor():number { }
}
module.exports = C;
// the return type of a constructor overrides the type of the class
declare class Bar<T> {}
declare class Foo<T> {
constructor<U>(iterable: U): Bar<U>;
}
(new Foo('x'): Bar<string>); // ok
(new Foo(123): Bar<string>); // error, number !~> string
// also overrides when it returns a different specialization of the same class
declare class Baz<T> {
constructor<U>(iterable: U): Baz<U>;
}
(new Baz('x'): Baz<string>); // ok
(new Baz(123): Baz<string>); // error, number !~> string

View File

@ -6,6 +6,7 @@ exports[`nested_test.js 1`] = `
var docblock = require('qux/docblock');
var min = require('d3/min.js');
var corge = require('qux/corge');
var SomeOtherModule = require('SomeOtherModule');
// make sure we don't pick up non-header @providesModule
// annotations - see node_modules/qux/docblock.js
@ -14,12 +15,14 @@ var unreachable = require('annotation');
(docblock.fun(): string);
(min.fun(): string);
(corge.fun(): string);
(SomeOtherModule.fun(): string);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/* @flow */
var docblock = require(\\"qux/docblock\\");
var min = require(\\"d3/min.js\\");
var corge = require(\\"qux/corge\\");
var SomeOtherModule = require(\\"SomeOtherModule\\");
// make sure we don't pick up non-header @providesModule
// annotations - see node_modules/qux/docblock.js
@ -28,5 +31,6 @@ var unreachable = require(\\"annotation\\");
(docblock.fun(): string);
(min.fun(): string);
(corge.fun(): string);
(SomeOtherModule.fun(): string);
"
`;

View File

@ -3,6 +3,7 @@
var docblock = require('qux/docblock');
var min = require('d3/min.js');
var corge = require('qux/corge');
var SomeOtherModule = require('SomeOtherModule');
// make sure we don't pick up non-header @providesModule
// annotations - see node_modules/qux/docblock.js
@ -11,3 +12,4 @@ var unreachable = require('annotation');
(docblock.fun(): string);
(min.fun(): string);
(corge.fun(): string);
(SomeOtherModule.fun(): string);

View File

@ -0,0 +1,4 @@
/* @flow */
class AImplementation {}
export function foo(): AImplementation { return new AImplementation(); }

View File

@ -0,0 +1,35 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`A.js 1`] = `
"/* @flow */
class AImplementation {}
export function foo(): AImplementation { return new AImplementation(); }
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/* @flow */
class AImplementation {}
export function foo(): AImplementation {
return new AImplementation();
}
"
`;
exports[`index.js 1`] = `
"/* @flow */
var A = require('A');
(A.foo(): boolean); // Error: Either AImplementation ~> boolean or ADefinition ~> boolean
var test = require('test');
(test.foo(): boolean); // Error: Either TestImplementation ~> boolean or TestDefinition ~> boolean
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/* @flow */
var A = require(\\"A\\");
(A.foo(): boolean); // Error: Either AImplementation ~> boolean or ADefinition ~> boolean
var test = require(\\"test\\");
(test.foo(): boolean); // Error: Either TestImplementation ~> boolean or TestDefinition ~> boolean
"
`;

View File

@ -0,0 +1,7 @@
/* @flow */
var A = require('A');
(A.foo(): boolean); // Error: Either AImplementation ~> boolean or ADefinition ~> boolean
var test = require('test');
(test.foo(): boolean); // Error: Either TestImplementation ~> boolean or TestDefinition ~> boolean

View File

@ -0,0 +1 @@
run_spec(__dirname);

View File

@ -2,11 +2,8 @@
* @flow
*/
// TODO: adding an empty line should not be dropped.
declare module ModuleAliasFoo {
declare type baz = number;
declare type toz = string;
declare function foo(bar : baz) : toz;
}
declare type Foo = string;

View File

@ -1,31 +1,25 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`declare_module_type_alias.js 1`] = `
exports[`DeclareModule_TypeAlias.js 1`] = `
"/**
* @flow
*/
// TODO: adding an empty line should not be dropped.
declare module ModuleAliasFoo {
declare type baz = number;
declare type toz = string;
declare function foo(bar : baz) : toz;
}
declare type Foo = string;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* @flow
*/
// TODO: adding an empty line should not be dropped.
declare module ModuleAliasFoo {
declare type baz = number;
declare type toz = string;
declare function foo(bar: baz): toz;
}
declare type Foo = string;
"
`;

View File

@ -372,37 +372,6 @@ var cp2_err: string = others.childprop2; // Error: number ~> string
"
`;
exports[`destructuring_param.js 1`] = `
"function f(a, { b }) {
return a + b;
}
// Doesn't parse right now
// function g(a, { a }) {
// return a;
// }
// function h({ a, { b } }, { c }, { { d } }) {
// return a + b + c + d;
// }
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
function f(a, { b }) {
return a + b;
}
// Doesn't parse right now
// function g(a, { a }) {
// return a;
// }
// function h({ a, { b } }, { c }, { { d } }) {
// return a + b + c + d;
// }
"
`;
exports[`eager.js 1`] = `
"var x;
({x} = null); // error, property \`x\` can not be accessed on \`null\`
@ -427,7 +396,6 @@ function tup_pattern<X>([ proj ] : [X]) {} // proj: X
type Proj<X> = [X];
function tup_pattern2<X>([ proj ] : Proj<X>) {} // proj: X
function rest_antipattern<T>(...t: T) {} // nonsense
function rest_pattern<X>(...r: X[]) {} // r: X[]
function obj_rest_pattern<X>({ _, ...o } : { _: any, x: X }) { // o: { x: X }
@ -460,7 +428,6 @@ function tup_pattern<X>([proj]: [X]) {} // proj: X
type Proj<X> = [X];
function tup_pattern2<X>([proj]: Proj<X>) {} // proj: X
function rest_antipattern<T>(...t: T) {} // nonsense
function rest_pattern<X>(...r: X[]) {} // r: X[]
function obj_rest_pattern<X>({ _, ...o }: { _: any, x: X }) {
@ -530,6 +497,31 @@ var { x: o } = o;
"
`;
exports[`refinement_non_termination.js 1`] = `
"// @flow
function _([argArray]: Array<Value>) {
if (argArray instanceof NullValue || argArray instanceof UndefinedValue) {
}
};
class Value { }
class NullValue extends Value { }
class UndefinedValue extends Value { }
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// @flow
function _([argArray]: Array<Value>) {
if (argArray instanceof NullValue || argArray instanceof UndefinedValue) {
}
}
class Value {}
class NullValue extends Value {}
class UndefinedValue extends Value {}
"
`;
exports[`string_lit.js 1`] = `
"var { \\"key\\": val } = { key: \\"val\\" };
(val: void); // error: string ~> void

View File

@ -1,13 +0,0 @@
function f(a, { b }) {
return a + b;
}
// Doesn't parse right now
// function g(a, { a }) {
// return a;
// }
// function h({ a, { b } }, { c }, { { d } }) {
// return a + b + c + d;
// }

View File

@ -12,7 +12,6 @@ function tup_pattern<X>([ proj ] : [X]) {} // proj: X
type Proj<X> = [X];
function tup_pattern2<X>([ proj ] : Proj<X>) {} // proj: X
function rest_antipattern<T>(...t: T) {} // nonsense
function rest_pattern<X>(...r: X[]) {} // r: X[]
function obj_rest_pattern<X>({ _, ...o } : { _: any, x: X }) { // o: { x: X }

View File

@ -0,0 +1,10 @@
// @flow
function _([argArray]: Array<Value>) {
if (argArray instanceof NullValue || argArray instanceof UndefinedValue) {
}
};
class Value { }
class NullValue extends Value { }
class UndefinedValue extends Value { }

View File

@ -7,5 +7,6 @@ let tests = [
(document.createElement('link'): HTMLLinkElement);
(document.createElement('option'): HTMLOptionElement);
(document.createElement('select'): HTMLSelectElement);
(document.querySelector('select'): HTMLSelectElement | null);
}
];

View File

@ -64,6 +64,7 @@ let tests = [
(document.createElement('link'): HTMLLinkElement);
(document.createElement('option'): HTMLOptionElement);
(document.createElement('select'): HTMLSelectElement);
(document.querySelector('select'): HTMLSelectElement | null);
}
];
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -76,6 +77,7 @@ let tests = [
(document.createElement(\\"link\\"): HTMLLinkElement);
(document.createElement(\\"option\\"): HTMLOptionElement);
(document.createElement(\\"select\\"): HTMLSelectElement);
(document.querySelector(\\"select\\"): HTMLSelectElement | null);
}
];
"
@ -644,15 +646,15 @@ let tests = [
function() {
document.createNodeIterator(document.body, -1, node => NodeFilter.FILTER_ACCEPT); // valid
document.createNodeIterator(document.body, -1, node => 'accept'); // invalid
document.createNodeIterator(document.body, -1, { accept: node => NodeFilter.FILTER_ACCEPT }); // valid
document.createNodeIterator(document.body, -1, { accept: node => 'accept' }); // invalid
document.createNodeIterator(document.body, -1, { acceptNode: node => NodeFilter.FILTER_ACCEPT }); // valid
document.createNodeIterator(document.body, -1, { acceptNode: node => 'accept' }); // invalid
document.createNodeIterator(document.body, -1, {}); // invalid
},
function() {
document.createTreeWalker(document.body, -1, node => NodeFilter.FILTER_ACCEPT); // valid
document.createTreeWalker(document.body, -1, node => 'accept'); // invalid
document.createTreeWalker(document.body, -1, { accept: node => NodeFilter.FILTER_ACCEPT }); // valid
document.createTreeWalker(document.body, -1, { accept: node => 'accept' }); // invalid
document.createTreeWalker(document.body, -1, { acceptNode: node => NodeFilter.FILTER_ACCEPT }); // valid
document.createTreeWalker(document.body, -1, { acceptNode: node => 'accept' }); // invalid
document.createTreeWalker(document.body, -1, {}); // invalid
},
];
@ -849,10 +851,10 @@ let tests = [
); // valid
document.createNodeIterator(document.body, -1, node => \\"accept\\"); // invalid
document.createNodeIterator(document.body, -1, {
accept: node => NodeFilter.FILTER_ACCEPT
acceptNode: node => NodeFilter.FILTER_ACCEPT
}); // valid
document.createNodeIterator(document.body, -1, {
accept: node => \\"accept\\"
acceptNode: node => \\"accept\\"
}); // invalid
document.createNodeIterator(document.body, -1, {}); // invalid
},
@ -864,9 +866,11 @@ let tests = [
); // valid
document.createTreeWalker(document.body, -1, node => \\"accept\\"); // invalid
document.createTreeWalker(document.body, -1, {
accept: node => NodeFilter.FILTER_ACCEPT
acceptNode: node => NodeFilter.FILTER_ACCEPT
}); // valid
document.createTreeWalker(document.body, -1, { accept: node => \\"accept\\" }); // invalid
document.createTreeWalker(document.body, -1, {
acceptNode: node => \\"accept\\"
}); // invalid
document.createTreeWalker(document.body, -1, {}); // invalid
}
];

View File

@ -181,15 +181,15 @@ let tests = [
function() {
document.createNodeIterator(document.body, -1, node => NodeFilter.FILTER_ACCEPT); // valid
document.createNodeIterator(document.body, -1, node => 'accept'); // invalid
document.createNodeIterator(document.body, -1, { accept: node => NodeFilter.FILTER_ACCEPT }); // valid
document.createNodeIterator(document.body, -1, { accept: node => 'accept' }); // invalid
document.createNodeIterator(document.body, -1, { acceptNode: node => NodeFilter.FILTER_ACCEPT }); // valid
document.createNodeIterator(document.body, -1, { acceptNode: node => 'accept' }); // invalid
document.createNodeIterator(document.body, -1, {}); // invalid
},
function() {
document.createTreeWalker(document.body, -1, node => NodeFilter.FILTER_ACCEPT); // valid
document.createTreeWalker(document.body, -1, node => 'accept'); // invalid
document.createTreeWalker(document.body, -1, { accept: node => NodeFilter.FILTER_ACCEPT }); // valid
document.createTreeWalker(document.body, -1, { accept: node => 'accept' }); // invalid
document.createTreeWalker(document.body, -1, { acceptNode: node => NodeFilter.FILTER_ACCEPT }); // valid
document.createTreeWalker(document.body, -1, { acceptNode: node => 'accept' }); // invalid
document.createTreeWalker(document.body, -1, {}); // invalid
},
];

View File

@ -0,0 +1,22 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`test.js 1`] = `
"/* @flow */
class Foo {
annotationOnly: string;
initOnly = 'asdf'
initWithAnnotation: string = 'asdf';
[computed]: string;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/* @flow */
class Foo {
annotationOnly: string;
initOnly = \\"asdf\\";
initWithAnnotation: string = \\"asdf\\";
[computed]: string;
}
"
`;

View File

@ -0,0 +1 @@
run_spec(__dirname);

View File

@ -0,0 +1,8 @@
/* @flow */
class Foo {
annotationOnly: string;
initOnly = 'asdf'
initWithAnnotation: string = 'asdf';
[computed]: string;
}

View File

@ -0,0 +1,20 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`test.js 1`] = `
"/* @flow */
class Foo {
annotationOnly: string;
initOnly = 'asdf';
initWithAnnotation: string = 'asdf';
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/* @flow */
class Foo {
annotationOnly: string;
initOnly = \\"asdf\\";
initWithAnnotation: string = \\"asdf\\";
}
"
`;

View File

@ -0,0 +1 @@
run_spec(__dirname);

View File

@ -0,0 +1,7 @@
/* @flow */
class Foo {
annotationOnly: string;
initOnly = 'asdf';
initWithAnnotation: string = 'asdf';
}

View File

@ -0,0 +1,20 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`test.js 1`] = `
"/* @flow */
class Foo {
static annotationOnly: string;
static initOnly = 'asdf';
static initWithAnnotation: string = 'asdf';
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/* @flow */
class Foo {
static annotationOnly: string;
static initOnly = \\"asdf\\";
static initWithAnnotation: string = \\"asdf\\";
}
"
`;

View File

@ -0,0 +1 @@
run_spec(__dirname);

View File

@ -0,0 +1,7 @@
/* @flow */
class Foo {
static annotationOnly: string;
static initOnly = 'asdf';
static initWithAnnotation: string = 'asdf';
}

View File

@ -0,0 +1,20 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`test.js 1`] = `
"/* @flow */
class Foo {
static annotationOnly: string;
static initOnly = 'asdf';
static initWithAnnotation: string = 'asdf';
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/* @flow */
class Foo {
static annotationOnly: string;
static initOnly = \\"asdf\\";
static initWithAnnotation: string = \\"asdf\\";
}
"
`;

View File

@ -0,0 +1 @@
run_spec(__dirname);

View File

@ -0,0 +1,7 @@
/* @flow */
class Foo {
static annotationOnly: string;
static initOnly = 'asdf';
static initWithAnnotation: string = 'asdf';
}

View File

@ -35,3 +35,14 @@ export var str = \\"asdf\\";
export var num = 42;
"
`;
exports[`test.js 1`] = `
"// @flow
export * as source from \\"./source\\";
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// @flow
export * as source from \\"./source\\";
"
`;

View File

@ -0,0 +1,3 @@
// @flow
export * as source from "./source";

View File

@ -35,3 +35,14 @@ export var str = \\"asdf\\";
export var num = 42;
"
`;
exports[`test.js 1`] = `
"// @flow
export * as source from \\"./source\\";
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// @flow
export * as source from \\"./source\\";
"
`;

View File

@ -0,0 +1,3 @@
// @flow
export * as source from "./source";

View File

@ -0,0 +1,12 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`test.js 1`] = `
"/* @flow */
export * as foo from \\"./test\\";
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/* @flow */
export * as foo from \\"./test\\";
"
`;

View File

@ -0,0 +1 @@
run_spec(__dirname);

View File

@ -0,0 +1,3 @@
/* @flow */
export * as foo from "./test";

View File

@ -134,7 +134,30 @@ const h: Request = new Request('http://example.org', {
cache: 'default'
}) // correct
var bodyUsed: boolean = h.bodyUsed;
h.text().then((t: string) => t); // correct
h.text().then((t: Buffer) => t); // incorrect
h.arrayBuffer().then((ab: ArrayBuffer) => ab); // correct
h.arrayBuffer().then((ab: Buffer) => ab); // incorrect
const i: Request = new Request('http://example.org', {
method: 'POST',
headers: {
'Content-Type': 'application/octet-stream'
},
body: new ArrayBuffer(10),
}); // correct
const j: Request = new Request('http://example.org', {
method: 'POST',
headers: {
'Content-Type': 'application/octet-stream'
},
body: new Uint8Array(10),
}); // correct
const k: Request = new Request('http://example.org', {
method: 'POST',
headers: {
'Content-Type': 'image/jpeg'
@ -144,14 +167,14 @@ const i: Request = new Request('http://example.org', {
cache: 'default'
}) // correct
const j: Request = new Request('http://example.org', {
const l: Request = new Request('http://example.org', {
method: 'GET',
headers: 'Content-Type: image/jpeg',
mode: 'cors',
cache: 'default'
}) // incorrect - headers is string
const k: Request = new Request('http://example.org', {
const m: Request = new Request('http://example.org', {
method: 'CONNECT',
headers: {
'Content-Type': 'image/jpeg'
@ -159,13 +182,6 @@ const k: Request = new Request('http://example.org', {
mode: 'cors',
cache: 'default'
}) // incorrect - CONNECT is forbidden
var l: boolean = h.bodyUsed;
h.text().then((t: string) => t); // correct
h.text().then((t: Buffer) => t); // incorrect
h.arrayBuffer().then((ab: ArrayBuffer) => ab); // correct
h.arrayBuffer().then((ab: Buffer) => ab); // incorrect
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/* @flow */
const a: Request = new Request(); // incorrect
@ -186,7 +202,30 @@ const h: Request = new Request(\\"http://example.org\\", {
cache: \\"default\\"
}); // correct
var bodyUsed: boolean = h.bodyUsed;
h.text().then((t: string) => t); // correct
h.text().then((t: Buffer) => t); // incorrect
h.arrayBuffer().then((ab: ArrayBuffer) => ab); // correct
h.arrayBuffer().then((ab: Buffer) => ab); // incorrect
const i: Request = new Request(\\"http://example.org\\", {
method: \\"POST\\",
headers: {
\\"Content-Type\\": \\"application/octet-stream\\"
},
body: new ArrayBuffer(10)
}); // correct
const j: Request = new Request(\\"http://example.org\\", {
method: \\"POST\\",
headers: {
\\"Content-Type\\": \\"application/octet-stream\\"
},
body: new Uint8Array(10)
}); // correct
const k: Request = new Request(\\"http://example.org\\", {
method: \\"POST\\",
headers: {
\\"Content-Type\\": \\"image/jpeg\\"
@ -196,14 +235,14 @@ const i: Request = new Request(\\"http://example.org\\", {
cache: \\"default\\"
}); // correct
const j: Request = new Request(\\"http://example.org\\", {
const l: Request = new Request(\\"http://example.org\\", {
method: \\"GET\\",
headers: \\"Content-Type: image/jpeg\\",
mode: \\"cors\\",
cache: \\"default\\"
}); // incorrect - headers is string
const k: Request = new Request(\\"http://example.org\\", {
const m: Request = new Request(\\"http://example.org\\", {
method: \\"CONNECT\\",
headers: {
\\"Content-Type\\": \\"image/jpeg\\"
@ -211,13 +250,6 @@ const k: Request = new Request(\\"http://example.org\\", {
mode: \\"cors\\",
cache: \\"default\\"
}); // incorrect - CONNECT is forbidden
var l: boolean = h.bodyUsed;
h.text().then((t: string) => t); // correct
h.text().then((t: Buffer) => t); // incorrect
h.arrayBuffer().then((ab: ArrayBuffer) => ab); // correct
h.arrayBuffer().then((ab: Buffer) => ab); // incorrect
"
`;

View File

@ -17,7 +17,30 @@ const h: Request = new Request('http://example.org', {
cache: 'default'
}) // correct
var bodyUsed: boolean = h.bodyUsed;
h.text().then((t: string) => t); // correct
h.text().then((t: Buffer) => t); // incorrect
h.arrayBuffer().then((ab: ArrayBuffer) => ab); // correct
h.arrayBuffer().then((ab: Buffer) => ab); // incorrect
const i: Request = new Request('http://example.org', {
method: 'POST',
headers: {
'Content-Type': 'application/octet-stream'
},
body: new ArrayBuffer(10),
}); // correct
const j: Request = new Request('http://example.org', {
method: 'POST',
headers: {
'Content-Type': 'application/octet-stream'
},
body: new Uint8Array(10),
}); // correct
const k: Request = new Request('http://example.org', {
method: 'POST',
headers: {
'Content-Type': 'image/jpeg'
@ -27,14 +50,14 @@ const i: Request = new Request('http://example.org', {
cache: 'default'
}) // correct
const j: Request = new Request('http://example.org', {
const l: Request = new Request('http://example.org', {
method: 'GET',
headers: 'Content-Type: image/jpeg',
mode: 'cors',
cache: 'default'
}) // incorrect - headers is string
const k: Request = new Request('http://example.org', {
const m: Request = new Request('http://example.org', {
method: 'CONNECT',
headers: {
'Content-Type': 'image/jpeg'
@ -42,10 +65,3 @@ const k: Request = new Request('http://example.org', {
mode: 'cors',
cache: 'default'
}) // incorrect - CONNECT is forbidden
var l: boolean = h.bodyUsed;
h.text().then((t: string) => t); // correct
h.text().then((t: Buffer) => t); // incorrect
h.arrayBuffer().then((ab: ArrayBuffer) => ab); // correct
h.arrayBuffer().then((ab: Buffer) => ab); // incorrect

View File

@ -0,0 +1,34 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`a.js 1`] = `
"// @flow
(require('./b'): number);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// @flow
(require(\\"./b\\"): number);
"
`;
exports[`b.js 1`] = `
"// @flow
module.exports = \\"\\";
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// @flow
module.exports = \\"\\";
"
`;
exports[`test.js 1`] = `
"// @flow
(\\"\\": number);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// @flow
(\\"\\": number);
"
`;

View File

@ -0,0 +1,3 @@
// @flow
(require('./b'): number);

View File

@ -0,0 +1,3 @@
// @flow
module.exports = "";

View File

@ -0,0 +1 @@
run_spec(__dirname);

View File

@ -0,0 +1,3 @@
// @flow
("": number);

View File

@ -11,8 +11,8 @@ test.apply(\\"\\", [\\"\\", 0]);
// wrong this is an error
test.apply(0, [\\"\\", 0]); // error: lookup \`length\` on Number
// not enough arguments is an error (via incompatible RestT)
test.apply(\\"\\", [\\"\\"]); // error: string ~> number
// not enough arguments is an error
test.apply(\\"\\", [\\"\\"]); // error: void ~> number
// mistyped arguments is an error
test.apply(\\"\\", [\\"\\", \\"\\"]); // error: string ~> number (2nd arg)
@ -43,6 +43,12 @@ test.apply(\\"\\", \\"not array\\"); // error: expect array of args
function test2(): number { return 0; }
(test2.apply(): number);
(test2.apply(\\"\\"): number);
// callable objects
function test3(x: { (a: string, b: string): void }) {
x.apply(x, ['foo', 'bar']); // ok
x.apply(x, ['foo', 123]); // error, number !~> string
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
function test(a: string, b: number): number {
return this.length; // expect []/\\"\\" this
@ -54,8 +60,8 @@ test.apply(\\"\\", [\\"\\", 0]);
// wrong this is an error
test.apply(0, [\\"\\", 0]); // error: lookup \`length\` on Number
// not enough arguments is an error (via incompatible RestT)
test.apply(\\"\\", [\\"\\"]); // error: string ~> number
// not enough arguments is an error
test.apply(\\"\\", [\\"\\"]); // error: void ~> number
// mistyped arguments is an error
test.apply(\\"\\", [\\"\\", \\"\\"]); // error: string ~> number (2nd arg)
@ -90,6 +96,12 @@ function test2(): number {
}
(test2.apply(): number);
(test2.apply(\\"\\"): number);
// callable objects
function test3(x: { (a: string, b: string): void }) {
x.apply(x, [\\"foo\\", \\"bar\\"]); // ok
x.apply(x, [\\"foo\\", 123]); // error, number !~> string
}
"
`;
@ -102,6 +114,25 @@ let tests = [
y('bar'); // ok
y(123); // error, number !~> string
},
// callable objects
function(x: { (a: string, b: string): void }) {
let y = x.bind(x, 'foo');
y('bar'); // ok
y(123); // error, number !~> string
},
// non-callable objects
function(x: { a: string }) {
x.bind(x, 'foo'); // error
},
// callable objects with overridden \`bind\` method
function(x: {(a: string, b: string): void, bind(a: string): void}) {
(x.bind('foo'): void); // ok
(x.bind(123): void); // error, number !~> string
},
];
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// @flow
@ -111,6 +142,24 @@ let tests = [
let y = x.bind(x, \\"foo\\");
y(\\"bar\\"); // ok
y(123); // error, number !~> string
},
// callable objects
function(x: { (a: string, b: string): void }) {
let y = x.bind(x, \\"foo\\");
y(\\"bar\\"); // ok
y(123); // error, number !~> string
},
// non-callable objects
function(x: { a: string }) {
x.bind(x, \\"foo\\"); // error
},
// callable objects with overridden \`bind\` method
function(x: { (a: string, b: string): void, bind(a: string): void }) {
(x.bind(\\"foo\\"): void); // ok
(x.bind(123): void); // error, number !~> string
}
];
"
@ -129,8 +178,8 @@ test.call(\\"\\", \\"\\", 0);
// wrong this is an error
test.call(0, \\"\\", 0); // error: lookup \`length\` on Number
// not enough arguments is an error (via incompatible RestT)
test.call(\\"\\", \\"\\"); // error: string ~> number
// not enough arguments is an error
test.call(\\"\\", \\"\\"); // error: void ~> number
// mistyped arguments is an error
test.call(\\"\\", \\"\\", \\"\\"); // error: string ~> number (2nd arg)
@ -152,6 +201,12 @@ f([0, 0]); // error: number ~> string (1st arg)
function test2(): number { return 0; }
(test2.call(): number);
(test2.call(\\"\\"): number);
// callable objects
function test3(x: { (a: string, b: string): void }) {
x.call(x, 'foo', 'bar'); // ok
x.call(x, 'foo', 123); // error, number !~> string
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// @flow
@ -165,8 +220,8 @@ test.call(\\"\\", \\"\\", 0);
// wrong this is an error
test.call(0, \\"\\", 0); // error: lookup \`length\` on Number
// not enough arguments is an error (via incompatible RestT)
test.call(\\"\\", \\"\\"); // error: string ~> number
// not enough arguments is an error
test.call(\\"\\", \\"\\"); // error: void ~> number
// mistyped arguments is an error
test.call(\\"\\", \\"\\", \\"\\"); // error: string ~> number (2nd arg)
@ -192,6 +247,12 @@ function test2(): number {
}
(test2.call(): number);
(test2.call(\\"\\"): number);
// callable objects
function test3(x: { (a: string, b: string): void }) {
x.call(x, \\"foo\\", \\"bar\\"); // ok
x.call(x, \\"foo\\", 123); // error, number !~> string
}
"
`;
@ -337,42 +398,202 @@ var e = (d.bind(1): Function)();
exports[`rest.js 1`] = `
"/* regression tests */
function rest_array<T>(...xs: Array<T>): T {
function rest_array<T>(...xs: Array<T>): T { // Ok, arrays can be rest params
return xs[0];
}
// Warn, singleton tuple types don't represent rest params
function rest_tuple<T>(...xs: [T]): T {
function rest_tuple<T>(...xs: [T]): T { // Ok, tuples can be rest params
return xs[0];
}
function rest_any(...xs: any): any {
function rest_ro_array<T>(...xs: $ReadOnlyArray<T>): T { // Ok
return xs[0];
}
// Warn, arbitrary subtypes of an array type don't represent rest params
function rest_t<U, T: Array<U>>(...xs: T): U {
function rest_any(...xs: any): any { // Ok, any can be a rest param
return xs[0];
}
function rest_t<U, T: Array<U>>(...xs: T): U { // Ok, bounded targ can be rest
return xs[0];
}
// These are ok bounds for the rest param
function unbound_rest_t<T>(...xs: T): void {}
function mixed_rest_t<T: mixed>(...xs: T): void {}
function array_rest_t<T: Array<mixed>>(...xs: T): void {}
function roarray_rest_t<T: $ReadOnlyArray<mixed>>(...xs: T): void {}
function iterable_rest_t<T: Iterable<mixed>>(...xs: T): void {}
function empty_rest_t<T: empty>(...xs: T): void {}
function bounds_on_bounds<T>() {
return function<U: T>(...xs: T): void {}
}
// These are bad bounds for the rest param
function bad_unbound_rest_t<T>(...xs: T): T {
return xs.pop(); // Error - no bound on T
}
function string_rest_t<T: string>(...xs: T): void {} // Error - rest param can't be a string
function empty_rest_t<T: empty>(...xs: T): void {} // Error - rest param can't be empty
type Rest = Array<string>;
function rest_alias(...xs: Rest): void {} // Ok
function rest_union(...xs: [1,2] | Array<number>): number { // OK
return xs[0];
}
function rest_intersection(...xs: { x: number } & [1,2]): number { // OK
return xs[0] + xs.x;
}
function empty_rest<T:Array<mixed>>(...xs: T): T { return xs; }
(empty_rest(): empty); // Error Array ~> empty
function return_rest_param<Args:Array<mixed>>(
f: (...args: Args) => void,
): (...args: Args) => number {
return function(...args) {
return args; // Error: Array ~> number
}
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/* regression tests */
function rest_array<T>(...xs: Array<T>): T {
// Ok, arrays can be rest params
return xs[0];
}
// Warn, singleton tuple types don't represent rest params
function rest_tuple<T>(...xs: [T]): T {
// Ok, tuples can be rest params
return xs[0];
}
function rest_ro_array<T>(...xs: $ReadOnlyArray<T>): T {
// Ok
return xs[0];
}
function rest_any(...xs: any): any {
// Ok, any can be a rest param
return xs[0];
}
// Warn, arbitrary subtypes of an array type don't represent rest params
function rest_t<U, T: Array<U>>(...xs: T): U {
// Ok, bounded targ can be rest
return xs[0];
}
// These are ok bounds for the rest param
function unbound_rest_t<T>(...xs: T): void {}
function mixed_rest_t<T: mixed>(...xs: T): void {}
function array_rest_t<T: Array<mixed>>(...xs: T): void {}
function roarray_rest_t<T: $ReadOnlyArray<mixed>>(...xs: T): void {}
function iterable_rest_t<T: Iterable<mixed>>(...xs: T): void {}
function empty_rest_t<T: empty>(...xs: T): void {}
function bounds_on_bounds<T>() {
return function<U: T>(...xs: T): void {};
}
// These are bad bounds for the rest param
function bad_unbound_rest_t<T>(...xs: T): T {
return xs.pop(); // Error - no bound on T
}
function string_rest_t<T: string>(...xs: T): void {} // Error - rest param can't be a string
function empty_rest_t<T: empty>(...xs: T): void {} // Error - rest param can't be empty
type Rest = Array<string>;
function rest_alias(...xs: Rest): void {} // Ok
function rest_union(...xs: [1, 2] | Array<number>): number {
// OK
return xs[0];
}
function rest_intersection(...xs: { x: number } & [1, 2]): number {
// OK
return xs[0] + xs.x;
}
function empty_rest<T: Array<mixed>>(...xs: T): T {
return xs;
}
(empty_rest(): empty); // Error Array ~> empty
function return_rest_param<Args: Array<mixed>>(
f: (...args: Args) => void
): (...args: Args) => number {
return function(...args) {
return args; // Error: Array ~> number
};
}
"
`;
exports[`rest_type.js 1`] = `
"/* regression tests */
type rest_array = <T>(...xs: Array<T>) => T; // Ok, arrays can be rest params
type rest_tuple = <T>(...xs: [T]) => T; // Ok, tuples can be rest params
type rest_ro_array = <T>(...xs: $ReadOnlyArray<T>) => T; // Ok
type rest_any = (...xs: any) => any; // Ok, any can be a rest param
type rest_t = <U, T: Array<U>>(...xs: T) => U; // Ok, bounded targ can be rest
type unbound_rest_t = <T>(...xs: T) => void; // Should be error but no way to check yet :(
function test_unbound_rest(f: <T>(x: T, ...xs: T) => void) {
f(123); // Error - number ~> array - luckily this errors
}
type string_rest_t = (...xs: string) => void; // Should be error but no way to check yet :(
function test_string_rest(f: string_rest_t) {
f('hello'); // Error - string ~> array - luckily this errors
}
type Rest = Array<string>;
type rest_alias = (...xs: Rest) => void; // Ok
type rest_union = (...xs: [1,2] | Array<number>) => number; // OK
type rest_intersection = (...xs: { x: number } & [1,2]) => number; // OK
type empty_rest = <T:Array<mixed>>(...xs: T) => T; // OK
((f: empty_rest) => (f(): empty)); // Error Array ~> empty
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/* regression tests */
type rest_array = <T>(...xs: Array<T>) => T; // Ok, arrays can be rest params
type rest_tuple = <T>(...xs: [T]) => T; // Ok, tuples can be rest params
type rest_ro_array = <T>(...xs: $ReadOnlyArray<T>) => T; // Ok
type rest_any = (...xs: any) => any; // Ok, any can be a rest param
type rest_t = <U, T: Array<U>>(...xs: T) => U; // Ok, bounded targ can be rest
type unbound_rest_t = <T>(...xs: T) => void; // Should be error but no way to check yet :(
function test_unbound_rest(f: <T>(x: T, ...xs: T) => void) {
f(123); // Error - number ~> array - luckily this errors
}
type string_rest_t = (...xs: string) => void; // Should be error but no way to check yet :(
function test_string_rest(f: string_rest_t) {
f(\\"hello\\"); // Error - string ~> array - luckily this errors
}
type Rest = Array<string>;
type rest_alias = (...xs: Rest) => void; // Ok
type rest_union = (...xs: [1, 2] | Array<number>) => number; // OK
type rest_intersection = (...xs: { x: number } & [1, 2]) => number; // OK
type empty_rest = <T: Array<mixed>>(...xs: T) => T; // OK
(f: empty_rest) => (f(): empty); // Error Array ~> empty
"
`;

View File

@ -8,8 +8,8 @@ test.apply("", ["", 0]);
// wrong this is an error
test.apply(0, ["", 0]); // error: lookup `length` on Number
// not enough arguments is an error (via incompatible RestT)
test.apply("", [""]); // error: string ~> number
// not enough arguments is an error
test.apply("", [""]); // error: void ~> number
// mistyped arguments is an error
test.apply("", ["", ""]); // error: string ~> number (2nd arg)
@ -40,3 +40,9 @@ test.apply("", "not array"); // error: expect array of args
function test2(): number { return 0; }
(test2.apply(): number);
(test2.apply(""): number);
// callable objects
function test3(x: { (a: string, b: string): void }) {
x.apply(x, ['foo', 'bar']); // ok
x.apply(x, ['foo', 123]); // error, number !~> string
}

View File

@ -6,4 +6,23 @@ let tests = [
y('bar'); // ok
y(123); // error, number !~> string
},
// callable objects
function(x: { (a: string, b: string): void }) {
let y = x.bind(x, 'foo');
y('bar'); // ok
y(123); // error, number !~> string
},
// non-callable objects
function(x: { a: string }) {
x.bind(x, 'foo'); // error
},
// callable objects with overridden `bind` method
function(x: {(a: string, b: string): void, bind(a: string): void}) {
(x.bind('foo'): void); // ok
(x.bind(123): void); // error, number !~> string
},
];

View File

@ -10,8 +10,8 @@ test.call("", "", 0);
// wrong this is an error
test.call(0, "", 0); // error: lookup `length` on Number
// not enough arguments is an error (via incompatible RestT)
test.call("", ""); // error: string ~> number
// not enough arguments is an error
test.call("", ""); // error: void ~> number
// mistyped arguments is an error
test.call("", "", ""); // error: string ~> number (2nd arg)
@ -33,3 +33,9 @@ f([0, 0]); // error: number ~> string (1st arg)
function test2(): number { return 0; }
(test2.call(): number);
(test2.call(""): number);
// callable objects
function test3(x: { (a: string, b: string): void }) {
x.call(x, 'foo', 'bar'); // ok
x.call(x, 'foo', 123); // error, number !~> string
}

View File

@ -1,19 +1,61 @@
/* regression tests */
function rest_array<T>(...xs: Array<T>): T {
function rest_array<T>(...xs: Array<T>): T { // Ok, arrays can be rest params
return xs[0];
}
// Warn, singleton tuple types don't represent rest params
function rest_tuple<T>(...xs: [T]): T {
function rest_tuple<T>(...xs: [T]): T { // Ok, tuples can be rest params
return xs[0];
}
function rest_any(...xs: any): any {
function rest_ro_array<T>(...xs: $ReadOnlyArray<T>): T { // Ok
return xs[0];
}
// Warn, arbitrary subtypes of an array type don't represent rest params
function rest_t<U, T: Array<U>>(...xs: T): U {
function rest_any(...xs: any): any { // Ok, any can be a rest param
return xs[0];
}
function rest_t<U, T: Array<U>>(...xs: T): U { // Ok, bounded targ can be rest
return xs[0];
}
// These are ok bounds for the rest param
function unbound_rest_t<T>(...xs: T): void {}
function mixed_rest_t<T: mixed>(...xs: T): void {}
function array_rest_t<T: Array<mixed>>(...xs: T): void {}
function roarray_rest_t<T: $ReadOnlyArray<mixed>>(...xs: T): void {}
function iterable_rest_t<T: Iterable<mixed>>(...xs: T): void {}
function empty_rest_t<T: empty>(...xs: T): void {}
function bounds_on_bounds<T>() {
return function<U: T>(...xs: T): void {}
}
// These are bad bounds for the rest param
function bad_unbound_rest_t<T>(...xs: T): T {
return xs.pop(); // Error - no bound on T
}
function string_rest_t<T: string>(...xs: T): void {} // Error - rest param can't be a string
function empty_rest_t<T: empty>(...xs: T): void {} // Error - rest param can't be empty
type Rest = Array<string>;
function rest_alias(...xs: Rest): void {} // Ok
function rest_union(...xs: [1,2] | Array<number>): number { // OK
return xs[0];
}
function rest_intersection(...xs: { x: number } & [1,2]): number { // OK
return xs[0] + xs.x;
}
function empty_rest<T:Array<mixed>>(...xs: T): T { return xs; }
(empty_rest(): empty); // Error Array ~> empty
function return_rest_param<Args:Array<mixed>>(
f: (...args: Args) => void,
): (...args: Args) => number {
return function(...args) {
return args; // Error: Array ~> number
}
}

View File

@ -0,0 +1,31 @@
/* regression tests */
type rest_array = <T>(...xs: Array<T>) => T; // Ok, arrays can be rest params
type rest_tuple = <T>(...xs: [T]) => T; // Ok, tuples can be rest params
type rest_ro_array = <T>(...xs: $ReadOnlyArray<T>) => T; // Ok
type rest_any = (...xs: any) => any; // Ok, any can be a rest param
type rest_t = <U, T: Array<U>>(...xs: T) => U; // Ok, bounded targ can be rest
type unbound_rest_t = <T>(...xs: T) => void; // Should be error but no way to check yet :(
function test_unbound_rest(f: <T>(x: T, ...xs: T) => void) {
f(123); // Error - number ~> array - luckily this errors
}
type string_rest_t = (...xs: string) => void; // Should be error but no way to check yet :(
function test_string_rest(f: string_rest_t) {
f('hello'); // Error - string ~> array - luckily this errors
}
type Rest = Array<string>;
type rest_alias = (...xs: Rest) => void; // Ok
type rest_union = (...xs: [1,2] | Array<number>) => number; // OK
type rest_intersection = (...xs: { x: number } & [1,2]) => number; // OK
type empty_rest = <T:Array<mixed>>(...xs: T) => T; // OK
((f: empty_rest) => (f(): empty)); // Error Array ~> empty

View File

@ -577,6 +577,73 @@ if (multiple_return_result.done) {
"
`;
exports[`refi.js 1`] = `
"function *a(x: {a: void | string}): Generator<void, void, void> {
if (!x.a) return;
(x.a: string); // ok
yield;
(x.a: string); // error
}
function *b(x: void | string): Generator<void, void, void> {
if (!x) return;
(x: string); // ok
yield;
(x: string); // ok
}
declare function fn(): Generator<void, void, void>;
function *c(x: {a: void | string}): Generator<void, void, void> {
const gen = fn();
if (!x.a) return;
(x.a: string); // ok
yield * gen;
(x.a: string); // error
}
function *d(x: void | string): Generator<void, void, void> {
const gen = fn();
if (!x) return;
(x: string); // ok
yield * gen;
(x: string); // ok
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
function* a(x: { a: void | string }): Generator<void, void, void> {
if (!x.a) return;
(x.a: string); // ok
yield;
(x.a: string); // error
}
function* b(x: void | string): Generator<void, void, void> {
if (!x) return;
(x: string); // ok
yield;
(x: string); // ok
}
declare function fn(): Generator<void, void, void>;
function* c(x: { a: void | string }): Generator<void, void, void> {
const gen = fn();
if (!x.a) return;
(x.a: string); // ok
yield* gen;
(x.a: string); // error
}
function* d(x: void | string): Generator<void, void, void> {
const gen = fn();
if (!x) return;
(x: string); // ok
yield* gen;
(x: string); // ok
}
"
`;
exports[`return.js 1`] = `
"function test1(gen: Generator<void, string, void>) {
// You can pass whatever you like to return, it doesn't need to be related to

View File

@ -0,0 +1,31 @@
function *a(x: {a: void | string}): Generator<void, void, void> {
if (!x.a) return;
(x.a: string); // ok
yield;
(x.a: string); // error
}
function *b(x: void | string): Generator<void, void, void> {
if (!x) return;
(x: string); // ok
yield;
(x: string); // ok
}
declare function fn(): Generator<void, void, void>;
function *c(x: {a: void | string}): Generator<void, void, void> {
const gen = fn();
if (!x.a) return;
(x.a: string); // ok
yield * gen;
(x.a: string); // error
}
function *d(x: void | string): Generator<void, void, void> {
const gen = fn();
if (!x) return;
(x: string); // ok
yield * gen;
(x: string); // ok
}

View File

@ -0,0 +1 @@
run_spec(__dirname);

View File

@ -7,9 +7,31 @@ exports[`getters_and_setters.js 1`] = `
var f = {
get a() { return 4; },
set b(x: number) { this.c = b; },
set b(x: number) { this.c = x; },
c: 10,
get ['d']() { return 'foo'; },
set ['d'](x: number) {},
};
type T = {
get a(): number,
set b(x: number): void,
c: 10,
}
declare class Foo {
get a(): number;
set b(x: number): void;
c: 10;
}
class Bar {
get a() { return 4; }
set b(x: number) { this.c = x; }
c: number;
get ['d']() { return 'foo'; }
set ['d'](x: number) {}
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* @flow
@ -20,9 +42,39 @@ var f = {
return 4;
},
set b(x: number) {
this.c = b;
this.c = x;
},
c: 10,
get [\\"d\\"]() {
return \\"foo\\";
},
set [\\"d\\"](x: number) {}
};
type T = {
a: () => number,
b: (x: number) => void,
c: 10
};
declare class Foo {
a: () => number,
b: (x: number) => void,
c: 10
}
class Bar {
get a() {
return 4;
}
set b(x: number) {
this.c = x;
}
c: number;
get [\\"d\\"]() {
return \\"foo\\";
}
set [\\"d\\"](x: number) {}
}
"
`;

View File

@ -4,6 +4,28 @@
var f = {
get a() { return 4; },
set b(x: number) { this.c = b; },
set b(x: number) { this.c = x; },
c: 10,
get ['d']() { return 'foo'; },
set ['d'](x: number) {},
};
type T = {
get a(): number,
set b(x: number): void,
c: 10,
}
declare class Foo {
get a(): number;
set b(x: number): void;
c: 10;
}
class Bar {
get a() { return 4; }
set b(x: number) { this.c = x; }
c: number;
get ['d']() { return 'foo'; }
set ['d'](x: number) {}
}

View File

@ -33,6 +33,9 @@ class Foo {
propOverriddenWithSetter: number;
set propOverriddenWithSetter(x: string) { }
set [z](x: string) {}
get [z](): string { return string; }
};
var foo = new Foo();
@ -107,6 +110,11 @@ class Foo {
propOverriddenWithSetter: number;
set propOverriddenWithSetter(x: string) {}
set [z](x: string) {}
get [z](): string {
return string;
}
}
var foo = new Foo();
@ -133,6 +141,105 @@ foo.propOverriddenWithSetter = 123; // Error number ~> string
"
`;
exports[`declare_class.js 1`] = `
"/**
* @flow
*/
var z: number = 123;
declare class Foo {
get goodGetterWithAnnotation(): number;
set goodSetterWithAnnotation(x: number): void;
get propWithMatchingGetterAndSetter(): number;
set propWithMatchingGetterAndSetter(x: number): void;
// The getter and setter need not have the same type - no error
get propWithSubtypingGetterAndSetter(): ?number;
set propWithSubtypingGetterAndSetter(x: number): void;
// The getter and setter need not have the same type - no error
set propWithSubtypingGetterAndSetterReordered(x: number): void;
get propWithSubtypingGetterAndSetterReordered(): ?number;
get propWithMismatchingGetterAndSetter(): number;
set propWithMismatchingGetterAndSetter(x: string): void; // doesn't match getter (OK)
propOverriddenWithGetter: number;
get propOverriddenWithGetter(): string;
propOverriddenWithSetter: number;
set propOverriddenWithSetter(x: string): void;
};
var foo = new Foo();
// Test getting properties with getters
var testGetterNoError2: number = foo.goodGetterWithAnnotation;
var testGetterWithError2: string = foo.goodGetterWithAnnotation; // Error number ~> string
// Test setting properties with getters
foo.goodSetterWithAnnotation = 123;
foo.goodSetterWithAnnotation = \\"hello\\"; // Error string ~> number
var testSubtypingGetterAndSetter: number = foo.propWithSubtypingGetterAndSetter; // Error ?number ~> number
var testPropOverridenWithGetter: number = foo.propOverriddenWithGetter; // Error string ~> number
foo.propOverriddenWithSetter = 123; // Error number ~> string
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* @flow
*/
var z: number = 123;
declare class Foo {
goodGetterWithAnnotation: () => number,
goodSetterWithAnnotation: (x: number) => void,
propWithMatchingGetterAndSetter: () => number,
propWithMatchingGetterAndSetter: (x: number) => void,
// The getter and setter need not have the same type - no error
propWithSubtypingGetterAndSetter: () => ?number,
propWithSubtypingGetterAndSetter: (x: number) => void,
// The getter and setter need not have the same type - no error
propWithSubtypingGetterAndSetterReordered: (x: number) => void,
propWithSubtypingGetterAndSetterReordered: () => ?number,
propWithMismatchingGetterAndSetter: () => number,
propWithMismatchingGetterAndSetter: (x: string) => void, // doesn't match getter (OK)
propOverriddenWithGetter: number,
propOverriddenWithGetter: () => string,
propOverriddenWithSetter: number,
propOverriddenWithSetter: (x: string) => void
}
var foo = new Foo();
// Test getting properties with getters
var testGetterNoError2: number = foo.goodGetterWithAnnotation;
var testGetterWithError2: string = foo.goodGetterWithAnnotation; // Error number ~> string
// Test setting properties with getters
foo.goodSetterWithAnnotation = 123;
foo.goodSetterWithAnnotation = \\"hello\\"; // Error string ~> number
var testSubtypingGetterAndSetter: number = foo.propWithSubtypingGetterAndSetter; // Error ?number ~> number
var testPropOverridenWithGetter: number = foo.propOverriddenWithGetter; // Error string ~> number
foo.propOverriddenWithSetter = 123; // Error number ~> string
"
`;
exports[`object.js 1`] = `
"/**
* @flow
@ -166,6 +273,9 @@ var obj = {
set exampleOfOrderOfGetterAndSetterReordered(x: B) {},
get exampleOfOrderOfGetterAndSetterReordered(): A { return new A(); },
set [z](x: string) {},
get [z](): string { return string; },
};
@ -244,6 +354,11 @@ var obj = {
set exampleOfOrderOfGetterAndSetterReordered(x: B) {},
get exampleOfOrderOfGetterAndSetterReordered(): A {
return new A();
},
set [z](x: string) {},
get [z](): string {
return string;
}
};
@ -273,34 +388,153 @@ var testExampleOrOrderOfGetterAndSetterReordered: number = obj.exampleOfOrderOfG
"
`;
exports[`react.js 1`] = `
exports[`object_type.js 1`] = `
"/**
* @flow
*/
React.createClass({
propTypes: {
get a() { return 4; },
set b(x: number) { this.c = x; },
c: 10,
}
});
var z: number = 123;
class A {}
class B extends A {}
class C extends A {}
type T = {
get goodGetterWithAnnotation(): number,
set goodSetterWithAnnotation(x: number): void,
get propWithMatchingGetterAndSetter(): number,
set propWithMatchingGetterAndSetter(x: number): void,
// The getter and setter need not have the same type
get propWithSubtypingGetterAndSetter(): ?number, // OK
set propWithSubtypingGetterAndSetter(x: number): void,
set propWithSubtypingGetterAndSetterReordered(x: number): void, // OK
get propWithSubtypingGetterAndSetterReordered(): ?number,
get exampleOfOrderOfGetterAndSetter(): A,
set exampleOfOrderOfGetterAndSetter(x: B): void,
set exampleOfOrderOfGetterAndSetterReordered(x: B): void,
get exampleOfOrderOfGetterAndSetterReordered(): A,
};
function test(obj: T) {
// Test getting properties with getters
var testGetterNoError2: number = obj.goodGetterWithAnnotation;
var testGetterWithError2: string = obj.goodGetterWithAnnotation; // Error number ~> string
// Test setting properties with getters
obj.goodSetterWithAnnotation = 123;
obj.goodSetterWithAnnotation = \\"hello\\"; // Error string ~> number
var testSubtypingGetterAndSetter: number = obj.propWithSubtypingGetterAndSetter; // Error ?number ~> number
// When building this feature, it was tempting to flow the setter into the
// getter and then use either the getter or setter as the type of the
// property. This example shows the danger of using the getter's type
obj.exampleOfOrderOfGetterAndSetter = new C(); // Error C ~> B
// And this example shows the danger of using the setter's type.
var testExampleOrOrderOfGetterAndSetterReordered: number =
obj.exampleOfOrderOfGetterAndSetterReordered; // Error A ~> B
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* @flow
*/
React.createClass({
var z: number = 123;
class A {}
class B extends A {}
class C extends A {}
type T = {
goodGetterWithAnnotation: () => number,
goodSetterWithAnnotation: (x: number) => void,
propWithMatchingGetterAndSetter: () => number,
propWithMatchingGetterAndSetter: (x: number) => void,
// The getter and setter need not have the same type
propWithSubtypingGetterAndSetter: () => ?number, // OK
propWithSubtypingGetterAndSetter: (x: number) => void,
propWithSubtypingGetterAndSetterReordered: (x: number) => void, // OK
propWithSubtypingGetterAndSetterReordered: () => ?number,
exampleOfOrderOfGetterAndSetter: () => A,
exampleOfOrderOfGetterAndSetter: (x: B) => void,
exampleOfOrderOfGetterAndSetterReordered: (x: B) => void,
exampleOfOrderOfGetterAndSetterReordered: () => A
};
function test(obj: T) {
// Test getting properties with getters
var testGetterNoError2: number = obj.goodGetterWithAnnotation;
var testGetterWithError2: string = obj.goodGetterWithAnnotation; // Error number ~> string
// Test setting properties with getters
obj.goodSetterWithAnnotation = 123;
obj.goodSetterWithAnnotation = \\"hello\\"; // Error string ~> number
var testSubtypingGetterAndSetter: number = obj.propWithSubtypingGetterAndSetter; // Error ?number ~> number
// When building this feature, it was tempting to flow the setter into the
// getter and then use either the getter or setter as the type of the
// property. This example shows the danger of using the getter's type
obj.exampleOfOrderOfGetterAndSetter = new C(); // Error C ~> B
// And this example shows the danger of using the setter's type.
var testExampleOrOrderOfGetterAndSetterReordered: number = obj.exampleOfOrderOfGetterAndSetterReordered; // Error A ~> B
}
"
`;
exports[`react.js 1`] = `
"/**
* @flow
*/
import React from \\"react\\";
const Example = React.createClass({
propTypes: {
get a() { return React.PropTypes.number.isRequired; },
set b(x: number) { this.c = x; },
c: React.PropTypes.string,
}
});
(<Example />); // error: property \`a\` not found
(<Example a={0} />); // ok
(<Example a=\\"bad\\" />); // error: number ~> string
(<Example a={0} c={0} />); // error: number ~> string
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* @flow
*/
import React from \\"react\\";
const Example = React.createClass({
propTypes: {
get a() {
return 4;
return React.PropTypes.number.isRequired;
},
set b(x: number) {
this.c = x;
},
c: 10
c: React.PropTypes.string
}
});
<Example />; // error: property \`a\` not found
<Example a={0} />; // ok
<Example a=\\"bad\\" />; // error: number ~> string
<Example a={0} c={0} />; // error: number ~> string
"
`;

View File

@ -30,6 +30,9 @@ class Foo {
propOverriddenWithSetter: number;
set propOverriddenWithSetter(x: string) { }
set [z](x: string) {}
get [z](): string { return string; }
};
var foo = new Foo();

View File

@ -0,0 +1,47 @@
/**
* @flow
*/
var z: number = 123;
declare class Foo {
get goodGetterWithAnnotation(): number;
set goodSetterWithAnnotation(x: number): void;
get propWithMatchingGetterAndSetter(): number;
set propWithMatchingGetterAndSetter(x: number): void;
// The getter and setter need not have the same type - no error
get propWithSubtypingGetterAndSetter(): ?number;
set propWithSubtypingGetterAndSetter(x: number): void;
// The getter and setter need not have the same type - no error
set propWithSubtypingGetterAndSetterReordered(x: number): void;
get propWithSubtypingGetterAndSetterReordered(): ?number;
get propWithMismatchingGetterAndSetter(): number;
set propWithMismatchingGetterAndSetter(x: string): void; // doesn't match getter (OK)
propOverriddenWithGetter: number;
get propOverriddenWithGetter(): string;
propOverriddenWithSetter: number;
set propOverriddenWithSetter(x: string): void;
};
var foo = new Foo();
// Test getting properties with getters
var testGetterNoError2: number = foo.goodGetterWithAnnotation;
var testGetterWithError2: string = foo.goodGetterWithAnnotation; // Error number ~> string
// Test setting properties with getters
foo.goodSetterWithAnnotation = 123;
foo.goodSetterWithAnnotation = "hello"; // Error string ~> number
var testSubtypingGetterAndSetter: number = foo.propWithSubtypingGetterAndSetter; // Error ?number ~> number
var testPropOverridenWithGetter: number = foo.propOverriddenWithGetter; // Error string ~> number
foo.propOverriddenWithSetter = 123; // Error number ~> string

View File

@ -1 +1,5 @@
run_spec(__dirname, null, ["babylon"]);
run_spec(__dirname);
// TODO: These tests used to be run in babylon as well, but currently babylon
// fails to parse them.
// run_spec(__dirname, null, ["babylon"]);

View File

@ -30,6 +30,9 @@ var obj = {
set exampleOfOrderOfGetterAndSetterReordered(x: B) {},
get exampleOfOrderOfGetterAndSetterReordered(): A { return new A(); },
set [z](x: string) {},
get [z](): string { return string; },
};

View File

@ -0,0 +1,53 @@
/**
* @flow
*/
var z: number = 123;
class A {}
class B extends A {}
class C extends A {}
type T = {
get goodGetterWithAnnotation(): number,
set goodSetterWithAnnotation(x: number): void,
get propWithMatchingGetterAndSetter(): number,
set propWithMatchingGetterAndSetter(x: number): void,
// The getter and setter need not have the same type
get propWithSubtypingGetterAndSetter(): ?number, // OK
set propWithSubtypingGetterAndSetter(x: number): void,
set propWithSubtypingGetterAndSetterReordered(x: number): void, // OK
get propWithSubtypingGetterAndSetterReordered(): ?number,
get exampleOfOrderOfGetterAndSetter(): A,
set exampleOfOrderOfGetterAndSetter(x: B): void,
set exampleOfOrderOfGetterAndSetterReordered(x: B): void,
get exampleOfOrderOfGetterAndSetterReordered(): A,
};
function test(obj: T) {
// Test getting properties with getters
var testGetterNoError2: number = obj.goodGetterWithAnnotation;
var testGetterWithError2: string = obj.goodGetterWithAnnotation; // Error number ~> string
// Test setting properties with getters
obj.goodSetterWithAnnotation = 123;
obj.goodSetterWithAnnotation = "hello"; // Error string ~> number
var testSubtypingGetterAndSetter: number = obj.propWithSubtypingGetterAndSetter; // Error ?number ~> number
// When building this feature, it was tempting to flow the setter into the
// getter and then use either the getter or setter as the type of the
// property. This example shows the danger of using the getter's type
obj.exampleOfOrderOfGetterAndSetter = new C(); // Error C ~> B
// And this example shows the danger of using the setter's type.
var testExampleOrOrderOfGetterAndSetterReordered: number =
obj.exampleOfOrderOfGetterAndSetterReordered; // Error A ~> B
}

View File

@ -2,10 +2,17 @@
* @flow
*/
React.createClass({
import React from "react";
const Example = React.createClass({
propTypes: {
get a() { return 4; },
get a() { return React.PropTypes.number.isRequired; },
set b(x: number) { this.c = x; },
c: 10,
c: React.PropTypes.string,
}
});
(<Example />); // error: property `a` not found
(<Example a={0} />); // ok
(<Example a="bad" />); // error: number ~> string
(<Example a={0} c={0} />); // error: number ~> string

View File

@ -0,0 +1,7 @@
/* @flow */
var test = require('test');
(test: boolean);
module.exports = test;

Some files were not shown because too many files have changed in this diff Show More