Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve type checking and inference for Generators and Async Generators #30790

Merged
merged 16 commits into from Jul 4, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
793 changes: 566 additions & 227 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/compiler/commandLineParser.ts
Expand Up @@ -40,6 +40,7 @@ namespace ts {
["es2017.string", "lib.es2017.string.d.ts"],
["es2017.intl", "lib.es2017.intl.d.ts"],
["es2017.typedarrays", "lib.es2017.typedarrays.d.ts"],
["es2018.asyncgenerator", "lib.es2018.asyncgenerator.d.ts"],
["es2018.asynciterable", "lib.es2018.asynciterable.d.ts"],
["es2018.intl", "lib.es2018.intl.d.ts"],
["es2018.promise", "lib.es2018.promise.d.ts"],
Expand Down
8 changes: 8 additions & 0 deletions src/compiler/diagnosticMessages.json
Expand Up @@ -3915,6 +3915,10 @@
"category": "Message",
"code": 6217
},
"Enable strict checking of generator types.": {
"category": "Message",
"code": 6218
},

"Projects to reference": {
"category": "Message",
Expand Down Expand Up @@ -4272,6 +4276,10 @@
"category": "Error",
"code": 7051
},
"Generator implicitly has type '{0}' because it does not yield or return any values. Consider supplying a return type.": {
"category": "Error",
"code": 7052
},

"You cannot rename this element.": {
"category": "Error",
Expand Down
16 changes: 12 additions & 4 deletions src/compiler/types.ts
Expand Up @@ -4234,13 +4234,21 @@ namespace ts {
regularType: ResolvedType; // Regular version of fresh type
}

/* @internal */
export interface IterationTypes {
yieldType?: Type;
returnType?: Type;
nextType?: Type;
}

// Just a place to cache element types of iterables and iterators
/* @internal */
export interface IterableOrIteratorType extends ObjectType, UnionType {
iteratedTypeOfIterable?: Type;
iteratedTypeOfIterator?: Type;
iteratedTypeOfAsyncIterable?: Type;
iteratedTypeOfAsyncIterator?: Type;
iterationTypesOfIterable?: IterationTypes;
iterationTypesOfIterator?: IterationTypes;
iterationTypesOfAsyncIterable?: IterationTypes;
iterationTypesOfAsyncIterator?: IterationTypes;
iterationTypesOfIteratorResult?: IterationTypes;
}

/* @internal */
Expand Down
4 changes: 2 additions & 2 deletions src/harness/vfs.ts
Expand Up @@ -683,7 +683,7 @@ namespace vfs {

if (isDirectory(node)) throw createIOError("EISDIR");
if (!isFile(node)) throw createIOError("EBADF");
node.buffer = Buffer.isBuffer(data) ? data.slice() : ts.sys.bufferFrom!("" + data, encoding || "utf8");
node.buffer = Buffer.isBuffer(data) ? data.slice() : ts.sys.bufferFrom!("" + data, encoding || "utf8") as Buffer;
node.size = node.buffer.byteLength;
node.mtimeMs = time;
node.ctimeMs = time;
Expand Down Expand Up @@ -1204,7 +1204,7 @@ namespace vfs {
}
},
readFileSync(path: string): Buffer {
return ts.sys.bufferFrom!(host.readFile(path)!, "utf8"); // TODO: GH#18217
return ts.sys.bufferFrom!(host.readFile(path)!, "utf8") as Buffer; // TODO: GH#18217
}
};
}
Expand Down
9 changes: 8 additions & 1 deletion src/lib/es2015.generator.d.ts
@@ -1,4 +1,11 @@
interface Generator extends Iterator<any> { }
/// <reference lib="es2015.iterable" />

interface Generator<TYield = unknown, TReturn = void, TNext = unknown> {
next(value?: TNext): IteratorResult<TYield, TReturn>;
return(value: TReturn): IteratorResult<TYield, TReturn>;
throw(e: unknown): IteratorResult<TYield, TReturn>;
[Symbol.iterator](): Generator<TYield, TReturn, TNext>;
}

interface GeneratorFunction {
/**
Expand Down
13 changes: 10 additions & 3 deletions src/lib/es2015.iterable.d.ts
Expand Up @@ -8,11 +8,18 @@ interface SymbolConstructor {
readonly iterator: symbol;
}

interface IteratorResult<T> {
done: boolean;
value: T;
interface IteratorYieldResult<TYield> {
done: false;
rbuckton marked this conversation as resolved.
Show resolved Hide resolved
value: TYield;
}

interface IteratorReturnResult<TReturn> {
rbuckton marked this conversation as resolved.
Show resolved Hide resolved
done: true;
value: TReturn;
}

type IteratorResult<T, TReturn = T | void> = IteratorYieldResult<T> | IteratorReturnResult<TReturn>;

interface Iterator<T> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this not changed? Does this PR not improve the experience when writing iterators manually (without generator syntax)?

next(value?: any): IteratorResult<T>;
return?(value?: any): IteratorResult<T>;
Expand Down
58 changes: 58 additions & 0 deletions src/lib/es2018.asyncgenerator.d.ts
@@ -0,0 +1,58 @@
/// <reference lib="es2018.asynciterable" />

interface AsyncGenerator<TYield = unknown, TReturn = void, TNext = unknown> {
next(value?: TNext): Promise<IteratorResult<TYield, TReturn>>;
return(value: TReturn): Promise<IteratorResult<TYield, TReturn>>;
throw(e: unknown): Promise<IteratorResult<TYield, TReturn>>;
[Symbol.asyncIterator](): AsyncGenerator<TYield, TReturn, TNext>;
}

interface AsyncGeneratorFunction {
/**
* Creates a new AsyncGenerator object.
* @param args A list of arguments the function accepts.
*/
new (...args: any[]): AsyncGenerator;
/**
* Creates a new AsyncGenerator object.
* @param args A list of arguments the function accepts.
*/
(...args: any[]): AsyncGenerator;
/**
* The length of the arguments.
*/
readonly length: number;
/**
* Returns the name of the function.
*/
readonly name: string;
/**
* A reference to the prototype.
*/
readonly prototype: AsyncGenerator;
}

interface AsyncGeneratorFunctionConstructor {
/**
* Creates a new AsyncGenerator function.
* @param args A list of arguments the function accepts.
*/
new (...args: string[]): AsyncGeneratorFunction;
/**
* Creates a new AsyncGenerator function.
* @param args A list of arguments the function accepts.
*/
(...args: string[]): AsyncGeneratorFunction;
/**
* The length of the arguments.
*/
readonly length: number;
/**
* Returns the name of the function.
*/
readonly name: string;
/**
* A reference to the prototype.
*/
readonly prototype: AsyncGeneratorFunction;
}
1 change: 1 addition & 0 deletions src/lib/es2018.d.ts
@@ -1,4 +1,5 @@
/// <reference lib="es2017" />
/// <reference lib="es2018.asyncgenerator" />
/// <reference lib="es2018.asynciterable" />
/// <reference lib="es2018.promise" />
/// <reference lib="es2018.regexp" />
Expand Down
1 change: 1 addition & 0 deletions src/lib/libs.json
Expand Up @@ -31,6 +31,7 @@
"es2017.string",
"es2017.intl",
"es2017.typedarrays",
"es2018.asyncgenerator",
"es2018.asynciterable",
"es2018.regexp",
"es2018.promise",
Expand Down
4 changes: 2 additions & 2 deletions src/testRunner/parallel/host.ts
Expand Up @@ -269,8 +269,8 @@ namespace Harness.Parallel.Host {
worker.accumulatedOutput += d.toString();
console.log(`[Worker ${i}]`, d.toString());
};
worker.process.stderr.on("data", appendOutput);
worker.process.stdout.on("data", appendOutput);
worker.process.stderr!.on("data", appendOutput);
worker.process.stdout!.on("data", appendOutput);
const killChild = (timeout: TaskTimeout) => {
worker.process.kill();
console.error(`Worker exceeded ${timeout.duration}ms timeout ${worker.currentTasks && worker.currentTasks.length ? `while running test '${worker.currentTasks[0].file}'.` : `during test setup.`}`);
Expand Down
6 changes: 3 additions & 3 deletions src/testRunner/unittests/config/commandLineParsing.ts
Expand Up @@ -57,7 +57,7 @@ namespace ts {
assertParseResult(["--lib", "es5,invalidOption", "0.ts"],
{
errors: [{
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'es2019', 'es2020', 'esnext', 'dom', 'dom.iterable', 'webworker', 'webworker.importscripts', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'es2018.asynciterable', 'es2018.intl', 'es2018.promise', 'es2018.regexp', 'es2019.array', 'es2019.string', 'es2019.symbol', 'es2020.string', 'es2020.symbol.wellknown', 'esnext.array', 'esnext.symbol', 'esnext.asynciterable', 'esnext.intl', 'esnext.bigint'.",
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'es2019', 'es2020', 'esnext', 'dom', 'dom.iterable', 'webworker', 'webworker.importscripts', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'es2018.asyncgenerator', 'es2018.asynciterable', 'es2018.intl', 'es2018.promise', 'es2018.regexp', 'es2019.array', 'es2019.string', 'es2019.symbol', 'es2020.string', 'es2020.symbol.wellknown', 'esnext.array', 'esnext.symbol', 'esnext.asynciterable', 'esnext.intl', 'esnext.bigint'.",
category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category,
code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
file: undefined,
Expand Down Expand Up @@ -259,7 +259,7 @@ namespace ts {
assertParseResult(["--lib", "es5,", "es7", "0.ts"],
{
errors: [{
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'es2019', 'es2020', 'esnext', 'dom', 'dom.iterable', 'webworker', 'webworker.importscripts', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'es2018.asynciterable', 'es2018.intl', 'es2018.promise', 'es2018.regexp', 'es2019.array', 'es2019.string', 'es2019.symbol', 'es2020.string', 'es2020.symbol.wellknown', 'esnext.array', 'esnext.symbol', 'esnext.asynciterable', 'esnext.intl', 'esnext.bigint'.",
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'es2019', 'es2020', 'esnext', 'dom', 'dom.iterable', 'webworker', 'webworker.importscripts', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'es2018.asyncgenerator', 'es2018.asynciterable', 'es2018.intl', 'es2018.promise', 'es2018.regexp', 'es2019.array', 'es2019.string', 'es2019.symbol', 'es2020.string', 'es2020.symbol.wellknown', 'esnext.array', 'esnext.symbol', 'esnext.asynciterable', 'esnext.intl', 'esnext.bigint'.",
category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category,
code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
file: undefined,
Expand All @@ -278,7 +278,7 @@ namespace ts {
assertParseResult(["--lib", "es5, ", "es7", "0.ts"],
{
errors: [{
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'es2019', 'es2020', 'esnext', 'dom', 'dom.iterable', 'webworker', 'webworker.importscripts', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'es2018.asynciterable', 'es2018.intl', 'es2018.promise', 'es2018.regexp', 'es2019.array', 'es2019.string', 'es2019.symbol', 'es2020.string', 'es2020.symbol.wellknown', 'esnext.array', 'esnext.symbol', 'esnext.asynciterable', 'esnext.intl', 'esnext.bigint'.",
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'es2019', 'es2020', 'esnext', 'dom', 'dom.iterable', 'webworker', 'webworker.importscripts', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'es2018.asyncgenerator', 'es2018.asynciterable', 'es2018.intl', 'es2018.promise', 'es2018.regexp', 'es2019.array', 'es2019.string', 'es2019.symbol', 'es2020.string', 'es2020.symbol.wellknown', 'esnext.array', 'esnext.symbol', 'esnext.asynciterable', 'esnext.intl', 'esnext.bigint'.",
category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category,
code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
file: undefined,
Expand Down
2 changes: 1 addition & 1 deletion src/tsconfig-base.json
@@ -1,7 +1,7 @@
{
"compilerOptions": {
"pretty": true,
"lib": ["es2015.iterable", "es5"],
"lib": ["es2015.iterable", "es2015.generator", "es5"],
"target": "es5",
"rootDir": ".",

Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/FunctionDeclaration11_es6.types
@@ -1,4 +1,4 @@
=== tests/cases/conformance/es6/functionDeclarations/FunctionDeclaration11_es6.ts ===
function * yield() {
>yield : () => IterableIterator<any>
>yield : () => Generator<never, void, unknown>
}
2 changes: 1 addition & 1 deletion tests/baselines/reference/FunctionDeclaration13_es6.types
@@ -1,6 +1,6 @@
=== tests/cases/conformance/es6/functionDeclarations/FunctionDeclaration13_es6.ts ===
function * foo() {
>foo : () => IterableIterator<any>
>foo : () => Generator<never, void, unknown>

// Legal to use 'yield' in a type context.
var v: yield;
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/FunctionDeclaration1_es6.types
@@ -1,4 +1,4 @@
=== tests/cases/conformance/es6/functionDeclarations/FunctionDeclaration1_es6.ts ===
function * foo() {
>foo : () => IterableIterator<any>
>foo : () => Generator<never, void, unknown>
}
2 changes: 1 addition & 1 deletion tests/baselines/reference/FunctionDeclaration6_es6.types
@@ -1,6 +1,6 @@
=== tests/cases/conformance/es6/functionDeclarations/FunctionDeclaration6_es6.ts ===
function*foo(a = yield) {
>foo : (a?: any) => IterableIterator<any>
>foo : (a?: any) => Generator<never, void, unknown>
>a : any
>yield : any
}
4 changes: 2 additions & 2 deletions tests/baselines/reference/FunctionDeclaration7_es6.types
@@ -1,10 +1,10 @@
=== tests/cases/conformance/es6/functionDeclarations/FunctionDeclaration7_es6.ts ===
function*bar() {
>bar : () => IterableIterator<any>
>bar : () => Generator<never, void, unknown>

// 'yield' here is an identifier, and not a yield expression.
function*foo(a = yield) {
>foo : (a?: any) => IterableIterator<any>
>foo : (a?: any) => Generator<never, void, unknown>
>a : any
>yield : any
}
Expand Down
10 changes: 5 additions & 5 deletions tests/baselines/reference/FunctionDeclaration9_es6.types
@@ -1,11 +1,11 @@
=== tests/cases/conformance/es6/functionDeclarations/FunctionDeclaration9_es6.ts ===
function * foo() {
>foo : () => IterableIterator<any>
>foo : () => Generator<any, void, unknown>

var v = { [yield]: foo }
>v : { [x: number]: () => IterableIterator<any>; }
>{ [yield]: foo } : { [x: number]: () => IterableIterator<any>; }
>[yield] : () => IterableIterator<any>
>v : { [x: number]: () => Generator<any, void, unknown>; }
>{ [yield]: foo } : { [x: number]: () => Generator<any, void, unknown>; }
>[yield] : () => Generator<any, void, unknown>
>yield : any
>foo : () => IterableIterator<any>
>foo : () => Generator<any, void, unknown>
}
4 changes: 2 additions & 2 deletions tests/baselines/reference/FunctionExpression1_es6.types
@@ -1,5 +1,5 @@
=== tests/cases/conformance/es6/functionExpressions/FunctionExpression1_es6.ts ===
var v = function * () { }
>v : () => IterableIterator<any>
>function * () { } : () => IterableIterator<any>
>v : () => Generator<never, void, unknown>
>function * () { } : () => Generator<never, void, unknown>

6 changes: 3 additions & 3 deletions tests/baselines/reference/FunctionExpression2_es6.types
@@ -1,6 +1,6 @@
=== tests/cases/conformance/es6/functionExpressions/FunctionExpression2_es6.ts ===
var v = function * foo() { }
>v : () => IterableIterator<any>
>function * foo() { } : () => IterableIterator<any>
>foo : () => IterableIterator<any>
>v : () => Generator<never, void, unknown>
>function * foo() { } : () => Generator<never, void, unknown>
>foo : () => Generator<never, void, unknown>

@@ -1,6 +1,6 @@
=== tests/cases/conformance/es6/functionPropertyAssignments/FunctionPropertyAssignments1_es6.ts ===
var v = { *foo() { } }
>v : { foo(): IterableIterator<any>; }
>{ *foo() { } } : { foo(): IterableIterator<any>; }
>foo : () => IterableIterator<any>
>v : { foo(): Generator<never, void, unknown>; }
>{ *foo() { } } : { foo(): Generator<never, void, unknown>; }
>foo : () => Generator<never, void, unknown>

@@ -1,6 +1,6 @@
=== tests/cases/conformance/es6/functionPropertyAssignments/FunctionPropertyAssignments2_es6.ts ===
var v = { *() { } }
>v : { (Missing)(): IterableIterator<any>; }
>{ *() { } } : { (Missing)(): IterableIterator<any>; }
> : () => IterableIterator<any>
>v : { (Missing)(): Generator<never, void, unknown>; }
>{ *() { } } : { (Missing)(): Generator<never, void, unknown>; }
> : () => Generator<never, void, unknown>

@@ -1,6 +1,6 @@
=== tests/cases/conformance/es6/functionPropertyAssignments/FunctionPropertyAssignments3_es6.ts ===
var v = { *{ } }
>v : { (Missing)(): IterableIterator<any>; }
>{ *{ } } : { (Missing)(): IterableIterator<any>; }
> : () => IterableIterator<any>
>v : { (Missing)(): Generator<never, void, unknown>; }
>{ *{ } } : { (Missing)(): Generator<never, void, unknown>; }
> : () => Generator<never, void, unknown>

@@ -1,8 +1,8 @@
=== tests/cases/conformance/es6/functionPropertyAssignments/FunctionPropertyAssignments5_es6.ts ===
var v = { *[foo()]() { } }
>v : { [x: number]: () => IterableIterator<any>; }
>{ *[foo()]() { } } : { [x: number]: () => IterableIterator<any>; }
>[foo()] : () => IterableIterator<any>
>v : { [x: number]: () => Generator<never, void, unknown>; }
>{ *[foo()]() { } } : { [x: number]: () => Generator<never, void, unknown>; }
>[foo()] : () => Generator<never, void, unknown>
>foo() : any
>foo : any

@@ -1,6 +1,6 @@
=== tests/cases/conformance/es6/functionPropertyAssignments/FunctionPropertyAssignments6_es6.ts ===
var v = { *<T>() { } }
>v : { (Missing)<T>(): IterableIterator<any>; }
>{ *<T>() { } } : { (Missing)<T>(): IterableIterator<any>; }
> : <T>() => IterableIterator<any>
>v : { (Missing)<T>(): Generator<never, void, unknown>; }
>{ *<T>() { } } : { (Missing)<T>(): Generator<never, void, unknown>; }
> : <T>() => Generator<never, void, unknown>

Expand Up @@ -3,5 +3,5 @@ class C {
>C : C

*foo() { }
>foo : () => IterableIterator<any>
>foo : () => Generator<never, void, unknown>
}
Expand Up @@ -3,5 +3,5 @@ class C {
>C : C

public * foo() { }
>foo : () => IterableIterator<any>
>foo : () => Generator<never, void, unknown>
}
Expand Up @@ -3,6 +3,6 @@ class C {
>C : C

*[foo]() { }
>[foo] : () => IterableIterator<any>
>[foo] : () => Generator<never, void, unknown>
>foo : any
}
Expand Up @@ -3,5 +3,5 @@ class C {
>C : C

*() { }
> : () => IterableIterator<any>
> : () => Generator<never, void, unknown>
}
Expand Up @@ -3,5 +3,5 @@ class C {
>C : C

*foo<T>() { }
>foo : <T>() => IterableIterator<any>
>foo : <T>() => Generator<never, void, unknown>
}