Skip to content

Instantly share code, notes, and snippets.

@bakerface
Last active July 3, 2021 17:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bakerface/26d680ab0d42f247d528d1fc17a61afd to your computer and use it in GitHub Desktop.
Save bakerface/26d680ab0d42f247d528d1fc17a61afd to your computer and use it in GitHub Desktop.
Basic pattern matching with TypeScript template literals
export type KeysOfParams<S extends string> =
S extends `${infer Before} :${infer Name} ${infer After}`
? KeysOfParams<Before> | Name | KeysOfParams<After>
: S extends `:${infer Name} ${infer After}`
? Name | KeysOfParams<After>
: S extends `${infer Before} :${infer Name}`
? KeysOfParams<Before> | Name
: S extends `:${infer Name}`
? Name
: never;
export type Params<S extends string> = {
readonly [K in KeysOfParams<S>]: string;
};
export function match<S extends string>(
template: S,
input: string
): Params<S> | null {
const ts = template.split(" ");
const is = input.split(" ");
if (ts.length !== is.length) {
return null;
}
const params: Record<string, string> = {};
for (let i = 0; i < ts.length; i++) {
if (ts[i][0] === ":") {
params[ts[i].slice(1)] = is[i];
} else if (ts[i] !== is[i]) {
return null;
}
}
return params as Params<S>;
}
export function matches<S extends string>(
template: S
): (input: string) => Params<S> | null {
return match.bind(null, template);
}
const friend = matches("a friend named :first :last");
console.log(friend("a friend named Jane Doe"));
// => { first: "Jane", last: "Doe" }
console.log(friend("a friend"));
// => null
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment