type TokenTable<T> = {
plus: T,
minus: T,
bang: T,
parenOpen: T,
//etc.
};
I also have a type defined as 'type TokenID = keyof TokenTable<unknown>;' this makes it possible to check if a string is a valid key at compile-time. The innermost loop of the lexer is a for..in loop. This gives you the keys of the object. One problem: if you try to apply the TokenID type to the loop variable, you get this message: "The left-hand side of a 'for...in' statement cannot use a type annotation." Because of the design of JavaScript, TS cannot give object keys any other type but 'string', even though this type seems like a clear match.To get the typechecking back on the keys, you either need to declare the loop variable outside of the loop itself, or use type casting like this:
let token = "";
let id: TokenID | undefined;
for (let key in patterns) {
const match = patterns[key as TokenID].exec(substring);
if (match && (match[0].length > token.length || key == "EOF")) {
token = match[0];
id = key as TokenID;
}
}
Neither is particularly clean.There's some boilerplate in the definition, but it's fairly clean and non-repetitive. And easy to use in the "client code".
https://www.typescriptlang.org/play?#code/KYOwrgtgBAKg9ga1AS...
const tokens = ['parenOpen', 'bang', 'plus', 'minus'] as const;
type Token = typeof tokens[number];
type TokenTable<T> = Record<Token, T> // alias for { [key in Token]: T }
const isToken = (t: string): t is Token => tokens.includes(t);
const patterns: TokenTable<RegExp> = { bang: /\+/, // rest }
https://jsfiddle.net/knvztxq7/ ^ that code will work, it outputs the key names to the console
https://jsfiddle.net/knvztxq7/1/ ^ that code... is also working even though I swear it hasn't worked for me before so now I think I'm actually just losing my mind.
Yeah wow I can't reproduce my error now... I'll come back here if I figure out what I did.