{"action":"opened","issue":{"url":"https://api.github.com/repos/Microsoft/TypeScript/issues/21729","repository_url":"https://api.github.com/repos/Microsoft/TypeScript","labels_url":"https://api.github.com/repos/Microsoft/TypeScript/issues/21729/labels{/name}","comments_url":"https://api.github.com/repos/Microsoft/TypeScript/issues/21729/comments","events_url":"https://api.github.com/repos/Microsoft/TypeScript/issues/21729/events","html_url":"https://github.com/Microsoft/TypeScript/issues/21729","id":295234201,"number":21729,"title":"Bug: Mapped, generic type with inferred generic parameter is not assignable to the type it just passed the condition for","user":{"login":"krryan","id":6392679,"avatar_url":"https://avatars2.githubusercontent.com/u/6392679?v=4","gravatar_id":"","url":"https://api.github.com/users/krryan","html_url":"https://github.com/krryan","followers_url":"https://api.github.com/users/krryan/followers","following_url":"https://api.github.com/users/krryan/following{/other_user}","gists_url":"https://api.github.com/users/krryan/gists{/gist_id}","starred_url":"https://api.github.com/users/krryan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/krryan/subscriptions","organizations_url":"https://api.github.com/users/krryan/orgs","repos_url":"https://api.github.com/users/krryan/repos","events_url":"https://api.github.com/users/krryan/events{/privacy}","received_events_url":"https://api.github.com/users/krryan/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":0,"created_at":"2018-02-07T17:55:09Z","updated_at":"2018-02-07T17:55:09Z","closed_at":null,"author_association":"NONE","body":"Our project uses a concept we call \"fields,\" which are places where data can go from user input, and fields can be set or unset, valid or invalid, etc. And we have a big union of field types representing every combination of those things for different types of fields, and that's called `Field<T>`. The different types of fields encode a lot of behavior, but one of the things we have in there is that, for example, if a field is required, and we have no value for it, then we have an invalid field—period, there is no type in the union that allows required, unset, and valid to exist simultaneously in the same field. That gives us a lot of type-safety.\r\n\r\nWhen it comes time to save a form full of these fields, we want to make sure all fields match the types that are valid. I attempted to use conditional types for this, first to determine what the appropriate type for a given field should be (e.g. given a union of required fields, the savable version is the one that's required, set, and valid), calling that `SavableField<F, T>`. That works fine. Then came the next step: a `SavableForm<Form>` type that maps all properties, and if a property is a field, replacing it with its `SavableField` variant, and otherwise just leaves it as-is. That does not work.\r\n\r\nA simplified example:\r\n\r\n```ts\r\ndeclare type Field<T>; // Field is a big union of generic types\r\ndeclare type SavableField<F extends Field<T>, T>; // big series of type conditions to select \"savable\" types from the union\r\n\r\nexport type SavableForm<Form> = {\r\n    [K in keyof Form]: Form[K] extends Field<infer T> ? SavableField<Form[K], T> : Form[K];\r\n/*                                                                   ^^^^^^^\r\nType 'Form[K]' does not satisfy the constraint 'Field<T>'.\r\n  Type 'Form[K]' is not assignable to type 'ReadOnlyAlwaysSetInvalidForged<T>'. */\r\n}\r\n```\r\n\r\n`Form[K]` here has literally just passed the `extends Field<infer T>` condition, so it failing to be assignable to `Field<T>` is very confusing/surprising here. (For the record, `ReadOnlyAlwaysSetInvalidForged<T>` is the member of the `Field<T>` union that happens to be considered \"last\" by TS, as seen by hovering over the definition of `Field<T>`.)"}}
{"actor":{"display_login":"krryan"}}