GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Tuples in rest parameters and spread expressions by ahejlsberg · Pull Request #24897 · microsoft/TypeScript

Conversation

@ahejlsberg

@ahejlsberg Anders Hejlsberg (ahejlsberg) commented Jun 12, 2018 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

This PR includes the following:

  • Expansion of rest parameters with tuple types into discrete parameters.
  • Expansion of spread expressions with tuple types into discrete arguments.
  • Generic rest parameters and corresponding inference of tuple types.
  • Optional elements in tuple types.
  • Rest elements in tuple types.

With these features it becomes possible to strongly type a number of higher-order functions that transform functions and their parameter lists (such as JavaScript's bind, call, and apply).

The PR implements the a number of the capabilities discussed in #5453, but with considerably less complexity.

Rest parameters with tuple types

When a rest parameter has a tuple type, the tuple type is expanded into a sequence of discrete parameters. For example the following two declarations are equivalent:

declare function foo(...args: [number, string, boolean]): void;
declare function foo(args_0: number, args_1: string, args_2: boolean): void;

EDIT: With #26676 the type of rest parameter can be a union of tuple types. This effectively provides a form of overloading expressed in a single function signature.

Spread expressions with tuple types

When a function call includes a spread expression of a tuple type as the last argument, the spread expression corresponds to a sequence of discrete arguments of the tuple element types. Thus, the following calls are equivalent:

const args: [number, string, boolean] = [42, "hello", true];
foo(42, "hello", true);
foo(args[0], args[1], args[2]);
foo(...args);

Generic rest parameters

A rest parameter is permitted to have a generic type that is constrained to an array type, and type inference can infer tuple types for such generic rest parameters. This enables higher-order capturing and spreading of partial parameter lists:

declare function bind<T, U extends any[], V>(f: (x: T, ...args: U) => V, x: T): (...args: U) => V;

declare function f3(x: number, y: string, z: boolean): void;

const f2 = bind(f3, 42);  // (y: string, z: boolean) => void
const f1 = bind(f2, "hello");  // (z: boolean) => void
const f0 = bind(f1, true);  // () => void

f3(42, "hello", true);
f2("hello", true);
f1(true);
f0();

In the declaration of f2 above, type inference infers types number, [string, boolean] and void for T, U and V respectively.

Note that when a tuple type is inferred from a sequence of parameters and later expanded into a parameter list, as is the case for U, the original parameter names are used in the expansion (however, the names have no semantic meaning and are not otherwise observable).

Optional elements in tuple types

Tuple types now permit a ? postfix on element types to indicate that the element is optional:

let t: [number, string?, boolean?];
t = [42, "hello", true];
t = [42, "hello"];
t = [42];

In --strictNullChecks mode, a ? modifier automatically includes undefined in the element type, similar to optional parameters.

A tuple type permits an element to be omitted if it has a postfix ? modifier on its type and all elements to the right of it also have ? modifiers.

When tuple types are inferred for rest parameters, optional parameters in the source become optional tuple elements in the inferred type.

The length property of a tuple type with optional elements is a union of numeric literal types representing the possible lengths. For example, the type of the length property in the tuple type [number, string?, boolean?] is 1 | 2 | 3.

Rest elements in tuple types

EDIT: Updated to reflect change from string* to ...string[] syntax for rest elements.

The last element of a tuple type can be a rest element of the form ...X, where X is an array type. A rest element indicates that the tuple type is open-ended and may have zero or more additional elements of the array element type. For example, [number, ...string[]] means tuples with a number element followed by any number of string elements.

function tuple<T extends any[]>(...args: T): T {
    return args;
}

const numbers: number[] = getArrayOfNumbers();
const t1 = tuple("foo", 1, true);  // [string, number, boolean]
const t2 = tuple("bar", ...numbers);  // [string, ...number[]]

The type of the length property of a tuple type with a rest element is number.

Strong typing of bind, call, and apply

With this PR it becomes possible to strongly type the bind, call, and apply methods on function objects. This is however a breaking change for some existing code so we need to investigate the repercussions.

Fixes #1024.
Fixes #4130.
Fixes #5331.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Kingwl, cartant, zzj3720, chgc, dested, gcnew, olaf89, filipbech, niba, otodockal, and 227 more reacted with thumbs up emoji Kingwl, zzj3720, MQuy, mweststrate, johannesnagl, j-oliveras, BassT, Bnaya, VitorLuizC, bgoscinski, and 36 more reacted with laugh emoji Kingwl, pelotom, cartant, arjunyel, zzj3720, otodockal, j-oliveras, TobiaszCudnik, s-ve, MQuy, and 112 more reacted with hooray emoji Kingwl, johnnyreilly, zzj3720, chgc, j-oliveras, blakeembrey, gabro, MQuy, JoshuaKGoldberg, karol-majewski, and 100 more reacted with heart emoji
All reactions
  • 👍 237 reactions
  • 😄 46 reactions
  • 🎉 122 reactions
  • ❤️ 110 reactions
Anders Hejlsberg (ahejlsberg) added 11 commits June 7, 2018 12:23
@ahejlsberg
Allow generic rest parameters and infer tuples when possible
8cd8edb
@ahejlsberg
Accept new baselines
43bfccf
@ahejlsberg
Optional elements in tuple types + empty tuple types + other fixes.
2e04322
@ahejlsberg
Accept new baselines
346276c
@ahejlsberg
Expand rest parameter with tuple types when emitting signatures
c69e4e7
@ahejlsberg
Accept new baselines
73e008d
@ahejlsberg
Record parameter names in inferred tuple types
7aee647
@ahejlsberg
Fix lint errors
9ae9371
@ahejlsberg
Flatten spread expressions of tuple types
e239f86
@ahejlsberg
Accept new baselines
0b0a91a
@ahejlsberg
Accept baseline API changes
5245642
@Kingwl

Wenlu Wang (Kingwl) commented Jun 12, 2018

Copy link
Copy Markdown
Contributor

thanks for your great working🤩

rkovalov, vrachels, georgyfarniev, TeoTN, and mahoo12138 reacted with thumbs up emoji fvilante reacted with laugh emoji
All reactions
  • 👍 5 reactions
  • 😄 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@felixfbecker

Felix Becker (felixfbecker) commented Jun 12, 2018 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

This looks incredible and I can't wait to make use of this.


Open-ended tuple types (in progress).

That sounds amazing - this would allow us to type the return value of string.split() as always having at least one element:

split(delimiter: string): [string, string*]

In a world where we have #13778, that would mean the types for destructured split would be correctly inferred:

// [string, string | undefined]
const [package, version] = '[email protected]'.split('@')

The * Kleene star looks a bit weird because it is not used for rest syntax in JS. Maybe spread syntax could be used? This would align with a future generic type spread operator: [string, ...number[]]
The inner array type is spread into the tuple.

Kingwl, Cryrivers, jovdb, timocov, ajafff, AlexGalays, rhalff, sergeysova, jaen, dsherret, and 14 more reacted with thumbs up emoji
All reactions
  • 👍 24 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@jovdb

Van den Berghe Jo (jovdb) commented Jun 12, 2018 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

Great!,
I think you made a typo in the comment of f1. shouldn't z be of type boolean?

const f1 = bind(f2, "hello");  // (z: boolean) => void
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@Cryrivers

Zhongliang Wang (Cryrivers) commented Jun 12, 2018

Copy link
Copy Markdown

Great work! Now TypeScript is one step closer to #5453!

Rather than having [string, number*], could we support spread operator in types as well, so we could have something like [string, ...number[]]?

jovdb, Strate, sergeysova, goodmind, jaen, Lenne231, esamattis, KiaraGrouwstra, bterlson, SlurpTheo, and 3 more reacted with thumbs up emoji
All reactions
  • 👍 13 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@Strate

Artur Eshenbrener (Strate) commented Jun 12, 2018

Copy link
Copy Markdown

Zhongliang Wang (@Cryrivers)

Rather than having [string, number*], could we support spread operator in types as well, so we could have something like [string, ...number[]]?

Or just have multiple rest arguments: (a: string, ...rest: [string, number], ...restLast: string[])

jovdb and greg-hornby-roam reacted with confused emoji
All reactions
  • 😕 2 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@goodmind

andretshurotshka (goodmind) commented Jun 12, 2018

Copy link
Copy Markdown

But tuples can't be infinite by definition?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ahejlsberg
Merge branch 'master' into restTuples
39099a0
# Conflicts:
#	tests/baselines/reference/api/tsserverlibrary.d.ts
#	tests/baselines/reference/api/typescript.d.ts
@goodmind andretshurotshka (goodmind) mentioned this pull request Jun 12, 2018
operate on tuples KiaraGrouwstra/typical#17
Open
@ahejlsberg
Accept baseline changes in APIs
dee51ed
@j-oliveras

Jordi Oliveras Rovira (j-oliveras) commented Jun 12, 2018

Copy link
Copy Markdown
Contributor

andretshurotshka (@goodmind) before this PR, tuples are fixed sized.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@j-oliveras Jordi Oliveras Rovira (j-oliveras) mentioned this pull request Jun 12, 2018
Allow use of spread operator in tuple types #24902
Closed
4 tasks done
@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Jun 12, 2018

Copy link
Copy Markdown
Member Author

Van den Berghe Jo (@jovdb) Yes, that was a typo. Now fixed. Thanks for catching.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@goodmind

andretshurotshka (goodmind) commented Jun 12, 2018

Copy link
Copy Markdown

Anders Hejlsberg (@ahejlsberg) can this be used to type compose, pipe or curry?

Bnaya, danielyogel, cah-sam-beran, KiaraGrouwstra, felixfbecker, haltcase, and karthikiyengar reacted with thumbs up emoji
All reactions
  • 👍 7 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Jun 12, 2018

Copy link
Copy Markdown
Member Author

Felix Becker (@felixfbecker) Zhongliang Wang (@Cryrivers) There are two ways we could go on the syntax for the last element in an open-ended tuple:

[string, number*]
[string, ...number[]]

The latter is longer but certainly makes it easier to see the connection to rest parameters. If we go with the latter, the last element will be required to have an array type. This PR will specifically not permit the equivalent of generic rest parameters in tuples--that would add considerable complexity. But certainly the ...X syntax would better accommodate that feature later on, should we choose to go there.

ibezkrovnyi, Lenne231, and fvilante reacted with thumbs up emoji
All reactions
  • 👍 3 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@felixfbecker

Felix Becker (felixfbecker) commented Jun 12, 2018

Copy link
Copy Markdown
Contributor

I think the last type being required to be an array is perfectly acceptable.

ckknight, KiaraGrouwstra, jovdb, lazarljubenovic, benjamingr, abenhamdine, SlurpTheo, and SpencerPark reacted with thumbs up emoji
All reactions
  • 👍 8 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@KiaraGrouwstra

kiara (KiaraGrouwstra) commented Jun 12, 2018 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

This is a wonderful step forward!

+1 to the [string, ...number[]] syntax, I feel despite its verbosity it would keep the syntax intuitive to JS users.

Potential extensions needed for the functions andretshurotshka (@goodmind) mentioned:

  • generic support (the ...X mentioned by Anders Hejlsberg (@ahejlsberg))
  • expanding tuple types in tuple type literals (tuple type spread #17884) (edit: same as Concat, defined by @fightingcat at TypeScripts Type System is Turing Complete #14833):
type Tpl = [string, number];
type MyTpl = [...Tpl, boolean];

(^ or using a generic Tpl, as above)

agentcooper, Cryrivers, Akiq2016, SlurpTheo, jameslaneconkling, and ethanresnick reacted with thumbs up emoji agalazis and chrisfls reacted with heart emoji
All reactions
  • 👍 6 reactions
  • ❤️ 2 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@bterlson

Brian Terlson (bterlson) commented Jun 12, 2018 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

I believe that the better choice for the open-ended tuple syntax is [ ... string[] ].

Star has two benefits: arguably better syntax, and preventing generic rest parameters (edit: for now 😝). The latter is solved as Anders Hejlsberg (@ahejlsberg) notes by always requiring the last element to have an array type, which I think is a reasonable restriction.

Syntactically, I guess * looks very foreign to JS developers and that most would prefer a more familiar syntax (this thread is good evidence, thanks everyone!). * also opens up a whole can of worms around which "regexp-type operators" are supported (e.g. can we write [string, string*] as [string+]?). Lastly, if we get full support for variadic types, the ... syntax will have to be supported anyway, and thus we will end up with two syntaxes for the same thing (i.e. [number, string*] and [number, ... string[]] and presumably a lint rule to make one or the other preferred).

I think we should just adopt the javascripty syntax that will be intuitive to those with experience using rest/spread and is a pure subset of existing proposals for variadic types and others.

DanielRosenwasser, weswigham, mshustov, ckknight, Cryrivers, codahk, benjamingr, capaj, SlurpTheo, mariusnn, and 2 more reacted with thumbs up emoji
All reactions
  • 👍 12 reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@mshustov Mikhail Shustov (mshustov) mentioned this pull request Jun 12, 2018
Typescript args issue redux-saga/redux-saga#1482
Closed
@ahejlsberg
Better algorithm for combined co- and contra-variant inferences.
f93f6ec
@nspaeth Nathan (nspaeth) mentioned this pull request Sep 4, 2018
Self reference model with Typescript and MST v3 mobxjs/mobx-state-tree#943
Closed
@kgtkr

kgtkr (kgtkr) commented Sep 7, 2018 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

T["length"] example

type Take<N extends number, T extends any[], R extends any[]=[]> = {
  0: Reverse<R>,
  1: Take<N, Tail<T>, Cons<Head<T>, R>>
}[T extends [] ? 0 : R["length"] extends N ? 0 : 1];

export type Group<N extends number, T extends any[], R1 extends any[]=[], R2 extends any[]=[]> = {
  0: Reverse<R2>,
  1: Group<N, T, [], Cons<Reverse<R1>, R2>>,
  2: Group<N, Tail<T>, Cons<Head<T>, R1>, R2>
}[T extends [] ? R1 extends [] ? 0 : 1 : (R1["length"] extends N ? 1 : 2)];

export type Drop<N extends number, T extends any[], R extends any[]=[]> = {
  0: T,
  1: Drop<N, Tail<T>, Cons<Head<T>, R>>
}[T extends [] ? 0 : R["length"] extends N ? 0 : 1];
TylorS reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

emmanueltouzery referenced this pull request in emmanueltouzery/prelude-ts Sep 9, 2018
@emmanueltouzery
some work on flipped, doesn't work and will give up due to TS authors…
e5acd48
… discouraging this https://github.com/Microsoft/TypeScript/pull/24897\#issuecomment-401418254
@emmanueltouzery emmanueltouzery mentioned this pull request Sep 9, 2018
Add Future.liftXXX emmanueltouzery/prelude-ts#12
Closed
@ahejlsberg Anders Hejlsberg (ahejlsberg) mentioned this pull request Sep 11, 2018
Strict bind, call, and apply methods on functions #27028
Merged
@jcalz Joe Calzaretta (jcalz) mentioned this pull request Sep 14, 2018
Feature request: lift the circular constraint for conditional types #26980
Closed
4 tasks done
@ahejlsberg Anders Hejlsberg (ahejlsberg) mentioned this pull request Sep 18, 2018
Add a way to prefer narrowing array to tuple if possible #27179
Closed
@jcalz Joe Calzaretta (jcalz) mentioned this pull request Sep 21, 2018
Mapped tuple types #25947
Closed
4 tasks done
This was referenced Sep 22, 2018
Proposal: support pathof along with current keyof #20423
Open
tuple type spread #17884
Closed
@damyanpetev Damyan Petev (damyanpetev) mentioned this pull request Oct 31, 2018
refactor(toggle): update overlay subscription filter to tuple IgniteUI/igniteui-angular#2898
Merged
@ahejlsberg Anders Hejlsberg (ahejlsberg) mentioned this pull request Jan 6, 2019
Fails to calculate arguments length, when combining tuple spread with extra arguments #29248
Closed
@MattiasBuelens Mattias Buelens (MattiasBuelens) mentioned this pull request Feb 4, 2019
Improve type of RollupOutput.output rollup/rollup#2679
Merged
3 of 9 tasks
@otofu-square Hidemi Yukita (otofu-square) mentioned this pull request Mar 1, 2019
Add support for TypeScript testing-library/react-hooks-testing-library#8
Merged
3 tasks done
@ahejlsberg Anders Hejlsberg (ahejlsberg) mentioned this pull request Mar 9, 2019
Higher order function type inference #30215
Merged
@JakeTunaley JakeTunaley mentioned this pull request Apr 10, 2019
Recursive type definitions regression in 3.4.0 #30841
Closed
This was referenced May 1, 2019
Spread / Flatten Types #31192
Closed
Unable to append a type to an existing tuple #31286
Closed
@ahejlsberg Anders Hejlsberg (ahejlsberg) mentioned this pull request Jun 4, 2019
3.5.1 regression when using this with an index signature of a generic union type #31731
Closed
@ChristianMurphy Christian Murphy (ChristianMurphy) mentioned this pull request Jul 13, 2019
Stop allowing extraSettings unifiedjs/unified#60
Closed
@anion155 Anton Mikhailov (anion155) mentioned this pull request Oct 3, 2019
Spread operator in tuple types not supported Swatinem/rollup-plugin-dts#59
Closed
@jcalz Joe Calzaretta (jcalz) mentioned this pull request Dec 18, 2019
Flatten Function Parameters and remember param names? #35752
Closed
@trickeyd

idiosync (trickeyd) commented Feb 23, 2020 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

Hello - this fix apparently makes this possible: #24897 but I can't for the life of me figure out how and that thread has been closed now.

I am trying to implement a signal that is used in the following way:

const mySignal = Signal<string, number, SomeType>()
mySignal.emit('sdf', 123, someObj)

So basically emit uses rest arguments that are typed when the signal is created.

Could someone point me in the right direction regarding what the generics for this might look like?

Many thanks!

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@jcalz Joe Calzaretta (jcalz) mentioned this pull request May 1, 2020
Accurate Array.prototype.flat definition #32131
Merged
@jack-williams Jack Williams (jack-williams) mentioned this pull request Aug 15, 2020
Conditional type doesn't work inside tuple #40067
Closed
@stuft2 Spencer Tuft (stuft2) mentioned this pull request Mar 4, 2021
Improve typescript types Gi60s/openapi-enforcer-middleware#40
Merged
@SSShuai1999 Jeffery Song (SSShuai1999) mentioned this pull request Jun 23, 2021
20 - Promise.all type-challenges/type-challenges#1924
Open
@microsoft Microsoft (microsoft) locked as resolved and limited conversation to collaborators Oct 21, 2025
@typescript-bot TypeScript Bot (typescript-bot) assigned Anders Hejlsberg (ahejlsberg) and Nathan Shively-Sanders (sandersn) Oct 21, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Reviewers

@sandersn Nathan Shively-Sanders (sandersn) Awaiting requested review from sandersn
@DanielRosenwasser Daniel Rosenwasser (DanielRosenwasser) Awaiting requested review from DanielRosenwasser
1 more reviewer
@mhegazy Mohamed Hegazy (mhegazy) mhegazy approved these changes
Reviewers whose approvals may not affect merge requirements

Uh oh!

There was an error while loading. Please reload this page.

Assignees

@sandersn Nathan Shively-Sanders (sandersn)

@ahejlsberg Anders Hejlsberg (ahejlsberg)

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Tuples as types for rest ...arguments Compiler incorrectly reports parameter/call target signature mismatch when using the spread operator Typed ...rest parameters with generics

Uh oh!

There was an error while loading. Please reload this page.

20 participants

@ahejlsberg @Kingwl @felixfbecker @jovdb @Cryrivers @Strate @goodmind @j-oliveras @KiaraGrouwstra @bterlson @ckknight @timocov @MartinJohns @icholy @mpawelski @itsMapleLeaf @HerringtonDarkholme @Igorbek @pleerock @Lodin
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.