{"action":"created","issue":{"url":"https://api.github.com/repos/microsoft/TypeScript/issues/10421","repository_url":"https://api.github.com/repos/microsoft/TypeScript","labels_url":"https://api.github.com/repos/microsoft/TypeScript/issues/10421/labels{/name}","comments_url":"https://api.github.com/repos/microsoft/TypeScript/issues/10421/comments","events_url":"https://api.github.com/repos/microsoft/TypeScript/issues/10421/events","html_url":"https://github.com/microsoft/TypeScript/issues/10421","id":172008546,"node_id":"MDU6SXNzdWUxNzIwMDg1NDY=","number":10421,"title":"[Proposal] Type assertion statement (type cast) at block-scope level","user":{"login":"yahiko00","id":5117144,"node_id":"MDQ6VXNlcjUxMTcxNDQ=","avatar_url":"https://avatars3.githubusercontent.com/u/5117144?v=4","gravatar_id":"","url":"https://api.github.com/users/yahiko00","html_url":"https://github.com/yahiko00","followers_url":"https://api.github.com/users/yahiko00/followers","following_url":"https://api.github.com/users/yahiko00/following{/other_user}","gists_url":"https://api.github.com/users/yahiko00/gists{/gist_id}","starred_url":"https://api.github.com/users/yahiko00/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yahiko00/subscriptions","organizations_url":"https://api.github.com/users/yahiko00/orgs","repos_url":"https://api.github.com/users/yahiko00/repos","events_url":"https://api.github.com/users/yahiko00/events{/privacy}","received_events_url":"https://api.github.com/users/yahiko00/received_events","type":"User","site_admin":false},"labels":[{"id":119463126,"node_id":"MDU6TGFiZWwxMTk0NjMxMjY=","url":"https://api.github.com/repos/microsoft/TypeScript/labels/Revisit","name":"Revisit","color":"006b75","default":false},{"id":113971155,"node_id":"MDU6TGFiZWwxMTM5NzExNTU=","url":"https://api.github.com/repos/microsoft/TypeScript/labels/Suggestion","name":"Suggestion","color":"006b75","default":false}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":52,"created_at":"2016-08-18T21:44:41Z","updated_at":"2019-08-06T17:18:33Z","closed_at":null,"author_association":"NONE","body":"This is a proposal in order to simplify the way we have to deal with type guards in TypeScript in order to enforce the type inference.\n\nThe use case is the following. Let us assume we have dozens (and dozens) of interfaces as the following:\n\n**Code**\n\n``` ts\ninterface AARect {\n    x: number; // top left corner\n    y: number; // top left corner\n    width: number;\n    height: number;\n}\n\ninterface AABox {\n    x: number; // center\n    y: number; // center\n    halfDimX: number;\n    halfDimY: number;\n}\n\ninterface Circle {\n    x: number; // center\n    y: number; // center\n    radius: number;\n}\n\n// And much more...\n```\n\nAnd we have a union type like this one:\n\n``` ts\ntype Geometry = AARect | AABox | Circle | // ... And much more\n```\n\nIt is quite easy to discriminate a type from another with `hasOwnProperty` or the `in` keyword:\n\n``` ts\nfunction processGeometry(obj: Geometry): void {\n    if (\"width\" in obj) {\n        let width = (obj as AARect).width;\n        // ...\n    }\n    if (\"halfDimX\" in obj) {\n        let halfDimX = (obj as AABox).halfDimX;\n        // ...\n    }\n    else if (\"radius\" in obj) {\n        let radius = (obj as Circle).radius;\n        // ...\n    }\n    // And much more...\n}\n```\n\nBut, as we can see, this is quite burdensome when we need to manipulate `obj` inside each `if` block, since we need to type cast each time we use `obj`.\n\nA first way to mitigate this issue would be to create an helper variable like this:\n\n``` ts\n    if (\"width\" in obj) {\n        let helpObj = obj as AARect;\n        let width = helpObj.width;\n        // ...\n    }\n```\n\nBut this is not really satisfying since it creates an artefact we will find in the emitted JavaScript file, which is here just for the sake of the type inference.\n\nSo another solution could be to use user-defined type guard functions:\n\n``` ts\nfunction isAARect(obj: Geometry): obj is AARect {\n    return \"width\" in obj;\n}\n\nfunction isAABox(obj: Geometry): obj is AABox {\n    return \"halfDimX\" in obj;\n}\n\nfunction isCircle(obj: Geometry): obj is Circle {\n    return \"radius\" in obj;\n}\n\n// And much more...\n\nfunction processGeometry(obj: Geometry): void {\n    if (isAARect(obj)) {\n        let width = obj.width;\n        // ...\n    }\n    if (isAABox(obj)) {\n        let halfDimX = obj.halfDimX;\n        // ...\n    }\n    else if (isCircle(obj)) {\n        let radius = obj.radius;\n        // ...\n    }\n    // And much more...\n}\n```\n\nBut again, I find this solution not really satisfying since it still creates persistent helpers functions just for the sake of the type inference and can be overkill for situations when we do not often need to perform type guards.\n\nSo, my proposal is to introduce a new syntax in order to _force_ the type of an identifier at a block-scope level.\n\n``` ts\nfunction processGeometry(obj: Geometry): void {\n    if (\"width\" in obj) {\n        assume obj is AARect;\n        let width = obj.width;\n        // ...\n    }\n    if (\"halfDimX\" in obj) {\n        assume obj is AABox;\n        let halfDimX = obj.halfDimX;\n        // ...\n    }\n    else if (\"radius\" in obj) {\n        assume obj is Circle;\n        let radius = obj.radius;\n        // ...\n    }\n    // And much more...\n}\n```\n\nAbove, the syntax `assume <identifier> is <type>` gives the information to the type inference that inside the block, following this annotation, `<identifier>` has to be considered as `<type>`. No need to type cast any more. Such a way has the advantage over the previous techniques not to generate any code in the emitted JavaScript. And in my opinion, it is less tedious than creating dedicated helper functions.\n\nThis syntax can be simplified or changed. For instance we could just have :\n`<identifier> is <obj>`\nwithout a new keyword `assume`, but I am unsure this would be compliant with the current grammar and design goals of the TypeScript team.\nNevertheless, whatever any welcomed optimization, I think the general idea is relevant for making TypeScript clearer, less verbose in the source code and in the final JavaScript, and less tedious to write when we have to deal with union types.\n"},"comment":{"url":"https://api.github.com/repos/microsoft/TypeScript/issues/comments/518763196","html_url":"https://github.com/microsoft/TypeScript/issues/10421#issuecomment-518763196","issue_url":"https://api.github.com/repos/microsoft/TypeScript/issues/10421","id":518763196,"node_id":"MDEyOklzc3VlQ29tbWVudDUxODc2MzE5Ng==","user":{"login":"jdom","id":1361686,"node_id":"MDQ6VXNlcjEzNjE2ODY=","avatar_url":"https://avatars3.githubusercontent.com/u/1361686?v=4","gravatar_id":"","url":"https://api.github.com/users/jdom","html_url":"https://github.com/jdom","followers_url":"https://api.github.com/users/jdom/followers","following_url":"https://api.github.com/users/jdom/following{/other_user}","gists_url":"https://api.github.com/users/jdom/gists{/gist_id}","starred_url":"https://api.github.com/users/jdom/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jdom/subscriptions","organizations_url":"https://api.github.com/users/jdom/orgs","repos_url":"https://api.github.com/users/jdom/repos","events_url":"https://api.github.com/users/jdom/events{/privacy}","received_events_url":"https://api.github.com/users/jdom/received_events","type":"User","site_admin":false},"created_at":"2019-08-06T17:18:33Z","updated_at":"2019-08-06T17:18:33Z","author_association":"MEMBER","body":"Are there any plans to support this?"}}
{"actor":{"display_login":"jdom"}}