#how fix typeguard for payload in this example

33 messages · Page 1 of 1 (latest)

proud cradle
#

how to make a fix to be sure when we us typeguard here , payload should be boolean in this case ?
thanks

enum MSGTYPES {
  a,b,c,d,e,f
}


type MessageTypeObject<T extends MSGTYPES, V> = { type: T; payload: V; };

// a messages Maps entries where we define enumerations data type
type MessageTypeMap =[
    [[MSGTYPES.a, MSGTYPES.b, MSGTYPES.c], boolean],
    [[MSGTYPES.d, MSGTYPES.e], string],
    [[MSGTYPES.f], FormData],
]

type MessageType<T extends MSGTYPES[]> = {
    [K in T[number]]: K extends MessageTypeMap[number][0][number] ? MessageTypeObject<K, MessageTypeMap[number][1]> : never;
} [T[number]];


type testmsg = [MSGTYPES.a, MSGTYPES.f]

function onMessage( messageType: MessageType<testmsg> ): void {
    const { type, payload } = messageType;
    if ( type === MSGTYPES.a ) {
        payload; // should be boolean 🔴
    }
}
tawny pagodaBOT
#
jonLepage#7984

Preview:```ts
enum MSGTYPES {
a,
b,
c,
d,
e,
f,
}

type MessageTypeObject<T extends MSGTYPES, V> = {
type: T
payload: V
}

// a messages Maps entries where we define enumerations data type
type MessageTypeMap = [
[[MSGTYPES.a, MSGTYPES.b, MSGTYPES.c], boolean],
[[MSGTYPES.d, MSGTYPES.e], string],
...```

thick gust
#

!hb discriminated union

tawny pagodaBOT
thick gust
#

use that rather than, uh, any of this

#

if you really want MessageTypeMap, create a discriminated union by mapping over it

raven mountain
#

rather

#

it's impossible by using any of that

#

ah actually it should be fine if it outputs a union

merry abyss
#

make suer your TypeScript >= 4.6 https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-6.html#control-flow-analysis-for-destructured-discriminated-unions

you can check the playground URL below

enum MSGTYPES {
  a,
  b,
  c,
  d,
  e,
  f,
}

type MessageTypeObject<T extends MSGTYPES, V> = { type: T; payload: V };

type MessageTypes =
  | MessageTypeObject<MSGTYPES.a | MSGTYPES.b | MSGTYPES.c, boolean>
  | MessageTypeObject<MSGTYPES.d | MSGTYPES.e, string>
  | MessageTypeObject<MSGTYPES.f, FormData>;

function onMessage(messageType: MessageTypes): void {
  const { type, payload } = messageType;
  if (type === MSGTYPES.a) {
    console.log(payload);
  }
}

tawny pagodaBOT
#
homura#6897

Preview:```ts
enum MSGTYPES {
a,
b,
c,
d,
e,
f,
}

type MessageTypeObject<T extends MSGTYPES, V> = {
type: T
payload: V
}

type MessageTypes =
| MessageTypeObject<
MSGTYPES.a | MSGTYPES.b | MSGTYPES.c,
boolean
>
| MessageTypeObject<MSGTYPES.d | MSGTYPES.e, string
...```

raven mountain
#

well...

#

<4.6 is fine if you don't destructure

#

note that the original approach is perfectly fine

#

after fixing it up:

tawny pagodaBOT
raven mountain
#

!ts

tawny pagodaBOT
#
// 8<
enum MSGTYPES {
  a,b,c,d,e,f
}

type MessageTypeMap = {
	[MSGTYPES.a]: boolean;
	[MSGTYPES.b]: boolean;
	[MSGTYPES.c]: boolean;
	[MSGTYPES.d]: string;
	[MSGTYPES.e]: string;
	[MSGTYPES.f]: FormData;
}

type MessageType<T extends MSGTYPES[]> = {
	[K in T[number]]: { type: K; payload: MessageTypeMap[K]; };
}[T[number]];

function onMessage( messageType: MessageType<[MSGTYPES.a, MSGTYPES.f]> ): void {
	const { type, payload } = messageType;
	if ( type === MSGTYPES.a ) {
		payload; // should be boolean 🔴
//   ^? - const payload: boolean
	}
}```
raven mountain
#

there are ways to keep a, b, and c together to reduce duplication, like homura's approach

tawny pagodaBOT
raven mountain
#

or something simpler like this maybe?

#

!ts

tawny pagodaBOT
#
// 8<
enum MSGTYPES { a, b, c, d, e, f }

type MessageType<T extends MSGTYPES = MSGTYPES> = T extends T ? (
	| { type: MSGTYPES.a | MSGTYPES.b | MSGTYPES.c; payload: boolean; }
	| { type: MSGTYPES.d | MSGTYPES.e; payload: string }
	| { type: MSGTYPES.f; payload: FormData }
) & { type: T; } : never;

function onMessage(messageType: MessageType<MSGTYPES.a | MSGTYPES.e>): void {
	const { type, payload } = messageType;
	if (type === MSGTYPES.a) {
		payload;
//   ^? - const payload: boolean
	}
}```
proud cradle
#

thanks you so much all example idea are awesome !
@raven mountain yes duplication was a big issue for me, my first try was so ugly !
thanks for your code , i think is the most clean way to avoid duplication and the structure is not so ugly for the eyes.

interface MessageTypeMap {
    [MSGTYPES.scene_change]: { type: MSGTYPES.scene_change; message: boolean; };
    [MSGTYPES.battle_fallAnimation]: { type: MSGTYPES.battle_fallAnimation; message: string; };
    [MSGTYPES.battle_poolGenerated]: { type: MSGTYPES.battle_poolGenerated; message: number; };
}

type MessageType<T extends MSGTYPES[]> = {
    [K in T[number]]: K extends keyof MessageTypeMap ? MessageTypeMap[K] : never;
}[T[number]];

type testmsg = [MSGTYPES.scene_change, MSGTYPES.battle_fallAnimation, MSGTYPES.worldTreeView_selectEntity]
function onMessage( messageType: MessageType<testmsg> ): void {
#

!resolved

raven mountain
#
interface MessageTypeMap {
    [MSGTYPES.scene_change]: { type: MSGTYPES.scene_change; message: boolean; };
    [MSGTYPES.battle_fallAnimation]: { type: MSGTYPES.battle_fallAnimation; message: string; };
    [MSGTYPES.battle_poolGenerated]: { type: MSGTYPES.battle_poolGenerated; message: number; };
}

type MessageType<T extends keyof MessageTypeMap> = T extends T ? MessageTypeMap[T] : never;
function onMessage( messageType: MessageType<MSGTYPES.scene_change | MSGTYPES.battle_fallAnimation | MSGTYPES.worldTreeView_selectEntity> ): void {
}
#

👀

proud cradle
raven mountain
#

oh wait is that your first try

#

i mean, it doesn't look that bad

proud cradle
raven mountain
#

you could extract the union out...
... i think it'd be mostly for maintainability though - since MessageTypes and MessageType (leaving out the generic argument T) should be the same type