From 69e230d49b570aa28ec94965f6ea70dd472a9f78 Mon Sep 17 00:00:00 2001 From: Aziz Khambati Date: Wed, 25 Mar 2020 02:15:39 +0530 Subject: [PATCH] Correct the section about discrinated types --- ADVANCED.md | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index cf29bed3..46c5fa37 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -511,23 +511,6 @@ You may also want to use Discriminated Unions, please check out [Expressive Reac Here is a brief intuition for **Discriminated Union Types**: -```ts -type UserTextEvent = { value: string; target: HTMLInputElement }; -type UserMouseEvent = { value: [number, number]; target: HTMLElement }; -type UserEvent = UserTextEvent | UserMouseEvent; -function handle(event: UserEvent) { - if (typeof event.value === "string") { - event.value; // string - event.target; // HTMLInputElement | HTMLElement (!!!!) - return; - } - event.value; // [number, number] - event.target; // HTMLInputElement | HTMLElement (!!!!) -} -``` - -Even though we have narrowed based on `event.value`, the logic doesn't filter up and sideways to `event.target`. This is because a union type `UserTextEvent | UserMouseEvent` could be BOTH at once. So TypeScript needs a better hint. The solution is to use a literal type to tag each case of your union type: - ```ts type UserTextEvent = { type: "TextEvent"; @@ -551,6 +534,30 @@ function handle(event: UserEvent) { } ``` +
+ + Take care: TypeScript does not narrow the type of a Discriminated Union on the basis of typeof checks. The type guard has to be on the value of a key and not it's type. + + +```ts +type UserTextEvent = { value: string; target: HTMLInputElement }; +type UserMouseEvent = { value: [number, number]; target: HTMLElement }; +type UserEvent = UserTextEvent | UserMouseEvent; +function handle(event: UserEvent) { + if (typeof event.value === "string") { + event.value; // string + event.target; // HTMLInputElement | HTMLElement (!!!!) + return; + } + event.value; // [number, number] + event.target; // HTMLInputElement | HTMLElement (!!!!) +} +``` + +The above example does not work as we are not checking the value of `event.value` but only it's type. Read more about it [microsoft/TypeScript#30506 (comment)](https://github.com/microsoft/TypeScript/issues/30506#issuecomment-474858198) + +
+ To streamline this you may also combine this with the concept of **User-Defined Type Guards**: ```ts