I spend a decent amount of time in the redux world so `redux-toolkit` is a great library to see how types are done *correctly* in a real codebase. To be clear, they do a fantastic job with types, but the level of complexity is pretty startling.
https://github.com/reduxjs/redux-toolkit/blob/master/package...
That is just one example but the codebase is riddled with complex types. Also, when you look around, note the amount of types vs actual code.
It's pretty common in style guides to never nest ternaries. In typescript, that's the only way to narrow types based on other types. It's a mess!
Strongly typed shell, whatever works core.
In that vein, if anyone here can tell me how the hell you functionally map over a typed object by key in typescript I'll be eternally grateful.
I always wanna do something like Object.keys(typedObjName).map(...) but that doesn't work.
It's so stupid and small and minor and it's never really prevented me from getting something done but it drives me out of my mind that I still can't find any clear documentation on how to do it.
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.
Is this a type space/value space thing? Like Object.keys(...) is always a string[] instead of Array<keyof TypedObj> like you might expect?