{"action":"created","issue":{"url":"https://api.github.com/repos/Microsoft/TypeScript/issues/5453","repository_url":"https://api.github.com/repos/Microsoft/TypeScript","labels_url":"https://api.github.com/repos/Microsoft/TypeScript/issues/5453/labels{/name}","comments_url":"https://api.github.com/repos/Microsoft/TypeScript/issues/5453/comments","events_url":"https://api.github.com/repos/Microsoft/TypeScript/issues/5453/events","html_url":"https://github.com/Microsoft/TypeScript/issues/5453","id":114100808,"number":5453,"title":"Proposal: Variadic Kinds -- Give specific types to variadic functions","user":{"login":"sandersn","id":293473,"avatar_url":"https://avatars.githubusercontent.com/u/293473?v=3","gravatar_id":"","url":"https://api.github.com/users/sandersn","html_url":"https://github.com/sandersn","followers_url":"https://api.github.com/users/sandersn/followers","following_url":"https://api.github.com/users/sandersn/following{/other_user}","gists_url":"https://api.github.com/users/sandersn/gists{/gist_id}","starred_url":"https://api.github.com/users/sandersn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sandersn/subscriptions","organizations_url":"https://api.github.com/users/sandersn/orgs","repos_url":"https://api.github.com/users/sandersn/repos","events_url":"https://api.github.com/users/sandersn/events{/privacy}","received_events_url":"https://api.github.com/users/sandersn/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/Microsoft/TypeScript/labels/In%20Discussion","name":"In Discussion","color":"556b75"},{"url":"https://api.github.com/repos/Microsoft/TypeScript/labels/Suggestion","name":"Suggestion","color":"006b75"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":47,"created_at":"2015-10-29T17:14:57Z","updated_at":"2016-02-27T19:09:50Z","closed_at":null,"body":"# Variadic Kinds \r\n\r\n## Give Specific Types to Variadic Functions\r\n\r\nThis proposal lets Typescript give types to higher-order functions that take a variable number of parameters.\r\nFunctions like this include `concat`, `apply`, `curry`, `compose` and almost any decorator that wraps a function.\r\nIn Javascript, these higher-order functions are expected to accept variadic functionsas arguments.\r\nWith the ES2015 and ES2017 standards, this use will become even more common as programmers start using spread arguments and rest parameters for both arrays and objects.\r\nThis proposal addresses these use cases with a single, very general typing strategy based on higher-order kinds.\r\n\r\nThis proposal would completely or partially address several issues, including:\r\n1. #5331 -- Tuples as types for rest ...arguments\r\n2. #4130 -- Compiler incorrectly reports parameter/call target signature mismatch when using the spread operator\r\n3. #4988 -- Tuples should be clonable with Array.prototype.slice()\r\n4. #1773 -- Variadic generics?\r\n5. #3870 -- Rest types in generics for intersection types.\r\n6. #212 -- bind, call and apply are untyped (requires #3694's this-function types).\r\n7. #1024 -- Typed ...rest parameters with generics\r\n\r\nI'll be updating this proposal on my fork of the Typescript-Handbook: sandersn/TypeScript-Handbook@76f5a75868de3fb1ad4dbed5db437a8ab61a2698\r\nI have an in-progress implementation at sandersn/TypeScript@f3c327aef22f6251532309ba046874133c32f4c7 which currently has the simple parts of the proposal implemented.\r\nIt supercedes part 2 of my previous proposal, #5296.\r\nEdit: Added a section on assignability. I'm no longer sure that it strictly supercedes #5296.\r\n\r\n## Preview example with `curry`\r\n\r\n`curry` for functions with two arguments is simple to write in Javascript and Typescript:\r\n\r\n```js\r\nfunction curry(f, a) {\r\n    return b => f(a, b);\r\n}\r\n```\r\n\r\nand in Typescript with type annotations:\r\n\r\n```ts\r\nfunction curry<T, U, V>(f: (t: T, u: U) => V, a:T): (b:U) => V {\r\n    return b => f(a, b);\r\n}\r\n```\r\n\r\nHowever, a variadic version is easy to write in Javascript but cannot be given a type in TypeScript:\r\n\r\n```js\r\nfunction curry(f, ...a) {\r\n    return ...b => f(...a, ...b);\r\n}\r\n```\r\n\r\nHere's an example of using variadic kinds from this proposal to type `curry`:\r\n\r\n```ts\r\nfunction curry<...T,...U,V>(f: (...ts: [...T, ...U]) => V, ...as:...T): (...bs:...U) => V {\r\n    return ...b => f(...a, ...b);\r\n}\r\n```\r\n\r\nThe syntax for variadic tuple types that I use here matches the spread and rest syntax used for values in Javascript.\r\nThis is easier to learn but might make it harder to distinguish type annotations from value expressions.\r\nSimilarly, the syntax for concatenating looks like tuple construction, even though it's really concatenation of two tuple types.\r\n\r\nNow let's look at an example call to `curry`:\r\n\r\n```ts\r\nfunction f(n: number, m: number, s: string, c: string): [number, number, string, string] {\r\n    return [n,m,s,c];\r\n}\r\nlet [n,m,s,c] = curry(f, 1, 2)('foo', 'x');\r\nlet [n,m,s,c] = curry(f, 1, 2, 'foo', 'x')();\r\n```\r\n\r\nIn the first call, \r\n\r\n```\r\nV = [number, number, string, string]\r\n...T = [number, number]\r\n...U = [string, string]\r\n```\r\n\r\nIn the second call, \r\n\r\n```\r\nV = [number, number, string, string]\r\n...T = [number, number, string, string]\r\n...U = []\r\n```\r\n\r\n## Syntax\r\n\r\nThe syntax of a variadic kind variable is `...T` where *T* is an identifier that is by convention a single upper-case letter, or `T` followed by a `PascalCase` identifier.\r\nVariadic kind variables can be used in a number of syntactic contexts:\r\n\r\nVariadic kinds can be bound in the usual location for type parameter binding, including functions and classes:\r\n\r\n```ts\r\nfunction f<...T,...U>() {}\r\n}\r\nclass C<...T> {\r\n}\r\n```\r\n\r\nAnd they can be referenced in any type annotation location:\r\n\r\n```ts\r\nfunction makeTuple<...T>(ts:...T): ...T {\r\n    return ts;\r\n}\r\nfunction f<...T,...U>(ts:...T): [...T,...U] {\r\n    // note that U is constrained to [string,string] in this function\r\n    let us: ...U = makeTuple('hello', 'world');\r\n    return [...ts, ...us];\r\n}\r\n```\r\n\r\nVariadic kind variables, like type variables, are quite opaque.\r\nThey do have one operation, unlike type variables.\r\nThey can be concatenated with other kinds or with actual tuples.\r\nThe syntax used for this is identical to the tuple-spreading syntax, but in type annotation location:\r\n\r\n```ts\r\nlet t1: [...T,...U] = [...ts,...uProducer<...U>()];\r\nlet t2: [...T,string,string,...U,number] = [...ts,'foo','bar',...uProducer<...U>(),12];\r\n```\r\n\r\nTuple types are instances of variadic kinds, so they continue to appear wherever type annotations were previously allowed:\r\n\r\n```ts\r\nfunction f<...T>(ts:...T): [...T,string,string] { \r\n    // note the type of `us` could have been inferred here\r\n    let us: [string,string] = makeTuple('hello', 'world');\r\n    return [...ts, ...us];\r\n}\r\n\r\nlet tuple: [number, string] = [1,'foo'];\r\nf<[number,string]>(tuple);\r\n```\r\n\r\n## Semantics\r\n\r\nA variadic kind variable represents a tuple type of any length. \r\nSince it represents a set of types, we use the term 'kind' to refer to it, following its use in type theory.\r\nBecause the set of types it represents is tuples of any length, we qualify 'kind' with 'variadic'.\r\n\r\nTherefore, declaring a variable of variadic tuple kind allows it to take on any *single* tuple type.\r\nLike type variables, kind variables can only be declared as parameters to functions, classes, etc, which then allows them to be used inside the body:\r\n\r\n```ts\r\nfunction f<...T>(): ...T {\r\n    let a: ...T;\r\n}\r\n```\r\n\r\nCalling a function with arguments typed as a variadic kind will assign a specific tuple type to the kind:\r\n\r\n```ts\r\nf([1,2,\"foo\"]);\r\n```\r\n\r\nAssigns the tuple type `...T=[number,number,string]`...T`. \r\nSo in this application of `f`, `let a:...T` is instantiated as `let a:[number,number,string]`.\r\nHowever, because the type of `a` is not known when the function is written, the elements of the tuple cannot be referenced in the body of the function.\r\nOnly creating a new tuple from `a` is allowed.\r\nFor example, new elements can be added to the tuple: \r\n\r\n```ts\r\nfunction cons<H,...Tail>(head: H, tail: ...Tail): [H,...Tail] {\r\n    return [head, ...tail];\r\n}\r\nlet l: [number, string, string, boolean]; \r\nl = cons(1, cons(\"foo\", [\"baz\", false]));\r\n```\r\n\r\nLike type variables, variadic kind variables can usually be inferred.\r\nThe calls to `cons` could have been annotated:\r\n\r\n```ts\r\nl = cons<number,[string,string,boolean]>(1, cons<string,[string,boolean]>(\"foo\", [\"baz\", false]));\r\n```\r\n\r\nFor example, `cons` must infer two variables, a type *H* and a kind *...Tail*. \r\nIn the innermost call, `cons(\"foo\", [\"baz\", false])`, `H=string` and `...Tail=[string,boolean]`. \r\nIn the outermost call, `H=number` and `...Tail=[string, string, boolean]`.\r\nThe types assigned to *...Tail* are obtained by typing list literals as tuples -- variables of a tuple type can also be used:\r\n\r\n```ts\r\nlet tail: [number, boolean] = [\"baz\", false];\r\nlet l = cons(1, cons(\"foo\", tail));\r\n```\r\n\r\nAdditionally, variadic kind variables can be inferred when concatenated with types:\r\n\r\n```ts\r\nfunction car<H,...Tail>(l: [H, ...Tail]): H {\r\n    let [head, ...tail] = l;\r\n    return head;\r\n}\r\ncar([1, \"foo\", false]);\r\n```\r\n\r\nHere, the type of `l` is inferred as `[number, string, boolean]`. \r\nThen `H=number` and `...Tail=[string, boolean]`.\r\n\r\n### Limits on type inference\r\n\r\nConcatenated kinds cannot be inferred because the checker cannot guess where the boundary between two kinds should be:\r\n\r\n```ts\r\nfunction twoKinds<...T,...U>(total: [...T,string,...U]) {\r\n}\r\ntwoKinds(\"an\", \"ambiguous\", \"call\", \"to\", \"twoKinds\")\r\n```\r\n\r\nThe checker cannot decide whether to assign \r\n\r\n1. `...T = [string,string,string], ...U = [string]`\r\n1. `...T = [string,string], ...U = [string,string]`\r\n1. `...T = [string], ...U = [string,string,string]`\r\n\r\nSome unambiguous calls are a casualty of this restriction:\r\n\r\n```ts\r\ntwoKinds(1, \"unambiguous\", 12); // but still needs an annotation!\r\n```\r\n\r\nThe solution is to add type annotations:\r\n\r\n```ts\r\ntwoKinds<[string,string],[string,string]>(\"an\", \"ambiguous\", \"call\", \"to\", \"twoKinds\");\r\ntwoKinds<[number],[number]>(1, \"unambiguous\", 12);\r\n```\r\n\r\nUncheckable dependencies between type arguments and the function body can arise, as in `rotate`:\r\n\r\n```ts\r\nfunction rotate(l:[...T, ...U], n: number): [...U, ...T] {\r\n    let first: ...T = l.slice(0, n);\r\n    let rest: ...U = l.slice(n);\r\n    return [...rest, ...first];\r\n}\r\nrotate<[boolean, boolean, string], [string, number]>([true, true, 'none', 12', 'some'], 3);\r\n```\r\n\r\nThis function can be typed, but there is a dependency between `n` and the kind variables: `n === ...T.length` must be true for the type to be correct.\r\nI'm not sure whether this is code that should actually be allowed.\r\n\r\n### Semantics on classes and interfaces\r\n\r\nThe semantics are the same on classes and interfaces.\r\n\r\nTODO: There are probably some class-specific wrinkles in the semantics.\r\n\r\n## Assignability between tuples and parameter lists\r\n\r\nTuple kinds can be used to give a type to rest arguments of functions inside their scope:\r\n\r\n```ts\r\nfunction apply<...T,U>(ap: (...args:...T) => U, args: ...T): U {\r\n    return ap(...args);\r\n}\r\nfunction f(a: number, b: string) => string {\r\n    return b + a;\r\n}\r\napply(f, [1, 'foo']);\r\n```\r\n\r\nIn this example, the parameter list of `f: (a: number, b:string) => string` must be assignable to the tuple type instantiated for the kind `...T`.\r\nThe tuple type that is inferred is `[number, string]`, which means that `(a: number, b: string) => string` must be assignable to `(...args: [number, string]) => string`.\r\n\r\nAs a side effect, function calls will be able to take advantage of this assignability by spreading tuples into rest parameters, even if the function doesn't have a tuple kind:\r\n\r\n```ts\r\nfunction g(a: number, ...b: [number, string]) {\r\n    return a + b[0];\r\n}\r\ng(a, ...[12, 'foo']);\r\n```\r\n\r\n### Tuple types generated for optional and rest parameters\r\n\r\nSince tuples can't represent optional parameters directly, when a function is assigned to a function parameter that is typed by a tuple kind, the generated tuple type is a union of tuple types.\r\nLook at the type of `h` after it has been curried:\r\n\r\n```ts\r\nfunction curry<...T,...U,V>(cur: (...args:[...T,...U]) => V, ...ts:...T): (...us:...U) => V {\r\n    return ...us => cur(...ts, ...us);\r\n}\r\nfunction h(a: number, b?:string): number {\r\n}\r\nlet curried = curry(h, 12);\r\ncurried('foo'); // ok\r\ncurried(); // ok\r\n```\r\n\r\nHere `...T=([number] | [number, string])`, so `curried: ...([number] | [number, string]) => number` which can be called as you would expect. Unfortunately, this strategy does not work for rest parameters. These just get turned into arrays:\r\n\r\n```ts\r\nfunction i(a: number, b?: string, ...c: boolean[]): number {\r\n}\r\nlet curried = curry(i, 12);\r\ncurried('foo', [true, false]);\r\ncurried([true, false]);\r\n```\r\n\r\nHere, `curried: ...([string, boolean[]] | [boolean[]]) => number`.\r\nI think this could be supported if there were a special case for functions with a tuple rest parameter, where the last element of the tuple is an array.\r\nIn that case the function call would allow extra arguments of the correct type to match the array.\r\nHowever, that seems too complex to be worthwhile.\r\n\r\n## Extensions to the other parts of typescript\r\n\r\n1. Typescript does not allow users to write an empty tuple type.\r\n  However, this proposal requires variadic kinds to be bindable to a empty tuple.\r\n  So Typescript will need to support empty tuples, even if only internally.\r\n\r\n## Examples\r\n\r\nMost of these examples are possible as fixed-argument functions in current Typescript, but with this proposal they can be written as variadic.\r\nSome, like `cons` and `concat`, can be written for homogeneous arrays in current Typescript but can now be written for heteregoneous tuples using tuple kinds.\r\nThis follows typical Javascript practise more closely.\r\n\r\n### Return a concatenated type\r\n\r\n```ts\r\nfunction cons<H,...T>(head: H, tail:...T): [H, ...T] {\r\n    return [head, ...tail];\r\n}\r\nfunction concat<...T,...U>(first: ...T, ...second: ...U): [...T, ...U] {\r\n    return [...first, ...second];\r\n}\r\ncons(1, [\"foo\", false]); // === [1, \"foo\", false]\r\nconcat(['a', true], 1, 'b'); // === ['a', true, 1, 'b']\r\nconcat(['a', true]); // === ['a', true, 1, 'b']\r\n\r\nlet start: [number,number] = [1,2]; // type annotation required here\r\ncons(3, start); // == [3,1,2]\r\n```\r\n\r\n### Concatenated type as parameter\r\n\r\n```ts\r\nfunction car<H,...T>(l: [H,...T]): H {\r\n    let [head, ...tail] = l;\r\n    return head;\r\n}\r\nfunction cdr<H,...T>(l: [H,...T]): ...T {\r\n    let [head, ...tail] = l;\r\n    return ...tail;\r\n}\r\n\r\ncdr([\"foo\", 1, 2]); // => [1,2]\r\ncar([\"foo\", 1, 2]); // => \"foo\"\r\n```\r\n\r\n### Variadic functions as arguments\r\n\r\n```ts\r\nfunction apply<...T,U>(f: (...args:...T) => U, args: ...T): U {\r\n    return f(...args);\r\n}\r\n\r\nfunction f(x: number, y: string) {\r\n}\r\nfunction g(x: number, y: string, z: string) {\r\n}\r\n\r\napply(f, [1, 'foo']); // ok\r\napply(f, [1, 'foo', 'bar']); // too many arguments\r\napply(g, [1, 'foo', 'bar']); // ok\r\n```\r\n\r\n```ts\r\nfunction curry<...T,...U,V>(f: (...args:[...T,...U]) => V, ...ts:...T): (...us: ...U) => V {\r\n    return us => f(...ts, ...us);\r\n}\r\nlet h: (...us: [string, string]) = curry(f, 1);\r\nlet i: (s: string, t: string) = curry(f, 2);\r\nh('hello', 'world');\r\n```\r\n\r\n```ts\r\nfunction compose<...T,U,V>(f: (u:U) => U, g: (ts:...T) => V): (args: ...T) => V {\r\n    return ...args => f(g(...args));\r\n}\r\nfunction first(x: number, y: number): string {\r\n}\r\nfunction second(s: string) {\r\n}\r\nlet j: (x: number, y: number) => void = compose(second, first);\r\nj(1, 2);\r\n\r\n```\r\n\r\nTODO: Could `f` return `...U` instead of `U`?\r\n\r\n### Decorators\r\n\r\n```ts\r\nfunction logged<...T,U>(target, name, descriptor: { value: (...T) => U }) {\r\n    let method = descriptor.value;\r\n    descriptor.value = function (...args: ...T): U {\r\n        console.log(args);\r\n        method.apply(this, args);\r\n    }\r\n}\r\n```\r\n\r\n\r\n## Open questions\r\n\r\n1. Does the tuple-to-parameter-list assignability story hold up? It's especially shaky around optional and rest parameters.\r\n2. Will the inferred type be a union of tuples as with the optional-parameter case? Because `bind`, `call` and `apply` are methods defined on Function, their type arguments need to be bound at function-creation time rather than the `bind` call site (for example). But this means that functions with overloads can't take or return types specific to their arguments -- they have to be a union of the overload types. Additionally, Function doesn't have a constructor that specifies type arguments directly, so there's really no way provide the correct types to `bind` et al. TODO: Add an example here. Note that this problem isn't necessarily unique to variadic functions. \r\n3. Should rest parameters be special-cased to retain their nice calling syntax, even when they are generated from a tuple type? (In this proposal, functions typed by a tuple kind have to pass arrays to their rest parameters, they can't have extra parameters.)"},"comment":{"url":"https://api.github.com/repos/Microsoft/TypeScript/issues/comments/189703556","html_url":"https://github.com/Microsoft/TypeScript/issues/5453#issuecomment-189703556","issue_url":"https://api.github.com/repos/Microsoft/TypeScript/issues/5453","id":189703556,"user":{"login":"Igorbek","id":1929486,"avatar_url":"https://avatars.githubusercontent.com/u/1929486?v=3","gravatar_id":"","url":"https://api.github.com/users/Igorbek","html_url":"https://github.com/Igorbek","followers_url":"https://api.github.com/users/Igorbek/followers","following_url":"https://api.github.com/users/Igorbek/following{/other_user}","gists_url":"https://api.github.com/users/Igorbek/gists{/gist_id}","starred_url":"https://api.github.com/users/Igorbek/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Igorbek/subscriptions","organizations_url":"https://api.github.com/users/Igorbek/orgs","repos_url":"https://api.github.com/users/Igorbek/repos","events_url":"https://api.github.com/users/Igorbek/events{/privacy}","received_events_url":"https://api.github.com/users/Igorbek/received_events","type":"User","site_admin":false},"created_at":"2016-02-27T19:09:49Z","updated_at":"2016-02-27T19:09:49Z","body":"@JsonFreeman I'd better use that syntax\r\n```ts\r\ndeclare function foo<T>(cb: (...cbArgs: T) => void, ...args: T): T;\r\ndeclare function foo<T>(cb: (...cbArgs: T) => void, ...args: T): [...T]; // same\r\n```"}}