{"action":"created","issue":{"url":"https://api.github.com/repos/Microsoft/TypeScript/issues/12114","repository_url":"https://api.github.com/repos/Microsoft/TypeScript","labels_url":"https://api.github.com/repos/Microsoft/TypeScript/issues/12114/labels{/name}","comments_url":"https://api.github.com/repos/Microsoft/TypeScript/issues/12114/comments","events_url":"https://api.github.com/repos/Microsoft/TypeScript/issues/12114/events","html_url":"https://github.com/Microsoft/TypeScript/pull/12114","id":188141684,"number":12114,"title":"Mapped types","user":{"login":"ahejlsberg","id":4226954,"avatar_url":"https://avatars.githubusercontent.com/u/4226954?v=3","gravatar_id":"","url":"https://api.github.com/users/ahejlsberg","html_url":"https://github.com/ahejlsberg","followers_url":"https://api.github.com/users/ahejlsberg/followers","following_url":"https://api.github.com/users/ahejlsberg/following{/other_user}","gists_url":"https://api.github.com/users/ahejlsberg/gists{/gist_id}","starred_url":"https://api.github.com/users/ahejlsberg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ahejlsberg/subscriptions","organizations_url":"https://api.github.com/users/ahejlsberg/orgs","repos_url":"https://api.github.com/users/ahejlsberg/repos","events_url":"https://api.github.com/users/ahejlsberg/events{/privacy}","received_events_url":"https://api.github.com/users/ahejlsberg/received_events","type":"User","site_admin":false},"labels":[{"id":164593272,"url":"https://api.github.com/repos/Microsoft/TypeScript/labels/cla-not-required","name":"cla-not-required","color":"009800","default":false}],"state":"closed","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":94,"created_at":"2016-11-09T00:33:11Z","updated_at":"2016-12-13T20:25:34Z","closed_at":"2016-11-13T23:39:32Z","pull_request":{"url":"https://api.github.com/repos/Microsoft/TypeScript/pulls/12114","html_url":"https://github.com/Microsoft/TypeScript/pull/12114","diff_url":"https://github.com/Microsoft/TypeScript/pull/12114.diff","patch_url":"https://github.com/Microsoft/TypeScript/pull/12114.patch"},"body":"This PR introduces *Mapped Types*, a new kind of object type that maps a type representing property names over a property declaration template. In combination with *index types* and *indexed access types* (#11929), mapped types enable a number of interesting and useful type transformations. In particular, mapped types enable more accurate typing of intrinsic functions such as `Object.assign` and `Object.freeze` as well as APIs that map or transform shapes of objects.\r\n\r\nA mapped type takes one of the forms\r\n\r\n```ts\r\n{ [ P in K ] : T }\r\n{ [ P in K ] ? : T }\r\n{ readonly [ P in K ] : T }\r\n{ readonly [ P in K ] ? : T }\r\n```\r\n\r\nwhere `P` is an identifier, `K` is a type that must be assignable to `string`, and `T` is some type that can use `P` as a type parameter. A mapped type resolves to an object type with a set of properties constructed by introducing a type parameter `P` and iterating it over the constituent types in `K`, for each such `P` declaring a property or index signature with the type given by `T` (which possibly references `P` as a type parameter). When `P` is a string literal type, a property with that name is introduced. Otherwise, when `P` is type `string`, an index signature is introduced.\r\n\r\n```ts\r\ntype Item = { a: string, b: number, c: boolean };\r\n\r\ntype T1 = { [P in \"x\" | \"y\"]: number };  // { x: number, y: number }\r\ntype T2 = { [P in \"x\" | \"y\"]: P };  // { x: \"x\", y: \"y\" }\r\ntype T3 = { [P in \"a\" | \"b\"]: Item[P] };  // { a: string, b: number }\r\ntype T4 = { [P in keyof Item]: Date };  // { a: Date, b: Date, c: Date }\r\ntype T5 = { [P in keyof Item]: Item[P] };  // { a: string, b: number, c: boolean }\r\ntype T6 = { readonly [P in keyof Item]: Item[P] };  // { readonly a: string, readonly b: number, readonly c: boolean }\r\ntype T7 = { [P in keyof Item]: Array<Item[P]> };  // { a: string[], b: number[], c: boolean[] }\r\n```\r\n\r\nType relationships involving mapped types are described in #12351. For information on type inference involving mapped types, see #12528 and #12589. For information on preservation of property modifiers with mapped types, see #12563.\r\n\r\nThe following four mapped types are predefined in lib.d.ts as of #12276:\r\n\r\n```ts\r\n// Make all properties in T optional\r\ntype Partial<T> = {\r\n    [P in keyof T]?: T[P];\r\n};\r\n\r\n// Make all properties in T readonly\r\ntype Readonly<T> = {\r\n    readonly [P in keyof T]: T[P];\r\n};\r\n\r\n// From T pick a set of properties K\r\ntype Pick<T, K extends keyof T> = {\r\n    [P in K]: T[P];\r\n}\r\n\r\n// Construct a type with a set of properties K of type T\r\ntype Record<K extends string, T> = {\r\n    [P in K]: T;\r\n}\r\n```\r\n\r\nSome functions that use the above types:\r\n\r\n```ts\r\nfunction assign<T>(obj: T, props: Partial<T>): void;\r\nfunction freeze<T>(obj: T): Readonly<T>;\r\nfunction pick<T, K extends keyof T>(obj: T, ...keys: K[]): Pick<T, K>;\r\nfunction mapObject<K extends string, T, U>(obj: Record<K, T>, f: (x: T) => U): Record<K, U>;\r\n```\r\n\r\nAnd some code that uses the functions:\r\n\r\n```ts\r\ninterface Shape {\r\n    name: string;\r\n    width: number;\r\n    height: number;\r\n    visible: boolean;\r\n}\r\n\r\nfunction f1(s1: Shape, s2: Shape) {\r\n    assign(s1, { name: \"circle\" });\r\n    assign(s2, { width: 10, height: 20 });\r\n}\r\n\r\nfunction f2(shape: Shape) {\r\n    const frozen = freeze(shape);\r\n    frozen.name = \"circle\";  // Error, name is read-only\r\n}\r\n\r\nfunction f3(shape: Shape) {\r\n    const x = pick(shape, \"name\", \"visible\");  // { name: string, visible: boolean }\r\n}\r\n\r\nfunction f4() {\r\n    const rec = { foo: \"hello\", bar: \"world\", baz: \"bye\" };\r\n    const lengths = mapObject(rec, s => s.length);  // { foo: number, bar: number, baz: number }\r\n}\r\n```\r\n\r\nThe `mapObject` example above shows how type inference can be used for mapped types. When inferring from an object type `S` to a mapped type `{ [P in K]: T }`, `keyof S` is inferred for `K` and `S[keyof S]` is inferred for `T`. In other words, a literal union type of all property *names* in `S` is inferred for `K` and a union of all property *types* in `S` is inferred for `T`.\r\n\r\nAnother common pattern:\r\n\r\n```ts\r\n// A proxy for a given type\r\ntype Proxy<T> = {\r\n    get(): T;\r\n    set(value: T): void;\r\n}\r\n\r\n// Proxify all properties in T\r\ntype Proxify<T> = {\r\n    [P in keyof T]: Proxy<T[P]>;\r\n}\r\n\r\nfunction proxify<T>(obj: T): Proxify<T> {\r\n    // Wrap proxies around properties of obj\r\n}\r\n\r\nfunction f5(shape: Shape) {\r\n    const p = proxify(shape);\r\n    let name = p.name.get();\r\n    p.visible.set(false);\r\n}\r\n```\r\n\r\nRelated issues include #1295, #2710, #4889, #6613, #10725, #11100, #11233."},"comment":{"url":"https://api.github.com/repos/Microsoft/TypeScript/issues/comments/266851672","html_url":"https://github.com/Microsoft/TypeScript/pull/12114#issuecomment-266851672","issue_url":"https://api.github.com/repos/Microsoft/TypeScript/issues/12114","id":266851672,"user":{"login":"JKillian","id":1400151,"avatar_url":"https://avatars.githubusercontent.com/u/1400151?v=3","gravatar_id":"","url":"https://api.github.com/users/JKillian","html_url":"https://github.com/JKillian","followers_url":"https://api.github.com/users/JKillian/followers","following_url":"https://api.github.com/users/JKillian/following{/other_user}","gists_url":"https://api.github.com/users/JKillian/gists{/gist_id}","starred_url":"https://api.github.com/users/JKillian/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/JKillian/subscriptions","organizations_url":"https://api.github.com/users/JKillian/orgs","repos_url":"https://api.github.com/users/JKillian/repos","events_url":"https://api.github.com/users/JKillian/events{/privacy}","received_events_url":"https://api.github.com/users/JKillian/received_events","type":"User","site_admin":false},"created_at":"2016-12-13T20:25:34Z","updated_at":"2016-12-13T20:25:34Z","body":"I'm trying to write typings for [React's update helper](https://facebook.github.io/react/docs/update.html#examples) and am struggling a bit.\r\n\r\n[Here's what I have so far](http://www.typescriptlang.org/play/#src=type%20Command%20%3D%0A%20%20%20%20%7B%20%24set%3A%20any%3B%20%7D%20%7C%0A%20%20%20%20%7B%20%24merge%3A%20%7B%7D%20%7D%20%7C%0A%20%20%20%20%7B%20%24apply(value%3A%20any)%3A%20any%3B%20%7D%3B%0Atype%20Update%3CT%3E%20%3D%20%7B%20%5BP%20in%20keyof%20T%5D%3F%3A%20Update%3CT%5BP%5D%3E%20%7C%20Command%3B%20%7D%3B%0Adeclare%20function%20update%3CT%3E(value%3A%20T%2C%20updateObj%3A%20Update%3CT%3E)%3B%0A%0Atype%20Foo%20%3D%20%7B%0A%20%20%20%20a%3A%20number%3B%0A%20%20%20%20b%3A%20%7B%20b1%3A%20number%3B%20%7D%3B%0A%7D%0Alet%20bar%3A%20Foo%20%3D%20%7B%20a%3A%201%2C%20b%3A%20%7B%20b1%3A%202%7D%20%7D%3B%0A%0A%2F%2F%20doesn't%20work%20without%20explicit%20%3CFoo%3E%3A%0A%2F%2F%20The%20type%20argument%20for%20type%20parameter%20'T'%20cannot%20be%20inferred%20from%20the%20usage.%20Consider%20specifying%20the%20type%20arguments%20explicitly.%0A%2F%2F%20%20%20Type%20argument%20candidate%20'Foo'%20is%20not%20a%20valid%20type%20argument%20because%20it%20is%20not%20a%20supertype%20of%20candidate%20'%7B%20a%3A%20%7B%20%24set%3A%20number%3B%20%7D%3B%20%7D'.%0A%2F%2F%20%20%20%20%20Types%20of%20property%20'a'%20are%20incompatible%0A%2F%2F%20%20%20%20%20%20%20Type%20'%7B%20%24set%3A%20number%3B%20%7D'%20is%20not%20assignable%20to%20type%20'number'.%0Aupdate%3CFoo%3E(bar%2C%20%7B%20a%3A%20%7B%20%24set%3A%203%20%7D%20%7D)%0A%0A%2F%2F%20also%20doesn't%20work%20without%20%3CFoo%3E%0Aupdate%3CFoo%3E(bar%2C%20%7B%20b%3A%20%7B%20b1%3A%20%7B%20%24set%3A%204%20%7D%20%7D%20%7D)%0A%0A%2F%2F%20shouldn't%20work%20at%20all%2C%20but%20does%0Aupdate%3CFoo%3E(bar%2C%20%7B%20b%3A%207%20%7D)%3B), trying to get things working for a subset of the library's functionality for now. I'm having a few issues which are noted in the code below:\r\n\r\n```ts\r\ntype Command =\r\n    { $set: any; } |\r\n    { $merge: {} } |\r\n    { $apply(value: any): any; };\r\ntype Update<T> = { [P in keyof T]?: Update<T[P]> | Command; };\r\ndeclare function update<T>(value: T, updateObj: Update<T>);\r\n\r\ntype Foo = {\r\n    a: number;\r\n    b: { b1: number; };\r\n}\r\nlet bar: Foo = { a: 1, b: { b1: 2} };\r\n\r\n// doesn't work without explicit <Foo>:\r\n// The type argument for type parameter 'T' cannot be inferred from the usage. Consider specifying the type arguments explicitly.\r\n//   Type argument candidate 'Foo' is not a valid type argument because it is not a supertype of candidate '{ a: { $set: number; }; }'.\r\n//     Types of property 'a' are incompatible\r\n//       Type '{ $set: number; }' is not assignable to type 'number'.\r\nupdate<Foo>(bar, { a: { $set: 3 } })\r\n\r\n// also doesn't work without <Foo>\r\nupdate<Foo>(bar, { b: { b1: { $set: 4 } } })\r\n\r\n// shouldn't work at all, but does\r\nupdate<Foo>(bar, { b: 7 });\r\n```\r\n\r\nAny ideas for ways to improve things? At the least, I think it may be an improvement over [the current typings](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/react-addons-update/index.d.ts), but I wish it could be even better."}}
{"actor":{"display_login":"JKillian"}}