From ce1368aad56a4eee29dccac2534955381d4986bb Mon Sep 17 00:00:00 2001 From: zojize Date: Wed, 14 Aug 2024 13:30:21 +0800 Subject: [PATCH 1/2] Add TupleGenerator --- src/compiler/checker.ts | 54 +++++++++++++++++++++++++++++++++++ src/lib/es2015.generator.d.ts | 3 ++ 2 files changed, 57 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4b8d44e62fc9f..90bb6529dd39e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1185,6 +1185,7 @@ interface IterationTypesResolver { getGlobalIteratorObjectType: (reportErrors: boolean) => GenericType; getGlobalGeneratorType: (reportErrors: boolean) => GenericType; getGlobalBuiltinIteratorTypes: () => readonly GenericType[]; + getGlobalTupleGeneratorType: (reportErrors: boolean) => GenericType; resolveIterationType: (type: Type, errorNode: Node | undefined) => Type | undefined; mustHaveANextMethodDiagnostic: DiagnosticMessage; mustBeAMethodDiagnostic: DiagnosticMessage; @@ -2171,6 +2172,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { getGlobalIterableIteratorType: getGlobalAsyncIterableIteratorType, getGlobalIteratorObjectType: getGlobalAsyncIteratorObjectType, getGlobalGeneratorType: getGlobalAsyncGeneratorType, + getGlobalTupleGeneratorType, getGlobalBuiltinIteratorTypes: getGlobalBuiltinAsyncIteratorTypes, resolveIterationType: (type, errorNode) => getAwaitedType(type, errorNode, Diagnostics.Type_of_await_operand_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member), mustHaveANextMethodDiagnostic: Diagnostics.An_async_iterator_must_have_a_next_method, @@ -2187,6 +2189,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { getGlobalIterableIteratorType, getGlobalIteratorObjectType, getGlobalGeneratorType, + getGlobalTupleGeneratorType, getGlobalBuiltinIteratorTypes, resolveIterationType: (type, _errorNode) => type, mustHaveANextMethodDiagnostic: Diagnostics.An_iterator_must_have_a_next_method, @@ -2250,6 +2253,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { var deferredGlobalIterableIteratorType: GenericType | undefined; var deferredGlobalIteratorObjectType: GenericType | undefined; var deferredGlobalGeneratorType: GenericType | undefined; + var deferredGlobalTupleGeneratorType: GenericType | undefined; var deferredGlobalIteratorYieldResultType: GenericType | undefined; var deferredGlobalIteratorReturnResultType: GenericType | undefined; var deferredGlobalAsyncIterableType: GenericType | undefined; @@ -17044,6 +17048,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return (deferredGlobalGeneratorType ||= getGlobalType("Generator" as __String, /*arity*/ 3, reportErrors)) || emptyGenericType; } + function getGlobalTupleGeneratorType(reportErrors: boolean) { + return (deferredGlobalTupleGeneratorType ||= getGlobalType("TupleGenerator" as __String, /*arity*/ 3, reportErrors)) || emptyGenericType; + } + function getGlobalIteratorYieldResultType(reportErrors: boolean) { return (deferredGlobalIteratorYieldResultType ||= getGlobalType("IteratorYieldResult" as __String, /*arity*/ 1, reportErrors)) || emptyGenericType; } @@ -32505,10 +32513,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { checkExternalEmitHelpers(e, compilerOptions.downlevelIteration ? ExternalEmitHelpers.SpreadIncludes : ExternalEmitHelpers.SpreadArray); } const spreadType = checkExpression((e as SpreadElement).expression, checkMode, forceTuple); + let maybeTuple: Type | undefined; if (isArrayLikeType(spreadType)) { elementTypes.push(spreadType); elementFlags.push(ElementFlags.Variadic); } + else if ((maybeTuple = getTupleTypeFromIterableOrUndefined(spreadType))) { + elementTypes.push(maybeTuple); + elementFlags.push(ElementFlags.Variadic); + } else if (inDestructuringPattern) { // Given the following situation: // var c: {}; @@ -35434,6 +35447,18 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { effectiveArgs.push(syntheticArg); }); } + else if (spreadType) { + const spreadTypeArgs = getTupleTypeFromIterableOrUndefined(spreadType); + if (spreadTypeArgs) { + forEach(getElementTypes(spreadTypeArgs), (t, i) => { + const flags = spreadTypeArgs.target.elementFlags[i]; + const syntheticArg = createSyntheticExpression(arg, flags & ElementFlags.Rest ? createArrayType(t) : t, !!(flags & ElementFlags.Variable), spreadTypeArgs.target.labeledElementDeclarations?.[i]); + effectiveArgs.push(syntheticArg); + }); + } else { + effectiveArgs.push(arg); + } + } else { effectiveArgs.push(arg); } @@ -35443,6 +35468,35 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return args; } + function getTupleTypeFromIterableOrUndefined(type: Type) { + let maybeTuple = isReferenceToType(type, syncIterationTypesResolver.getGlobalTupleGeneratorType(/*reportErrors*/ false)) + ? getTypeArguments(type as GenericType)[0] + : undefined; + + if (!maybeTuple) { + const method = getPropertyOfType(type, getPropertyNameForKnownSymbolName(syncIterationTypesResolver.iteratorSymbolName)); + const methodType = method && !(method.flags & SymbolFlags.Optional) ? getTypeOfSymbol(method) : undefined; + + if (isTypeAny(methodType)) { + return; + } + + const signatures = methodType && getSignaturesOfType(methodType, SignatureKind.Call); + if (!some(signatures)) { + return; + } + + const returnTypes = getIntersectionType(map(signatures, getReturnTypeOfSignature)) + if (isReferenceToType(returnTypes, syncIterationTypesResolver.getGlobalTupleGeneratorType(/*reportErrors*/ false))) { + maybeTuple = getTypeArguments(returnTypes as GenericType)[0]; + } + } + + if (maybeTuple && isTupleType(maybeTuple)) { + return maybeTuple; + } + } + /** * Returns the synthetic argument list for a decorator invocation. */ diff --git a/src/lib/es2015.generator.d.ts b/src/lib/es2015.generator.d.ts index 435806e925f1e..922f89c53d878 100644 --- a/src/lib/es2015.generator.d.ts +++ b/src/lib/es2015.generator.d.ts @@ -8,6 +8,9 @@ interface Generator extends IteratorObj [Symbol.iterator](): Generator; } +interface TupleGenerator + extends Generator {} + interface GeneratorFunction { /** * Creates a new Generator object. From 2cfc2a2ce8836f925762154d7f58c32543d61336 Mon Sep 17 00:00:00 2001 From: zojize Date: Wed, 14 Aug 2024 13:30:36 +0800 Subject: [PATCH 2/2] Test TupleGenerator --- .../reference/spreadTupleGenerator.js | 25 ++++++++ .../reference/spreadTupleGenerator.symbols | 34 +++++++++++ .../reference/spreadTupleGenerator.types | 59 +++++++++++++++++++ tests/cases/compiler/spreadTupleGenerator.ts | 14 +++++ 4 files changed, 132 insertions(+) create mode 100644 tests/baselines/reference/spreadTupleGenerator.js create mode 100644 tests/baselines/reference/spreadTupleGenerator.symbols create mode 100644 tests/baselines/reference/spreadTupleGenerator.types create mode 100644 tests/cases/compiler/spreadTupleGenerator.ts diff --git a/tests/baselines/reference/spreadTupleGenerator.js b/tests/baselines/reference/spreadTupleGenerator.js new file mode 100644 index 0000000000000..a567106d56517 --- /dev/null +++ b/tests/baselines/reference/spreadTupleGenerator.js @@ -0,0 +1,25 @@ +//// [tests/cases/compiler/spreadTupleGenerator.ts] //// + +//// [spreadTupleGenerator.ts] +class V { + *[Symbol.iterator](): TupleGenerator<[number, number]> { + yield 1; yield 2; + } +} + +declare const v: V; +declare function foo(x: number, y: number): void; + +foo(...v); +const a: [number, number] = [...v]; + + +//// [spreadTupleGenerator.js] +class V { + *[Symbol.iterator]() { + yield 1; + yield 2; + } +} +foo(...v); +const a = [...v]; diff --git a/tests/baselines/reference/spreadTupleGenerator.symbols b/tests/baselines/reference/spreadTupleGenerator.symbols new file mode 100644 index 0000000000000..151a5353dcb7a --- /dev/null +++ b/tests/baselines/reference/spreadTupleGenerator.symbols @@ -0,0 +1,34 @@ +//// [tests/cases/compiler/spreadTupleGenerator.ts] //// + +=== spreadTupleGenerator.ts === +class V { +>V : Symbol(V, Decl(spreadTupleGenerator.ts, 0, 0)) + + *[Symbol.iterator](): TupleGenerator<[number, number]> { +>[Symbol.iterator] : Symbol(V[Symbol.iterator], Decl(spreadTupleGenerator.ts, 0, 9)) +>Symbol.iterator : Symbol(SymbolConstructor.iterator, Decl(lib.es2015.iterable.d.ts, --, --)) +>Symbol : Symbol(Symbol, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2019.symbol.d.ts, --, --)) +>iterator : Symbol(SymbolConstructor.iterator, Decl(lib.es2015.iterable.d.ts, --, --)) +>TupleGenerator : Symbol(TupleGenerator, Decl(lib.es2015.generator.d.ts, --, --)) + + yield 1; yield 2; + } +} + +declare const v: V; +>v : Symbol(v, Decl(spreadTupleGenerator.ts, 6, 13)) +>V : Symbol(V, Decl(spreadTupleGenerator.ts, 0, 0)) + +declare function foo(x: number, y: number): void; +>foo : Symbol(foo, Decl(spreadTupleGenerator.ts, 6, 19)) +>x : Symbol(x, Decl(spreadTupleGenerator.ts, 7, 21)) +>y : Symbol(y, Decl(spreadTupleGenerator.ts, 7, 31)) + +foo(...v); +>foo : Symbol(foo, Decl(spreadTupleGenerator.ts, 6, 19)) +>v : Symbol(v, Decl(spreadTupleGenerator.ts, 6, 13)) + +const a: [number, number] = [...v]; +>a : Symbol(a, Decl(spreadTupleGenerator.ts, 10, 5)) +>v : Symbol(v, Decl(spreadTupleGenerator.ts, 6, 13)) + diff --git a/tests/baselines/reference/spreadTupleGenerator.types b/tests/baselines/reference/spreadTupleGenerator.types new file mode 100644 index 0000000000000..c9adb0a784236 --- /dev/null +++ b/tests/baselines/reference/spreadTupleGenerator.types @@ -0,0 +1,59 @@ +//// [tests/cases/compiler/spreadTupleGenerator.ts] //// + +=== spreadTupleGenerator.ts === +class V { +>V : V +> : ^ + + *[Symbol.iterator](): TupleGenerator<[number, number]> { +>[Symbol.iterator] : () => TupleGenerator<[number, number]> +> : ^^^^^^ +>Symbol.iterator : unique symbol +> : ^^^^^^^^^^^^^ +>Symbol : SymbolConstructor +> : ^^^^^^^^^^^^^^^^^ +>iterator : unique symbol +> : ^^^^^^^^^^^^^ + + yield 1; yield 2; +>yield 1 : any +>1 : 1 +> : ^ +>yield 2 : any +>2 : 2 +> : ^ + } +} + +declare const v: V; +>v : V +> : ^ + +declare function foo(x: number, y: number): void; +>foo : (x: number, y: number) => void +> : ^ ^^ ^^ ^^ ^^^^^ +>x : number +> : ^^^^^^ +>y : number +> : ^^^^^^ + +foo(...v); +>foo(...v) : void +> : ^^^^ +>foo : (x: number, y: number) => void +> : ^ ^^ ^^ ^^ ^^^^^ +>...v : number +> : ^^^^^^ +>v : V +> : ^ + +const a: [number, number] = [...v]; +>a : [number, number] +> : ^^^^^^^^^^^^^^^^ +>[...v] : [number, number] +> : ^^^^^^^^^^^^^^^^ +>...v : number +> : ^^^^^^ +>v : V +> : ^ + diff --git a/tests/cases/compiler/spreadTupleGenerator.ts b/tests/cases/compiler/spreadTupleGenerator.ts new file mode 100644 index 0000000000000..f50c727df5572 --- /dev/null +++ b/tests/cases/compiler/spreadTupleGenerator.ts @@ -0,0 +1,14 @@ +// @lib: esnext +// @target: esnext + +class V { + *[Symbol.iterator](): TupleGenerator<[number, number]> { + yield 1; yield 2; + } +} + +declare const v: V; +declare function foo(x: number, y: number): void; + +foo(...v); +const a: [number, number] = [...v];