Skip to content

Instantly share code, notes, and snippets.

@broguinn
Created November 9, 2024 14:43
Show Gist options
  • Save broguinn/209129b7a18134571497334b1993abe7 to your computer and use it in GitHub Desktop.
Save broguinn/209129b7a18134571497334b1993abe7 to your computer and use it in GitHub Desktop.
An attempt at type narrowing
// Original, working solution that uses explicit "type" property key.
export function Cases<Key extends string, Union extends { type: Key }, U>(
args: {
[key in Key]: (_: Extract<Union, { type: key }>) => U;
},
data: Extract<Union, { type: Key }>
) {
return args[data.type](data);
}
type MyUnion = { type: "A"; extra: string } | { type: "B" };
const data: MyUnion = { type: "A", extra: "extra" };
Cases<MyUnion["type"], MyUnion, string>(
{
A: (d) => `A ${d.extra} return value`,
B: () => "B return value",
},
data
);
// Rework with StringLiteral to remove the explicit "data" property key that fails to narrow the type for the "data" argument.
type StringLiteral<T> = T extends string
? string extends T
? never
: T
: never;
export function Cases2<
Key extends string,
Union extends { [k in StringLiteral<Key>]: string },
U,
>(
key: StringLiteral<Key>,
args: {
[c in Union[StringLiteral<Key>]]: (_: Extract<Union, { k: c }>) => U;
},
data: Union
) {
return args[data[key]](data);
}
Cases2<"type", MyUnion, string>(
"type",
{
A: (d) => `A ${d.extra} return value`,
B: () => "B return value",
},
data
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment